From 42ed3da0d31f39e77b01be3404ec55e33df2361a Mon Sep 17 00:00:00 2001 From: Matthew R Date: Sun, 17 Nov 2019 16:11:41 -0500 Subject: [PATCH 01/15] Error Handling & Minification --- package.json | 2 ++ src/api/Security.ts | 8 +++++++ src/api/Server.ts | 13 ++++++++++- src/api/routes/Account.ts | 47 ++++++++++++++++++++++----------------- src/class/Route.ts | 23 +++++++++++++++++-- 5 files changed, 69 insertions(+), 24 deletions(-) diff --git a/package.json b/package.json index a6f520d..ad00d84 100644 --- a/package.json +++ b/package.json @@ -18,6 +18,7 @@ "eris-pagination": "bsian03/eris-pagination", "express": "^4.17.1", "fs-extra": "^8.1.0", + "helmet": "^3.21.2", "ioredis": "^4.14.1", "moment": "^2.24.0", "moment-precise-range-plugin": "^1.3.0", @@ -29,6 +30,7 @@ "devDependencies": { "@types/express": "^4.17.2", "@types/fs-extra": "^8.0.0", + "@types/helmet": "^0.0.45", "@types/ioredis": "^4.0.18", "@types/mongoose": "^5.5.20", "@types/nodemailer": "^6.2.1", diff --git a/src/api/Security.ts b/src/api/Security.ts index e5ec505..bd478a5 100644 --- a/src/api/Security.ts +++ b/src/api/Security.ts @@ -38,6 +38,10 @@ export default class Security { return `${salt}:${encrypted}`; } + /** + * If the bearer token is valid, will return the Account, else will return null. + * @param bearer The bearer token provided. + */ public async checkBearer(bearer: string): Promise { const decipher = crypto.createDecipheriv('aes-256-gcm', this.keys.key, this.keys.iv); try { @@ -57,6 +61,10 @@ export default class Security { } } + /** + * Returns the Bearer token, searches in headers and query. + * @param req The Request object from Express. + */ public extractBearer(req: Request): string { if (req.headers.authorization && req.headers.authorization.split(' ')[0] === 'Bearer') { return req.headers.authorization.split(' ')[1]; diff --git a/src/api/Server.ts b/src/api/Server.ts index bc36325..81c5055 100644 --- a/src/api/Server.ts +++ b/src/api/Server.ts @@ -1,6 +1,7 @@ /* eslint-disable no-useless-return */ import express from 'express'; import bodyParser from 'body-parser'; +import helmet from 'helmet'; import fs from 'fs-extra'; import { Client } from '..'; import { Security } from '.'; @@ -34,7 +35,11 @@ export default class Server { try { // eslint-disable-next-line new-cap const route = new (require(`${__dirname}/routes/${routeFile}`).default)(this); - route.bind(); + if (route.conf.deprecated === true) { + route.deprecated(); + } else { + route.bind(); + } this.routes.set(route.conf.path, route); this.app.use(route.conf.path, route.router); this.client.signale.success(`Successfully loaded route ${route.conf.path}`); @@ -45,6 +50,12 @@ export default class Server { } private connect(): void { + this.app.set('trust proxy', 'loopback'); + this.app.use(helmet({ + hsts: false, + hidePoweredBy: false, + contentSecurityPolicy: true, + })); this.app.use(bodyParser.json()); this.app.listen(this.options.port, () => { this.client.signale.success(`API Server listening on port ${this.options.port}`); diff --git a/src/api/routes/Account.ts b/src/api/routes/Account.ts index 284f3d4..6240f3d 100644 --- a/src/api/routes/Account.ts +++ b/src/api/routes/Account.ts @@ -10,33 +10,38 @@ export default class Account extends Route { public bind() { this.router.use(async (req, res, next) => { - const account = await this.server.security.checkBearer(this.server.security.extractBearer(req)); - if (!account) return res.status(401).json({ code: this.constants.codes.UNAUTHORIZED, message: 'BEARER_TOKEN_INVALID' }); - Object.defineProperty(req, 'account', { value: account, writable: true, enumerable: true, configurable: true }); - next(); + await this.authorize(req, res, next); }); this.router.get('/', async (req: Req, res) => { - const acc: any = {}; - acc.username = req.account.username; - acc.userID = req.account.userID; - acc.email = req.account.emailAddress; - acc.locked = req.account.locked; - acc.root = req.account.root; - acc.createdAt = req.account.createdAt; - acc.createdBy = req.account.createdBy; - acc.permissions = req.account.permissions; - res.status(200).json({ code: this.constants.codes.SUCCESS, message: acc }); + try { + const acc: any = {}; + acc.username = req.account.username; + acc.userID = req.account.userID; + acc.email = req.account.emailAddress; + acc.locked = req.account.locked; + acc.root = req.account.root; + acc.createdAt = req.account.createdAt; + acc.createdBy = req.account.createdBy; + acc.permissions = req.account.permissions; + res.status(200).json({ code: this.constants.codes.SUCCESS, message: acc }); + } catch (error) { + this.handleError(error, res); + } }); this.router.get('/moderations/:id?', async (req: Req, res) => { - const moderations = await this.server.client.db.Moderation.find({ username: req.account.username }); - if (!moderations.length) res.sendStatus(204); - if (req.params.id) { - const filtered = moderations.filter((moderation) => moderation.logID === req.params.id); - res.status(200).json({ code: this.constants.codes.SUCCESS, message: { filtered } }); - } else { - res.status(200).json({ code: this.constants.codes.SUCCESS, message: moderations }); + try { + const moderations = await this.server.client.db.Moderation.find({ username: req.account.username }); + if (!moderations.length) res.sendStatus(204); + if (req.params.id) { + const filtered = moderations.filter((moderation) => moderation.logID === req.params.id); + res.status(200).json({ code: this.constants.codes.SUCCESS, message: { filtered } }); + } else { + res.status(200).json({ code: this.constants.codes.SUCCESS, message: moderations }); + } + } catch (error) { + this.handleError(error, res); } }); } diff --git a/src/class/Route.ts b/src/class/Route.ts index 1fbc728..5488924 100644 --- a/src/class/Route.ts +++ b/src/class/Route.ts @@ -1,4 +1,5 @@ -import { Router as router } from 'express'; +/* eslint-disable consistent-return */ +import { Request, Response, NextFunction, Router as router } from 'express'; import { Server } from '../api'; export default class Route { @@ -16,6 +17,24 @@ export default class Route { public bind() {} + public deprecated() { + this.router.all('*', (_req, res) => { + res.status(501).json({ code: this.constants.codes.DEPRECATED, message: 'This endpoint is deprecated.' }); + }); + } + + public async authorize(req: Request, res: Response, next: NextFunction) { + const account = await this.server.security.checkBearer(this.server.security.extractBearer(req)); + if (!account) return res.status(401).json({ code: this.constants.codes.UNAUTHORIZED, message: 'BEARER_TOKEN_INVALID' }); + Object.defineProperty(req, 'account', { value: account, writable: true, enumerable: true, configurable: true }); + next(); + } + + public handleError(error: Error, res: Response) { + this.server.client.util.handleError(error); + res.status(500).json({ code: this.constants.codes.SERVER_ERROR, message: 'An internal error has occurred, Engineers have been notified.' }); + } + get constants() { return { codes: { @@ -26,7 +45,7 @@ export default class Route { ACCOUNT_NOT_FOUND: 1041, CLIENT_ERROR: 1044, SERVER_ERROR: 105, - UNKNOWN_SERVER_ERROR: 1051, + DEPRECATED: 1051, }, }; } From a45f1b66925f3a287222a7b06fd414f6fdd77f3d Mon Sep 17 00:00:00 2001 From: Matthew R Date: Sun, 17 Nov 2019 16:12:00 -0500 Subject: [PATCH 02/15] New Root endpoint --- src/api/routes/Root.ts | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 src/api/routes/Root.ts diff --git a/src/api/routes/Root.ts b/src/api/routes/Root.ts new file mode 100644 index 0000000..1401401 --- /dev/null +++ b/src/api/routes/Root.ts @@ -0,0 +1,41 @@ +import os from 'os'; +import { Server } from '..'; +import { Route } from '../../class'; + +export default class Root extends Route { + constructor(server: Server) { + super(server, { path: '/', deprecated: false }); + } + + public bind() { + this.router.get('/', async (req, res) => { + try { + const date = new Date(); + date.setSeconds(-process.uptime()); + const accounts = await this.server.client.db.Account.find(); + const administrators = accounts.filter((account) => account.root === true); + const response = { + nodeVersion: process.version, + uptime: process.uptime(), + server: { + users: accounts.length, + administrators, + }, + stats: { + uptime: os.uptime(), + loadAverage: os.loadavg(), + cpuModel: os.cpus()[0].model, + cpuClock: os.cpus()[0].speed / 1000, + cpuCores: os.cpus().length, + hostname: os.hostname(), + ipv4: os.networkInterfaces().eth0.filter((r) => r.family === 'IPv4')[0].address, + ipv6: os.networkInterfaces().eth0.filter((r) => r.family === 'IPv6')[0].address, + }, + }; + res.status(200).json({ code: this.constants.codes.SUCCESS, message: response }); + } catch (error) { + this.handleError(error, res); + } + }); + } +} From d219a1eb7e183bd00811132c39903a33b3059e64 Mon Sep 17 00:00:00 2001 From: Matthew R Date: Sun, 17 Nov 2019 16:20:56 -0500 Subject: [PATCH 03/15] fix issue with CSP --- src/api/Server.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/api/Server.ts b/src/api/Server.ts index 81c5055..8cd6063 100644 --- a/src/api/Server.ts +++ b/src/api/Server.ts @@ -54,7 +54,11 @@ export default class Server { this.app.use(helmet({ hsts: false, hidePoweredBy: false, - contentSecurityPolicy: true, + contentSecurityPolicy: { + directives: { + defaultSrc: ['self'], + }, + }, })); this.app.use(bodyParser.json()); this.app.listen(this.options.port, () => { From 09eb30d813214685d607605f9e421c8e2219778d Mon Sep 17 00:00:00 2001 From: Matthew R Date: Sun, 17 Nov 2019 16:23:24 -0500 Subject: [PATCH 04/15] fix csp again --- src/api/Server.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api/Server.ts b/src/api/Server.ts index 8cd6063..6668ad4 100644 --- a/src/api/Server.ts +++ b/src/api/Server.ts @@ -56,7 +56,7 @@ export default class Server { hidePoweredBy: false, contentSecurityPolicy: { directives: { - defaultSrc: ['self'], + defaultSrc: ["'self'"], }, }, })); From cc1b9a2342ace157e001626f8d84974ba2f7ddca Mon Sep 17 00:00:00 2001 From: Matthew R Date: Sun, 17 Nov 2019 16:26:07 -0500 Subject: [PATCH 05/15] fix routing issue --- src/api/routes/Root.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api/routes/Root.ts b/src/api/routes/Root.ts index 1401401..4832b99 100644 --- a/src/api/routes/Root.ts +++ b/src/api/routes/Root.ts @@ -13,7 +13,7 @@ export default class Root extends Route { const date = new Date(); date.setSeconds(-process.uptime()); const accounts = await this.server.client.db.Account.find(); - const administrators = accounts.filter((account) => account.root === true); + const administrators = accounts.filter((account) => account.root === true).length; const response = { nodeVersion: process.version, uptime: process.uptime(), From f76c2c4da3566e77907cceeb20e76060f08c7051 Mon Sep 17 00:00:00 2001 From: Matthew R Date: Sun, 17 Nov 2019 18:02:26 -0500 Subject: [PATCH 06/15] prevent body error with eslint constructors --- .eslintrc.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.eslintrc.json b/.eslintrc.json index 3a31097..f1f15b9 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -36,6 +36,8 @@ "camelcase": "off", "indent": "warn", "object-curly-newline": "off", - "import/prefer-default-export": "off" + "import/prefer-default-export": "off", + "no-useless-constructor": "off", + "@typescript-eslint/no-useless-constructor": 2 } } \ No newline at end of file From 7e2339cbc8573d4b23ca3a7f27bea6539a146204 Mon Sep 17 00:00:00 2001 From: Matthew R Date: Sun, 17 Nov 2019 18:02:40 -0500 Subject: [PATCH 07/15] add maintenance binding routes --- src/api/Server.ts | 2 ++ src/class/Route.ts | 23 +++++++++++++++++++---- 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/src/api/Server.ts b/src/api/Server.ts index 6668ad4..0c85699 100644 --- a/src/api/Server.ts +++ b/src/api/Server.ts @@ -37,6 +37,8 @@ export default class Server { const route = new (require(`${__dirname}/routes/${routeFile}`).default)(this); if (route.conf.deprecated === true) { route.deprecated(); + } else if (route.conf.maintenance === true) { + route.maintenance(); } else { route.bind(); } diff --git a/src/class/Route.ts b/src/class/Route.ts index 5488924..ddcb40a 100644 --- a/src/class/Route.ts +++ b/src/class/Route.ts @@ -9,7 +9,7 @@ export default class Route { public conf: { path: string, deprecated?: boolean }; - constructor(server: Server, conf: { path: string, deprecated?: boolean }) { + constructor(server: Server, conf: { path: string, deprecated?: boolean, maintenance?: boolean }) { this.server = server; this.router = router(); this.conf = conf; @@ -19,20 +19,26 @@ export default class Route { public deprecated() { this.router.all('*', (_req, res) => { - res.status(501).json({ code: this.constants.codes.DEPRECATED, message: 'This endpoint is deprecated.' }); + res.status(501).json({ code: this.constants.codes.DEPRECATED, message: this.constants.messages.DEPRECATED }); + }); + } + + public maintenance() { + this.router.all('*;', (_req, res) => { + res.status(503).json({ code: this.constants.codes.MAINTENANCE_OR_UNAVAILABLE, message: this.constants.messages.MAINTENANCE_OR_UNAVAILABLE }); }); } public async authorize(req: Request, res: Response, next: NextFunction) { const account = await this.server.security.checkBearer(this.server.security.extractBearer(req)); - if (!account) return res.status(401).json({ code: this.constants.codes.UNAUTHORIZED, message: 'BEARER_TOKEN_INVALID' }); + if (!account) return res.status(401).json({ code: this.constants.codes.UNAUTHORIZED, message: this.constants.messages.UNAUTHORIZED }); Object.defineProperty(req, 'account', { value: account, writable: true, enumerable: true, configurable: true }); next(); } public handleError(error: Error, res: Response) { this.server.client.util.handleError(error); - res.status(500).json({ code: this.constants.codes.SERVER_ERROR, message: 'An internal error has occurred, Engineers have been notified.' }); + res.status(500).json({ code: this.constants.codes.SERVER_ERROR, message: this.constants.messages.SERVER_ERROR }); } get constants() { @@ -46,6 +52,15 @@ export default class Route { CLIENT_ERROR: 1044, SERVER_ERROR: 105, DEPRECATED: 1051, + MAINTENANCE_OR_UNAVAILABLE: 1053, + }, + messages: { + UNAUTHORIZED: ['CREDENTIALS_INVALID', 'The credentials you supplied are invalid.'], + PERMISSION_DENIED: ['PERMISSION_DENIED', 'You do not have valid credentials to access this resource.'], + NOT_FOUND: ['NOT_FOUND', 'The resource you requested cannot be located.'], + SERVER_ERROR: ['INTERNAL_ERROR', 'An internal error has occurred, Engineers have been notified.'], + DEPRECATED: ['ENDPOINT_OR_RESOURCE_DEPRECATED', 'The endpoint or resource you\'re trying to access has been deprecated.'], + MAINTENANCE_OR_UNAVAILABLE: ['SERVICE_UNAVAILABLE', 'The endpoint or resource you\'re trying to access is either in maintenance or is not available.'], }, }; } From 95f78b3643e84707298490a322eee760059b5031 Mon Sep 17 00:00:00 2001 From: Matthew R Date: Sun, 17 Nov 2019 18:02:48 -0500 Subject: [PATCH 08/15] add storage information route --- src/api/routes/Account.ts | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/api/routes/Account.ts b/src/api/routes/Account.ts index 6240f3d..a676310 100644 --- a/src/api/routes/Account.ts +++ b/src/api/routes/Account.ts @@ -44,5 +44,14 @@ export default class Account extends Route { this.handleError(error, res); } }); + + this.router.get('/storage', async (req: Req, res) => { + try { + const data = await this.server.client.redis.get(`storage-${req.account.username}`) ? await this.server.client.redis.get(`storage-${req.account.username}`) : null; + res.status(200).json({ code: this.constants.codes.SUCCESS, message: data }); + } catch (error) { + this.handleError(error, res); + } + }); } } From 1f2791db4540ba1971684939296f84b65d239942 Mon Sep 17 00:00:00 2001 From: Matthew R Date: Sun, 17 Nov 2019 18:02:54 -0500 Subject: [PATCH 09/15] FS route --- src/api/routes/FileSystem.ts | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 src/api/routes/FileSystem.ts diff --git a/src/api/routes/FileSystem.ts b/src/api/routes/FileSystem.ts new file mode 100644 index 0000000..814e676 --- /dev/null +++ b/src/api/routes/FileSystem.ts @@ -0,0 +1,18 @@ +/* eslint-disable consistent-return */ +import { Server } from '..'; +import { Route } from '../../class'; +import { Req } from '../interfaces'; + +export default class FileSystem extends Route { + constructor(server: Server) { + super(server, { path: '/fs', deprecated: false, maintenance: true }); + } + + public bind() { + this.router.use(async (req, res, next) => { + await this.authorize(req, res, next); + }); + + this.router.get('/:'); + } +} From 51839c20ebd0ad75b48bde60aeb8e2907d761323 Mon Sep 17 00:00:00 2001 From: Matthew R Date: Sun, 17 Nov 2019 18:13:09 -0500 Subject: [PATCH 10/15] fix typo --- src/api/routes/FileSystem.ts | 1 - src/class/Route.ts | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/api/routes/FileSystem.ts b/src/api/routes/FileSystem.ts index 814e676..f5f11ec 100644 --- a/src/api/routes/FileSystem.ts +++ b/src/api/routes/FileSystem.ts @@ -1,7 +1,6 @@ /* eslint-disable consistent-return */ import { Server } from '..'; import { Route } from '../../class'; -import { Req } from '../interfaces'; export default class FileSystem extends Route { constructor(server: Server) { diff --git a/src/class/Route.ts b/src/class/Route.ts index ddcb40a..9c8545e 100644 --- a/src/class/Route.ts +++ b/src/class/Route.ts @@ -24,7 +24,7 @@ export default class Route { } public maintenance() { - this.router.all('*;', (_req, res) => { + this.router.all('*', (_req, res) => { res.status(503).json({ code: this.constants.codes.MAINTENANCE_OR_UNAVAILABLE, message: this.constants.messages.MAINTENANCE_OR_UNAVAILABLE }); }); } From 2b2b4f6830114606ea7c58c928bb34273043366e Mon Sep 17 00:00:00 2001 From: Matthew R Date: Sun, 17 Nov 2019 18:13:17 -0500 Subject: [PATCH 11/15] string should be a number --- src/api/routes/Account.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api/routes/Account.ts b/src/api/routes/Account.ts index a676310..198bebd 100644 --- a/src/api/routes/Account.ts +++ b/src/api/routes/Account.ts @@ -47,7 +47,7 @@ export default class Account extends Route { this.router.get('/storage', async (req: Req, res) => { try { - const data = await this.server.client.redis.get(`storage-${req.account.username}`) ? await this.server.client.redis.get(`storage-${req.account.username}`) : null; + const data = await this.server.client.redis.get(`storage-${req.account.username}`) ? Number(await this.server.client.redis.get(`storage-${req.account.username}`)) : null; res.status(200).json({ code: this.constants.codes.SUCCESS, message: data }); } catch (error) { this.handleError(error, res); From 7e8256a47434a10880f72dbe2e0a345f56025e19 Mon Sep 17 00:00:00 2001 From: Matthew R Date: Sun, 17 Nov 2019 20:44:26 -0500 Subject: [PATCH 12/15] remove redudant await and remove logging --- src/class/Util.ts | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/class/Util.ts b/src/class/Util.ts index 5fe2862..2197a92 100644 --- a/src/class/Util.ts +++ b/src/class/Util.ts @@ -43,8 +43,6 @@ export default class Util { */ public resolveCommand(command: string, args?: string[], message?: Message): Promise<{cmd: Command, args: string[] }> { try { - this.client.signale.info(command); - this.client.signale.info(args); let resolvedCommand: Command; if (this.client.commands.has(command)) resolvedCommand = this.client.commands.get(command); @@ -117,7 +115,7 @@ export default class Util { } } - public splitFields(fields: {name: string, value: string, inline?: boolean}[]): {name: string, value: string, inline?: boolean}[][] { + public splitFields(fields: { name: string, value: string, inline?: boolean }[]): { name: string, value: string, inline?: boolean }[][] { let index = 0; const array: {name: string, value: string, inline?: boolean}[][] = [[]]; while (fields.length) { @@ -145,7 +143,7 @@ export default class Util { } - public async createHash(password: string) { + public async createHash(password: string): Promise { const hashed = await this.exec(`mkpasswd -m sha-512 "${password}"`); return hashed; } @@ -225,7 +223,7 @@ export default class Util { const expiration = { date, processed }; logInput.expiration = expiration; - const log = await new this.client.db.Moderation(logInput); + const log = new this.client.db.Moderation(logInput); await log.save(); let embedTitle: string; From 0b25a675755ea5622c0b61d6db366a527b36321d Mon Sep 17 00:00:00 2001 From: Matthew R Date: Sun, 17 Nov 2019 20:52:41 -0500 Subject: [PATCH 13/15] change Map to Collection in server routes --- src/api/Server.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/api/Server.ts b/src/api/Server.ts index 0c85699..3c4d16b 100644 --- a/src/api/Server.ts +++ b/src/api/Server.ts @@ -5,10 +5,10 @@ import helmet from 'helmet'; import fs from 'fs-extra'; import { Client } from '..'; import { Security } from '.'; -import { Route } from '../class'; +import { Collection, Route } from '../class'; export default class Server { - public routes: Map; + public routes: Collection public client: Client; @@ -20,7 +20,7 @@ export default class Server { constructor(client: Client, options?: { port: number }) { this.options = options; - this.routes = new Map(); + this.routes = new Collection(); this.client = client; this.security = new Security(this.client); this.app = express(); From 254f48990989ecae5a40877b9c0b0e354e39ae3f Mon Sep 17 00:00:00 2001 From: Matthew R Date: Sun, 17 Nov 2019 20:52:54 -0500 Subject: [PATCH 14/15] some jsdoc in Route class --- src/class/Route.ts | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/src/class/Route.ts b/src/class/Route.ts index 9c8545e..33db377 100644 --- a/src/class/Route.ts +++ b/src/class/Route.ts @@ -17,18 +17,29 @@ export default class Route { public bind() {} - public deprecated() { + public deprecated(): void { this.router.all('*', (_req, res) => { res.status(501).json({ code: this.constants.codes.DEPRECATED, message: this.constants.messages.DEPRECATED }); }); } - public maintenance() { + public maintenance(): void { this.router.all('*', (_req, res) => { res.status(503).json({ code: this.constants.codes.MAINTENANCE_OR_UNAVAILABLE, message: this.constants.messages.MAINTENANCE_OR_UNAVAILABLE }); }); } + /** + * This function checks for the presense of a Bearer token with Security.extractBearer(), + * then it will attempt to validate it with Security.checkBearer(). + * If it can authenticate the request, it'll add a custom property on Request called + * `account`, which will hold an the bearer token's account owner. The account is of the + * type `AccountInterface`. + * @param req The Request object from Express. + * @param res The Response object from Express. + * @param next The NextFunction from Express. + * @example Security.authorize(req, res, next); + */ public async authorize(req: Request, res: Response, next: NextFunction) { const account = await this.server.security.checkBearer(this.server.security.extractBearer(req)); if (!account) return res.status(401).json({ code: this.constants.codes.UNAUTHORIZED, message: this.constants.messages.UNAUTHORIZED }); @@ -36,7 +47,13 @@ export default class Route { next(); } - public handleError(error: Error, res: Response) { + /** + * This function calls Util.handleError() internally, however it also sends a generic + * response to the user. + * @param error The Error object. + * @param res The Response object from Express. + */ + public handleError(error: Error, res: Response): void { this.server.client.util.handleError(error); res.status(500).json({ code: this.constants.codes.SERVER_ERROR, message: this.constants.messages.SERVER_ERROR }); } From 6f9569b38a7b16454d299c8b6fb25fef7948cd14 Mon Sep 17 00:00:00 2001 From: Matthew R Date: Sun, 17 Nov 2019 20:53:01 -0500 Subject: [PATCH 15/15] fix spacing in cwg_create --- src/commands/cwg_create.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/commands/cwg_create.ts b/src/commands/cwg_create.ts index 0dd4b7e..ef145c1 100644 --- a/src/commands/cwg_create.ts +++ b/src/commands/cwg_create.ts @@ -78,7 +78,7 @@ export default class CWG_Create extends Command { Port: ${domain.port}
Certificate Issuer: ${cert.issuer.organizationName}
Certificate Subject: ${cert.subject.commonName}
- Responsible Engineer: ${message.author.username}#${message.author.discriminator}
+ Responsible Engineer: ${message.author.username}#${message.author.discriminator}

If you have any questions about additional setup, you can reply to this email or send a message in #cloud-support in our Discord server.