From eb49193f98e65230448e90a596fc7fc3c055150a Mon Sep 17 00:00:00 2001 From: "Navy.gif" Date: Wed, 31 May 2023 23:31:13 +0300 Subject: [PATCH] expectContenType utility middleware improve error logging middleware --- options.json | 2 +- src/server/Server.ts | 18 ++++++++++++------ src/server/middleware/Util.ts | 25 +++++++++++++++++++++++++ src/util/PermissionManager.ts | 2 +- 4 files changed, 39 insertions(+), 8 deletions(-) diff --git a/options.json b/options.json index d145f7e..075c766 100644 --- a/options.json +++ b/options.json @@ -61,7 +61,7 @@ "broadcastLevel": 3 }, "databases": { - "messageBroker": { + "broker": { "load": false }, "mariadb": { diff --git a/src/server/Server.ts b/src/server/Server.ts index 9502842..250a88b 100644 --- a/src/server/Server.ts +++ b/src/server/Server.ts @@ -266,12 +266,18 @@ class Server extends EventEmitter { req.remoteAddress = req.get('X-Forwarded-For') || req.socket.remoteAddress as string; next(); } - - // eslint-disable-next-line no-unused-vars, @typescript-eslint/no-unused-vars - #logError (err: Error, req: Request, res: Response, _next: NextFunction) { - this.#logger.error(`Unhandled error:\n${err.stack || err}`); - if (!req.complete) - res.status(500).send('An internal error was encountered'); + + #logError (err: Error, _req: Request, res: Response, _next: NextFunction) { + + if (err.name === 'SyntaxError' && err.message.includes('JSON') && err.stack?.includes('body-parser')) { + if (!res.closed) + res.status(400).send(err.message); + // else quietly eat the error, user posted bad json + } else { + this.#logger.error(`Unhandled error:\n${err.stack || err}`); + if (!res.closed) + res.status(500).send('An internal error was encountered'); + } } #logRequest (req: Request, res: Response, next: NextFunction) { diff --git a/src/server/middleware/Util.ts b/src/server/middleware/Util.ts index d60b70b..c06faf1 100644 --- a/src/server/middleware/Util.ts +++ b/src/server/middleware/Util.ts @@ -7,6 +7,8 @@ type FileFilterOptions = { fileExtensions?: FileExtensions[] } +type ContentTypeOptions = string | string[]; + class UtilityMiddleware { constructor () { @@ -21,6 +23,29 @@ class UtilityMiddleware { }; } + static expectContentType (opts: ContentTypeOptions): MiddlewareFunction { + + if (!opts) + throw new Error('Missing expected content type'); + + let options: string[] | null = null; + if ((opts instanceof Array)) + options = opts; + else + options = [ opts ]; + + return (req: Request, res: Response, next: NextFunction): void => { + const contentType = req.get('content-type'); + if (!contentType) + return void res.status(400).send('Must provide Content-Type'); + + if (!options?.includes(contentType)) + return void res.status(400).send(`Invalid content type; Expecting ${options?.join('/')}`); + + next(); + }; + } + static denyFileTypes (options: FileFilterOptions): MiddlewareFunction { if (!options) throw new Error('Need to supply options'); diff --git a/src/util/PermissionManager.ts b/src/util/PermissionManager.ts index 7133e02..0fb422f 100644 --- a/src/util/PermissionManager.ts +++ b/src/util/PermissionManager.ts @@ -105,7 +105,7 @@ class PermissionManager { PermissionManager.merge(to[key] as Permissions, from[key] as Permissions); // eslint-disable-next-line no-undefined } else if (!(key in to)) { - to[key] = from[key]; + to[key] = from[key]; } } return to;