{"id":21452629,"url":"https://github.com/mserajnik/emote-server","last_synced_at":"2025-07-14T23:30:49.885Z","repository":{"id":40595242,"uuid":"507156709","full_name":"mserajnik/emote-server","owner":"mserajnik","description":"A simple application to list and serve emotes","archived":false,"fork":false,"pushed_at":"2023-10-16T22:37:54.000Z","size":280,"stargazers_count":3,"open_issues_count":0,"forks_count":1,"subscribers_count":1,"default_branch":"master","last_synced_at":"2023-10-17T09:51:51.453Z","etag":null,"topics":["emotes","server"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"agpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mserajnik.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2022-06-24T22:14:43.000Z","updated_at":"2023-06-22T15:42:25.000Z","dependencies_parsed_at":"2023-02-10T09:01:26.580Z","dependency_job_id":null,"html_url":"https://github.com/mserajnik/emote-server","commit_stats":null,"previous_names":[],"tags_count":7,"template":null,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mserajnik%2Femote-server","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mserajnik%2Femote-server/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mserajnik%2Femote-server/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mserajnik%2Femote-server/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mserajnik","download_url":"https://codeload.github.com/mserajnik/emote-server/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":226002754,"owners_count":17558149,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["emotes","server"],"created_at":"2024-11-23T04:31:04.529Z","updated_at":"2024-11-23T04:31:05.160Z","avatar_url":"https://github.com/mserajnik.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# emote-server [![GitHub Actions status][actions-status-badge]][actions-status]\n\n\u003e A simple application to list and serve emotes\n\nThis is a small application to serve emotes (or other files) over a simple HTTP\nAPI.\n\n## Table of contents\n\n+ [Install](#install)\n  + [Installing with Docker](#installing-with-docker)\n  + [Installing without Docker](#installing-without-docker)\n  + [Dependencies](#dependencies)\n    + [When using Docker](#when-using-docker)\n    + [When not using Docker](#when-not-using-docker)\n  + [Updating](#updating)\n    + [Updating with Docker](#updating-with-docker)\n    + [Updating without Docker](#updating-without-docker)\n    + [Updating to major version `2`](#updating-to-to-major-version-2)\n+ [Usage](#usage)\n  + [Running with Docker](#running-with-docker)\n  + [Running without Docker](#running-without-docker)\n  + [Configuration](#configuration)\n  + [API](#api)\n    + [General](#general)\n    + [Routes](#routes)\n      + [Base](#base)\n      + [Emotes](#emotes)\n        + [Adding emotes](#adding-emotes)\n        + [Deleting emotes](#deleting-emotes)\n        + [Listing emotes](#listing-emotes)\n        + [Getting emotes](#getting-emotes)\n        + [Getting frozen emotes](#getting-frozen-emotes)\n        + [Deleting frozen emotes](#deleting-frozen-emotes)\n+ [Maintainer](#maintainer)\n+ [Contribute](#contribute)\n+ [License](#license)\n\n## Install\n\nThe recommended way to run is via [Docker][docker]. Basic instructions on how\nto run without it are also provided.\n\n### Installing with Docker\n\nTo use this application with Docker, you can simply pull the prebuilt image:\n\n```zsh\nuser@local:~$ docker pull ghcr.io/mserajnik/emote-server\n```\n\nAlternatively, you can also build the image yourself. The user that is used\ninside the container has UID `1000` and GID `1000` by default. You can adjust\nthis (e.g., to match your host UID/GID) by providing the arguments `USER_ID`\nand `GROUP_ID` when making a build.\n\n### Installing without Docker\n\nTo install without Docker, you can simply clone the repository and install\ndependencies.\n\n```zsh\nuser@local:~$ git clone https://github.com/mserajnik/emote-server.git\nuser@local:~$ cd emote-server\nuser@local:emote-server$ npm i\n```\n\n### Dependencies\n\n#### When using Docker\n\nNone, besides [Docker][docker] itself.\n\n#### When not using Docker\n\n+ [Node.js][node-js]\n+ [GraphicsMagick][graphicsmagick] or [ImageMagick][imagemagick] (set\n  `EMOTE_SERVER_USE_IMAGE_MAGICK` accordingly)\n\nThis application should work with both the latest LTS and the latest stable\nversion of Node.js. If you encounter any issues with either of those versions\nwhen not using Docker, please [let me know][issues].\n\n### Updating\n\nThis application follows [semantic versioning][semantic-versioning] and any\nbreaking changes that require additional attention will be released under a new\nmajor version (e.g., `2.0.0`). Minor version updates (e.g., `1.1.0` or `1.2.0`)\nare therefore always safe to simply install.\n\nWhen necessary, this section will be expanded with upgrade guides for new major\nversions.\n\n#### Updating with Docker\n\nSimply pull the latest Docker image to update:\n\n```zsh\nuser@local:~$ docker pull ghcr.io/mserajnik/emote-server\n```\n\n#### Updating without Docker\n\nIf you chose not to use Docker, you can update via Git:\n\n```zsh\nuser@local:emote-server$ git pull\nuser@local:emote-server$ npm i\n```\n\n#### Updating to to major version `2`\n\nVersion `2.0.0` got rid of some (outdated) dependencies for frozen emote\ngeneration. Instead, [GraphicsMagick][graphicsmagick] or\n[ImageMagick][imagemagick] is used (to have a maintained and just overall\nbetter solution).\n\nIf you are using Docker there is nothing to do aside from pulling the latest\nimage (as GraphicsMagick is installed inside the container).\n\nIf you are not using Docker you have to install  either of those libraries. By\ndefault it is assumed that you are using GraphicsMagick; if you want to use\nImageMagick instead, you will have to set `EMOTE_SERVER_USE_IMAGE_MAGICK` to\n`true`.\n\n## Usage\n\n### Running with Docker\n\nTo make using Docker as easy as possible, a working\n[Docker Compose][docker-compose] example setup is provided. To get started with\nthis example setup, simply duplicate `docker-compose.yml.example` as\n`docker-compose.yml` and adjust the variables in the `environment` section as\ndescribed [here](#configuration).\n\nFinally, start the containers:\n\n```zsh\nuser@local:emote-server$ docker-compose up -d\n```\n\n### Running without Docker\n\nTo run without Docker, you will first need to duplicate the `.env.example` as\n`.env` and adjust the variables as described [here](#configuration).\n\nAfter that, you can start the application:\n\n```zsh\nuser@local:emote-server$ npm run start\n```\n\n### Configuration\n\nConfiguration is done entirely via environment variables. Please pay special\nattention to the instructions to prevent issues.\n\n+ `EMOTE_SERVER_DEBUG`: prints errors to stderr if set to `true`.\n+ `EMOTE_SERVER_USE_IMAGE_MAGICK=false`: instructs the server to use\n  ImageMagick instead of GraphicsMagick when set to `true`.\n+ `EMOTE_SERVER_PUBLIC_URL=http://localhost:8000`: the public URL the server\n  will use to display file URLs. __No trailings slashes.__\n+ `EMOTE_SERVER_PORT=8000`: the port the server is listening on.\n+ `EMOTE_SERVER_ACCESS_KEY=`: an arbitrary string used as access key for the\n  server's API. Can be of any (reasonable) length. If left empty, the\n  respective routes will be publicly accessible.\n+ `EMOTE_SERVER_NUMBER_OF_WORKERS=`: sets the number of workers. By default,\n  one worker per logical CPU core is used. You might want to decrease or\n  increase that number, depending on your needs/hardware. In general, the more\n  workers are running, the more requests can be handled simultaneously. But\n  note that increasing the number of workers beyond the number of logical CPUs\n  might be detrimental to performance or cause even more serious issues (e.g.,\n  crashes).\n+ `EMOTE_SERVER_SUPPORTED_FILE_EXTENSIONS=png,gif,apng`: sets the file\n  extensions for the files the server should serve. The extensions need to be\n  separated with `,`.\n+ `EMOTE_SERVER_FILE_SIZE_LIMIT=0`: sets the file size limit in bytes when\n  uploading files via the HTTP API. If set to `0`, there is no limit.\n+ `EMOTE_SERVER_EMOTES_PATH=./emotes`: the path emotes are served from. Can be\n  relative or absolute.\n+ `EMOTE_SERVER_FROZEN_EMOTES_PATH=./frozen-emotes`: the path frozen emotes are\n  served from. Can be relative or absolute.\n\n### API\n\n#### General\n\nRequest and response bodies are always in JSON format (except when uploading\nor downloading the actual files). The Authorization header in the format\n`Authorization: Bearer \u003cEMOTE_SERVER_ACCESS_KEY\u003e` or the query parameter\n`accessKey=\u003cEMOTE_SERVER_ACCESS_KEY\u003e` is used to authenticate for all routes\nunless `EMOTE_SERVER_ACCESS_KEY` is empty, in which case these routes will be\npublicly accessible as well.\n\nThe base route (`GET /`) does not require authentication, but can _optionally_\nbe used to verify access keys. To do this, simply pass an access key as you\nnormally would, in which case it will either respond normally when using a\nvalid access key or with an `InvalidAccessKey` error when using an invalid one.\n\nIn case of any occuring errors, the response will have the following format and\nan appropriate HTTP status code:\n\n```json5\n{\n  \"success\": false,\n  \"error\": \u003cerror name\u003e\n}\n```\n\n#### Routes\n\n##### Base\n\nResponds with the version number and the API version number of the\ninstallation. The API version number will increase by 1 every time an existing\nAPI endpoint is modified in a way it behaves differently than before or removed\naltogether.\n\n__Route:__ `GET /`\n\n__Response on success:__\n\n```json5\n{\n  \"emoteServer\": {\n    \"version\": \u003cversion number of the application\u003e,\n    \"apiVersion\": \u003cAPI version number of the application\u003e\n  }\n}\n```\n\n__Possible errors:__\n\n+ `InvalidAccessKey` (`401`): indicates a missing or wrong access key\n  (configured via `EMOTE_SERVER_ACCESS_KEY`)\n\n##### Emotes\n\n###### Adding emotes\n\nAdds a new emote. The request has to be of type `multipart-form-data` and the\nfile needs to have the key `emote`.\n\n__Route:__ `POST /emotes`\n\n__Response on success:__\n\n```json5\n{\n  \"success\": true\n}\n```\n\n__Possible errors:__\n\n+ `InvalidAccessKey` (`401`): indicates a missing or wrong access key\n  (configured via `EMOTE_SERVER_ACCESS_KEY`)\n+ `MissingFile` (`400`): indicates that the file is missing\n+ `UnsupportedFileExtension` (`400`): indicates that the file has an\n  unsupported file extension (configured via\n  `EMOTE_SERVER_SUPPORTED_FILE_EXTENSIONS`)\n+ `FileSizeLimitExceeded` (`400`): indicates that the file exceeds the file\n  size limit (configured via `EMOTE_SERVER_FILE_SIZE_LIMIT`)\n+ `FileExists` (`403`): indicates that a file of the same name already exists\n+ `IO` (`500`): indicates an I/O issue (e.g., missing write permissions)\n\n###### Deleting emotes\n\nDeletes the emote with the given filename. If the file does not exist, it will\nrespond with an error.\n\n__Route:__ `DELETE /emotes/\u003cemote filename\u003e`\n\n__Response on success:__\n\n```json5\n{\n  \"success\": true\n}\n```\n\n__Possible errors:__\n\n+ `InvalidAccessKey` (`401`): indicates a missing or wrong access key\n  (configured via `EMOTE_SERVER_ACCESS_KEY`)\n+ `FileNotFound` (`404`): indicates that the file does not exist\n+ `IO` (`500`): indicates an I/O issue (e.g., missing write permissions)\n\n###### Listing emotes\n\nResponds with the list of emotes available to be served.\n\n__Route:__ `GET /emotes`\n\n__Response on success:__\n\n```json5\n{\n  \"success\": true,\n  \"emotes\": [\n    {\n      \"name\": \u003cname of the emote\u003e,\n      \"url\": \u003cURL of the emote\u003e\n    }\n    // […]\n  ]\n}\n```\n\n__Possible errors:__\n\n+ `InvalidAccessKey` (`401`): indicates a missing or wrong access key\n  (configured via `EMOTE_SERVER_ACCESS_KEY`)\n+ `IO` (`500`): indicates an I/O issue (e.g., missing write permissions)\n\n###### Getting emotes\n\nResponds with the requested emote.\n\n__Route:__ `GET /emotes/\u003cemote filename\u003e`\n\n__Output on success:__ The requested emote\n\n__Possible errors:__\n\n+ `InvalidAccessKey` (`401`): indicates a missing or wrong access key\n  (configured via `EMOTE_SERVER_ACCESS_KEY`)\n+ `FileNotFound` (`404`): indicates that the file does not exist\n\n###### Getting frozen emotes\n\nResponds with a \"frozen\" PNG version (containing the first frame) of the\nrequested GIF or APNG emote.\n\n__Route:__ `GET /frozen-emotes/\u003cGIF/APNG emote filename\u003e`\n\n__Output on success:__ The requested frozen emote\n\n__Possible errors:__\n\n+ `InvalidAccessKey` (`401`): indicates a missing or wrong access key\n  (configured via `EMOTE_SERVER_ACCESS_KEY`)\n+ `FileNotFound` (`404`): indicates that the file does not exist\n+ `IO` (`500`): indicates an I/O issue (e.g., missing write permissions)\n\n###### Deleting frozen emotes\n\nDeletes all frozen emotes (without touching their source emotes). Useful for\nforcing regeneration (e.g., after overwriting an emote with another of the same\nname).\n\n__Route:__ `DELETE /frozen-emotes`\n\n__Response on success:__\n\n```json5\n{\n  \"success\": true\n}\n```\n\n__Possible errors:__\n\n+ `InvalidAccessKey` (`401`): indicates a missing or wrong access key\n  (configured via `EMOTE_SERVER_ACCESS_KEY`)\n+ `IO` (`500`): indicates an I/O issue (e.g., missing write permissions)\n\n## Maintainer\n\n[Michael Serajnik][maintainer]\n\n## Contribute\n\nYou are welcome to help out!\n\n[Open an issue][issues] or [make a pull request][pull-requests].\n\n## License\n\n[AGPLv3](LICENSE) © Michael Serajnik\n\n[actions-status]: https://github.com/mserajnik/emote-server/actions\n[actions-status-badge]: https://github.com/mserajnik/emote-server/actions/workflows/docker.yml/badge.svg\n[docker]: https://www.docker.com/\n[docker-compose]: https://docs.docker.com/compose/\n[graphicsmagick]: http://www.graphicsmagick.org/\n[imagemagick]: https://imagemagick.org/\n[issues]: https://github.com/mserajnik/emote-server/issues\n[maintainer]: https://github.com/mserajnik\n[node-js]: https://nodejs.org/en/\n[pull-requests]: https://github.com/mserajnik/emote-server/pulls\n[semantic-versioning]: https://semver.org/\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmserajnik%2Femote-server","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmserajnik%2Femote-server","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmserajnik%2Femote-server/lists"}