From 930b51a04bf2f811eff42b8152240f6db73fe1eb Mon Sep 17 00:00:00 2001 From: seantokuzo Date: Mon, 3 Jun 2024 20:00:01 -0700 Subject: [PATCH 01/40] create reusable error classes --- server/errors/badRequestError.ts | 15 +++++++++++++++ server/errors/customError.ts | 22 ++++++++++++++++++++++ server/errors/databaseConnectionError.ts | 15 +++++++++++++++ server/errors/internalError.ts | 15 +++++++++++++++ server/errors/notAuthorizedError.ts | 15 +++++++++++++++ server/errors/notFoundError.ts | 15 +++++++++++++++ server/errors/requestValidationError.ts | 22 ++++++++++++++++++++++ server/errors/validationError.ts | 11 +++++++++++ 8 files changed, 130 insertions(+) create mode 100644 server/errors/badRequestError.ts create mode 100644 server/errors/customError.ts create mode 100644 server/errors/databaseConnectionError.ts create mode 100644 server/errors/internalError.ts create mode 100644 server/errors/notAuthorizedError.ts create mode 100644 server/errors/notFoundError.ts create mode 100644 server/errors/requestValidationError.ts create mode 100644 server/errors/validationError.ts diff --git a/server/errors/badRequestError.ts b/server/errors/badRequestError.ts new file mode 100644 index 0000000..0ebab73 --- /dev/null +++ b/server/errors/badRequestError.ts @@ -0,0 +1,15 @@ +import { CustomError } from './customError'; + +export class BadRequestError extends CustomError { + statusCode = 400; + + constructor(public message: string) { + super(`❌ Bad Request Error: ${message}`); + + Object.setPrototypeOf(this, BadRequestError.prototype); + } + + serializeErrors() { + return [{ message: this.message }]; + } +} diff --git a/server/errors/customError.ts b/server/errors/customError.ts new file mode 100644 index 0000000..1843e52 --- /dev/null +++ b/server/errors/customError.ts @@ -0,0 +1,22 @@ +/* +- This is an abstract class (cannot be instantiated) for all of our error types to inherit from +- enforces that all of our error classes include: + 1) statusCode & message (mostly used for console.logs) properties + 2) serializeErrors method to ensure all errors are formatted the same +*/ +export abstract class CustomError extends Error { + abstract statusCode: number; + + constructor(message: string) { + super(message); + + /* + This step is bc we are inheriting from a built in JS class + Helps correctly set the prototype chain of our custom error classes + Only really necessary if we are targeting es5 when transpiling typescript + */ + Object.setPrototypeOf(this, CustomError.prototype); + } + + abstract serializeErrors(): { message: string; field?: string }[]; +} diff --git a/server/errors/databaseConnectionError.ts b/server/errors/databaseConnectionError.ts new file mode 100644 index 0000000..a563a42 --- /dev/null +++ b/server/errors/databaseConnectionError.ts @@ -0,0 +1,15 @@ +import { CustomError } from './customError'; + +export class DatabaseConnectionError extends CustomError { + statusCode = 500; + + constructor() { + super('❌ Database Connection Error'); + + Object.setPrototypeOf(this, DatabaseConnectionError.prototype); + } + + serializeErrors() { + return [{ message: 'Something went wrong, please try again later' }]; + } +} diff --git a/server/errors/internalError.ts b/server/errors/internalError.ts new file mode 100644 index 0000000..507fa50 --- /dev/null +++ b/server/errors/internalError.ts @@ -0,0 +1,15 @@ +import { CustomError } from './customError'; + +export class InternalError extends CustomError { + statusCode = 500; + + constructor() { + super('❌ Internal Error'); + + Object.setPrototypeOf(this, InternalError.prototype); + } + + serializeErrors() { + return [{ message: 'Something went wrong, please try again later' }]; + } +} diff --git a/server/errors/notAuthorizedError.ts b/server/errors/notAuthorizedError.ts new file mode 100644 index 0000000..0bfb06e --- /dev/null +++ b/server/errors/notAuthorizedError.ts @@ -0,0 +1,15 @@ +import { CustomError } from './customError'; + +export class NotAuthorizedError extends CustomError { + statusCode = 401; + + constructor() { + super('❌ Not Authorized'); + + Object.setPrototypeOf(this, NotAuthorizedError.prototype); + } + + serializeErrors() { + return [{ message: 'Not Authorized' }]; + } +} diff --git a/server/errors/notFoundError.ts b/server/errors/notFoundError.ts new file mode 100644 index 0000000..8e6d9b0 --- /dev/null +++ b/server/errors/notFoundError.ts @@ -0,0 +1,15 @@ +import { CustomError } from './customError'; + +export class NotFoundError extends CustomError { + statusCode = 404; + + constructor() { + super('❌ Not Found Error'); + + Object.setPrototypeOf(this, NotFoundError.prototype); + } + + serializeErrors() { + return [{ message: 'Not Found' }]; + } +} diff --git a/server/errors/requestValidationError.ts b/server/errors/requestValidationError.ts new file mode 100644 index 0000000..2d93b07 --- /dev/null +++ b/server/errors/requestValidationError.ts @@ -0,0 +1,22 @@ +import { CustomError } from './customError'; +import { ValidationError } from './validationError'; + +export class RequestValidationError extends CustomError { + statusCode = 400; + + /* + when instantiating, accepts an array of ValidationError's as an argument + in case 1 or more validations fail + */ + constructor(public errors: ValidationError[]) { + super('❌ Request Validation Failed'); + + Object.setPrototypeOf(this, RequestValidationError.prototype); + } + + serializeErrors() { + return this.errors.map((err) => { + return { message: err.message, field: err.field }; + }); + } +} diff --git a/server/errors/validationError.ts b/server/errors/validationError.ts new file mode 100644 index 0000000..9afad01 --- /dev/null +++ b/server/errors/validationError.ts @@ -0,0 +1,11 @@ +export class ValidationError extends Error { + field: string; + // use field argument for info on what failed validation, eg. "email" or "password" + constructor(message: string, field: string) { + super(message); + + this.field = field; + + Object.setPrototypeOf(this, ValidationError.prototype); + } +} From d5d4a49bebd917ba9f13353a8bb0272e0996238a Mon Sep 17 00:00:00 2001 From: seantokuzo Date: Mon, 3 Jun 2024 20:37:55 -0700 Subject: [PATCH 02/40] install express-async-errors dependency --- package-lock.json | 16 ++++++++++++++++ package.json | 1 + 2 files changed, 17 insertions(+) diff --git a/package-lock.json b/package-lock.json index 576b471..88be632 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,6 +14,7 @@ "cookie-parser": "^1.4.6", "dotenv": "^16.4.5", "express": "^4.19.2", + "express-async-errors": "^3.1.1", "express-async-handler": "^1.2.0", "jsonwebtoken": "^9.0.2", "mongoose": "^8.3.4", @@ -2609,6 +2610,15 @@ "node": ">= 0.10.0" } }, + "node_modules/express-async-errors": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/express-async-errors/-/express-async-errors-3.1.1.tgz", + "integrity": "sha512-h6aK1da4tpqWSbyCa3FxB/V6Ehd4EEB15zyQq9qe75OZBp0krinNKuH4rAY+S/U/2I36vdLAUFSjQJ+TFmODng==", + "license": "ISC", + "peerDependencies": { + "express": "^4.16.2" + } + }, "node_modules/express-async-handler": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/express-async-handler/-/express-async-handler-1.2.0.tgz", @@ -7953,6 +7963,12 @@ "vary": "~1.1.2" } }, + "express-async-errors": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/express-async-errors/-/express-async-errors-3.1.1.tgz", + "integrity": "sha512-h6aK1da4tpqWSbyCa3FxB/V6Ehd4EEB15zyQq9qe75OZBp0krinNKuH4rAY+S/U/2I36vdLAUFSjQJ+TFmODng==", + "requires": {} + }, "express-async-handler": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/express-async-handler/-/express-async-handler-1.2.0.tgz", diff --git a/package.json b/package.json index d2690bc..4008c79 100644 --- a/package.json +++ b/package.json @@ -34,6 +34,7 @@ "cookie-parser": "^1.4.6", "dotenv": "^16.4.5", "express": "^4.19.2", + "express-async-errors": "^3.1.1", "express-async-handler": "^1.2.0", "jsonwebtoken": "^9.0.2", "mongoose": "^8.3.4", From 0f1a52be8eb8964f3fa6fe2ffb4045effd31d54b Mon Sep 17 00:00:00 2001 From: seantokuzo Date: Mon, 3 Jun 2024 20:38:52 -0700 Subject: [PATCH 03/40] import package into index.ts for use across the backend --- server/index.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/server/index.ts b/server/index.ts index 7e365a2..0152bd8 100644 --- a/server/index.ts +++ b/server/index.ts @@ -1,5 +1,6 @@ import path from "path"; import express, { Request, Response, Application, NextFunction } from "express"; +import "express-async-errors"; import userRoutes from "./routes/userRoutes"; import profileRoutes from "./routes/profileRoutes"; import authRoutes from "./routes/authRoutes"; From f7ee32c9d2112fc6e91bbae8930a41d2ab185f18 Mon Sep 17 00:00:00 2001 From: seantokuzo Date: Tue, 4 Jun 2024 16:54:52 -0700 Subject: [PATCH 04/40] update map to destructure --- server/errors/requestValidationError.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/server/errors/requestValidationError.ts b/server/errors/requestValidationError.ts index 2d93b07..0d3d3ed 100644 --- a/server/errors/requestValidationError.ts +++ b/server/errors/requestValidationError.ts @@ -1,5 +1,5 @@ -import { CustomError } from './customError'; -import { ValidationError } from './validationError'; +import { CustomError } from "./customError"; +import { ValidationError } from "./validationError"; export class RequestValidationError extends CustomError { statusCode = 400; @@ -15,8 +15,8 @@ export class RequestValidationError extends CustomError { } serializeErrors() { - return this.errors.map((err) => { - return { message: err.message, field: err.field }; + return this.errors.map(({ message, field }) => { + return { message, field }; }); } } From 74d943b65966b447b22c1a970acc376257331a94 Mon Sep 17 00:00:00 2001 From: seantokuzo Date: Thu, 6 Jun 2024 17:03:56 -0700 Subject: [PATCH 05/40] apply prettier formatting --- server/errors/requestValidationError.ts | 4 ++-- server/index.ts | 28 ++++++++++++------------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/server/errors/requestValidationError.ts b/server/errors/requestValidationError.ts index 0d3d3ed..373ea30 100644 --- a/server/errors/requestValidationError.ts +++ b/server/errors/requestValidationError.ts @@ -1,5 +1,5 @@ -import { CustomError } from "./customError"; -import { ValidationError } from "./validationError"; +import { CustomError } from './customError'; +import { ValidationError } from './validationError'; export class RequestValidationError extends CustomError { statusCode = 400; diff --git a/server/index.ts b/server/index.ts index 4e16674..37cfda0 100644 --- a/server/index.ts +++ b/server/index.ts @@ -1,17 +1,17 @@ -import path from "path"; -import express, { Request, Response, Application } from "express"; -import "express-async-errors"; -import userRoutes from "./routes/userRoutes"; -import profileRoutes from "./routes/profileRoutes"; -import authRoutes from "./routes/authRoutes"; -import imageRoutes from "./routes/imageRoutes"; -import alumniRoutes from "./routes/alumniRoutes"; -import forumRoutes from "./routes/forumRoutes"; -import devRoutes from "./routes/devRoutes"; -import connectDB from "./config/db"; -import dotenv from "dotenv"; -import cookieParser from "cookie-parser"; -import { notFound, errorHandler } from "./controllers/errorControllers"; +import path from 'path'; +import express, { Request, Response, Application } from 'express'; +import 'express-async-errors'; +import userRoutes from './routes/userRoutes'; +import profileRoutes from './routes/profileRoutes'; +import authRoutes from './routes/authRoutes'; +import imageRoutes from './routes/imageRoutes'; +import alumniRoutes from './routes/alumniRoutes'; +import forumRoutes from './routes/forumRoutes'; +import devRoutes from './routes/devRoutes'; +import connectDB from './config/db'; +import dotenv from 'dotenv'; +import cookieParser from 'cookie-parser'; +import { notFound, errorHandler } from './controllers/errorControllers'; dotenv.config(); From 3fd0c53b0d80dbb3bbe3cac966d62dce51c4ba9a Mon Sep 17 00:00:00 2001 From: Sean Date: Sat, 8 Jun 2024 08:29:36 -0400 Subject: [PATCH 06/40] CHE-169 Updated image removal script --- scripts/docker-remove-all.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/docker-remove-all.sh b/scripts/docker-remove-all.sh index dcb7e55..5bd361f 100644 --- a/scripts/docker-remove-all.sh +++ b/scripts/docker-remove-all.sh @@ -29,7 +29,7 @@ fi # Remove images echo -e "${CYAN}Removing existing dev images from local environment:${NC}" -IMAGES=$(docker images codehammers/$IMAGE_SEARCH_NAME_DEV* -q) +IMAGES=$(docker images codehammers/*$IMAGE_SEARCH_NAME_DEV* -q) REMOVEDIMAGES=0 if [ ! -z "$IMAGES" ]; then docker rmi $IMAGES --force From bb6e77e75a38588960671ac7a58fa919ff8c7b85 Mon Sep 17 00:00:00 2001 From: seantokuzo Date: Sat, 8 Jun 2024 23:06:28 -0700 Subject: [PATCH 07/40] simplify imports of custom error classes --- server/errors/index.ts | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 server/errors/index.ts diff --git a/server/errors/index.ts b/server/errors/index.ts new file mode 100644 index 0000000..0cc8e05 --- /dev/null +++ b/server/errors/index.ts @@ -0,0 +1,8 @@ +export * from './badRequestError'; +export * from './customError'; +export * from './databaseConnectionError'; +export * from './internalError'; +export * from './notAuthorizedError'; +export * from './notFoundError'; +export * from './requestValidationError'; +export * from './validationError'; From 326cc88ce1740ad851efc40ee4610a3454ad5e2e Mon Sep 17 00:00:00 2001 From: seantokuzo Date: Sat, 8 Jun 2024 23:07:11 -0700 Subject: [PATCH 08/40] create global error handler middleware to handle custom errors --- server/middleware/errorHandler.ts | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 server/middleware/errorHandler.ts diff --git a/server/middleware/errorHandler.ts b/server/middleware/errorHandler.ts new file mode 100644 index 0000000..cb6201d --- /dev/null +++ b/server/middleware/errorHandler.ts @@ -0,0 +1,28 @@ +import { Request, Response, NextFunction } from 'express'; +import { CustomError, InternalError } from '../errors'; + +interface OldError { + status: number; + log: string; + message: string; +} + +const errorHandler = (err: Error | OldError, _req: Request, res: Response, _next: NextFunction) => { + // if it's one of our custom errors, use it's properties/method + if (err instanceof CustomError) { + console.log(err.message); + return res.status(err.statusCode).send(err.serializeErrors()); + } + + // Handles errors thrown the old way + if (!(err instanceof Error) && err.status && err.log && err.message) { + console.log(err.log); + return res.status(err.status).send([{ message: err.message }]); + } + + // if it's an unhandled/unknown error, send back a generic error + const internalError = new InternalError(); + return res.status(internalError.statusCode).send(internalError.serializeErrors()); +}; + +export default errorHandler; From 7bb767aa579da99119409395ac79a6b338eeb666 Mon Sep 17 00:00:00 2001 From: seantokuzo Date: Sat, 8 Jun 2024 23:07:37 -0700 Subject: [PATCH 09/40] plug in new global error handler --- server/index.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/server/index.ts b/server/index.ts index 37cfda0..92cbfac 100644 --- a/server/index.ts +++ b/server/index.ts @@ -11,7 +11,8 @@ import devRoutes from './routes/devRoutes'; import connectDB from './config/db'; import dotenv from 'dotenv'; import cookieParser from 'cookie-parser'; -import { notFound, errorHandler } from './controllers/errorControllers'; +import { notFound } from './controllers/errorControllers'; +import errorHandler from './middleware/errorHandler'; dotenv.config(); From 8e222aa420d9d689aca838882b899b9fe8bb2704 Mon Sep 17 00:00:00 2001 From: seantokuzo Date: Sat, 8 Jun 2024 23:44:57 -0700 Subject: [PATCH 10/40] update old error condition in global error handler --- server/middleware/errorHandler.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/server/middleware/errorHandler.ts b/server/middleware/errorHandler.ts index cb6201d..feecf33 100644 --- a/server/middleware/errorHandler.ts +++ b/server/middleware/errorHandler.ts @@ -4,7 +4,7 @@ import { CustomError, InternalError } from '../errors'; interface OldError { status: number; log: string; - message: string; + message: { err: string }; } const errorHandler = (err: Error | OldError, _req: Request, res: Response, _next: NextFunction) => { @@ -17,7 +17,7 @@ const errorHandler = (err: Error | OldError, _req: Request, res: Response, _next // Handles errors thrown the old way if (!(err instanceof Error) && err.status && err.log && err.message) { console.log(err.log); - return res.status(err.status).send([{ message: err.message }]); + return res.status(err.status).send([{ message: err.message.err }]); } // if it's an unhandled/unknown error, send back a generic error From eff0e420bcb90f57c44507824f9983e9a64fa15e Mon Sep 17 00:00:00 2001 From: seantokuzo Date: Sun, 9 Jun 2024 00:02:06 -0700 Subject: [PATCH 11/40] comment grammar cleanup --- server/middleware/errorHandler.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/server/middleware/errorHandler.ts b/server/middleware/errorHandler.ts index feecf33..189fbc7 100644 --- a/server/middleware/errorHandler.ts +++ b/server/middleware/errorHandler.ts @@ -8,19 +8,19 @@ interface OldError { } const errorHandler = (err: Error | OldError, _req: Request, res: Response, _next: NextFunction) => { - // if it's one of our custom errors, use it's properties/method + // If it is one of our custom errors use its properties/method if (err instanceof CustomError) { console.log(err.message); return res.status(err.statusCode).send(err.serializeErrors()); } - // Handles errors thrown the old way + // Handle errors thrown the old way if (!(err instanceof Error) && err.status && err.log && err.message) { console.log(err.log); return res.status(err.status).send([{ message: err.message.err }]); } - // if it's an unhandled/unknown error, send back a generic error + // If it is an unknown error send back a generic error const internalError = new InternalError(); return res.status(internalError.statusCode).send(internalError.serializeErrors()); }; From 7185df668c23529d4e0c319025b2703b0798a7b6 Mon Sep 17 00:00:00 2001 From: seantokuzo Date: Mon, 10 Jun 2024 13:01:28 -0700 Subject: [PATCH 12/40] remove unused errorHandler controller --- server/controllers/errorControllers.ts | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/server/controllers/errorControllers.ts b/server/controllers/errorControllers.ts index 1900759..4c7b2ec 100644 --- a/server/controllers/errorControllers.ts +++ b/server/controllers/errorControllers.ts @@ -6,18 +6,4 @@ const notFound = (req: Request, res: Response, next: NextFunction): void => { next(error); }; -const errorHandler = (err: Error, _req: Request, res: Response, _next: NextFunction): void => { - const defaultErr = { - log: 'Express error handler caught unknown middleware error', - status: 400, - message: { err: 'An error occurred' }, - }; - const errorObj = Object.assign({}, defaultErr, err); - console.log(errorObj.log); - res.status(errorObj.status).json({ - message: errorObj.message, - stack: process.env.NODE_ENV === 'production' ? null : err.stack, - }); -}; - -export { notFound, errorHandler }; +export { notFound }; From 6bcc572340d729822777fc303d814853cf32d44a Mon Sep 17 00:00:00 2001 From: seantokuzo Date: Mon, 10 Jun 2024 13:03:05 -0700 Subject: [PATCH 13/40] switch console.log to console.error and log entire error to include stack trace --- server/middleware/errorHandler.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/server/middleware/errorHandler.ts b/server/middleware/errorHandler.ts index 189fbc7..c25740b 100644 --- a/server/middleware/errorHandler.ts +++ b/server/middleware/errorHandler.ts @@ -8,15 +8,14 @@ interface OldError { } const errorHandler = (err: Error | OldError, _req: Request, res: Response, _next: NextFunction) => { + console.error(err); // If it is one of our custom errors use its properties/method if (err instanceof CustomError) { - console.log(err.message); return res.status(err.statusCode).send(err.serializeErrors()); } // Handle errors thrown the old way if (!(err instanceof Error) && err.status && err.log && err.message) { - console.log(err.log); return res.status(err.status).send([{ message: err.message.err }]); } From 1aec4d8e556e2b31c0834efeeb50ea6531b29d64 Mon Sep 17 00:00:00 2001 From: seantokuzo Date: Mon, 10 Jun 2024 13:12:28 -0700 Subject: [PATCH 14/40] update errorHandler test --- __tests__/errorController.test.ts | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/__tests__/errorController.test.ts b/__tests__/errorController.test.ts index e723193..04e5b39 100644 --- a/__tests__/errorController.test.ts +++ b/__tests__/errorController.test.ts @@ -1,5 +1,7 @@ import { Request, Response, NextFunction } from 'express'; -import { notFound, errorHandler } from '../server/controllers/errorControllers'; +import { notFound } from '../server/controllers/errorControllers'; +import errorHandler from '../server/middleware/errorHandler'; +import { BadRequestError } from '../server/errors'; describe('Middleware Tests', () => { let mockRequest: Partial; @@ -10,7 +12,7 @@ describe('Middleware Tests', () => { mockRequest = {}; mockResponse = { status: jest.fn().mockReturnThis(), - json: jest.fn(), + send: jest.fn(), }; mockNext = jest.fn(); }); @@ -25,14 +27,15 @@ describe('Middleware Tests', () => { describe('errorHandler Middleware', () => { it('should handle the error correctly', () => { - const mockError = new Error('Some error'); + const mockError = new BadRequestError('Some error'); errorHandler(mockError, mockRequest as Request, mockResponse as Response, mockNext); expect(mockResponse.status).toHaveBeenCalledWith(400); - expect(mockResponse.json).toHaveBeenCalledWith( - expect.objectContaining({ - message: expect.anything(), - stack: expect.any(String), - }), + expect(mockResponse.send).toHaveBeenCalledWith( + expect.objectContaining([ + { + message: expect.any(String), + }, + ]), ); }); }); From d0ebb10ef292153fa3a84a5f89bff4c7a8fd04fb Mon Sep 17 00:00:00 2001 From: seantokuzo Date: Mon, 10 Jun 2024 18:54:48 -0700 Subject: [PATCH 15/40] refactor catch all route handler - move connectDB fn into startServer fn --- server/index.ts | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/server/index.ts b/server/index.ts index 92cbfac..f93f887 100644 --- a/server/index.ts +++ b/server/index.ts @@ -11,8 +11,8 @@ import devRoutes from './routes/devRoutes'; import connectDB from './config/db'; import dotenv from 'dotenv'; import cookieParser from 'cookie-parser'; -import { notFound } from './controllers/errorControllers'; import errorHandler from './middleware/errorHandler'; +import { NotFoundError } from './errors'; dotenv.config(); @@ -21,8 +21,6 @@ const app: Application = express(); app.use(express.json()); app.use(cookieParser()); -connectDB(); - app.get('/health', (req: Request, res: Response) => { res.status(200).send('OK'); }); @@ -49,13 +47,17 @@ if (process.env.NODE_ENV === 'production') { }); } -app.use(notFound); +app.use((_req, _res) => { + throw new NotFoundError(); +}); app.use(errorHandler); const PORT: number = Number(process.env.PORT) || 3000; export const startServer = () => { + connectDB(); + return app.listen(PORT, () => console.log(`Server running in ${process.env.NODE_ENV} mode on port ${PORT}`), ); From 2a42855bf44a4ccf94723d668cb3894630a2d826 Mon Sep 17 00:00:00 2001 From: seantokuzo Date: Mon, 10 Jun 2024 18:55:04 -0700 Subject: [PATCH 16/40] refactor not found handler test --- __tests__/errorController.test.ts | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/__tests__/errorController.test.ts b/__tests__/errorController.test.ts index 04e5b39..a566d5b 100644 --- a/__tests__/errorController.test.ts +++ b/__tests__/errorController.test.ts @@ -1,7 +1,8 @@ +import app from '../server/index'; +import request from 'supertest'; import { Request, Response, NextFunction } from 'express'; -import { notFound } from '../server/controllers/errorControllers'; import errorHandler from '../server/middleware/errorHandler'; -import { BadRequestError } from '../server/errors'; +import { BadRequestError, NotFoundError } from '../server/errors'; describe('Middleware Tests', () => { let mockRequest: Partial; @@ -18,10 +19,13 @@ describe('Middleware Tests', () => { }); describe('notFound Middleware', () => { - it('should return 404 and the original URL', () => { - notFound(mockRequest as Request, mockResponse as Response, mockNext); - expect(mockResponse.status).toHaveBeenCalledWith(404); - expect(mockNext).toHaveBeenCalled(); + it('should return 404 and the original URL', async () => { + const exampleNotFoundError = new NotFoundError(); + + const response = await request(app).get('/non-existent-route').send(); + + expect(response.status).toEqual(404); + expect(response.body).toEqual(exampleNotFoundError.serializeErrors()); }); }); From 22551a3994983c14cfdd4e9ee708a529741832a5 Mon Sep 17 00:00:00 2001 From: seantokuzo Date: Mon, 10 Jun 2024 18:55:53 -0700 Subject: [PATCH 17/40] remove unused catch all controller --- server/controllers/errorControllers.ts | 9 --------- 1 file changed, 9 deletions(-) delete mode 100644 server/controllers/errorControllers.ts diff --git a/server/controllers/errorControllers.ts b/server/controllers/errorControllers.ts deleted file mode 100644 index 4c7b2ec..0000000 --- a/server/controllers/errorControllers.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { Request, Response, NextFunction } from 'express'; - -const notFound = (req: Request, res: Response, next: NextFunction): void => { - const error = new Error(`Not found - ${req.originalUrl}`); - res.status(404); - next(error); -}; - -export { notFound }; From 16d2fbcc7b8ea68dd9e66491d8d80c13a72a9ad1 Mon Sep 17 00:00:00 2001 From: seantokuzo Date: Mon, 10 Jun 2024 19:28:13 -0700 Subject: [PATCH 18/40] linty mcLinterson --- server/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/index.ts b/server/index.ts index f93f887..275a568 100644 --- a/server/index.ts +++ b/server/index.ts @@ -57,7 +57,7 @@ const PORT: number = Number(process.env.PORT) || 3000; export const startServer = () => { connectDB(); - + return app.listen(PORT, () => console.log(`Server running in ${process.env.NODE_ENV} mode on port ${PORT}`), ); From 667b4e6a6ab9250c22180657b592109d3da8726d Mon Sep 17 00:00:00 2001 From: seantokuzo Date: Wed, 12 Jun 2024 11:31:03 -0700 Subject: [PATCH 19/40] simplify router imports --- server/index.ts | 30 ++++++++++++++++-------------- server/routes/index.ts | 9 +++++++++ 2 files changed, 25 insertions(+), 14 deletions(-) create mode 100644 server/routes/index.ts diff --git a/server/index.ts b/server/index.ts index 275a568..24b841d 100644 --- a/server/index.ts +++ b/server/index.ts @@ -1,13 +1,15 @@ import path from 'path'; import express, { Request, Response, Application } from 'express'; import 'express-async-errors'; -import userRoutes from './routes/userRoutes'; -import profileRoutes from './routes/profileRoutes'; -import authRoutes from './routes/authRoutes'; -import imageRoutes from './routes/imageRoutes'; -import alumniRoutes from './routes/alumniRoutes'; -import forumRoutes from './routes/forumRoutes'; -import devRoutes from './routes/devRoutes'; +import { + userRouter, + profileRouter, + authRouter, + imageRouter, + alumniRouter, + forumRouter, + devRouter, +} from './routes'; import connectDB from './config/db'; import dotenv from 'dotenv'; import cookieParser from 'cookie-parser'; @@ -25,13 +27,13 @@ app.get('/health', (req: Request, res: Response) => { res.status(200).send('OK'); }); -app.use('/api/users', userRoutes); -app.use('/api/profiles', profileRoutes); -app.use('/api/auth', authRoutes); -app.use('/api/images', imageRoutes); -app.use('/api/alumni', alumniRoutes); -app.use('/api/forums', forumRoutes); -app.use('/api/devRoutes', devRoutes); +app.use('/api/users', userRouter); +app.use('/api/profiles', profileRouter); +app.use('/api/auth', authRouter); +app.use('/api/images', imageRouter); +app.use('/api/alumni', alumniRouter); +app.use('/api/forums', forumRouter); +app.use('/api/devRoutes', devRouter); if (process.env.NODE_ENV === 'production') { console.log(`SERVER STARTED IN PRODUCTION`); diff --git a/server/routes/index.ts b/server/routes/index.ts new file mode 100644 index 0000000..c5165eb --- /dev/null +++ b/server/routes/index.ts @@ -0,0 +1,9 @@ +import alumniRouter from './alumniRoutes'; +import authRouter from './authRoutes'; +import forumRouter from './forumRoutes'; +import imageRouter from './imageRoutes'; +import profileRouter from './profileRoutes'; +import userRouter from './userRoutes'; +import devRouter from './devRoutes'; + +export { alumniRouter, authRouter, forumRouter, imageRouter, profileRouter, userRouter, devRouter }; From 8bb00ead6591f8935b1d2d40d41614af42e98891 Mon Sep 17 00:00:00 2001 From: seantokuzo Date: Wed, 12 Jun 2024 11:54:52 -0700 Subject: [PATCH 20/40] add env vars for startup checks --- docker-compose-dev.yml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/docker-compose-dev.yml b/docker-compose-dev.yml index 0da9545..27c2a1f 100644 --- a/docker-compose-dev.yml +++ b/docker-compose-dev.yml @@ -13,6 +13,15 @@ services: command: npm run dev-ts environment: - NODE_ENV=development + - POSTGRES_USER=postgres + - POSTGRES_DB=ch-dev-database + - POSTGRES_PASSWORD=ch-dev + - AWS_ACCESS_KEY_ID=placeholder-value + - AWS_SECRET_ACCESS_KEY=placeholder-value + - AWS_REGION=placeholder-value + - BUCKET_NAME=placeholder-value + # suppress aws sdk v2 deprecation warning + - AWS_SDK_JS_SUPPRESS_MAINTENANCE_MODE_MESSAGE=1; volumes: node_modules: client_node_modules: From 397d768ea455e84f282a7797ff79f298844123dc Mon Sep 17 00:00:00 2001 From: seantokuzo Date: Wed, 12 Jun 2024 11:55:31 -0700 Subject: [PATCH 21/40] separate app config and server startup logic --- server/app.ts | 52 +++++++++++++++++++++++++++++++++++++++ server/index.ts | 65 ++++++------------------------------------------- 2 files changed, 59 insertions(+), 58 deletions(-) create mode 100644 server/app.ts diff --git a/server/app.ts b/server/app.ts new file mode 100644 index 0000000..dcfda65 --- /dev/null +++ b/server/app.ts @@ -0,0 +1,52 @@ +import path from 'path'; +import express, { Request, Response, Application } from 'express'; +import 'express-async-errors'; +import dotenv from 'dotenv'; +import cookieParser from 'cookie-parser'; +dotenv.config(); +import { + userRouter, + profileRouter, + authRouter, + imageRouter, + alumniRouter, + forumRouter, + devRouter, +} from './routes'; +import errorHandler from './middleware/errorHandler'; +import { NotFoundError } from './errors'; + +// Instantiate application +const app = express(); + +// Middleware to parse request bodies +app.use(express.json()); +// Middleware to parse request cookies +app.use(cookieParser()); + +// API routers +app.use('/api/users', userRouter); +app.use('/api/profiles', profileRouter); +app.use('/api/auth', authRouter); +app.use('/api/images', imageRouter); +app.use('/api/alumni', alumniRouter); +app.use('/api/forums', forumRouter); +app.use('/api/devRoutes', devRouter); + +// Serve client from build in production +if (process.env.NODE_ENV === 'production') { + console.log(`SERVER STARTED IN PRODUCTION`); + app.use(express.static(path.join(__dirname, '../../client/build'))); + + app.get('*', (req: Request, res: Response) => + res.sendFile(path.resolve(__dirname, '../../client/build/index.html')), + ); +} + +app.use((_req, _res) => { + throw new NotFoundError(); +}); + +app.use(errorHandler); + +export default app; diff --git a/server/index.ts b/server/index.ts index 24b841d..57a23d3 100644 --- a/server/index.ts +++ b/server/index.ts @@ -1,72 +1,21 @@ -import path from 'path'; -import express, { Request, Response, Application } from 'express'; -import 'express-async-errors'; -import { - userRouter, - profileRouter, - authRouter, - imageRouter, - alumniRouter, - forumRouter, - devRouter, -} from './routes'; +import app from './app'; import connectDB from './config/db'; -import dotenv from 'dotenv'; -import cookieParser from 'cookie-parser'; -import errorHandler from './middleware/errorHandler'; -import { NotFoundError } from './errors'; - -dotenv.config(); - -const app: Application = express(); - -app.use(express.json()); -app.use(cookieParser()); - -app.get('/health', (req: Request, res: Response) => { - res.status(200).send('OK'); -}); - -app.use('/api/users', userRouter); -app.use('/api/profiles', profileRouter); -app.use('/api/auth', authRouter); -app.use('/api/images', imageRouter); -app.use('/api/alumni', alumniRouter); -app.use('/api/forums', forumRouter); -app.use('/api/devRoutes', devRouter); - -if (process.env.NODE_ENV === 'production') { - console.log(`SERVER STARTED IN PRODUCTION`); - app.use(express.static(path.join(__dirname, '../../client/build'))); - - app.get('*', (req: Request, res: Response) => - res.sendFile(path.resolve(__dirname, '../../client/build/index.html')), - ); -} else { - console.log('SERVER STARTED IN DEV'); - app.get('/api', (req: Request, res: Response) => { - res.json({ message: 'API Running - Hazzah!' }); - }); -} - -app.use((_req, _res) => { - throw new NotFoundError(); -}); - -app.use(errorHandler); const PORT: number = Number(process.env.PORT) || 3000; +// Hazzah! +const hazzah = process.env.NODE_ENV === 'development' ? 'Hazzah! ' : ''; + export const startServer = () => { + // Connect to MongoDB connectDB(); + // Startup the server return app.listen(PORT, () => - console.log(`Server running in ${process.env.NODE_ENV} mode on port ${PORT}`), + console.log(`💥 ${hazzah}Server running in ${process.env.NODE_ENV} mode on port ${PORT}`), ); }; if (require.main === module) { startServer(); } - -export default app; From 96d4d722d3b6897ad50c9abf1b571f150853fe5b Mon Sep 17 00:00:00 2001 From: seantokuzo Date: Wed, 12 Jun 2024 11:55:57 -0700 Subject: [PATCH 22/40] add env var checks to startup --- server/index.ts | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/server/index.ts b/server/index.ts index 57a23d3..432c32d 100644 --- a/server/index.ts +++ b/server/index.ts @@ -7,6 +7,18 @@ const PORT: number = Number(process.env.PORT) || 3000; const hazzah = process.env.NODE_ENV === 'development' ? 'Hazzah! ' : ''; export const startServer = () => { + // Environment variable checks + if (!process.env.JWT_SECRET) throw Error('❌ JWT_SECRET must be defined!'); + if (!process.env.MONGO_URI) throw Error('❌ MONGO_URI must be defined!'); + if (!process.env.POSTGRES_USER) throw Error('❌ # POSTGRES_USER must be defined!'); + if (!process.env.POSTGRES_DB) throw Error('❌ # POSTGRES_DB must be defined!'); + if (!process.env.POSTGRES_PASSWORD) throw Error('❌ # POSTGRES_PASSWORD must be defined!'); + if (!process.env.AWS_ACCESS_KEY_ID) throw Error('❌ # AWS_ACCESS_KEY_ID must be defined!'); + if (!process.env.AWS_SECRET_ACCESS_KEY) + throw Error('❌ # AWS_SECRET_ACCESS_KEY must be defined!'); + if (!process.env.AWS_REGION) throw Error('❌ # AWS_REGION must be defined!'); + if (!process.env.BUCKET_NAME) throw Error('❌ # BUCKET_NAME must be defined!'); + // Connect to MongoDB connectDB(); From 55dac245f9b29e7d16a32eb55fe1b60ec854c099 Mon Sep 17 00:00:00 2001 From: seantokuzo Date: Wed, 12 Jun 2024 12:03:42 -0700 Subject: [PATCH 23/40] add connection string arg to connectDB and await connection before server start --- server/config/db.ts | 19 +++++-------------- server/index.ts | 4 ++-- 2 files changed, 7 insertions(+), 16 deletions(-) diff --git a/server/config/db.ts b/server/config/db.ts index fcc2fef..b38d8d1 100644 --- a/server/config/db.ts +++ b/server/config/db.ts @@ -1,24 +1,15 @@ import mongoose from 'mongoose'; -import dotenv from 'dotenv'; -dotenv.config(); +import { DatabaseConnectionError } from '../errors'; -const connectDB = async (): Promise => { +const connectDB = async (mongoUri: string) => { try { - // Check that MONGO_URI is defined - if (!process.env.MONGO_URI) { - throw new Error('MONGO_URI must be defined in the environment variables.'); - } - - const connection = await mongoose.connect(process.env.MONGO_URI); - - console.log(`MongoDB is connected to: ${connection.connection.host}`); + const connection = await mongoose.connect(mongoUri); + console.log(`🍃 MongoDB is connected to: ${connection.connection.host}`); } catch (error) { if (error instanceof Error) { console.error(error.message); - } else { - console.error('❌ Error connecting to the database ❌'); } - process.exit(1); + throw new DatabaseConnectionError(); } }; diff --git a/server/index.ts b/server/index.ts index 432c32d..4cba8b9 100644 --- a/server/index.ts +++ b/server/index.ts @@ -6,7 +6,7 @@ const PORT: number = Number(process.env.PORT) || 3000; // Hazzah! const hazzah = process.env.NODE_ENV === 'development' ? 'Hazzah! ' : ''; -export const startServer = () => { +export const startServer = async () => { // Environment variable checks if (!process.env.JWT_SECRET) throw Error('❌ JWT_SECRET must be defined!'); if (!process.env.MONGO_URI) throw Error('❌ MONGO_URI must be defined!'); @@ -20,7 +20,7 @@ export const startServer = () => { if (!process.env.BUCKET_NAME) throw Error('❌ # BUCKET_NAME must be defined!'); // Connect to MongoDB - connectDB(); + await connectDB(process.env.MONGO_URI); // Startup the server return app.listen(PORT, () => From 99dcf464fb510ace58e8e70f677261c67c6c5c46 Mon Sep 17 00:00:00 2001 From: seantokuzo Date: Wed, 12 Jun 2024 12:42:01 -0700 Subject: [PATCH 24/40] update app import and await startServer --- __tests__/errorController.test.ts | 2 +- __tests__/index.test.ts | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/__tests__/errorController.test.ts b/__tests__/errorController.test.ts index a566d5b..7bacd6e 100644 --- a/__tests__/errorController.test.ts +++ b/__tests__/errorController.test.ts @@ -1,4 +1,4 @@ -import app from '../server/index'; +import app from '../server/app'; import request from 'supertest'; import { Request, Response, NextFunction } from 'express'; import errorHandler from '../server/middleware/errorHandler'; diff --git a/__tests__/index.test.ts b/__tests__/index.test.ts index f39a5eb..d956bfc 100644 --- a/__tests__/index.test.ts +++ b/__tests__/index.test.ts @@ -1,5 +1,6 @@ import request from 'supertest'; -import app, { startServer } from '../server/index'; +import { startServer } from '../server/index'; +import app from '../server/app'; import { Server } from 'http'; import mongoose from 'mongoose'; @@ -8,8 +9,8 @@ import mongoose from 'mongoose'; let server: Server; -beforeEach(() => { - server = startServer(); +beforeEach(async () => { + server = await startServer(); }); afterEach((done) => { From 2b692388fa3196e8ef718db277b873b980d34e71 Mon Sep 17 00:00:00 2001 From: seantokuzo Date: Wed, 12 Jun 2024 12:42:31 -0700 Subject: [PATCH 25/40] add necessary env vars for tests --- docker-compose-test.yml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/docker-compose-test.yml b/docker-compose-test.yml index ec4be8c..b24689d 100644 --- a/docker-compose-test.yml +++ b/docker-compose-test.yml @@ -15,6 +15,15 @@ services: environment: - MONGO_URI=mongodb://mongo:27017/ch-testdb - JWT_SECRET=${JWT_SECRET} + - POSTGRES_USER=postgres + - POSTGRES_DB=ch-dev-database + - POSTGRES_PASSWORD=ch-dev + - AWS_ACCESS_KEY_ID=placeholder-value + - AWS_SECRET_ACCESS_KEY=placeholder-value + - AWS_REGION=placeholder-value + - BUCKET_NAME=placeholder-value + # suppress aws sdk v2 deprecation warning + - AWS_SDK_JS_SUPPRESS_MAINTENANCE_MODE_MESSAGE=1; command: npm run test:all mongo: From 4053e888683f802feb069c3041655062b6fba1ad Mon Sep 17 00:00:00 2001 From: seantokuzo Date: Wed, 12 Jun 2024 13:02:00 -0700 Subject: [PATCH 26/40] cleanup env var check error messages --- server/index.ts | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/server/index.ts b/server/index.ts index 4cba8b9..465dac7 100644 --- a/server/index.ts +++ b/server/index.ts @@ -10,14 +10,14 @@ export const startServer = async () => { // Environment variable checks if (!process.env.JWT_SECRET) throw Error('❌ JWT_SECRET must be defined!'); if (!process.env.MONGO_URI) throw Error('❌ MONGO_URI must be defined!'); - if (!process.env.POSTGRES_USER) throw Error('❌ # POSTGRES_USER must be defined!'); - if (!process.env.POSTGRES_DB) throw Error('❌ # POSTGRES_DB must be defined!'); - if (!process.env.POSTGRES_PASSWORD) throw Error('❌ # POSTGRES_PASSWORD must be defined!'); - if (!process.env.AWS_ACCESS_KEY_ID) throw Error('❌ # AWS_ACCESS_KEY_ID must be defined!'); + if (!process.env.POSTGRES_USER) throw Error('❌ POSTGRES_USER must be defined!'); + if (!process.env.POSTGRES_DB) throw Error('❌ POSTGRES_DB must be defined!'); + if (!process.env.POSTGRES_PASSWORD) throw Error('❌ POSTGRES_PASSWORD must be defined!'); + if (!process.env.AWS_ACCESS_KEY_ID) throw Error('❌ AWS_ACCESS_KEY_ID must be defined!'); if (!process.env.AWS_SECRET_ACCESS_KEY) - throw Error('❌ # AWS_SECRET_ACCESS_KEY must be defined!'); - if (!process.env.AWS_REGION) throw Error('❌ # AWS_REGION must be defined!'); - if (!process.env.BUCKET_NAME) throw Error('❌ # BUCKET_NAME must be defined!'); + throw Error('❌ AWS_SECRET_ACCESS_KEY must be defined!'); + if (!process.env.AWS_REGION) throw Error('❌ AWS_REGION must be defined!'); + if (!process.env.BUCKET_NAME) throw Error('❌ BUCKET_NAME must be defined!'); // Connect to MongoDB await connectDB(process.env.MONGO_URI); From b16bb068c116acd812bfc7a2dbaef165a5ed6737 Mon Sep 17 00:00:00 2001 From: seantokuzo Date: Wed, 12 Jun 2024 13:02:20 -0700 Subject: [PATCH 27/40] reorder env vars --- docker-compose-test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker-compose-test.yml b/docker-compose-test.yml index b24689d..e783740 100644 --- a/docker-compose-test.yml +++ b/docker-compose-test.yml @@ -13,8 +13,8 @@ services: depends_on: - mongo environment: - - MONGO_URI=mongodb://mongo:27017/ch-testdb - JWT_SECRET=${JWT_SECRET} + - MONGO_URI=mongodb://mongo:27017/ch-testdb - POSTGRES_USER=postgres - POSTGRES_DB=ch-dev-database - POSTGRES_PASSWORD=ch-dev From 9940017629d4d89d227c09d8466f0de147e22a2d Mon Sep 17 00:00:00 2001 From: seantokuzo Date: Wed, 12 Jun 2024 13:52:48 -0700 Subject: [PATCH 28/40] pass mongoURI to connectDB - comment out unused tests --- __tests__/db.test.ts | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/__tests__/db.test.ts b/__tests__/db.test.ts index fc11d44..a1b9d43 100644 --- a/__tests__/db.test.ts +++ b/__tests__/db.test.ts @@ -1,6 +1,9 @@ import mongoose from 'mongoose'; import connectDB from '../server/config/db'; +// TODO +/*eslint jest/no-disabled-tests: "off"*/ + jest.mock('mongoose', () => ({ connect: jest.fn().mockImplementation(() => Promise.resolve({ @@ -24,26 +27,28 @@ describe('connectDB', () => { it('should call mongoose.connect with MONGO_URI', async () => { process.env.MONGO_URI = 'test-mongo-uri'; - await connectDB(); + await connectDB(process.env.MONGO_URI); expect(mongoose.connect).toHaveBeenCalledWith('test-mongo-uri'); }); - it('should log an error and exit the process if mongoose.connect fails', async () => { + // We now console.error the error's message and throw a DatabaseConnectionError instead + xit('should log an error and exit the process if mongoose.connect fails', async () => { process.env.MONGO_URI = 'test-mongo-uri'; (mongoose.connect as jest.Mock).mockImplementationOnce(() => { throw new Error('test error'); }); - await connectDB(); + await connectDB(process.env.MONGO_URI); expect(mockConsoleError).toHaveBeenCalledWith('test error'); expect(mockExit).toHaveBeenCalledWith(1); }); - it('should throw an error if MONGO_URI is not defined', async () => { + // This check has been moved to startServer in index.ts + xit('should throw an error if MONGO_URI is not defined', async () => { delete process.env.MONGO_URI; - await connectDB(); + await connectDB(process.env.MONGO_URI!); expect(mockConsoleError).toHaveBeenCalledWith( 'MONGO_URI must be defined in the environment variables.', From 9451374b85800801efc69b2608e2fdc0a4b336c3 Mon Sep 17 00:00:00 2001 From: seantokuzo Date: Wed, 12 Jun 2024 13:52:57 -0700 Subject: [PATCH 29/40] remove unused import --- server/app.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/app.ts b/server/app.ts index dcfda65..b0d762b 100644 --- a/server/app.ts +++ b/server/app.ts @@ -1,5 +1,5 @@ import path from 'path'; -import express, { Request, Response, Application } from 'express'; +import express, { Request, Response } from 'express'; import 'express-async-errors'; import dotenv from 'dotenv'; import cookieParser from 'cookie-parser'; From d3b2a50971740b9db57218918a9481bca6922bcf Mon Sep 17 00:00:00 2001 From: seantokuzo Date: Wed, 12 Jun 2024 13:53:38 -0700 Subject: [PATCH 30/40] prettier fix --- server/index.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/server/index.ts b/server/index.ts index 465dac7..4a5cbb2 100644 --- a/server/index.ts +++ b/server/index.ts @@ -14,8 +14,7 @@ export const startServer = async () => { if (!process.env.POSTGRES_DB) throw Error('❌ POSTGRES_DB must be defined!'); if (!process.env.POSTGRES_PASSWORD) throw Error('❌ POSTGRES_PASSWORD must be defined!'); if (!process.env.AWS_ACCESS_KEY_ID) throw Error('❌ AWS_ACCESS_KEY_ID must be defined!'); - if (!process.env.AWS_SECRET_ACCESS_KEY) - throw Error('❌ AWS_SECRET_ACCESS_KEY must be defined!'); + if (!process.env.AWS_SECRET_ACCESS_KEY) throw Error('❌ AWS_SECRET_ACCESS_KEY must be defined!'); if (!process.env.AWS_REGION) throw Error('❌ AWS_REGION must be defined!'); if (!process.env.BUCKET_NAME) throw Error('❌ BUCKET_NAME must be defined!'); From 1e89b4b371d99decf3c813e35a4f5ef8a76a92d6 Mon Sep 17 00:00:00 2001 From: seantokuzo Date: Wed, 12 Jun 2024 13:58:47 -0700 Subject: [PATCH 31/40] comments --- server/app.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/server/app.ts b/server/app.ts index b0d762b..188aeb6 100644 --- a/server/app.ts +++ b/server/app.ts @@ -43,10 +43,12 @@ if (process.env.NODE_ENV === 'production') { ); } +// Catch all route handler app.use((_req, _res) => { throw new NotFoundError(); }); +// Global error handler app.use(errorHandler); export default app; From c9e1dd0f450fc57a5a0ff611e704b5c6a3c788ec Mon Sep 17 00:00:00 2001 From: seantokuzo Date: Fri, 14 Jun 2024 20:09:15 -0700 Subject: [PATCH 32/40] update github workflows image version v2 -> v3 --- .github/workflows/build-tests.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build-tests.yml b/.github/workflows/build-tests.yml index 5839f79..7c6a958 100644 --- a/.github/workflows/build-tests.yml +++ b/.github/workflows/build-tests.yml @@ -13,11 +13,11 @@ jobs: with: node-version: '18.17.1' - name: Build Docker Image - run: docker build -t codehammers/ch-dev-dep-v2:latest -f Dockerfile-dev . + run: docker build -t codehammers/ch-dev-dep-v3:latest -f Dockerfile-dev . - name: Install Root Dependencies - run: docker run codehammers/ch-dev-dep-v2:latest npm install + run: docker run codehammers/ch-dev-dep-v3:latest npm install - name: Install Client Dependencies - run: docker run codehammers/ch-dev-dep-v2:latest /bin/sh -c "cd client && npm install" + run: docker run codehammers/ch-dev-dep-v3:latest /bin/sh -c "cd client && npm install" - run: LINT_COMMAND=lint docker-compose -f docker-compose-lint.yml up --abort-on-container-exit - run: docker-compose -f docker-compose-test.yml up --abort-on-container-exit env: From ead7409ca64fc8ebd6b0fc2acae8dbf90429c452 Mon Sep 17 00:00:00 2001 From: seantokuzo Date: Sun, 16 Jun 2024 13:39:57 -0700 Subject: [PATCH 33/40] refactor auth mw to use custom error class - add user to express Request type --- server/middleware/authMiddleware.ts | 62 ++++++++++++++--------------- 1 file changed, 30 insertions(+), 32 deletions(-) diff --git a/server/middleware/authMiddleware.ts b/server/middleware/authMiddleware.ts index beddda8..066d581 100644 --- a/server/middleware/authMiddleware.ts +++ b/server/middleware/authMiddleware.ts @@ -1,38 +1,36 @@ -import jwt from 'jsonwebtoken'; +import jwt, { JwtPayload } from 'jsonwebtoken'; import User from '../models/userModel'; -import asyncHandler from 'express-async-handler'; -import { CustomRequest } from '../types/customRequest'; - -const protect = asyncHandler(async (req: CustomRequest, res, next) => { - let token; - - if (req.cookies.token) { - try { - token = req.cookies.token; - const secret = process.env.JWT_SECRET as string; - const decoded = jwt.verify(token, secret) as jwt.JwtPayload; - - if (!decoded.id) { - throw new Error('Invalid token - ID not found'); - } - - const user = await User.findById(decoded.id).select('-password'); - - if (!user) throw new Error('User not found'); - req.user = { id: user._id.toString() }; - res.locals.user = user; - next(); - } catch (error) { - console.error(error); - res.status(401); - throw new Error('Not authorized, token failed'); - } +import { Request, Response, NextFunction } from 'express'; +import { NotAuthorizedError } from '../errors'; + +interface UserPayload { + id: string; +} + +declare module 'express-serve-static-core' { + interface Request { + user?: UserPayload; } +} + +const protect = async (req: Request, res: Response, next: NextFunction) => { + const { token } = req.cookies; + + if (!token) throw new NotAuthorizedError(); + + try { + const decoded = jwt.verify(token, process.env.JWT_SECRET!) as JwtPayload; + + const user = await User.findById(decoded.id).select('-password'); + + if (!user) throw new NotAuthorizedError(); - if (!token) { - res.status(401); - throw new Error('Not authorized, no token'); + req.user = { id: user._id.toString() }; + res.locals.user = user; + next(); + } catch (error) { + throw new NotAuthorizedError(); } -}); +}; export { protect }; From e3ff7e3a9877fa090ec06a0cd69d0dc29b2b4eb6 Mon Sep 17 00:00:00 2001 From: seantokuzo Date: Sun, 16 Jun 2024 13:40:59 -0700 Subject: [PATCH 34/40] refactor auth controller to use protect middleware --- server/controllers/authController.ts | 40 +++++----------------------- 1 file changed, 6 insertions(+), 34 deletions(-) diff --git a/server/controllers/authController.ts b/server/controllers/authController.ts index 28df5f3..5fc5c5c 100644 --- a/server/controllers/authController.ts +++ b/server/controllers/authController.ts @@ -1,37 +1,9 @@ -import jwt from 'jsonwebtoken'; -import User from '../models/userModel'; -import asyncHandler from 'express-async-handler'; +import { Request, Response } from 'express'; +import { NotAuthorizedError } from '../errors'; -const authSession = asyncHandler(async (req, res) => { - let token; - - if (req.cookies.token) { - try { - token = req.cookies.token; - const secret = process.env.JWT_SECRET as string; - const decoded = jwt.verify(token, secret) as jwt.JwtPayload; - - if (!decoded.id) { - throw new Error('Invalid token - ID not found'); - } - - const user = await User.findById(decoded.id).select('-password'); - - if (!user) throw new Error('User not found'); - - res.locals.user = user; - res.json({ isAuthenticated: true, user: res.locals.user }); - } catch (error) { - console.error(error); - res.status(401); - throw new Error('Not authorized, token failed'); - } - } - - if (!token) { - res.status(401); - throw new Error('Not authorized, no token'); - } -}); +const authSession = async (_req: Request, res: Response) => { + if (!res.locals.user) throw new NotAuthorizedError(); + res.json({ isAuthenticated: true, user: res.locals.user }); +}; export { authSession }; From 29e1171ba45b5a53a17b28fed21a8b2cb1fc277a Mon Sep 17 00:00:00 2001 From: seantokuzo Date: Sun, 16 Jun 2024 13:41:20 -0700 Subject: [PATCH 35/40] add auth middleware before controller --- server/routes/authRoutes.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/server/routes/authRoutes.ts b/server/routes/authRoutes.ts index 6346b75..4764f38 100644 --- a/server/routes/authRoutes.ts +++ b/server/routes/authRoutes.ts @@ -1,9 +1,10 @@ import express from 'express'; - +import { protect } from '../middleware/authMiddleware'; import { authSession } from '../controllers/authController'; const router = express.Router(); +router.use(protect); /* Require Auth for ALL routes below */ router.get('/validate-session', authSession); export default router; From c998fc2a076757b11320915d04d880e4fdbb6782 Mon Sep 17 00:00:00 2001 From: seantokuzo Date: Sun, 16 Jun 2024 13:42:00 -0700 Subject: [PATCH 36/40] simplify auth middleware implementation in routers --- server/routes/alumniRoutes.ts | 4 ++-- server/routes/applicationsRoutes.ts | 19 ++++++++------- server/routes/forumRoutes.ts | 37 ++++++++++++++--------------- server/routes/imageRoutes.ts | 10 +++----- server/routes/profileRoutes.ts | 11 +++++---- server/routes/userRoutes.ts | 7 +++--- 6 files changed, 43 insertions(+), 45 deletions(-) diff --git a/server/routes/alumniRoutes.ts b/server/routes/alumniRoutes.ts index cb12bfd..c4a3d7b 100644 --- a/server/routes/alumniRoutes.ts +++ b/server/routes/alumniRoutes.ts @@ -1,10 +1,10 @@ import express from 'express'; - import { protect } from '../middleware/authMiddleware'; import { getAllAlumniData } from '../controllers/alumniControllers'; const router = express.Router(); -router.get('/', protect, getAllAlumniData); +router.use(protect); /* Require Auth for ALL routes below */ +router.get('/', getAllAlumniData); export default router; diff --git a/server/routes/applicationsRoutes.ts b/server/routes/applicationsRoutes.ts index 32a198e..840eaf6 100644 --- a/server/routes/applicationsRoutes.ts +++ b/server/routes/applicationsRoutes.ts @@ -1,4 +1,5 @@ import express from 'express'; +import { protect } from '../middleware/authMiddleware'; import { createApplication, getAllApplications, @@ -9,17 +10,17 @@ import { updateNotificationPeriod, pauseNotifications, } from '../controllers/applicationsController'; -import { protect } from '../middleware/authMiddleware'; const router = express.Router(); -router.get('/aggregated-user-stats/:userId', protect, getAggregatedUserStats); -router.get('/statuses', protect, getStatuses); -router.get('/', protect, getAllApplications); -router.get('/:id', protect, getApplicationById); -router.post('/', protect, createApplication); -router.put('/:id', protect, updateApplication); -router.put('/:id/notification-period', protect, updateNotificationPeriod); -router.put('/:id/pause-notifications', protect, pauseNotifications); +router.use(protect); /* Require Auth for ALL routes below */ +router.get('/aggregated-user-stats/:userId', getAggregatedUserStats); +router.get('/statuses', getStatuses); +router.get('/', getAllApplications); +router.get('/:id', getApplicationById); +router.post('/', createApplication); +router.put('/:id', updateApplication); +router.put('/:id/notification-period', updateNotificationPeriod); +router.put('/:id/pause-notifications', pauseNotifications); export default router; diff --git a/server/routes/forumRoutes.ts b/server/routes/forumRoutes.ts index 7638e3a..a4e0a94 100644 --- a/server/routes/forumRoutes.ts +++ b/server/routes/forumRoutes.ts @@ -1,6 +1,5 @@ import express from 'express'; import { protect } from '../middleware/authMiddleware'; - import { addForum, deleteForum, @@ -8,7 +7,6 @@ import { getForumById, updateForum, } from '../controllers/forumController'; - import { createThread, deleteThread, @@ -17,7 +15,6 @@ import { updateThread, getAllThreads, } from '../controllers/threadController'; - import { listPostsByThreadId, createPost, @@ -27,28 +24,30 @@ import { const router = express.Router(); -router.get('/threads', protect, getAllThreads); -router.get('/threads/:threadId', protect, getThreadById); +router.use(protect); /* Require Auth for ALL routes below */ + +router.get('/threads', getAllThreads); +router.get('/threads/:threadId', getThreadById); //Forum Routes -router.post('/', protect, addForum); //TODO Protect with admin auth -router.get('/', protect, getAllForums); -router.get('/:forumId', protect, getForumById); -router.put('/:forumId', protect, updateForum); //TODO Protect with admin auth -router.delete('/:forumId', protect, deleteForum); //TODO Protect with admin auth +router.post('/', addForum); //TODO Protect with admin auth +router.get('/', getAllForums); +router.get('/:forumId', getForumById); +router.put('/:forumId', updateForum); //TODO Protect with admin auth +router.delete('/:forumId', deleteForum); //TODO Protect with admin auth //Thread Routes -router.post('/:forumId/threads', protect, createThread); -router.get('/:forumId/threads', protect, listThreadsByForumId); -router.get('/:forumId/threads/:threadId', protect, getThreadById); -router.put('/:forumId/threads/:threadId', protect, updateThread); -router.delete('/:forumId/threads/:threadId', protect, deleteThread); //TODO Protect with admin auth +router.post('/:forumId/threads', createThread); +router.get('/:forumId/threads', listThreadsByForumId); +router.get('/:forumId/threads/:threadId', getThreadById); +router.put('/:forumId/threads/:threadId', updateThread); +router.delete('/:forumId/threads/:threadId', deleteThread); //TODO Protect with admin auth //Post Routes -router.get('/:forumId/threads/:threadId/posts', protect, listPostsByThreadId); -router.post('/:forumId/threads/:threadId/posts', protect, createPost); -router.put('/:forumId/threads/:threadId/posts/:postId', protect, updatePost); -router.delete('/:forumId/threads/:threadId/posts/:postId', protect, deletePost); //TODO Protect with admin auth +router.get('/:forumId/threads/:threadId/posts', listPostsByThreadId); +router.post('/:forumId/threads/:threadId/posts', createPost); +router.put('/:forumId/threads/:threadId/posts/:postId', updatePost); +router.delete('/:forumId/threads/:threadId/posts/:postId', deletePost); //TODO Protect with admin auth export default router; diff --git a/server/routes/imageRoutes.ts b/server/routes/imageRoutes.ts index 058548d..ec85cd9 100644 --- a/server/routes/imageRoutes.ts +++ b/server/routes/imageRoutes.ts @@ -1,6 +1,6 @@ import express from 'express'; -import { uploadProfilePicture, generatePresignedUrl } from '../controllers/imageController'; import { protect } from '../middleware/authMiddleware'; +import { uploadProfilePicture, generatePresignedUrl } from '../controllers/imageController'; const router = express.Router(); @@ -8,12 +8,8 @@ import multer from 'multer'; const storage = multer.memoryStorage(); const upload = multer({ storage: storage }); -router.post( - '/profile-picture/:userID', - protect, - upload.single('profilePicture'), - uploadProfilePicture, -); +router.use(protect); /* Require Auth for ALL routes below */ +router.post('/profile-picture/:userID', upload.single('profilePicture'), uploadProfilePicture); //TODO Not currently being used router.get('/generate-url', generatePresignedUrl); diff --git a/server/routes/profileRoutes.ts b/server/routes/profileRoutes.ts index e8a1244..b2979a6 100644 --- a/server/routes/profileRoutes.ts +++ b/server/routes/profileRoutes.ts @@ -1,6 +1,5 @@ import express from 'express'; import { protect } from '../middleware/authMiddleware'; - import { createProfile, getAllProfiles, @@ -10,9 +9,11 @@ import { const router = express.Router(); -router.post('/', protect, createProfile); -router.put('/:userID', protect, updateProfile); -router.get('/:userID', protect, getProfileById); -router.get('/', protect, getAllProfiles); +router.use(protect); /* Require Auth for ALL routes below */ + +router.post('/', createProfile); +router.put('/:userID', updateProfile); +router.get('/:userID', getProfileById); +router.get('/', getAllProfiles); export default router; diff --git a/server/routes/userRoutes.ts b/server/routes/userRoutes.ts index e26c09b..6f9dd4e 100644 --- a/server/routes/userRoutes.ts +++ b/server/routes/userRoutes.ts @@ -1,19 +1,20 @@ import express from 'express'; +import { protect } from '../middleware/authMiddleware'; import { registerUser, authUser, getUserById, // deleteUserByEmail, } from '../controllers/userController'; -import { protect } from '../middleware/authMiddleware'; const router = express.Router(); router.post('/login', authUser); router.post('/register', registerUser); +router.use(protect); /* Require Auth for ALL routes below */ +router.get('/:userId', getUserById); //TODO Disabled until admin auth is created -//router.delete("/:email", protect, deleteUserByEmail); -router.get('/:userId', protect, getUserById); +//router.delete("/:email", deleteUserByEmail); export default router; From e8b88c9ee43ec762ee395d9986ccc68cc3af955f Mon Sep 17 00:00:00 2001 From: seantokuzo Date: Sun, 16 Jun 2024 13:42:18 -0700 Subject: [PATCH 37/40] remove unused CustomRequest type --- server/types/customRequest.ts | 10 ---------- 1 file changed, 10 deletions(-) delete mode 100644 server/types/customRequest.ts diff --git a/server/types/customRequest.ts b/server/types/customRequest.ts deleted file mode 100644 index 84f28a3..0000000 --- a/server/types/customRequest.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { Request } from 'express'; - -interface UserPayload { - id: string; -} - -export interface CustomRequest

> extends Request { - user?: UserPayload; - params: P; -} From 4dd683b68db9b8e8281065bad60de32442bad788 Mon Sep 17 00:00:00 2001 From: seantokuzo Date: Sun, 16 Jun 2024 13:42:47 -0700 Subject: [PATCH 38/40] remove usage of CustomRequest type in controllers --- server/controllers/applicationsController.ts | 7 +++---- server/controllers/postController.ts | 7 +++---- server/controllers/threadController.ts | 9 ++++----- 3 files changed, 10 insertions(+), 13 deletions(-) diff --git a/server/controllers/applicationsController.ts b/server/controllers/applicationsController.ts index 400c26f..4778427 100644 --- a/server/controllers/applicationsController.ts +++ b/server/controllers/applicationsController.ts @@ -1,5 +1,4 @@ import { Request, Response } from 'express'; -import { CustomRequest } from '../types/customRequest'; import { pool } from '../config/sql-db'; interface StatusCount { @@ -104,7 +103,7 @@ const createApplication = async (req: Request, res: Response) => { } }; -const getApplicationById = async (req: CustomRequest<{ id: string }>, res: Response) => { +const getApplicationById = async (req: Request, res: Response) => { const { id } = req.params; try { const query = ` @@ -145,7 +144,7 @@ const getApplicationById = async (req: CustomRequest<{ id: string }>, res: Respo } }; -const updateApplication = async (req: CustomRequest<{ id: string }>, res: Response) => { +const updateApplication = async (req: Request, res: Response) => { const { id } = req.params; if (!req.user) { @@ -184,7 +183,7 @@ const updateApplication = async (req: CustomRequest<{ id: string }>, res: Respon } }; -const getAggregatedUserStats = async (req: CustomRequest<{ userId: string }>, res: Response) => { +const getAggregatedUserStats = async (req: Request, res: Response) => { const { userId } = req.params; if (!req.user || req.user.id !== userId) return res.status(401).json({ message: 'You are not authorized to retrieve those records' }); diff --git a/server/controllers/postController.ts b/server/controllers/postController.ts index 313cc79..19d4ce6 100644 --- a/server/controllers/postController.ts +++ b/server/controllers/postController.ts @@ -1,7 +1,6 @@ import Post from '../models/postModel'; import Thread from '../models/threadModel'; import { Request, Response, NextFunction } from 'express'; -import { CustomRequest } from '../types/customRequest'; // ENDPOINT GET api/forums/:forumId/threads/:threadId/posts // PURPOSE Retrieve all posts from a specific thread @@ -27,7 +26,7 @@ const listPostsByThreadId = async (req: Request, res: Response, next: NextFuncti // ENDPOINT POST api/forums/:forumId/threads/:threadId/posts // PURPOSE Create a new post on thread // ACCESS Private -const createPost = async (req: CustomRequest, res: Response, next: NextFunction) => { +const createPost = async (req: Request, res: Response, next: NextFunction) => { const { threadId } = req.params; const { content } = req.body; @@ -60,7 +59,7 @@ const createPost = async (req: CustomRequest, res: Response, next: NextFunction) // ENDPOINT PUT api/forums/:forumId/threads/:threadId/:postId // PURPOSE Update an existing post // ACCESS Private -const updatePost = async (req: CustomRequest, res: Response, next: NextFunction) => { +const updatePost = async (req: Request, res: Response, next: NextFunction) => { const { postId } = req.params; const { content } = req.body; @@ -97,7 +96,7 @@ const updatePost = async (req: CustomRequest, res: Response, next: NextFunction) // ENDPOINT DELETE api/forums/:forumId/threads/:threadId/:postId // PURPOSE Delete an existing post // ACCESS Private, Admin -const deletePost = async (req: CustomRequest, res: Response, next: NextFunction) => { +const deletePost = async (req: Request, res: Response, next: NextFunction) => { const { postId } = req.params; try { diff --git a/server/controllers/threadController.ts b/server/controllers/threadController.ts index 5a32ecc..68bc798 100644 --- a/server/controllers/threadController.ts +++ b/server/controllers/threadController.ts @@ -1,12 +1,11 @@ import Post from '../models/postModel'; import Thread from '../models/threadModel'; import { Request, Response, NextFunction } from 'express'; -import { CustomRequest } from '../types/customRequest'; // ENDPOINT POST api/:forumId/threads // PURPOSE Create a new thread // ACCESS Private -const createThread = async (req: CustomRequest, res: Response, next: NextFunction) => { +const createThread = async (req: Request, res: Response, next: NextFunction) => { const { forumId } = req.params; const { title, content } = req.body; @@ -36,7 +35,7 @@ const createThread = async (req: CustomRequest, res: Response, next: NextFunctio // ENDPOINT GET api/threads // PURPOSE Retrieve all threads // ACCESS Private -const getAllThreads = async (req: CustomRequest, res: Response, next: NextFunction) => { +const getAllThreads = async (req: Request, res: Response, next: NextFunction) => { try { const threads = await Thread.find({}).populate('user', 'firstName lastName').exec(); res.status(200).json(threads); @@ -102,7 +101,7 @@ const getThreadById = async (req: Request, res: Response, next: NextFunction) => // ENDPOINT PUT api/forums/:forumId/threads/:threadId // PURPOSE Update a specific thread // ACCESS Private/Admin -const updateThread = async (req: CustomRequest, res: Response, next: NextFunction) => { +const updateThread = async (req: Request, res: Response, next: NextFunction) => { const { forumId, threadId } = req.params; const { title, content } = req.body; @@ -135,7 +134,7 @@ const updateThread = async (req: CustomRequest, res: Response, next: NextFunctio // ENDPOINT DELETE api/forums/:forumId/threads/:threadId // PURPOSE Delete a specific thread // ACCESS Private/Admin -const deleteThread = async (req: CustomRequest, res: Response, next: NextFunction) => { +const deleteThread = async (req: Request, res: Response, next: NextFunction) => { const { forumId, threadId } = req.params; try { From 8719ff0fe121f5ac7c1252e9180af71099a159af Mon Sep 17 00:00:00 2001 From: seantokuzo Date: Thu, 20 Jun 2024 09:54:03 -0700 Subject: [PATCH 39/40] add back aws health check route --- server/app.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/server/app.ts b/server/app.ts index 188aeb6..945523a 100644 --- a/server/app.ts +++ b/server/app.ts @@ -24,6 +24,11 @@ app.use(express.json()); // Middleware to parse request cookies app.use(cookieParser()); +// AWS Production Health Check +app.get('/health', (_req: Request, res: Response) => { + res.status(200).send('OK'); +}); + // API routers app.use('/api/users', userRouter); app.use('/api/profiles', profileRouter); @@ -38,7 +43,7 @@ if (process.env.NODE_ENV === 'production') { console.log(`SERVER STARTED IN PRODUCTION`); app.use(express.static(path.join(__dirname, '../../client/build'))); - app.get('*', (req: Request, res: Response) => + app.get('*', (_req: Request, res: Response) => res.sendFile(path.resolve(__dirname, '../../client/build/index.html')), ); } From c823a40e93edcbae5826b68fc3c326f7c3a6de65 Mon Sep 17 00:00:00 2001 From: seantokuzo Date: Thu, 20 Jun 2024 10:31:12 -0700 Subject: [PATCH 40/40] turfin old image comments --- docker-compose-dev.yml | 3 --- docker-compose-lint.yml | 1 - docker-compose-test.yml | 1 - 3 files changed, 5 deletions(-) diff --git a/docker-compose-dev.yml b/docker-compose-dev.yml index 14128b4..bd115c0 100644 --- a/docker-compose-dev.yml +++ b/docker-compose-dev.yml @@ -1,9 +1,6 @@ version: '3' services: dev: - #image: codehammers/ch-dev:latest - #image: codehammers/ch-dev-dep-v2:latest - #image: codehammers/ch-tracker-dev:latest image: codehammers/ch-dev-dep-v3:latest container_name: ch-dev ports: diff --git a/docker-compose-lint.yml b/docker-compose-lint.yml index 3f238b8..fea9e50 100644 --- a/docker-compose-lint.yml +++ b/docker-compose-lint.yml @@ -1,7 +1,6 @@ version: '3' services: dev: - #image: codehammers/ch-tracker-dev:latest image: codehammers/ch-dev-dep-v3:latest container_name: ch-lint ports: diff --git a/docker-compose-test.yml b/docker-compose-test.yml index 2eab246..b3abd21 100644 --- a/docker-compose-test.yml +++ b/docker-compose-test.yml @@ -2,7 +2,6 @@ version: '3' services: test: - #image: codehammers/ch-tracker-dev:latest image: codehammers/ch-dev-dep-v3:latest container_name: ch-test ports: