Error Handling & Minification
parent
69a7af127b
commit
42ed3da0d3
|
@ -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",
|
||||
|
|
|
@ -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<null | AccountInterface> {
|
||||
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];
|
||||
|
|
|
@ -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);
|
||||
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}`);
|
||||
|
|
|
@ -10,13 +10,11 @@ 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) => {
|
||||
try {
|
||||
const acc: any = {};
|
||||
acc.username = req.account.username;
|
||||
acc.userID = req.account.userID;
|
||||
|
@ -27,9 +25,13 @@ export default class Account extends Route {
|
|||
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) => {
|
||||
try {
|
||||
const moderations = await this.server.client.db.Moderation.find({ username: req.account.username });
|
||||
if (!moderations.length) res.sendStatus(204);
|
||||
if (req.params.id) {
|
||||
|
@ -38,6 +40,9 @@ export default class Account extends Route {
|
|||
} else {
|
||||
res.status(200).json({ code: this.constants.codes.SUCCESS, message: moderations });
|
||||
}
|
||||
} catch (error) {
|
||||
this.handleError(error, res);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue