diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..a865156 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,6 @@ +npm-debug.log +node_modules +*.swp +*.swo +data +*.DS_Store diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..dd53efc --- /dev/null +++ b/Dockerfile @@ -0,0 +1,63 @@ +FROM node:14.8.0-stretch + +RUN mkdir -p /usr/src/app && \ + chown node:node /usr/src/app + +USER node:node + +WORKDIR /usr/src/app + +COPY --chown=node:node . . + +RUN npm install && \ + npm install redis@0.8.1 && \ + npm install pg@4.1.1 && \ + npm install memcached@2.2.2 && \ + npm install aws-sdk@2.738.0 && \ + npm install rethinkdbdash@2.3.31 + +ENV STORAGE_TYPE=memcached \ + STORAGE_HOST=127.0.0.1 \ + STORAGE_PORT=11211\ + STORAGE_EXPIRE_SECONDS=2592000\ + STORAGE_DB=2 \ + STORAGE_AWS_BUCKET= \ + STORAGE_AWS_REGION= \ + STORAGE_USENAMER= \ + STORAGE_PASSWORD= \ + STORAGE_FILEPATH= + +ENV LOGGING_LEVEL=verbose \ + LOGGING_TYPE=Console \ + LOGGING_COLORIZE=true + +ENV HOST=0.0.0.0\ + PORT=7777\ + KEY_LENGTH=10\ + MAX_LENGTH=400000\ + STATIC_MAX_AGE=86400\ + RECOMPRESS_STATIC_ASSETS=true + +ENV KEYGENERATOR_TYPE=phonetic \ + KEYGENERATOR_KEYSPACE= + +ENV RATELIMITS_NORMAL_TOTAL_REQUESTS=500\ + RATELIMITS_NORMAL_EVERY_MILLISECONDS=60000 \ + RATELIMITS_WHITELIST_TOTAL_REQUESTS= \ + RATELIMITS_WHITELIST_EVERY_MILLISECONDS= \ + # comma separated list for the whitelisted \ + RATELIMITS_WHITELIST=example1.whitelist,example2.whitelist \ + \ + RATELIMITS_BLACKLIST_TOTAL_REQUESTS= \ + RATELIMITS_BLACKLIST_EVERY_MILLISECONDS= \ + # comma separated list for the blacklisted \ + RATELIMITS_BLACKLIST=example1.blacklist,example2.blacklist +ENV DOCUMENTS=about=./about.md + +EXPOSE ${PORT} +STOPSIGNAL SIGINT +ENTRYPOINT [ "bash", "docker-entrypoint.sh" ] + +HEALTHCHECK --interval=30s --timeout=30s --start-period=5s \ + --retries=3 CMD [ "curl" , "-f" "localhost:${PORT}", "||", "exit", "1"] +CMD ["npm", "start"] diff --git a/README.md b/README.md index 23ca077..1cb06f7 100644 --- a/README.md +++ b/README.md @@ -236,6 +236,88 @@ your bucket: } ``` +## Docker + +### Build image + +```bash +docker build --tag haste-server . +``` + +### Run container + +For this example we will run haste-server, and connect it to a redis server + +```bash +docker run --name haste-server-container --env STORAGE_TYPE=redis --env STORAGE_HOST=redis-server --env STORAGE_PORT=6379 haste-server +``` + +### Use docker-compose example + +There is an example `docker-compose.yml` which runs haste-server together with memcached + +```bash +docker-compose up +``` + +### Configuration + +The docker image is configured using environmental variables as you can see in the example above. + +Here is a list of all the environment variables + +### Storage + +| Name | Default value | Description | +| :--------------------: | :-----------: | :-----------------------------------------------------------------------------------------------------------: | +| STORAGE_TYPE | memcached | Type of storage . Accepted values: "memcached","redis","postgres","rethinkdb", "amazon-s3", and "file" | +| STORAGE_HOST | 127.0.0.1 | Storage host. Applicable for types: memcached, redis, postgres, and rethinkdb | +| STORAGE_PORT | 11211 | Port on the storage host. Applicable for types: memcached, redis, postgres, and rethinkdb | +| STORAGE_EXPIRE_SECONDS | 2592000 | Number of seconds to expire keys in. Applicable for types. Redis, postgres, memcached. `expire` option to the | +| STORAGE_DB | 2 | The name of the database. Applicable for redis, postgres, and rethinkdb | +| STORAGE_PASSWORD | | Password for database. Applicable for redis, postges, rethinkdb . | +| STORAGE_USERNAME | | Database username. Applicable for postgres, and rethinkdb | +| STORAGE_AWS_BUCKET | | Applicable for amazon-s3. This is the name of the S3 bucket | +| STORAGE_AWS_REGION | | Applicable for amazon-s3. The region in which the bucket is located | +| STORAGE_FILEPATH | | Path to file to save data to. Applicable for type file | + +### Logging + +| Name | Default value | Description | +| :---------------: | :-----------: | :---------: | +| LOGGING_LEVEL | verbose | | +| LOGGING_TYPE= | Console | +| LOGGING_COLORIZE= | true | + +### Basics + +| Name | Default value | Description | +| :----------------------: | :--------------: | :---------------------------------------------------------------------------------------: | +| HOST | 0.0.0.0 | The hostname which the server answers on | +| PORT | 7777 | The port on which the server is running | +| KEY_LENGTH | 10 | the length of the keys to user | +| MAX_LENGTH | 400000 | maximum length of a paste | +| STATIC_MAX_AGE | 86400 | max age for static assets | +| RECOMPRESS_STATIC_ASSETS | true | whether or not to compile static js assets | +| KEYGENERATOR_TYPE | phonetic | Type of key generator. Acceptable values: "phonetic", or "random" | +| KEYGENERATOR_KEYSPACE | | keySpace argument is a string of acceptable characters | +| DOCUMENTS | about=./about.md | Comma separated list of static documents to serve. ex: \n about=./about.md,home=./home.md | + +### Rate limits + +| Name | Default value | Description | +| :----------------------------------: | :-----------------------------------: | :--------------------------------------------------------------------------------------: | +| RATELIMITS_NORMAL_TOTAL_REQUESTS | 500 | By default anyone uncategorized will be subject to 500 requests in the defined timespan. | +| RATELIMITS_NORMAL_EVERY_MILLISECONDS | 60000 | The timespan to allow the total requests for uncategorized users | +| RATELIMITS_WHITELIST_TOTAL_REQUESTS | | By default client names in the whitelist will not have their requests limited. | +| RATELIMITS_WHITELIST_EVERY_SECONDS | | By default client names in the whitelist will not have their requests limited. | +| RATELIMITS_WHITELIST | example1.whitelist,example2.whitelist | Comma separated list of the clients which are in the whitelist pool | +| RATELIMITS_BLACKLIST_TOTAL_REQUESTS | | By default client names in the blacklist will be subject to 0 requests per hours. | +| RATELIMITS_BLACKLIST_EVERY_SECONDS | | By default client names in the blacklist will be subject to 0 requests per hours | +| RATELIMITS_BLACKLIST | example1.blacklist,example2.blacklist | Comma separated list of the clients which are in the blacklistpool. | + + + ## Author John Crepezzi diff --git a/docker-compose.yaml b/docker-compose.yaml new file mode 100644 index 0000000..8365c5d --- /dev/null +++ b/docker-compose.yaml @@ -0,0 +1,19 @@ +version: '3.0' +services: + haste-server: + build: . + networks: + - db-network + environment: + - STORAGE_TYPE=memcached + - STORAGE_HOST=memcached + - STORAGE_PORT=11211 + ports: + - 7777:7777 + memcached: + image: memcached:latest + networks: + - db-network + +networks: + db-network: diff --git a/docker-entrypoint.js b/docker-entrypoint.js new file mode 100644 index 0000000..5afff14 --- /dev/null +++ b/docker-entrypoint.js @@ -0,0 +1,108 @@ +const { + HOST, + PORT, + KEY_LENGTH, + MAX_LENGTH, + STATIC_MAX_AGE, + RECOMPRESS_STATIC_ASSETS, + STORAGE_TYPE, + STORAGE_HOST, + STORAGE_PORT, + STORAGE_EXPIRE_SECONDS, + STORAGE_DB, + STORAGE_AWS_BUCKET, + STORAGE_AWS_REGION, + STORAGE_PASSWORD, + STORAGE_USERNAME, + STORAGE_FILEPATH, + LOGGING_LEVEL, + LOGGING_TYPE, + LOGGING_COLORIZE, + KEYGENERATOR_TYPE, + KEY_GENERATOR_KEYSPACE, + RATE_LIMITS_NORMAL_TOTAL_REQUESTS, + RATE_LIMITS_NORMAL_EVERY_MILLISECONDS, + RATE_LIMITS_WHITELIST_TOTAL_REQUESTS, + RATE_LIMITS_WHITELIST_EVERY_MILLISECONDS, + RATE_LIMITS_WHITELIST, + RATE_LIMITS_BLACKLIST_TOTAL_REQUESTS, + RATE_LIMITS_BLACKLIST_EVERY_MILLISECONDS, + RATE_LIMITS_BLACKLIST, + DOCUMENTS, +} = process.env; + +const config = { + host: HOST, + port: PORT, + + keyLength: KEY_LENGTH, + + maxLength: MAX_LENGTH, + + staticMaxAge: STATIC_MAX_AGE, + + recompressStaticAssets: RECOMPRESS_STATIC_ASSETS, + + logging: [ + { + level: LOGGING_LEVEL, + type: LOGGING_TYPE, + colorize: LOGGING_COLORIZE, + }, + ], + + keyGenerator: { + type: KEYGENERATOR_TYPE, + keyspace: KEY_GENERATOR_KEYSPACE, + }, + + rateLimits: { + whitelist: RATE_LIMITS_WHITELIST ? RATE_LIMITS_WHITELIST.split(",") : [], + blacklist: RATE_LIMITS_BLACKLIST ? RATE_LIMITS_BLACKLIST.split(",") : [], + categories: { + normal: { + totalRequests: RATE_LIMITS_NORMAL_TOTAL_REQUESTS, + every: RATE_LIMITS_NORMAL_EVERY_MILLISECONDS, + }, + whitelist: + RATE_LIMITS_WHITELIST_EVERY_MILLISECONDS || + RATE_LIMITS_WHITELIST_TOTAL_REQUESTS + ? { + totalRequests: RATE_LIMITS_WHITELIST_TOTAL_REQUESTS, + every: RATE_LIMITS_WHITELIST_EVERY_MILLISECONDS, + } + : null, + blacklist: + RATE_LIMITS_BLACKLIST_EVERY_MILLISECONDS || + RATE_LIMITS_BLACKLIST_TOTAL_REQUESTS + ? { + totalRequests: RATE_LIMITS_WHITELIST_TOTAL_REQUESTS, + every: RATE_LIMITS_BLACKLIST_EVERY_MILLISECONDS, + } + : null, + }, + }, + + storage: { + type: STORAGE_TYPE, + host: STORAGE_HOST, + port: STORAGE_PORT, + expire: STORAGE_EXPIRE_SECONDS, + bucket: STORAGE_AWS_BUCKET, + region: STORAGE_AWS_REGION, + connectionUrl: `postgres://${STORAGE_USERNAME}:${STORAGE_PASSWORD}@${STORAGE_HOST}:${STORAGE_PORT}/${STORAGE_DB}`, + db: STORAGE_DB, + user: STORAGE_USERNAME, + password: STORAGE_PASSWORD, + path: STORAGE_FILEPATH, + }, + + documents: DOCUMENTS + ? DOCUMENTS.split(",").reduce((acc, item) => { + const keyAndValueArray = item.replace(/\s/g, "").split("="); + return { ...acc, [keyAndValueArray[0]]: keyAndValueArray[1] }; + }, {}) + : null, +}; + +console.log(JSON.stringify(config)); diff --git a/docker-entrypoint.sh b/docker-entrypoint.sh new file mode 100644 index 0000000..0b089d8 --- /dev/null +++ b/docker-entrypoint.sh @@ -0,0 +1,9 @@ +#!/bin/bash + +# We use this file to translate environmental variables to .env files used by the application + +set -e + +node ./docker-entrypoint.js > ./config.js + +exec "$@"