diff --git a/server/package-lock.json b/server/package-lock.json index b8065aa..d4bf20f 100644 --- a/server/package-lock.json +++ b/server/package-lock.json @@ -15,6 +15,7 @@ "typeorm": "0.3.20" }, "devDependencies": { + "@types/express": "^4.17.21", "@types/node": "^16.11.10", "ts-node": "10.9.1", "typescript": "4.5.2" @@ -111,12 +112,100 @@ "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", "devOptional": true }, + "node_modules/@types/body-parser": { + "version": "1.19.5", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz", + "integrity": "sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==", + "dev": true, + "dependencies": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "node_modules/@types/connect": { + "version": "3.4.38", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", + "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/express": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.21.tgz", + "integrity": "sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==", + "dev": true, + "dependencies": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^4.17.33", + "@types/qs": "*", + "@types/serve-static": "*" + } + }, + "node_modules/@types/express-serve-static-core": { + "version": "4.17.42", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.42.tgz", + "integrity": "sha512-ckM3jm2bf/MfB3+spLPWYPUH573plBFwpOhqQ2WottxYV85j1HQFlxmnTq57X1yHY9awZPig06hL/cLMgNWHIQ==", + "dev": true, + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*", + "@types/send": "*" + } + }, + "node_modules/@types/http-errors": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.4.tgz", + "integrity": "sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==", + "dev": true + }, + "node_modules/@types/mime": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", + "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", + "dev": true + }, "node_modules/@types/node": { "version": "16.18.77", "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.77.tgz", "integrity": "sha512-zwqAbRkHjGlxH9PBv8i9dmeaDpBRgfQDSFuREMF2Z+WUi8uc13gfRquMV/8LxBqwm+7jBz+doTVkEEA1CIWOnQ==", "devOptional": true }, + "node_modules/@types/qs": { + "version": "6.9.11", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.11.tgz", + "integrity": "sha512-oGk0gmhnEJK4Yyk+oI7EfXsLayXatCWPHary1MtcmbAifkobT9cM9yutG/hZKIseOU0MqbIwQ/u2nn/Gb+ltuQ==", + "dev": true + }, + "node_modules/@types/range-parser": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", + "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", + "dev": true + }, + "node_modules/@types/send": { + "version": "0.17.4", + "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz", + "integrity": "sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==", + "dev": true, + "dependencies": { + "@types/mime": "^1", + "@types/node": "*" + } + }, + "node_modules/@types/serve-static": { + "version": "1.15.5", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.5.tgz", + "integrity": "sha512-PDRk21MnK70hja/YF8AHfC7yIsiQHn1rcXx7ijCFBX/k+XQJhQT/gw3xekXKJvx+5SXaMMS8oqQy09Mzvz2TuQ==", + "dev": true, + "dependencies": { + "@types/http-errors": "*", + "@types/mime": "*", + "@types/node": "*" + } + }, "node_modules/accepts": { "version": "1.3.8", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", diff --git a/server/package.json b/server/package.json index 029f7a9..f126695 100644 --- a/server/package.json +++ b/server/package.json @@ -4,19 +4,20 @@ "description": "Awesome project developed with TypeORM.", "type": "commonjs", "devDependencies": { - "ts-node": "10.9.1", + "@types/express": "^4.17.21", "@types/node": "^16.11.10", + "ts-node": "10.9.1", "typescript": "4.5.2" }, "dependencies": { - "typeorm": "0.3.20", - "reflect-metadata": "^0.1.13", - "mysql": "^2.14.1", + "body-parser": "^1.19.1", "express": "^4.17.2", - "body-parser": "^1.19.1" + "mysql": "^2.14.1", + "reflect-metadata": "^0.1.13", + "typeorm": "0.3.20" }, "scripts": { "start": "ts-node src/index.ts", "typeorm": "typeorm-ts-node-commonjs" } -} \ No newline at end of file +} diff --git a/server/src/controller/errorController.ts b/server/src/controller/errorController.ts new file mode 100644 index 0000000..41e61aa --- /dev/null +++ b/server/src/controller/errorController.ts @@ -0,0 +1,52 @@ +import {NextFunction, Request, Response} from "express"; +import {AppError} from "../../util/AppError"; + +function sendDevError(res: Response, err: AppError){ + + // During development, we want full error information + return res.status(err.statusCode).json({ + status: err.status, + message: err.message, + stack: err.stack, + err + }) +} + +function sendProdError(res: Response, err: AppError){ + if (err.isOperational){ + res.status(err.statusCode).json({ + status: err.status, + message: err.message + }) + } + + // Non-operational errors may leak information about vulnerabilities + console.log('ERROR', err); + res.status(500).json({ + status: "Error", + message: "Something went wrong on our side!" + }) +} + +export async function errorHandler( + err: AppError, + _req: Request, + res: Response, + _next: NextFunction + ){ + // Errors thrown by libraries + if (err.name === 'JsonWebTokenError'){ + err = new AppError('Invalid token. Please log in again', 401); + } + if (err.name === 'TokenExpiredError'){ + err = new AppError('Your token has expired. Please log in again', 401); + } + + // If we are in dev use sendDevError + if (process.env.NODE_ENV === 'development'){ + sendDevError(res, err); + return + } + + sendProdError(res, err) +} diff --git a/server/src/index.ts b/server/src/index.ts index df4cdf1..b6ea67c 100644 --- a/server/src/index.ts +++ b/server/src/index.ts @@ -4,6 +4,7 @@ import { Request, Response } from "express" import { AppDataSource } from "./data-source" import { Routes } from "./routes" import { User } from "./entity/User" +import {errorHandler} from "./controller/errorController"; AppDataSource.initialize().then(async () => { @@ -25,8 +26,7 @@ AppDataSource.initialize().then(async () => { }) // setup express app here - // ... - + app.use(errorHandler) // start express server app.listen(3000) diff --git a/server/util/AppError.ts b/server/util/AppError.ts new file mode 100644 index 0000000..08cb13b --- /dev/null +++ b/server/util/AppError.ts @@ -0,0 +1,23 @@ +enum Status { + fail = "Failure", + error = "Error", + success = "Success" +} + +export class AppError extends Error { + statusCode: number; + + status: Status; + + // Errors caused by the user are operational + isOperational: boolean; + + constructor(message: string, statusCode: number ){ + super(message); + this.statusCode = statusCode; + this.isOperational = true; + this.status = `${statusCode}`.startsWith("4") ? Status.fail : Status.error; + + Error.captureStackTrace(this, this.constructor); + } +} \ No newline at end of file diff --git a/server/util/catchAsync.ts b/server/util/catchAsync.ts new file mode 100644 index 0000000..fd5ba66 --- /dev/null +++ b/server/util/catchAsync.ts @@ -0,0 +1,8 @@ +import {NextFunction, Request, Response} from "express"; + +export function catchAsync +(fn: (req: Request, res: Response, next: NextFunction) => Promise){ + return async (req: Request, res: Response, next: NextFunction) => { + await fn(req, res, next).catch(next); + }; +} \ No newline at end of file