A simple application to list and serve emotes
This is a small application to serve emotes (or other files) over a simple HTTP API.
The recommended way to run is via Docker. Basic instructions on how to run without it are also provided.
To use this application with Docker, you can simply pull the prebuilt image:
user@local:~$ docker pull ghcr.io/mserajnik/emote-server
Alternatively, you can also build the image yourself. The user that is used
inside the container has UID 1000
and GID 1000
by default. You can adjust
this (e.g., to match your host UID/GID) by providing the arguments USER_ID
and GROUP_ID
when making a build.
To install without Docker, you can simply clone the repository and install dependencies.
user@local:~$ git clone https://github.com/mserajnik/emote-server.git
user@local:~$ cd emote-server
user@local:emote-server$ npm i
None, besides Docker itself.
- Node.js
- GraphicsMagick or ImageMagick (set
EMOTE_SERVER_USE_IMAGE_MAGICK
accordingly)
This application should work with both the latest LTS and the latest stable version of Node.js. If you encounter any issues with either of those versions when not using Docker, please let me know.
This application follows semantic versioning and any
breaking changes that require additional attention will be released under a new
major version (e.g., 2.0.0
). Minor version updates (e.g., 1.1.0
or 1.2.0
)
are therefore always safe to simply install.
When necessary, this section will be expanded with upgrade guides for new major versions.
Simply pull the latest Docker image to update:
user@local:~$ docker pull ghcr.io/mserajnik/emote-server
If you chose not to use Docker, you can update via Git:
user@local:emote-server$ git pull
user@local:emote-server$ npm i
Version 2.0.0
got rid of some (outdated) dependencies for frozen emote
generation. Instead, GraphicsMagick or
ImageMagick is used (to have a maintained and just overall
better solution).
If you are using Docker there is nothing to do aside from pulling the latest image (as GraphicsMagick is installed inside the container).
If you are not using Docker you have to install either of those libraries. By
default it is assumed that you are using GraphicsMagick; if you want to use
ImageMagick instead, you will have to set EMOTE_SERVER_USE_IMAGE_MAGICK
to
true
.
To make using Docker as easy as possible, a working
Docker Compose example setup is provided. To get started with
this example setup, simply duplicate docker-compose.yml.example
as
docker-compose.yml
and adjust the variables in the environment
section as
described here.
Finally, start the containers:
user@local:emote-server$ docker-compose up -d
To run without Docker, you will first need to duplicate the .env.example
as
.env
and adjust the variables as described here.
After that, you can start the application:
user@local:emote-server$ npm run start
Configuration is done entirely via environment variables. Please pay special attention to the instructions to prevent issues.
EMOTE_SERVER_DEBUG
: prints errors to stderr if set totrue
.EMOTE_SERVER_USE_IMAGE_MAGICK=false
: instructs the server to use ImageMagick instead of GraphicsMagick when set totrue
.EMOTE_SERVER_PUBLIC_URL=http://localhost:8000
: the public URL the server will use to display file URLs. No trailings slashes.EMOTE_SERVER_PORT=8000
: the port the server is listening on.EMOTE_SERVER_ACCESS_KEY=
: an arbitrary string used as access key for the server's API. Can be of any (reasonable) length. If left empty, the respective routes will be publicly accessible.EMOTE_SERVER_NUMBER_OF_WORKERS=
: sets the number of workers. By default, one worker per logical CPU core is used. You might want to decrease or increase that number, depending on your needs/hardware. In general, the more workers are running, the more requests can be handled simultaneously. But note that increasing the number of workers beyond the number of logical CPUs might be detrimental to performance or cause even more serious issues (e.g., crashes).EMOTE_SERVER_SUPPORTED_FILE_EXTENSIONS=png,gif,apng
: sets the file extensions for the files the server should serve. The extensions need to be separated with,
.EMOTE_SERVER_FILE_SIZE_LIMIT=0
: sets the file size limit in bytes when uploading files via the HTTP API. If set to0
, there is no limit.EMOTE_SERVER_EMOTES_PATH=./emotes
: the path emotes are served from. Can be relative or absolute.EMOTE_SERVER_FROZEN_EMOTES_PATH=./frozen-emotes
: the path frozen emotes are served from. Can be relative or absolute.
Request and response bodies are always in JSON format (except when uploading
or downloading the actual files). The Authorization header in the format
Authorization: Bearer <EMOTE_SERVER_ACCESS_KEY>
or the query parameter
accessKey=<EMOTE_SERVER_ACCESS_KEY>
is used to authenticate for all routes
unless EMOTE_SERVER_ACCESS_KEY
is empty, in which case these routes will be
publicly accessible as well.
The base route (GET /
) does not require authentication, but can optionally
be used to verify access keys. To do this, simply pass an access key as you
normally would, in which case it will either respond normally when using a
valid access key or with an InvalidAccessKey
error when using an invalid one.
In case of any occuring errors, the response will have the following format and an appropriate HTTP status code:
{
"success": false,
"error": <error name>
}
Responds with the version number and the API version number of the installation. The API version number will increase by 1 every time an existing API endpoint is modified in a way it behaves differently than before or removed altogether.
Route: GET /
Response on success:
{
"emoteServer": {
"version": <version number of the application>,
"apiVersion": <API version number of the application>
}
}
Possible errors:
InvalidAccessKey
(401
): indicates a missing or wrong access key (configured viaEMOTE_SERVER_ACCESS_KEY
)
Adds a new emote. The request has to be of type multipart-form-data
and the
file needs to have the key emote
.
Route: POST /emotes
Response on success:
{
"success": true
}
Possible errors:
InvalidAccessKey
(401
): indicates a missing or wrong access key (configured viaEMOTE_SERVER_ACCESS_KEY
)MissingFile
(400
): indicates that the file is missingUnsupportedFileExtension
(400
): indicates that the file has an unsupported file extension (configured viaEMOTE_SERVER_SUPPORTED_FILE_EXTENSIONS
)FileSizeLimitExceeded
(400
): indicates that the file exceeds the file size limit (configured viaEMOTE_SERVER_FILE_SIZE_LIMIT
)FileExists
(403
): indicates that a file of the same name already existsIO
(500
): indicates an I/O issue (e.g., missing write permissions)
Deletes the emote with the given filename. If the file does not exist, it will respond with an error.
Route: DELETE /emotes/<emote filename>
Response on success:
{
"success": true
}
Possible errors:
InvalidAccessKey
(401
): indicates a missing or wrong access key (configured viaEMOTE_SERVER_ACCESS_KEY
)FileNotFound
(404
): indicates that the file does not existIO
(500
): indicates an I/O issue (e.g., missing write permissions)
Responds with the list of emotes available to be served.
Route: GET /emotes
Response on success:
{
"success": true,
"emotes": [
{
"name": <name of the emote>,
"url": <URL of the emote>
}
// […]
]
}
Possible errors:
InvalidAccessKey
(401
): indicates a missing or wrong access key (configured viaEMOTE_SERVER_ACCESS_KEY
)IO
(500
): indicates an I/O issue (e.g., missing write permissions)
Responds with the requested emote.
Route: GET /emotes/<emote filename>
Output on success: The requested emote
Possible errors:
InvalidAccessKey
(401
): indicates a missing or wrong access key (configured viaEMOTE_SERVER_ACCESS_KEY
)FileNotFound
(404
): indicates that the file does not exist
Responds with a "frozen" PNG version (containing the first frame) of the requested GIF or APNG emote.
Route: GET /frozen-emotes/<GIF/APNG emote filename>
Output on success: The requested frozen emote
Possible errors:
InvalidAccessKey
(401
): indicates a missing or wrong access key (configured viaEMOTE_SERVER_ACCESS_KEY
)FileNotFound
(404
): indicates that the file does not existIO
(500
): indicates an I/O issue (e.g., missing write permissions)
Deletes all frozen emotes (without touching their source emotes). Useful for forcing regeneration (e.g., after overwriting an emote with another of the same name).
Route: DELETE /frozen-emotes
Response on success:
{
"success": true
}
Possible errors:
InvalidAccessKey
(401
): indicates a missing or wrong access key (configured viaEMOTE_SERVER_ACCESS_KEY
)IO
(500
): indicates an I/O issue (e.g., missing write permissions)
You are welcome to help out!
Open an issue or make a pull request.
AGPLv3 © Michael Serajnik