diff --git a/server/controllers/announcementsController.js b/server/controllers/announcementsController.js index b17b8eb91..aeb7a3b9d 100755 --- a/server/controllers/announcementsController.js +++ b/server/controllers/announcementsController.js @@ -1,5 +1,4 @@ import { createAnnouncementValidation } from "../validation/joi.js"; -import { asyncHandler } from "../utils/errorUtils.js"; const SERVICE_NAME = "announcementController"; @@ -10,10 +9,9 @@ const SERVICE_NAME = "announcementController"; * @class AnnouncementController */ -class AnnouncementController { - constructor(db, stringService) { - this.db = db; - this.stringService = stringService; +class AnnouncementController extends BaseController { + constructor(commonDependencies) { + super(commonDependencies); this.createAnnouncement = this.createAnnouncement.bind(this); this.getAnnouncement = this.getAnnouncement.bind(this); } diff --git a/server/controllers/authController.js b/server/controllers/authController.js index 8dac9f028..00fc2caf5 100755 --- a/server/controllers/authController.js +++ b/server/controllers/authController.js @@ -27,25 +27,19 @@ class AuthController extends BaseController { /** * Creates an instance of AuthController. * + * @param {Object} commonDependencies - Common dependencies injected into the controller * @param {Object} dependencies - The dependencies required by the controller - * @param {Object} dependencies.db - Database service for data operations * @param {Object} dependencies.settingsService - Service for application settings * @param {Object} dependencies.emailService - Service for email operations * @param {Object} dependencies.jobQueue - Service for job queue operations - * @param {Object} dependencies.stringService - Service for string/localization - * @param {Object} dependencies.logger - Logger service * @param {Object} dependencies.userService - User business logic service */ - constructor({ db, settingsService, emailService, jobQueue, stringService, logger, userService, errorService }) { - super(); - this.db = db; + constructor(commonDependencies, { settingsService, emailService, jobQueue, userService }) { + super(commonDependencies); this.settingsService = settingsService; this.emailService = emailService; this.jobQueue = jobQueue; - this.stringService = stringService; - this.logger = logger; this.userService = userService; - this.errorService = errorService; } /** diff --git a/server/controllers/baseController.js b/server/controllers/baseController.js index 81df59deb..a84806a24 100644 --- a/server/controllers/baseController.js +++ b/server/controllers/baseController.js @@ -1,8 +1,83 @@ -import { asyncHandler } from "../service/infrastructure/errorService.js"; +import { AppError } from "../service/infrastructure/errorService.js"; + +export const createCommonDependencies = (serviceRegistry, dbServiceName, loggerServiceName, errorServiceName, stringServiceName) => { + return { + db: serviceRegistry.get(dbServiceName), + errorService: serviceRegistry.get(errorServiceName), + logger: serviceRegistry.get(loggerServiceName), + stringService: serviceRegistry.get(stringServiceName), + }; +}; class BaseController { - constructor() { - this.asyncHandler = asyncHandler; + constructor({ db, logger, errorService, ...additionalDependencies }) { + this.db = db; + this.logger = logger; + this.errorService = errorService; + Object.assign(this, additionalDependencies); + + this.asyncHandler = (fn, serviceName, methodName) => { + return async (req, res, next) => { + try { + await fn(req, res, next); + } catch (error) { + // Handle validation errors + if (error.isJoi) { + const validationError = this.errorService.createValidationError(error.message, error.details, serviceName, methodName); + return next(validationError); + } + + if (error.name === "ValidationError") { + const validationError = this.errorService.createValidationError("Database validation failed", error.errors, serviceName, methodName); + return next(validationError); + } + + if (error.name === "CastError") { + const notFoundError = this.errorService.createNotFoundError( + "Invalid resource identifier", + { field: error.path, value: error.value }, + serviceName, + methodName + ); + return next(notFoundError); + } + + if (error.code === "11000") { + const conflictError = this.errorService.createConflictError("Resource already exists", { + originalError: error.message, + code: error.code, + }); + conflictError.service = serviceName; + conflictError.method = methodName; + return next(conflictError); + } + + if (error instanceof AppError) { + error.service = error.service || serviceName; + error.method = error.method || methodName; + return next(error); + } + + if (error.status) { + const appError = this.errorService.createError(error.message, error.status, serviceName, methodName, { + originalError: error.message, + stack: error.stack, + }); + return next(appError); + } + + // For unknown errors, create a server error + const appError = this.errorService.createServerError(error.message || "An unexpected error occurred", { + originalError: error.message, + stack: error.stack, + }); + appError.service = serviceName; + appError.method = methodName; + appError.stack = error.stack; // Preserve original stack + return next(appError); + } + }; + }; } } export default BaseController; diff --git a/server/controllers/checkController.js b/server/controllers/checkController.js index 693f5a32d..08b6c2de9 100755 --- a/server/controllers/checkController.js +++ b/server/controllers/checkController.js @@ -26,20 +26,15 @@ class CheckController extends BaseController { /** * Creates an instance of CheckController. * + * @param {Object} commonDependencies - Common dependencies injected into the controller * @param {Object} dependencies - The dependencies required by the controller - * @param {Object} dependencies.db - Database service for data operations * @param {Object} dependencies.settingsService - Service for application settings - * @param {Object} dependencies.stringService - Service for string/localization * @param {Object} dependencies.checkService - Check business logic service - * @param {Object} dependencies.errorService - Error service for error handling */ - constructor({ db, settingsService, stringService, checkService, errorService }) { - super(); - this.db = db; + constructor(commonDependencies, { settingsService, checkService }) { + super(commonDependencies); this.settingsService = settingsService; - this.stringService = stringService; this.checkService = checkService; - this.errorService = errorService; } /** diff --git a/server/controllers/diagnosticController.js b/server/controllers/diagnosticController.js index 308755109..91b90f96b 100755 --- a/server/controllers/diagnosticController.js +++ b/server/controllers/diagnosticController.js @@ -12,15 +12,13 @@ import BaseController from "./baseController.js"; class DiagnosticController extends BaseController { /** * Creates an instance of DiagnosticController. - * + * @param {Object} commonDependencies - Common dependencies injected into the controller * @param {Object} dependencies - The dependencies required by the controller * @param {Object} dependencies.diagnosticService - Service for system diagnostics and monitoring - * @param {Object} dependencies.errorService - Service for error handling */ - constructor({ diagnosticService, errorService }) { - super(); + constructor(commonDependencies, { diagnosticService }) { + super(commonDependencies); this.diagnosticService = diagnosticService; - this.errorService = errorService; } /** diff --git a/server/controllers/inviteController.js b/server/controllers/inviteController.js index 4cf7ec8ed..3f237c6ec 100755 --- a/server/controllers/inviteController.js +++ b/server/controllers/inviteController.js @@ -9,16 +9,12 @@ const SERVICE_NAME = "inviteController"; class InviteController extends BaseController { /** * Creates a new InviteController instance - * @param {Object} dependencies - Dependencies injected into the controller - * @param {Object} dependencies.stringService - Service for internationalized strings - * @param {Object} dependencies.inviteServ ice - Service for invite-related operations - * @param {Object} dependencies.errorService - Service for error handling + * @param {Object} commonDependencies - Common dependencies injected into the controller + * @param {Object} dependencies.inviteService - Service for invite-related operations */ - constructor({ stringService, inviteService, errorService }) { - super(); - this.stringService = stringService; + constructor(commonDependencies, { inviteService }) { + super(commonDependencies); this.inviteService = inviteService; - this.errorService = errorService; } /** diff --git a/server/controllers/logController.js b/server/controllers/logController.js index 14f7a6d58..aa2780eb6 100644 --- a/server/controllers/logController.js +++ b/server/controllers/logController.js @@ -2,10 +2,8 @@ import BaseController from "./baseController.js"; const SERVICE_NAME = "LogController"; class LogController extends BaseController { - constructor({ logger, errorService }) { - super(); - this.logger = logger; - this.errorService = errorService; + constructor(commonDependencies) { + super(commonDependencies); } getLogs = this.asyncHandler( diff --git a/server/controllers/maintenanceWindowController.js b/server/controllers/maintenanceWindowController.js index 59ea10140..e54726ea3 100755 --- a/server/controllers/maintenanceWindowController.js +++ b/server/controllers/maintenanceWindowController.js @@ -12,14 +12,10 @@ import BaseController from "./baseController.js"; const SERVICE_NAME = "maintenanceWindowController"; class MaintenanceWindowController extends BaseController { - constructor({ db, settingsService, stringService, maintenanceWindowService, errorService }) { - super(); - this.db = db; + constructor(commonDependencies, { settingsService, maintenanceWindowService }) { + super(commonDependencies); this.settingsService = settingsService; - this.stringService = stringService; this.maintenanceWindowService = maintenanceWindowService; - this.errorService = errorService; - this.asyncHandler = errorService.asyncHandler; } createMaintenanceWindows = this.asyncHandler( diff --git a/server/controllers/monitorController.js b/server/controllers/monitorController.js index 4e2c021d2..2187df5d0 100755 --- a/server/controllers/monitorController.js +++ b/server/controllers/monitorController.js @@ -18,16 +18,12 @@ import BaseController from "./baseController.js"; const SERVICE_NAME = "monitorController"; class MonitorController extends BaseController { - constructor({ db, settingsService, jobQueue, stringService, emailService, monitorService, errorService }) { - super(); - this.db = db; + constructor(commonDependencies, { settingsService, jobQueue, emailService, monitorService }) { + super(commonDependencies); this.settingsService = settingsService; this.jobQueue = jobQueue; - this.stringService = stringService; this.emailService = emailService; this.monitorService = monitorService; - this.errorService = errorService; - this.asyncHandler = errorService.asyncHandler; } async verifyTeamAccess(teamId, monitorId) { diff --git a/server/controllers/notificationController.js b/server/controllers/notificationController.js index 32ae22aae..6e85b97b8 100755 --- a/server/controllers/notificationController.js +++ b/server/controllers/notificationController.js @@ -4,14 +4,10 @@ import BaseController from "./baseController.js"; const SERVICE_NAME = "NotificationController"; class NotificationController extends BaseController { - constructor({ notificationService, stringService, statusService, db, errorService }) { - super(); + constructor(commonDependencies, { notificationService, statusService }) { + super(commonDependencies); this.notificationService = notificationService; - this.stringService = stringService; this.statusService = statusService; - this.db = db; - this.errorService = errorService; - this.asyncHandler = errorService.asyncHandler; } testNotification = this.asyncHandler( diff --git a/server/controllers/queueController.js b/server/controllers/queueController.js index 72864da38..757350652 100755 --- a/server/controllers/queueController.js +++ b/server/controllers/queueController.js @@ -2,11 +2,9 @@ import BaseController from "./baseController.js"; const SERVICE_NAME = "JobQueueController"; class JobQueueController extends BaseController { - constructor({ jobQueue, stringService, errorService }) { - super(); + constructor(commonDependencies, { jobQueue }) { + super(commonDependencies); this.jobQueue = jobQueue; - this.stringService = stringService; - this.errorService = errorService; } getMetrics = this.asyncHandler( diff --git a/server/controllers/settingsController.js b/server/controllers/settingsController.js index c54ba9e7e..d97c19e88 100755 --- a/server/controllers/settingsController.js +++ b/server/controllers/settingsController.js @@ -5,13 +5,10 @@ import BaseController from "./baseController.js"; const SERVICE_NAME = "SettingsController"; class SettingsController extends BaseController { - constructor({ db, settingsService, stringService, emailService, errorService }) { - super(); - this.db = db; + constructor(commonDependencies, { settingsService, emailService }) { + super(commonDependencies); this.settingsService = settingsService; - this.stringService = stringService; this.emailService = emailService; - this.errorService = errorService; } buildAppSettings = (dbSettings) => { diff --git a/server/controllers/statusPageController.js b/server/controllers/statusPageController.js index 79ebb50ab..09d1d8b5f 100755 --- a/server/controllers/statusPageController.js +++ b/server/controllers/statusPageController.js @@ -4,11 +4,8 @@ import BaseController from "./baseController.js"; const SERVICE_NAME = "statusPageController"; class StatusPageController extends BaseController { - constructor({ db, stringService, errorService }) { - super(); - this.db = db; - this.stringService = stringService; - this.errorService = errorService; + constructor(commonDependencies) { + super(commonDependencies); } createStatusPage = this.asyncHandler( diff --git a/server/index.js b/server/index.js index 7953d160b..b6f1dd3fe 100755 --- a/server/index.js +++ b/server/index.js @@ -7,11 +7,13 @@ import express from "express"; import helmet from "helmet"; import cors from "cors"; import compression from "compression"; +import { Logger } from "./utils/logger.js"; import logger from "./utils/logger.js"; import { verifyJWT } from "./middleware/verifyJWT.js"; import { handleErrors } from "./middleware/handleErrors.js"; import { responseHandler } from "./middleware/responseHandler.js"; import { fileURLToPath } from "url"; +import { createCommonDependencies } from "./controllers/baseController.js"; import AuthRoutes from "./routes/authRoute.js"; import AuthController from "./controllers/authController.js"; @@ -248,6 +250,7 @@ const startApp = async () => { db, settingsService, stringService, + errorService, }); const diagnosticService = new DiagnosticService(); const inviteService = new InviteService({ @@ -261,6 +264,7 @@ const startApp = async () => { db, settingsService, stringService, + errorService, }); const monitorService = new MonitorService({ db, @@ -275,6 +279,7 @@ const startApp = async () => { // Register services // ServiceRegistry.register(JobQueue.SERVICE_NAME, jobQueue); // ServiceRegistry.register(JobQueue.SERVICE_NAME, pulseQueue); + ServiceRegistry.register(Logger.SERVICE_NAME, logger); ServiceRegistry.register(JobQueue.SERVICE_NAME, superSimpleQueue); ServiceRegistry.register(ErrorService.SERVICE_NAME, errorService); ServiceRegistry.register(MongoDB.SERVICE_NAME, db); @@ -305,82 +310,63 @@ const startApp = async () => { process.on("SIGTERM", shutdown); //Create controllers - const authController = new AuthController({ - db: ServiceRegistry.get(MongoDB.SERVICE_NAME), + + const commonDependencies = createCommonDependencies( + ServiceRegistry, + MongoDB.SERVICE_NAME, + Logger.SERVICE_NAME, + ErrorService.SERVICE_NAME, + StringService.SERVICE_NAME + ); + + const authController = new AuthController(commonDependencies, { settingsService: ServiceRegistry.get(SettingsService.SERVICE_NAME), emailService: ServiceRegistry.get(EmailService.SERVICE_NAME), jobQueue: ServiceRegistry.get(JobQueue.SERVICE_NAME), - stringService: ServiceRegistry.get(StringService.SERVICE_NAME), - logger: logger, userService: ServiceRegistry.get(UserService.SERVICE_NAME), - errorService: ServiceRegistry.get(ErrorService.SERVICE_NAME), }); - const monitorController = new MonitorController({ - db: ServiceRegistry.get(MongoDB.SERVICE_NAME), + const monitorController = new MonitorController(commonDependencies, { settingsService: ServiceRegistry.get(SettingsService.SERVICE_NAME), jobQueue: ServiceRegistry.get(JobQueue.SERVICE_NAME), - stringService: ServiceRegistry.get(StringService.SERVICE_NAME), emailService: ServiceRegistry.get(EmailService.SERVICE_NAME), monitorService: ServiceRegistry.get(MonitorService.SERVICE_NAME), - errorService: ServiceRegistry.get(ErrorService.SERVICE_NAME), }); - const settingsController = new SettingsController({ - db: ServiceRegistry.get(MongoDB.SERVICE_NAME), + const settingsController = new SettingsController(commonDependencies, { settingsService: ServiceRegistry.get(SettingsService.SERVICE_NAME), - stringService: ServiceRegistry.get(StringService.SERVICE_NAME), emailService: ServiceRegistry.get(EmailService.SERVICE_NAME), - errorService: ServiceRegistry.get(ErrorService.SERVICE_NAME), }); - const checkController = new CheckController({ - db: ServiceRegistry.get(MongoDB.SERVICE_NAME), + const checkController = new CheckController(commonDependencies, { settingsService: ServiceRegistry.get(SettingsService.SERVICE_NAME), - stringService: ServiceRegistry.get(StringService.SERVICE_NAME), checkService: ServiceRegistry.get(CheckService.SERVICE_NAME), - errorService: ServiceRegistry.get(ErrorService.SERVICE_NAME), }); - const inviteController = new InviteController({ - stringService: ServiceRegistry.get(StringService.SERVICE_NAME), + const inviteController = new InviteController(commonDependencies, { inviteService: ServiceRegistry.get(InviteService.SERVICE_NAME), - errorService: ServiceRegistry.get(ErrorService.SERVICE_NAME), }); - const maintenanceWindowController = new MaintenanceWindowController({ - db: ServiceRegistry.get(MongoDB.SERVICE_NAME), + const maintenanceWindowController = new MaintenanceWindowController(commonDependencies, { settingsService: ServiceRegistry.get(SettingsService.SERVICE_NAME), - stringService: ServiceRegistry.get(StringService.SERVICE_NAME), maintenanceWindowService: ServiceRegistry.get(MaintenanceWindowService.SERVICE_NAME), - errorService: ServiceRegistry.get(ErrorService.SERVICE_NAME), }); - const queueController = new QueueController({ + const queueController = new QueueController(commonDependencies, { jobQueue: ServiceRegistry.get(JobQueue.SERVICE_NAME), - stringService: ServiceRegistry.get(StringService.SERVICE_NAME), - errorService: ServiceRegistry.get(ErrorService.SERVICE_NAME), }); - const logController = new LogController({ logger, errorService: ServiceRegistry.get(ErrorService.SERVICE_NAME) }); + const logController = new LogController(commonDependencies); - const statusPageController = new StatusPageController({ - db: ServiceRegistry.get(MongoDB.SERVICE_NAME), - stringService: ServiceRegistry.get(StringService.SERVICE_NAME), - errorService: ServiceRegistry.get(ErrorService.SERVICE_NAME), - }); + const statusPageController = new StatusPageController(commonDependencies); - const notificationController = new NotificationController({ + const notificationController = new NotificationController(commonDependencies, { notificationService: ServiceRegistry.get(NotificationService.SERVICE_NAME), - stringService: ServiceRegistry.get(StringService.SERVICE_NAME), statusService: ServiceRegistry.get(StatusService.SERVICE_NAME), - db: ServiceRegistry.get(MongoDB.SERVICE_NAME), - errorService: ServiceRegistry.get(ErrorService.SERVICE_NAME), }); - const diagnosticController = new DiagnosticController({ + const diagnosticController = new DiagnosticController(commonDependencies, { diagnosticService: ServiceRegistry.get(DiagnosticService.SERVICE_NAME), - errorService: ServiceRegistry.get(ErrorService.SERVICE_NAME), }); //Create routes @@ -393,9 +379,9 @@ const startApp = async () => { const queueRoutes = new QueueRoutes(queueController); const logRoutes = new LogRoutes(logController); const statusPageRoutes = new StatusPageRoutes(statusPageController); - const notificationRoutes = new NotificationRoutes(notificationController); const diagnosticRoutes = new DiagnosticRoutes(diagnosticController); + // Middleware app.use(express.static(frontendPath)); app.use(responseHandler); @@ -438,16 +424,16 @@ const startApp = async () => { //routes app.use("/api/v1/auth", authRoutes.getRouter()); - app.use("/api/v1/checks", verifyJWT, checkRoutes.getRouter()); - app.use("/api/v1/diagnostic", verifyJWT, diagnosticRoutes.getRouter()); - app.use("/api/v1/invite", inviteRoutes.getRouter()); - app.use("/api/v1/logs", verifyJWT, logRoutes.getRouter()); - app.use("/api/v1/maintenance-window", verifyJWT, maintenanceWindowRoutes.getRouter()); app.use("/api/v1/monitors", verifyJWT, monitorRoutes.getRouter()); - app.use("/api/v1/notifications", verifyJWT, notificationRoutes.getRouter()); - app.use("/api/v1/queue", verifyJWT, queueRoutes.getRouter()); app.use("/api/v1/settings", verifyJWT, settingsRoutes.getRouter()); + app.use("/api/v1/checks", verifyJWT, checkRoutes.getRouter()); + app.use("/api/v1/invite", inviteRoutes.getRouter()); + app.use("/api/v1/maintenance-window", verifyJWT, maintenanceWindowRoutes.getRouter()); + app.use("/api/v1/queue", verifyJWT, queueRoutes.getRouter()); + app.use("/api/v1/logs", verifyJWT, logRoutes.getRouter()); app.use("/api/v1/status-page", statusPageRoutes.getRouter()); + app.use("/api/v1/notifications", verifyJWT, notificationRoutes.getRouter()); + app.use("/api/v1/diagnostic", verifyJWT, diagnosticRoutes.getRouter()); app.use("/api/v1/health", (req, res) => { res.json({ diff --git a/server/service/business/checkService.js b/server/service/business/checkService.js index e52a4919a..3d8206644 100644 --- a/server/service/business/checkService.js +++ b/server/service/business/checkService.js @@ -3,37 +3,30 @@ const SERVICE_NAME = "checkService"; class CheckService { static SERVICE_NAME = SERVICE_NAME; - constructor({ db, settingsService, stringService }) { + constructor({ db, settingsService, stringService, errorService }) { this.db = db; this.settingsService = settingsService; this.stringService = stringService; + this.errorService = errorService; } getChecksByMonitor = async ({ monitorId, query, teamId }) => { if (!monitorId) { - throw new Error("No monitor ID in request"); + throw this.errorService.createBadRequestError("No monitor ID in request"); } if (!teamId) { - throw new Error("No team ID in request"); + throw this.errorService.createBadRequestError("No team ID in request"); } const monitor = await this.db.getMonitorById(monitorId); if (!monitor) { - const err = new Error("Monitor not found"); - err.status = 404; - err.service = SERVICE_NAME; - err.method = "getChecksByMonitor"; - throw err; + throw this.errorService.createNotFoundError("Monitor not found"); } if (!monitor.teamId.equals(teamId)) { - const err = new Error("Unauthorized"); - err.status = 403; - err.service = SERVICE_NAME; - err.method = "getChecksByMonitor"; - throw err; + throw this.errorService.createAuthorizationError(); } let { type, sortOrder, dateRange, filter, ack, page, rowsPerPage, status } = query; @@ -55,7 +48,7 @@ class CheckService { let { sortOrder, dateRange, filter, ack, page, rowsPerPage } = query; if (!teamId) { - throw new Error("No team ID in request"); + throw this.errorService.createBadRequestError("No team ID in request"); } const checkData = await this.db.getChecksByTeam({ @@ -72,7 +65,7 @@ class CheckService { getChecksSummaryByTeamId = async ({ teamId }) => { if (!teamId) { - throw new Error("No team ID in request"); + throw this.errorService.createBadRequestError("No team ID in request"); } const summary = await this.db.getChecksSummaryByTeamId({ teamId }); @@ -81,11 +74,11 @@ class CheckService { ackCheck = async ({ checkId, teamId, ack }) => { if (!checkId) { - throw new Error("No check ID in request"); + throw this.errorService.createBadRequestError("No check ID in request"); } if (!teamId) { - throw new Error("No team ID in request"); + throw this.errorService.createBadRequestError("No team ID in request"); } const updatedCheck = await this.db.ackCheck(checkId, teamId, ack); @@ -95,20 +88,16 @@ class CheckService { ackAllChecks = async ({ monitorId, path, teamId, ack }) => { if (path === "monitor") { if (!monitorId) { - throw new Error("No monitor ID in request"); + throw this.errorService.createBadRequestError("No monitor ID in request"); } const monitor = await this.db.getMonitorById(monitorId); if (!monitor) { - throw new Error("Monitor not found"); + throw this.errorService.createNotFoundError("Monitor not found"); } if (!monitor.teamId.equals(teamId)) { - const err = new Error("Unauthorized"); - err.status = 403; - err.service = SERVICE_NAME; - err.method = "ackAllChecks"; - throw err; + throw this.errorService.createAuthorizationError(); } } @@ -118,29 +107,21 @@ class CheckService { deleteChecks = async ({ monitorId, teamId }) => { if (!monitorId) { - throw new Error("No monitor ID in request"); + throw this.errorService.createBadRequestError("No monitor ID in request"); } if (!teamId) { - throw new Error("No team ID in request"); + throw this.errorService.createBadRequestError("No team ID in request"); } const monitor = await this.db.getMonitorById(monitorId); if (!monitor) { - const err = new Error("Monitor not found"); - err.status = 404; - err.service = SERVICE_NAME; - err.method = "deleteChecks"; - throw err; + throw this.errorService.createNotFoundError("Monitor not found"); } if (!monitor.teamId.equals(teamId)) { - const err = new Error("Unauthorized"); - err.status = 403; - err.service = SERVICE_NAME; - err.method = "deleteChecks"; - throw err; + throw this.errorService.createAuthorizationError(); } const deletedCount = await this.db.deleteChecks(monitorId); @@ -148,7 +129,7 @@ class CheckService { }; deleteChecksByTeamId = async ({ teamId }) => { if (!teamId) { - throw new Error("No team ID in request"); + throw this.errorService.createBadRequestError("No team ID in request"); } const deletedCount = await this.db.deleteChecksByTeamId(teamId); diff --git a/server/service/business/maintenanceWindowService.js b/server/service/business/maintenanceWindowService.js index 6140a5682..0f58aca85 100644 --- a/server/service/business/maintenanceWindowService.js +++ b/server/service/business/maintenanceWindowService.js @@ -2,10 +2,11 @@ const SERVICE_NAME = "maintenanceWindowService"; class MaintenanceWindowService { static SERVICE_NAME = SERVICE_NAME; - constructor({ db, settingsService, stringService }) { + constructor({ db, settingsService, stringService, errorService }) { this.db = db; this.settingsService = settingsService; this.stringService = stringService; + this.errorService = errorService; } createMaintenanceWindow = async ({ teamId, body }) => { @@ -15,11 +16,7 @@ class MaintenanceWindowService { const unauthorizedMonitors = monitors.filter((monitor) => !monitor.teamId.equals(teamId)); if (unauthorizedMonitors.length > 0) { - const error = new Error("Unauthorized access to one or more monitors"); - error.status = 403; - error.service = SERVICE_NAME; - error.method = "createMaintenanceWindows"; - throw error; + throw this.errorService.createAuthorizationError(); } const dbTransactions = monitorIds.map((monitorId) => { diff --git a/server/service/business/monitorService.js b/server/service/business/monitorService.js index babc3122f..a2bf06dfd 100644 --- a/server/service/business/monitorService.js +++ b/server/service/business/monitorService.js @@ -13,7 +13,6 @@ class MonitorService { this.papaparse = papaparse; this.logger = logger; this.errorService = errorService; - this.asyncHandler = errorService.asyncHandler; } verifyTeamAccess = async ({ teamId, monitorId }) => { diff --git a/server/service/business/userService.js b/server/service/business/userService.js index bd1ea3fa6..12dd4207f 100644 --- a/server/service/business/userService.js +++ b/server/service/business/userService.js @@ -154,23 +154,23 @@ class UserService { deleteUser = async (user) => { const email = user?.email; if (!email) { - throw new Error("No email in request"); + throw this.errorService.createBadRequestError("No email in request"); } const teamId = user?.teamId; const userId = user?._id; if (!teamId) { - throw new Error("No team ID in request"); + throw this.errorService.createBadRequestError("No team ID in request"); } if (!userId) { - throw new Error("No user ID in request"); + throw this.errorService.createBadRequestError("No user ID in request"); } const roles = user?.role; if (roles.includes("demo")) { - throw new Error("Demo user cannot be deleted"); + throw this.errorService.createBadRequestError("Demo user cannot be deleted"); } // 1. Find all the monitors associated with the team ID if superadmin diff --git a/server/service/infrastructure/errorService.js b/server/service/infrastructure/errorService.js index 098c5b4ac..552816b73 100644 --- a/server/service/infrastructure/errorService.js +++ b/server/service/infrastructure/errorService.js @@ -1,4 +1,4 @@ -class AppError extends Error { +export class AppError extends Error { constructor(message, status = 500, service = null, method = null, details = null) { super(message); this.status = status; @@ -96,66 +96,3 @@ class ErrorService { } export default ErrorService; - -export const asyncHandler = (fn, serviceName, methodName) => { - return async (req, res, next) => { - try { - await fn(req, res, next); - } catch (error) { - // Handle validation errors - if (error.isJoi) { - const validationError = this.createValidationError(error.message, error.details, serviceName, methodName); - return next(validationError); - } - - if (error.name === "ValidationError") { - const validationError = this.createValidationError("Database validation failed", error.errors, serviceName, methodName); - return next(validationError); - } - - if (error.name === "CastError") { - const notFoundError = this.createNotFoundError( - "Invalid resource identifier", - { field: error.path, value: error.value }, - serviceName, - methodName - ); - return next(notFoundError); - } - - if (error.code === "11000") { - const conflictError = this.createConflictError("Resource already exists", { - originalError: error.message, - code: error.code, - }); - conflictError.service = serviceName; - conflictError.method = methodName; - return next(conflictError); - } - - if (error instanceof AppError) { - error.service = error.service || serviceName; - error.method = error.method || methodName; - return next(error); - } - - if (error.status) { - const appError = this.createError(error.message, error.status, serviceName, methodName, { - originalError: error.message, - stack: error.stack, - }); - return next(appError); - } - - // For unknown errors, create a server error - const appError = this.createServerError(error.message || "An unexpected error occurred", { - originalError: error.message, - stack: error.stack, - }); - appError.service = serviceName; - appError.method = methodName; - appError.stack = error.stack; // Preserve original stack - return next(appError); - } - }; -}; diff --git a/server/utils/logger.js b/server/utils/logger.js index 6fc240e4c..8cb57f9a7 100755 --- a/server/utils/logger.js +++ b/server/utils/logger.js @@ -2,7 +2,10 @@ import { createLogger, format, transports } from "winston"; import dotenv from "dotenv"; dotenv.config(); +const SERVICE_NAME = "Logger"; + class Logger { + static SERVICE_NAME = SERVICE_NAME; constructor() { this.logCache = []; this.maxCacheSize = 1000;