From 666564867edb7ac444baf27106889e256fb02da3 Mon Sep 17 00:00:00 2001 From: Niek Candaele Date: Thu, 21 Mar 2019 20:09:34 +0100 Subject: [PATCH] Clean up & some internals (#6) * Update Travis CI configuration (#5) * Fix YML indentation * Add webhook notification on failure * Infra for generating API documentation * HTTP API doc generation * Linting * Expand travis config --- .gitignore | 2 + .travis.yml | 38 ++++- apidoc.json | 6 + app.js | 16 +-- bin/www | 1 - package-lock.json | 302 +++++++++++++++++++++++++++++++++++++++ package.json | 5 +- routes/ban/getBanById.js | 108 ++++++++++++++ routes/ban/postBan.js | 154 ++++++++++++++++++++ routes/ban/searchBans.js | 135 +++++++++++++++++ routes/bans.js | 140 ------------------ routes/index.js | 28 ++++ 12 files changed, 778 insertions(+), 157 deletions(-) create mode 100644 apidoc.json create mode 100644 routes/ban/getBanById.js create mode 100644 routes/ban/postBan.js create mode 100644 routes/ban/searchBans.js delete mode 100644 routes/bans.js create mode 100644 routes/index.js diff --git a/.gitignore b/.gitignore index d1bed12..5fe7ec9 100644 --- a/.gitignore +++ b/.gitignore @@ -59,3 +59,5 @@ typings/ # next.js build output .next + +docs/ \ No newline at end of file diff --git a/.travis.yml b/.travis.yml index 1dcc6a8..8523d4a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,9 +7,39 @@ services: before_install: - mysql -e 'CREATE DATABASE gbl_development;' install: - - npm install + - npm install script: - - npm run lint - - npm run db:init - - npm test + - npm run lint + - npm run db:init + - npm test +after_success: + - npm pack + - npm run docs:api + +after_failure: + - wget https://raw.githubusercontent.com/DiscordHooks/travis-ci-discord-webhook/master/send.sh + - chmod +x send.sh + - "./send.sh failure $WEBHOOK_URL" + +deploy: + - provider: releases + api_key: + secure: Ap+NxH5ncMku3xn9U5vmgMLlsSv4Njp0nFyAuKVH8Vr7YAQWb9VsNceH3XyOebaNZaU8mPZyorXCznH/T0rUMf/5VIVc4mQ+NtdXczNORLa3Mro0Z5XQdAW5y3Z3x6amg8V2n8OQolkO5cwm1lxiIVAKlbH+tekhpgExIm5mF7kqjwMDVuRyFkKrEMaNgzB3cDfSPP4YAh3Wl6Kbs9kZZZKn2zcPQU2fAT5wUV/4qdLwfE5qrJAeSEUHh6v5Ro8AF0zcu2XvdPK+ykMDSirtOVHGejJZWrRRvXw69EVuNdHGBWPdg8B3sujw0G6m9J9liyFbV/C1B1DtQ/1kIJn2cjZ9VpZjkjk9FyVFxs/YGx7O1lTCiVFMOEfNudKpe7XFqu11z2FVsc2GqTbiblgUs4F6YpwiLQuHefg+xav12Ikz98fAVZj3s15tEr/21smGp5s2m2IsuiPikOmZ591c9TTPV9KO6mNTM0AwyK7ZoDC6t2MqMkSCO+7Oe0FBG5bYq6O4Vauc8iEf4FCS1qHhQkndolO4aLsdb5jasphLVmU4qcxsUtaDrAemQmvlr0LMJrjDsBAuAOxha9A/bLxcppoqQYNrYvzsCsvXelVAbFJpfmKjbDSI11eeKBJ4/2ZhFVFEhbXYgnW+5EA5mJdIlOw1RAOH/SXU9tGJgD3yz24= + file_glob: true + file: global-ban-list-*.tgz + skip_cleanup: true + on: + tags: true + repo: CatalysmsServerManager/Global-ban-list + + - provider: pages + skip_cleanup: true + keep-history: true + local_dir: docs/api + github_token: + secure: Ap+NxH5ncMku3xn9U5vmgMLlsSv4Njp0nFyAuKVH8Vr7YAQWb9VsNceH3XyOebaNZaU8mPZyorXCznH/T0rUMf/5VIVc4mQ+NtdXczNORLa3Mro0Z5XQdAW5y3Z3x6amg8V2n8OQolkO5cwm1lxiIVAKlbH+tekhpgExIm5mF7kqjwMDVuRyFkKrEMaNgzB3cDfSPP4YAh3Wl6Kbs9kZZZKn2zcPQU2fAT5wUV/4qdLwfE5qrJAeSEUHh6v5Ro8AF0zcu2XvdPK+ykMDSirtOVHGejJZWrRRvXw69EVuNdHGBWPdg8B3sujw0G6m9J9liyFbV/C1B1DtQ/1kIJn2cjZ9VpZjkjk9FyVFxs/YGx7O1lTCiVFMOEfNudKpe7XFqu11z2FVsc2GqTbiblgUs4F6YpwiLQuHefg+xav12Ikz98fAVZj3s15tEr/21smGp5s2m2IsuiPikOmZ591c9TTPV9KO6mNTM0AwyK7ZoDC6t2MqMkSCO+7Oe0FBG5bYq6O4Vauc8iEf4FCS1qHhQkndolO4aLsdb5jasphLVmU4qcxsUtaDrAemQmvlr0LMJrjDsBAuAOxha9A/bLxcppoqQYNrYvzsCsvXelVAbFJpfmKjbDSI11eeKBJ4/2ZhFVFEhbXYgnW+5EA5mJdIlOw1RAOH/SXU9tGJgD3yz24= + on: + all_branches: true + tags: true + repo: CatalysmsServerManager/Global-ban-list \ No newline at end of file diff --git a/apidoc.json b/apidoc.json new file mode 100644 index 0000000..032e249 --- /dev/null +++ b/apidoc.json @@ -0,0 +1,6 @@ +{ + "name": "Global ban list", + "description": "API documentation for the global ban list", + "title": "GBL API", + "url" : "https://api.github.com/v1" + } \ No newline at end of file diff --git a/app.js b/app.js index 1cfa448..9965e96 100644 --- a/app.js +++ b/app.js @@ -7,6 +7,8 @@ const path = require('path'); const cookieParser = require('cookie-parser'); const logger = require('morgan'); const fs = require('fs'); +const bodyParser = require('body-parser'); + const app = express(); @@ -37,11 +39,6 @@ app.models.sequelize.sync().then(() => { }); }); - -const usersRouter = require('./routes/users')(app); -const bansRouter = require('./routes/bans')(app); -const pagesRouter = require('./routes/pages')(app); - // view engine setup app.set('view engine', 'pug'); app.set('views', path.join(__dirname, 'views')); @@ -54,16 +51,14 @@ app.use(express.json()); app.use(express.urlencoded({ extended: false, })); +app.use(bodyParser.json()); app.use(cookieParser()); app.use(express.static(path.join(__dirname, 'public'))); -app.use('/', pagesRouter); -app.use('/user', usersRouter); -app.use('/ban', bansRouter); - +require('./routes')(app); // catch 404 and forward to error handler app.use((req, res, next) => { - next(createError(404)); + next(createError(404, 'This page does not exist.')); }); // error handler @@ -78,7 +73,6 @@ app.use((err, req, res) => { res.render('error'); }); - // Register helper functions app.helpers = {}; fs diff --git a/bin/www b/bin/www index 591526e..76c90ba 100644 --- a/bin/www +++ b/bin/www @@ -5,7 +5,6 @@ */ var app = require('../app'); -var debug = require('debug')('global-ban-list:server'); var http = require('http'); diff --git a/package-lock.json b/package-lock.json index eef80b1..05f08da 100644 --- a/package-lock.json +++ b/package-lock.json @@ -136,6 +136,62 @@ "resolved": "https://registry.npmjs.org/ansicolors/-/ansicolors-0.3.2.tgz", "integrity": "sha1-ZlWX3oap/+Oqm/vmyuXG6kJrSXk=" }, + "apidoc": { + "version": "0.17.7", + "resolved": "https://registry.npmjs.org/apidoc/-/apidoc-0.17.7.tgz", + "integrity": "sha512-9Wf4bRPwCuWOIOxR42dDnsXnFw+rhJg5VrMQK+KmNxJwyIh30UqX6gvjjXSG6YO74MqE87F18bbQXUENK9dPGg==", + "dev": true, + "requires": { + "apidoc-core": "~0.8.2", + "commander": "^2.19.0", + "fs-extra": "^7.0.0", + "lodash": "^4.17.10", + "markdown-it": "^8.3.1", + "winston": "^3.0.0" + } + }, + "apidoc-core": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/apidoc-core/-/apidoc-core-0.8.3.tgz", + "integrity": "sha1-2dY1RYKd8lDSzKBJaDqH53U2S5Y=", + "dev": true, + "requires": { + "fs-extra": "^3.0.1", + "glob": "^7.1.1", + "iconv-lite": "^0.4.17", + "klaw-sync": "^2.1.0", + "lodash": "~4.17.4", + "semver": "~5.3.0" + }, + "dependencies": { + "fs-extra": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-3.0.1.tgz", + "integrity": "sha1-N5TzeMWLNC6n27sjCVEJxLO2IpE=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "jsonfile": "^3.0.0", + "universalify": "^0.1.0" + } + }, + "jsonfile": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-3.0.1.tgz", + "integrity": "sha1-pezG9l9T9mLEQVx2daAzHQmS7GY=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.6" + } + }, + "semver": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.3.0.tgz", + "integrity": "sha1-myzl094C0XxgEq0yaqa00M9U+U8=", + "dev": true + } + } + }, "argparse": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", @@ -476,6 +532,16 @@ "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", "dev": true }, + "color": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/color/-/color-3.0.0.tgz", + "integrity": "sha512-jCpd5+s0s0t7p3pHQKpnJ0TpQKKdleP71LWcA0aqiljpiuAkOSUFN/dyH8ZwF0hRmFlrIuRhufds1QyEP9EB+w==", + "dev": true, + "requires": { + "color-convert": "^1.9.1", + "color-string": "^1.5.2" + } + }, "color-convert": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", @@ -491,6 +557,38 @@ "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", "dev": true }, + "color-string": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.5.3.tgz", + "integrity": "sha512-dC2C5qeWoYkxki5UAXapdjqO672AM4vZuPGRQfO8b5HKuKGBbKWpITyDYN7TOFKvRW7kOgAn3746clDBMDJyQw==", + "dev": true, + "requires": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } + }, + "colornames": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/colornames/-/colornames-1.1.1.tgz", + "integrity": "sha1-+IiQMGhcfE/54qVZ9Qd+t2qBb5Y=", + "dev": true + }, + "colors": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.3.3.tgz", + "integrity": "sha512-mmGt/1pZqYRjMxB1axhTo16/snVZ5krrKkcmMeVKxzECMMXoCgnvTPp10QgHfcbQZw8Dq2jMNG6je4JlWU0gWg==", + "dev": true + }, + "colorspace": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/colorspace/-/colorspace-1.1.1.tgz", + "integrity": "sha512-pI3btWyiuz7Ken0BWh9Elzsmv2bM9AhA7psXib4anUXy/orfZ/E0MbQwhSOG/9L8hLlalqrU0UhOuqxW1YjmVw==", + "dev": true, + "requires": { + "color": "3.0.x", + "text-hex": "1.0.x" + } + }, "combined-stream": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.7.tgz", @@ -512,6 +610,12 @@ "typical": "^2.6.1" } }, + "commander": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.19.0.tgz", + "integrity": "sha512-6tvAOO+D6OENvRAh524Dh9jcfKTYDQAqvqezbCW82xj5X0pSrcpxtvRKHLG0yBY6SD7PSDrJaj+0AiOcKVd1Xg==", + "dev": true + }, "component-emitter": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", @@ -702,6 +806,17 @@ "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" }, + "diagnostics": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/diagnostics/-/diagnostics-1.1.1.tgz", + "integrity": "sha512-8wn1PmdunLJ9Tqbx+Fx/ZEuHfJf4NKSN2ZBj7SJC/OWRWha843+WsTjqMe1B5E3p28jqBlp+mJ2fPVxPyNgYKQ==", + "dev": true, + "requires": { + "colorspace": "1.1.x", + "enabled": "1.0.x", + "kuler": "1.0.x" + } + }, "diff": { "version": "3.5.0", "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", @@ -778,11 +893,32 @@ "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" }, + "enabled": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/enabled/-/enabled-1.0.2.tgz", + "integrity": "sha1-ll9lE9LC0cX0ZStkouM5ZGf8L5M=", + "dev": true, + "requires": { + "env-variable": "0.0.x" + } + }, "encodeurl": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" }, + "entities": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz", + "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==", + "dev": true + }, + "env-variable": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/env-variable/-/env-variable-0.0.5.tgz", + "integrity": "sha512-zoB603vQReOFvTg5xMl9I1P2PnHsHQQKTEowsKKD7nseUfJq6UWzK+4YtlWUO1nhiQUxe6XMkk+JleSZD1NZFA==", + "dev": true + }, "error-ex": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", @@ -1198,6 +1334,18 @@ "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", "dev": true }, + "fast-safe-stringify": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.0.6.tgz", + "integrity": "sha512-q8BZ89jjc+mz08rSxROs8VsrBBcn1SIw1kq9NjolL509tkABRk9io01RAjSaEv1Xb2uFLt8VtRiZbGp5H8iDtg==", + "dev": true + }, + "fecha": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fecha/-/fecha-2.3.3.tgz", + "integrity": "sha512-lUGBnIamTAwk4znq5BcqsDaxSmZ9nDVJaij6NvRt/Tg4R69gERA+otPKbS86ROw9nxVMw2/mp1fnaiWqbs6Sdg==", + "dev": true + }, "figures": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", @@ -1810,6 +1958,24 @@ "is-buffer": "^1.1.5" } }, + "klaw-sync": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/klaw-sync/-/klaw-sync-2.1.0.tgz", + "integrity": "sha1-PTvNhgDnv971MjHHOf8FOu1WDkQ=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.11" + } + }, + "kuler": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/kuler/-/kuler-1.0.1.tgz", + "integrity": "sha512-J9nVUucG1p/skKul6DU3PUZrhs0LPulNaeUOox0IyXDi8S4CztTHs1gQphhuZmzXG7VOQSf6NJfKuzteQLv9gQ==", + "dev": true, + "requires": { + "colornames": "^1.1.1" + } + }, "lazy-cache": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz", @@ -1834,6 +2000,15 @@ "type-check": "~0.3.2" } }, + "linkify-it": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-2.1.0.tgz", + "integrity": "sha512-4REs8/062kV2DSHxNfq5183zrqXMl7WP0WzABH9IeJI+NLm429FgE1PDecltYfnOoFDFlZGh2T8PfZn0r+GTRg==", + "dev": true, + "requires": { + "uc.micro": "^1.0.1" + } + }, "load-json-file": { "version": "2.0.0", "resolved": "http://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", @@ -1867,6 +2042,27 @@ "integrity": "sha1-soqmKIorn8ZRA1x3EfZathkDMaY=", "dev": true }, + "logform": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/logform/-/logform-2.1.2.tgz", + "integrity": "sha512-+lZh4OpERDBLqjiwDLpAWNQu6KMjnlXH2ByZwCuSqVPJletw0kTWJf5CgSNAUKn1KUkv3m2cUz/LK8zyEy7wzQ==", + "dev": true, + "requires": { + "colors": "^1.2.1", + "fast-safe-stringify": "^2.0.4", + "fecha": "^2.3.3", + "ms": "^2.1.1", + "triple-beam": "^1.3.0" + }, + "dependencies": { + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", + "dev": true + } + } + }, "long": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", @@ -1909,6 +2105,25 @@ "p-defer": "^1.0.0" } }, + "markdown-it": { + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-8.4.2.tgz", + "integrity": "sha512-GcRz3AWTqSUphY3vsUqQSFMbgR38a4Lh3GWlHRh/7MRwz8mcu9n2IO7HOh+bXHrR9kOPDl5RNCaEsrneb+xhHQ==", + "dev": true, + "requires": { + "argparse": "^1.0.7", + "entities": "~1.1.1", + "linkify-it": "^2.0.0", + "mdurl": "^1.0.1", + "uc.micro": "^1.0.5" + } + }, + "mdurl": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz", + "integrity": "sha1-/oWy7HWlkDfyrf7BAP1sYBdhFS4=", + "dev": true + }, "media-typer": { "version": "0.3.0", "resolved": "http://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", @@ -2231,6 +2446,12 @@ "wrappy": "1" } }, + "one-time": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/one-time/-/one-time-0.0.4.tgz", + "integrity": "sha1-+M33eISCb+Tf+T46nMN7HkSAdC4=", + "dev": true + }, "onetime": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", @@ -3245,6 +3466,23 @@ "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", "dev": true }, + "simple-swizzle": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", + "integrity": "sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo=", + "dev": true, + "requires": { + "is-arrayish": "^0.3.1" + }, + "dependencies": { + "is-arrayish": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", + "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==", + "dev": true + } + } + }, "slice-ansi": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-1.0.0.tgz", @@ -3313,6 +3551,12 @@ "tweetnacl": "~0.14.0" } }, + "stack-trace": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", + "integrity": "sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA=", + "dev": true + }, "statuses": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz", @@ -3460,6 +3704,12 @@ "typical": "^2.6.1" } }, + "text-hex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz", + "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==", + "dev": true + }, "text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", @@ -3517,6 +3767,12 @@ } } }, + "triple-beam": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.3.0.tgz", + "integrity": "sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw==", + "dev": true + }, "tslib": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.3.tgz", @@ -3566,6 +3822,12 @@ "integrity": "sha1-XAgOXWYcu+OCWdLnCjxyU+hziB0=", "dev": true }, + "uc.micro": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz", + "integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==", + "dev": true + }, "uglify-js": { "version": "2.8.29", "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.8.29.tgz", @@ -3691,6 +3953,46 @@ "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz", "integrity": "sha1-VDjNLqk7IC76Ohn+iIeu58lPnJ0=" }, + "winston": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/winston/-/winston-3.2.1.tgz", + "integrity": "sha512-zU6vgnS9dAWCEKg/QYigd6cgMVVNwyTzKs81XZtTFuRwJOcDdBg7AU0mXVyNbs7O5RH2zdv+BdNZUlx7mXPuOw==", + "dev": true, + "requires": { + "async": "^2.6.1", + "diagnostics": "^1.1.1", + "is-stream": "^1.1.0", + "logform": "^2.1.1", + "one-time": "0.0.4", + "readable-stream": "^3.1.1", + "stack-trace": "0.0.x", + "triple-beam": "^1.3.0", + "winston-transport": "^4.3.0" + }, + "dependencies": { + "readable-stream": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.2.0.tgz", + "integrity": "sha512-RV20kLjdmpZuTF1INEb9IA3L68Nmi+Ri7ppZqo78wj//Pn62fCoJyV9zalccNzDD/OuJpMG4f+pfMl8+L6QdGw==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + } + } + }, + "winston-transport": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.3.0.tgz", + "integrity": "sha512-B2wPuwUi3vhzn/51Uukcao4dIduEiPOcOt9HJ3QeaXgkJ5Z7UwpBzxS4ZGNHtrxrUvTwemsQiSys0ihOf8Mp1A==", + "dev": true, + "requires": { + "readable-stream": "^2.3.6", + "triple-beam": "^1.2.0" + } + }, "wkx": { "version": "0.4.5", "resolved": "https://registry.npmjs.org/wkx/-/wkx-0.4.5.tgz", diff --git a/package.json b/package.json index 0a6e00b..f713fdf 100644 --- a/package.json +++ b/package.json @@ -9,9 +9,11 @@ "lint:fix": "npm run lint -- --fix", "db:seed": "./node_modules/.bin/sequelize db:seed:all", "db:migrate": "./node_modules/.bin/sequelize db:migrate", - "db:init": "npm run db:migrate && npm run db:seed" + "db:init": "npm run db:migrate && npm run db:seed", + "docs:api": "./node_modules/.bin/apidoc -i routes/ -o docs/api/" }, "dependencies": { + "body-parser": "^1.18.3", "cookie-parser": "~1.4.3", "debug": "~2.6.9", "dotenv": "^6.1.0", @@ -28,6 +30,7 @@ "validator": "^10.8.0" }, "devDependencies": { + "apidoc": "^0.17.7", "chai": "^4.2.0", "chai-http": "^4.2.0", "eslint": "^5.8.0", diff --git a/routes/ban/getBanById.js b/routes/ban/getBanById.js new file mode 100644 index 0000000..b4f12b4 --- /dev/null +++ b/routes/ban/getBanById.js @@ -0,0 +1,108 @@ +/* eslint-disable max-len */ +/** + * + * @api {GET} /ban/:id GET /ban/:id + * @apiName GET /ban/:id + * @apiGroup Ban + * + * + * @apiParam {String} banId UUID of the ban to get + * + * @apiSuccess {Object} ban + * @apiSuccess {String} ban.id UUID + * @apiSuccess {Date} ban.bannedUntil Date when the ban expires + * @apiSuccess {Enum} ban.status active, elapsed or deleted + * @apiSuccess {boolean} ban.verified + * @apiSuccess {Object} ban.game + * @apiSuccess {Number} ban.game.id Integer ID + * @apiSuccess {String} ban.game.code short code for identifying the game + * @apiSuccess {String} ban.game.fullName full game name + * @apiSuccess {Object} ban.reason + * @apiSuccess {Number} ban.reason.id Integer ID + * @apiSuccess {String} ban.reason.reasonShort Short name of the reason + * @apiSuccess {String} ban.reason.reasonLong Long description of the reason + * @apiSuccess {Object} ban.player + * @apiSuccess {String} ban.player.id UUID + * @apiSuccess {String} ban.player.steamId Steam64 ID + * @apiSuccess {String} ban.player.username Name of the player + * @apiSuccess {Object} ban.server Which server this ban belongs to + * @apiSuccess {String} ban.server.id UUID + * @apiSuccess {String} ban.server.name name of the server + * + * @apiParamExample {String} Request-Example: + * { + * "id" : "0a40cf80-4b2a-11e9-a532-07f768aa6c74" + * } + * + * + * @apiSuccessExample {Object} Success-Response: + *{ + * "id": "0a40cf80-4b2a-11e9-a532-07f768aa6c74", + * "bannedUntil": "2020-01-27T18:27:34.000Z", + * "status": "active", + * "verified": false, + * "createdAt": "2019-03-20T16:05:54.000Z", + * "updatedAt": "2019-03-20T16:05:54.000Z", + * "game": { + * "id": 2, + * "code": "rust", + * "fullName": "Rust", + * "createdAt": "2019-03-20T17:05:48.000Z", + * "updatedAt": "2019-03-20T17:05:48.000Z" + * }, + * "reason": { + * "id": 3, + * "reasonShort": "Other", + * "reasonLong": "General reason when the defaults do not cover the actual reason of the ban.", + * "createdAt": "2019-03-20T17:05:48.000Z", + * "updatedAt": "2019-03-20T17:05:48.000Z" + * }, + * "player": { + * "id": "0a3fe520-4b2a-11e9-a532-07f768aa6c74", + * "steamId": "78956", + * "username": "Justice.Denesik", + * "createdAt": "2019-03-20T16:05:54.000Z", + * "updatedAt": "2019-03-20T16:05:54.000Z", + * "UserId": "0a3f21d0-4b2a-11e9-a532-07f768aa6c74" + * }, + * "server": { + * "id": "0a3e1060-4b2a-11e9-a532-07f768aa6c74", + * "name": "Gleason, Zieme and Ratke", + * "createdAt": "2019-03-20T16:05:54.000Z", + * "updatedAt": "2019-03-20T16:05:54.000Z", + * "ownedById": "0a3bed80-4b2a-11e9-a532-07f768aa6c74" + * } + *} + * + * + */ + +module.exports = function getBanById(app) { + app.get('/ban/:id', (req, res) => { + const { + id, + } = req.params; + return app.models.Ban.findByPk(id, { + include: [app.models.Player, app.models.Game, app.models.Server, app.models.Reason], + }) + .then((ban) => { + if (ban === null) { + res.status(404); + return res.end(); + } + const response = { + id: ban.id, + bannedUntil: ban.bannedUntil, + status: ban.status, + verified: ban.verified, + createdAt: ban.createdAt, + updatedAt: ban.updatedAt, + game: ban.Game, + reason: ban.Reason, + player: ban.Player, + server: ban.Server, + }; + return res.json(response); + }); + }); +}; diff --git a/routes/ban/postBan.js b/routes/ban/postBan.js new file mode 100644 index 0000000..66b2a75 --- /dev/null +++ b/routes/ban/postBan.js @@ -0,0 +1,154 @@ +const { + DateTime, +} = require('luxon'); +const _ = require('lodash'); +/** + * + * @api {POST} /ban POST /ban + * @apiName PostBan + * @apiGroup Ban + * + * + * @apiParam {String} bannedUntil ISO DateTime + * @apiParam {String} steamId Steam64 ID + * + * @apiSuccess (200) {Object} ban The newly created ban + * @apiSuccess {String} ban.id UUID + * @apiSuccess {Date} ban.bannedUntil Date when the ban expires + * @apiSuccess {Enum} ban.status active, elapsed or deleted + * @apiSuccess {boolean} ban.verified + * + * @apiParamExample {type} Request-Example: + * { + * "bannedUntil": "2019-05-15T08:30:00", + * "steamId": "76561198028175941", + * "reason": "other", + * "game": "7d2d" + * } + * + * + * @apiSuccessExample {type} Success-Response: + * { + * "id": "f151e930-4c07-11e9-a3be-61e864bef7f5", + * "bannedUntil": "2019-05-15T06:30:00.000Z", + * "status": "active", + * "verified": false, + * "createdAt": "2019-03-21T18:34:20.868Z", + * "updatedAt": "2019-03-21T18:34:20.868Z" + * } + * + * + */ + +module.exports = function postBan(app) { + app.post('/ban', async (req, res, next) => { + if (_.isEmpty(req.body)) { + res.status(400); + res.send('Empty body.'); + return res.end(); + } + + const { + bannedUntil, + steamId, + reason, + game, + } = req.body; + + if (_.isEmpty(bannedUntil)) { + res.status(400); + res.send('bannedUntil is required.'); + return res.end(); + } + + if (_.isEmpty(steamId)) { + res.status(400); + res.send('steamId is required.'); + return res.end(); + } + + if (_.isEmpty(reason)) { + res.status(400); + res.send('reason is required.'); + return res.end(); + } + + if (_.isEmpty(game)) { + res.status(400); + res.send('game is required.'); + return res.end(); + } + + const bannedUntilDate = DateTime.fromISO(bannedUntil); + const playerProfiles = await app.helpers.getSteamProfiles([steamId]); + + if (!bannedUntilDate.isValid) { + res.status(400); + res.send('bannedUntil must be in ISO 8601 format.'); + return res.end(); + } + + const dateNow = Date.now(); + + if (bannedUntilDate.toMillis() < dateNow) { + res.status(400); + res.send('bannedUntil must be in the future.'); + return res.end(); + } + + let isValidReason = false; + let ReasonId; + app.supportedReasons.forEach((r) => { + if (reason.toLowerCase() === r.reasonShort.toLowerCase()) { + isValidReason = true; + ReasonId = r.id; + } + }); + + if (!isValidReason) { + res.status(400); + res.send(`Reason must be one of: ${app.supportedReasons.map(r => r.reasonShort).join(', ')}`); + return res.end(); + } + + let isValidGame = false; + let GameId; + app.supportedGames.forEach((supportedGame) => { + if (req.body.game.toLowerCase() === supportedGame.code.toLowerCase()) { + isValidGame = true; + GameId = game.id; + } + }); + + if (!isValidGame) { + res.status(400); + res.send(`Game must be one of: ${app.supportedGames.map(supportedGame => supportedGame.code).join(', ')}`); + return res.end(); + } + + if (playerProfiles.length === 0) { + res.send(`Invalid steam ID provided: ${req.body.steamId}`); + return res.end(); + } + + + return app.models.Ban.create({ + bannedUntil, + ReasonId, + GameId, + PlayerId: playerProfiles[0].id, + }).then((newBan) => { + const response = { + id: newBan.id, + bannedUntil: newBan.bannedUntil, + status: newBan.status, + verified: newBan.verified, + createdAt: newBan.createdAt, + updatedAt: newBan.updatedAt, + }; + res.send(response); + }).catch((e) => { + next(e); + }); + }); +}; diff --git a/routes/ban/searchBans.js b/routes/ban/searchBans.js new file mode 100644 index 0000000..523a397 --- /dev/null +++ b/routes/ban/searchBans.js @@ -0,0 +1,135 @@ +/* eslint-disable max-len */ +const _ = require('lodash'); + +/** + * @api {get} /ban/:steamId GET /ban/:steamId + * @apiName GET /ban/:steamId + * @apiGroup Ban + * + * @apiParam {Number} id Steam64 ID + * + * @apiSuccess {Object[]} bans List of bans for a player. + * @apiSuccess {String} bans.id UUID + * @apiSuccess {Date} bans.bannedUntil Date when the ban expires + * @apiSuccess {Enum} bans.status active, elapsed or deleted + * @apiSuccess {boolean} bans.verified + * @apiSuccess {Object} bans.game + * @apiSuccess {Number} bans.game.id Integer ID + * @apiSuccess {String} bans.game.code short code for identifying the game + * @apiSuccess {String} bans.game.fullName full game name + * @apiSuccess {Object} bans.reason + * @apiSuccess {Number} bans.reason.id Integer ID + * @apiSuccess {String} bans.reason.reasonShort Short name of the reason + * @apiSuccess {String} bans.reason.reasonLong Long description of the reason + * @apiSuccess {Object} bans.player + * @apiSuccess {String} bans.player.id UUID + * @apiSuccess {String} bans.player.steamId Steam64 ID + * @apiSuccess {String} bans.player.username Name of the player + * @apiSuccess {Object} bans.server Which server this ban belongs to + * @apiSuccess {String} bans.server.id UUID + * @apiSuccess {String} bans.server.name name of the server + * + * @apiSuccessExample Success-Response: + * HTTP/1.1 200 OK + *[ + * { + * "id": "0a40cf80-4b2a-11e9-a532-07f768aa6c74", + * "bannedUntil": "2020-01-27T18:27:34.000Z", + * "status": "active", + * "verified": false, + * "createdAt": "2019-03-20T16:05:54.000Z", + * "updatedAt": "2019-03-20T16:05:54.000Z", + * "game": { + * "id": 2, + * "code": "rust", + * "fullName": "Rust", + * }, + * "reason": { + * "id": 3, + * "reasonShort": "Other", + * "reasonLong": "General reason when the defaults do not cover the actual reason of the ban.", + * }, + * "player": { + * "id": "0a3fe520-4b2a-11e9-a532-07f768aa6c74", + * "steamId": "78956", + * "username": "Justice.Denesik", + * }, + * "server": { + * "id": "0a3e1060-4b2a-11e9-a532-07f768aa6c74", + * "name": "Gleason, Zieme and Ratke", + * } + * }, + * { + * "id": "0a929900-4b2a-11e9-a532-07f768aa6c74", + * "bannedUntil": "2020-03-11T03:15:53.000Z", + * "status": "active", + * "verified": false, + * "game": { + * "id": 1, + * "code": "7d2d", + * "fullName": "7 Days to Die", + * }, + * "reason": { + * "id": 3, + * "reasonShort": "Other", + * "reasonLong": "General reason when the defaults do not cover the actual reason of the ban.", + * }, + * "player": { + * "id": "0a913970-4b2a-11e9-a532-07f768aa6c74", + * "steamId": "76561198028175941", + * "username": "Catalysm", + * }, + * "server": { + * "id": "0a3e1060-4b2a-11e9-a532-07f768aa6c74", + * "name": "Gleason, Zieme and Ratke", + * } + * } + *] + */ + +module.exports = function searchBans(app) { + app.get('/ban', async (req, res, next) => { + const { + steamId, + } = req.query; + + let bans; + + if (!_.isEmpty(steamId)) { + const player = await app.models.Player.findOne({ + where: { + steamId, + }, + }); + if (_.isNull(player)) { + res.status(404); + return next('Unknown steamId'); + } + bans = await app.models.Ban.findAll({ + where: { + PlayerId: player.id, + }, + include: [app.models.Player, app.models.Game, app.models.Server, app.models.Reason], + }); + } else { + bans = await app.models.Ban.findAll({ + include: [app.models.Player, app.models.Game, app.models.Server, app.models.Reason], + }); + } + + bans = bans.map(b => ({ + id: b.id, + bannedUntil: b.bannedUntil, + status: b.status, + verified: b.verified, + createdAt: b.createdAt, + updatedAt: b.updatedAt, + game: b.Game, + reason: b.Reason, + player: b.Player, + server: b.Server, + })); + res.send(bans); + return res.end(); + }); +}; diff --git a/routes/bans.js b/routes/bans.js deleted file mode 100644 index 5ab10a3..0000000 --- a/routes/bans.js +++ /dev/null @@ -1,140 +0,0 @@ -const express = require('express'); - -const router = express.Router(); -const _ = require('lodash'); - -const { - DateTime, -} = require('luxon'); - -module.exports = (app) => { - /* GET bans listing. */ - router.get('/', (req, res, next) => { - const { - steamId, - } = req.query; - if (!_.isEmpty(steamId)) { - app.models.Player.findOne({ - where: { - steamId, - }, - }).then((player) => { - app.models.Ban.findAll({ - where: { - PlayerId: player.id, - }, - include: [app.models.Player, app.models.Game, app.models.Server, app.models.Reason], - }).then((bans) => { - res.send(bans); - return res.end(); - }).catch(e => next(e)); - }).catch(e => next(e)); - } else { - app.models.Ban.findAll().then((bans) => { - res.send(bans); - res.end(); - }); - } - }); - - // GET one ban by id - app.get('/ban/:id', (req, res) => { - const { - id, - } = req.params; - return app.models.Ban.findByPk(id) - .then((ban) => { - if (ban === null) { - res.status(404); - return res.end(); - } - return res.json(ban); - }); - }); - - /* POST ban listing. */ - router.post('/', async (req, res, next) => { - if (req.body.bannedUntil === undefined) { - res.status(400); - res.send('bannedUntil is required.'); - return res.end(); - } - - if (req.body.steamId === undefined) { - res.status(400); - res.send('steamId is required.'); - return res.end(); - } - - const { - bannedUntil, - steamId, - } = req.body; - - const bannedUntilDate = DateTime.fromISO(bannedUntil); - const playerProfiles = await app.helpers.getSteamProfiles([steamId]); - - if (!bannedUntilDate.isValid) { - res.status(400); - res.send('bannedUntil must be in ISO 8601 format.'); - return res.end(); - } - - const dateNow = Date.now(); - - if (bannedUntilDate.toMillis() < dateNow) { - res.status(400); - res.send('bannedUntil must be in the future.'); - return res.end(); - } - - let isValidReason = false; - let ReasonId; - app.supportedReasons.forEach((r) => { - if (req.body.reason === r.reasonShort) { - isValidReason = true; - ReasonId = r.id; - } - }); - - if (!isValidReason) { - res.status(400); - res.send(`Reason must be one of: ${app.supportedReasons.map(r => r.reasonShort).join(', ')}`); - return res.end(); - } - - let isValidGame = false; - let GameId; - app.supportedGames.forEach((game) => { - if (req.body.game === game.code) { - isValidGame = true; - GameId = game.id; - } - }); - - if (!isValidGame) { - res.status(400); - res.send(`Game must be one of: ${app.supportedGames.map(game => game.code).join(', ')}`); - return res.end(); - } - - if (playerProfiles.length === 0) { - res.send(`Invalid steam ID provided: ${req.body.steamId}`); - return res.end(); - } - - - return app.models.Ban.create({ - bannedUntil, - ReasonId, - GameId, - PlayerId: playerProfiles[0].id, - }).then((newBan) => { - res.send(newBan); - }).catch((e) => { - next(e); - }); - }); - - return router; -}; diff --git a/routes/index.js b/routes/index.js new file mode 100644 index 0000000..348d6ad --- /dev/null +++ b/routes/index.js @@ -0,0 +1,28 @@ +/* eslint-disable import/no-dynamic-require */ +/* eslint-disable global-require */ +const fs = require('fs'); + +const validFileTypes = ['js']; + +function requireFiles(directory, app) { + fs.readdirSync(directory).forEach((fileName) => { + // Recurse if directory + if (fs.lstatSync(`${directory}/${fileName}`).isDirectory()) { + requireFiles(`${directory}/${fileName}`, app); + } else { + // Skip this file + if (fileName === 'index.js' && directory === __dirname) return; + + // Skip unknown filetypes + if (validFileTypes.indexOf(fileName.split('.').pop()) === -1) return; + + // Require the file. + console.log(`Loading route - ${`${directory}/${fileName}`}`); + require(`${directory}/${fileName}`)(app); + } + }); +} + +module.exports = function loadRoutes(app) { + requireFiles(__dirname, app); +};