mirror of
https://github.com/bluewave-labs/Checkmate.git
synced 2026-01-07 18:29:41 -06:00
Merge pull request #2681 from bluewave-labs/feat/error-service
feat: error service
This commit is contained in:
@@ -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);
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import BaseController from "./baseController.js";
|
||||
import {
|
||||
registrationBodyValidation,
|
||||
loginValidation,
|
||||
@@ -10,7 +11,6 @@ import {
|
||||
editUserByIdBodyValidation,
|
||||
editSuperadminUserByIdBodyValidation,
|
||||
} from "../validation/joi.js";
|
||||
import { asyncHandler, createError } from "../utils/errorUtils.js";
|
||||
|
||||
const SERVICE_NAME = "authController";
|
||||
|
||||
@@ -23,26 +23,22 @@ const SERVICE_NAME = "authController";
|
||||
* @class AuthController
|
||||
* @description Manages user authentication and authorization operations
|
||||
*/
|
||||
class AuthController {
|
||||
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 }) {
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -85,7 +81,7 @@ class AuthController {
|
||||
* "inviteToken": "abc123..."
|
||||
* }
|
||||
*/
|
||||
registerUser = asyncHandler(
|
||||
registerUser = this.asyncHandler(
|
||||
async (req, res) => {
|
||||
if (req.body?.email) {
|
||||
req.body.email = req.body.email?.toLowerCase();
|
||||
@@ -121,7 +117,7 @@ class AuthController {
|
||||
* "password": "SecurePass123!"
|
||||
* }
|
||||
*/
|
||||
loginUser = asyncHandler(
|
||||
loginUser = this.asyncHandler(
|
||||
async (req, res) => {
|
||||
if (req.body?.email) {
|
||||
req.body.email = req.body.email?.toLowerCase();
|
||||
@@ -173,7 +169,7 @@ class AuthController {
|
||||
* "newPassword": "NewPass123!"
|
||||
* }
|
||||
*/
|
||||
editUser = asyncHandler(
|
||||
editUser = this.asyncHandler(
|
||||
async (req, res) => {
|
||||
await editUserBodyValidation.validateAsync(req.body);
|
||||
|
||||
@@ -200,7 +196,7 @@ class AuthController {
|
||||
* GET /auth/users/superadmin
|
||||
* // Response: { "data": true } or { "data": false }
|
||||
*/
|
||||
checkSuperadminExists = asyncHandler(
|
||||
checkSuperadminExists = this.asyncHandler(
|
||||
async (req, res) => {
|
||||
const superAdminExists = await this.userService.checkSuperadminExists();
|
||||
return res.success({
|
||||
@@ -230,7 +226,7 @@ class AuthController {
|
||||
* "email": "john@example.com"
|
||||
* }
|
||||
*/
|
||||
requestRecovery = asyncHandler(
|
||||
requestRecovery = this.asyncHandler(
|
||||
async (req, res) => {
|
||||
await recoveryValidation.validateAsync(req.body);
|
||||
const email = req?.body?.email;
|
||||
@@ -262,7 +258,7 @@ class AuthController {
|
||||
* "recoveryToken": "abc123..."
|
||||
* }
|
||||
*/
|
||||
validateRecovery = asyncHandler(
|
||||
validateRecovery = this.asyncHandler(
|
||||
async (req, res) => {
|
||||
await recoveryTokenBodyValidation.validateAsync(req.body);
|
||||
await this.userService.validateRecovery(req.body.recoveryToken);
|
||||
@@ -294,7 +290,7 @@ class AuthController {
|
||||
* "recoveryToken": "abc123..."
|
||||
* }
|
||||
*/
|
||||
resetPassword = asyncHandler(
|
||||
resetPassword = this.asyncHandler(
|
||||
async (req, res) => {
|
||||
await newPasswordValidation.validateAsync(req.body);
|
||||
const { user, token } = await this.userService.resetPassword(req.body.password, req.body.recoveryToken);
|
||||
@@ -326,7 +322,7 @@ class AuthController {
|
||||
* DELETE /auth/user
|
||||
* // Requires JWT authentication
|
||||
*/
|
||||
deleteUser = asyncHandler(
|
||||
deleteUser = this.asyncHandler(
|
||||
async (req, res) => {
|
||||
await this.userService.deleteUser(req.user);
|
||||
return res.success({
|
||||
@@ -350,7 +346,7 @@ class AuthController {
|
||||
* GET /auth/users
|
||||
* // Requires JWT authentication with admin/superadmin role
|
||||
*/
|
||||
getAllUsers = asyncHandler(
|
||||
getAllUsers = this.asyncHandler(
|
||||
async (req, res) => {
|
||||
const allUsers = await this.userService.getAllUsers();
|
||||
return res.success({
|
||||
@@ -381,7 +377,7 @@ class AuthController {
|
||||
* GET /auth/users/507f1f77bcf86cd799439011
|
||||
* // Requires JWT authentication with superadmin role
|
||||
*/
|
||||
getUserById = asyncHandler(
|
||||
getUserById = this.asyncHandler(
|
||||
async (req, res) => {
|
||||
await getUserByIdParamValidation.validateAsync(req.params);
|
||||
const userId = req?.params?.userId;
|
||||
@@ -431,7 +427,7 @@ class AuthController {
|
||||
* }
|
||||
* // Requires JWT authentication with superadmin role
|
||||
*/
|
||||
editUserById = asyncHandler(
|
||||
editUserById = this.asyncHandler(
|
||||
async (req, res) => {
|
||||
const roles = req?.user?.role;
|
||||
if (!roles.includes("superadmin")) {
|
||||
|
||||
83
server/controllers/baseController.js
Normal file
83
server/controllers/baseController.js
Normal file
@@ -0,0 +1,83 @@
|
||||
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({ 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;
|
||||
@@ -1,3 +1,4 @@
|
||||
import BaseController from "./baseController.js";
|
||||
import {
|
||||
getChecksParamValidation,
|
||||
getChecksQueryValidation,
|
||||
@@ -9,7 +10,6 @@ import {
|
||||
ackAllChecksParamValidation,
|
||||
ackAllChecksBodyValidation,
|
||||
} from "../validation/joi.js";
|
||||
import { asyncHandler } from "../utils/errorUtils.js";
|
||||
|
||||
const SERVICE_NAME = "checkController";
|
||||
|
||||
@@ -22,20 +22,18 @@ const SERVICE_NAME = "checkController";
|
||||
* @class CheckController
|
||||
* @description Manages check operations and monitoring data
|
||||
*/
|
||||
class CheckController {
|
||||
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
|
||||
*/
|
||||
constructor({ db, settingsService, stringService, checkService }) {
|
||||
this.db = db;
|
||||
constructor(commonDependencies, { settingsService, checkService }) {
|
||||
super(commonDependencies);
|
||||
this.settingsService = settingsService;
|
||||
this.stringService = stringService;
|
||||
this.checkService = checkService;
|
||||
}
|
||||
|
||||
@@ -67,7 +65,7 @@ class CheckController {
|
||||
* GET /checks/monitor/507f1f77bcf86cd799439011?page=1&rowsPerPage=10&status=down
|
||||
* // Requires JWT authentication
|
||||
*/
|
||||
getChecksByMonitor = asyncHandler(
|
||||
getChecksByMonitor = this.asyncHandler(
|
||||
async (req, res) => {
|
||||
await getChecksParamValidation.validateAsync(req.params);
|
||||
await getChecksQueryValidation.validateAsync(req.query);
|
||||
@@ -109,7 +107,7 @@ class CheckController {
|
||||
* GET /checks/team?page=1&rowsPerPage=20&status=down&ack=false
|
||||
* // Requires JWT authentication
|
||||
*/
|
||||
getChecksByTeam = asyncHandler(
|
||||
getChecksByTeam = this.asyncHandler(
|
||||
async (req, res) => {
|
||||
await getTeamChecksQueryValidation.validateAsync(req.query);
|
||||
const checkData = await this.checkService.getChecksByTeam({
|
||||
@@ -140,7 +138,7 @@ class CheckController {
|
||||
* // Requires JWT authentication
|
||||
* // Response includes counts by status, time ranges, etc.
|
||||
*/
|
||||
getChecksSummaryByTeamId = asyncHandler(
|
||||
getChecksSummaryByTeamId = this.asyncHandler(
|
||||
async (req, res) => {
|
||||
const summary = await this.checkService.getChecksSummaryByTeamId({ teamId: req?.user?.teamId });
|
||||
return res.success({
|
||||
@@ -176,7 +174,7 @@ class CheckController {
|
||||
* }
|
||||
* // Requires JWT authentication
|
||||
*/
|
||||
ackCheck = asyncHandler(
|
||||
ackCheck = this.asyncHandler(
|
||||
async (req, res) => {
|
||||
await ackCheckBodyValidation.validateAsync(req.body);
|
||||
|
||||
@@ -220,7 +218,7 @@ class CheckController {
|
||||
* }
|
||||
* // Requires JWT authentication
|
||||
*/
|
||||
ackAllChecks = asyncHandler(
|
||||
ackAllChecks = this.asyncHandler(
|
||||
async (req, res) => {
|
||||
await ackAllChecksParamValidation.validateAsync(req.params);
|
||||
await ackAllChecksBodyValidation.validateAsync(req.body);
|
||||
@@ -266,7 +264,7 @@ class CheckController {
|
||||
* // Requires JWT authentication
|
||||
* // Response: { "data": { "deletedCount": 150 } }
|
||||
*/
|
||||
deleteChecks = asyncHandler(
|
||||
deleteChecks = this.asyncHandler(
|
||||
async (req, res) => {
|
||||
await deleteChecksParamValidation.validateAsync(req.params);
|
||||
|
||||
@@ -300,7 +298,7 @@ class CheckController {
|
||||
* // Requires JWT authentication
|
||||
* // Response: { "data": { "deletedCount": 1250 } }
|
||||
*/
|
||||
deleteChecksByTeamId = asyncHandler(
|
||||
deleteChecksByTeamId = this.asyncHandler(
|
||||
async (req, res) => {
|
||||
await deleteChecksByTeamIdParamValidation.validateAsync(req.params);
|
||||
|
||||
@@ -336,7 +334,7 @@ class CheckController {
|
||||
* // Requires JWT authentication
|
||||
* // Sets check TTL to 30 days
|
||||
*/
|
||||
updateChecksTTL = asyncHandler(
|
||||
updateChecksTTL = this.asyncHandler(
|
||||
async (req, res) => {
|
||||
await updateChecksTTLBodyValidation.validateAsync(req.body);
|
||||
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
import { createServerError } from "../utils/errorUtils.js";
|
||||
|
||||
const fetchMonitorCertificate = async (sslChecker, monitor) => {
|
||||
const monitorUrl = new URL(monitor.url);
|
||||
const hostname = monitorUrl.hostname;
|
||||
const cert = await sslChecker(hostname);
|
||||
// Throw an error if no cert or if cert.validTo is not present
|
||||
if (cert?.validTo === null || cert?.validTo === undefined) {
|
||||
throw createServerError("Certificate not found");
|
||||
throw new Error("Certificate not found");
|
||||
}
|
||||
return cert;
|
||||
};
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { asyncHandler } from "../utils/errorUtils.js";
|
||||
|
||||
const SERVICE_NAME = "diagnosticController";
|
||||
import BaseController from "./baseController.js";
|
||||
/**
|
||||
* Diagnostic Controller
|
||||
*
|
||||
@@ -10,14 +9,15 @@ const SERVICE_NAME = "diagnosticController";
|
||||
* @class DiagnosticController
|
||||
* @description Manages system diagnostics and performance monitoring
|
||||
*/
|
||||
class DiagnosticController {
|
||||
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
|
||||
*/
|
||||
constructor({ diagnosticService }) {
|
||||
constructor(commonDependencies, { diagnosticService }) {
|
||||
super(commonDependencies);
|
||||
this.diagnosticService = diagnosticService;
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ class DiagnosticController {
|
||||
* // - Database connection status
|
||||
* // - Active processes/connections
|
||||
*/
|
||||
getSystemStats = asyncHandler(
|
||||
getSystemStats = this.asyncHandler(
|
||||
async (req, res) => {
|
||||
const diagnostics = await this.diagnosticService.getSystemStats();
|
||||
return res.success({
|
||||
|
||||
@@ -1,21 +1,19 @@
|
||||
import { inviteBodyValidation, inviteVerificationBodyValidation } from "../validation/joi.js";
|
||||
import { asyncHandler } from "../utils/errorUtils.js";
|
||||
|
||||
import BaseController from "./baseController.js";
|
||||
const SERVICE_NAME = "inviteController";
|
||||
|
||||
/**
|
||||
* Controller for handling user invitation operations
|
||||
* Manages invite token generation, email sending, and token verification
|
||||
*/
|
||||
class 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} commonDependencies - Common dependencies injected into the controller
|
||||
* @param {Object} dependencies.inviteService - Service for invite-related operations
|
||||
*/
|
||||
constructor({ stringService, inviteService }) {
|
||||
this.stringService = stringService;
|
||||
constructor(commonDependencies, { inviteService }) {
|
||||
super(commonDependencies);
|
||||
this.inviteService = inviteService;
|
||||
}
|
||||
|
||||
@@ -28,7 +26,7 @@ class InviteController {
|
||||
* @param {Object} res - Express response object
|
||||
* @returns {Promise<Object>} Response with invite token data
|
||||
*/
|
||||
getInviteToken = asyncHandler(
|
||||
getInviteToken = this.asyncHandler(
|
||||
async (req, res) => {
|
||||
const invite = req.body;
|
||||
const teamId = req?.user?.teamId;
|
||||
@@ -54,7 +52,7 @@ class InviteController {
|
||||
* @param {Object} res - Express response object
|
||||
* @returns {Promise<Object>} Response with invite token data
|
||||
*/
|
||||
sendInviteEmail = asyncHandler(
|
||||
sendInviteEmail = this.asyncHandler(
|
||||
async (req, res) => {
|
||||
const inviteRequest = req.body;
|
||||
inviteRequest.teamId = req?.user?.teamId;
|
||||
@@ -81,7 +79,7 @@ class InviteController {
|
||||
* @param {Object} res - Express response object
|
||||
* @returns {Promise<Object>} Response with verified invite data
|
||||
*/
|
||||
verifyInviteToken = asyncHandler(
|
||||
verifyInviteToken = this.asyncHandler(
|
||||
async (req, res) => {
|
||||
await inviteVerificationBodyValidation.validateAsync(req.body);
|
||||
const invite = await this.inviteService.verifyInviteToken({ inviteToken: req?.body?.token });
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
import { asyncHandler } from "../utils/errorUtils.js";
|
||||
|
||||
import BaseController from "./baseController.js";
|
||||
const SERVICE_NAME = "LogController";
|
||||
|
||||
class LogController {
|
||||
constructor(logger) {
|
||||
this.logger = logger;
|
||||
class LogController extends BaseController {
|
||||
constructor(commonDependencies) {
|
||||
super(commonDependencies);
|
||||
}
|
||||
|
||||
getLogs = asyncHandler(
|
||||
async (req, res, next) => {
|
||||
getLogs = this.asyncHandler(
|
||||
async (req, res) => {
|
||||
const logs = await this.logger.getLogs();
|
||||
res.success({
|
||||
msg: "Logs fetched successfully",
|
||||
|
||||
@@ -7,25 +7,24 @@ import {
|
||||
getMaintenanceWindowsByTeamIdQueryValidation,
|
||||
deleteMaintenanceWindowByIdParamValidation,
|
||||
} from "../validation/joi.js";
|
||||
import { asyncHandler } from "../utils/errorUtils.js";
|
||||
import BaseController from "./baseController.js";
|
||||
|
||||
const SERVICE_NAME = "maintenanceWindowController";
|
||||
|
||||
class MaintenanceWindowController {
|
||||
constructor({ db, settingsService, stringService, maintenanceWindowService }) {
|
||||
this.db = db;
|
||||
class MaintenanceWindowController extends BaseController {
|
||||
constructor(commonDependencies, { settingsService, maintenanceWindowService }) {
|
||||
super(commonDependencies);
|
||||
this.settingsService = settingsService;
|
||||
this.stringService = stringService;
|
||||
this.maintenanceWindowService = maintenanceWindowService;
|
||||
}
|
||||
|
||||
createMaintenanceWindows = asyncHandler(
|
||||
createMaintenanceWindows = this.asyncHandler(
|
||||
async (req, res) => {
|
||||
await createMaintenanceWindowBodyValidation.validateAsync(req.body);
|
||||
|
||||
const teamId = req?.user?.teamId;
|
||||
if (!teamId) {
|
||||
throw new Error("Team ID is required");
|
||||
throw this.errorService.createBadRequestError("Team ID is required");
|
||||
}
|
||||
|
||||
await this.maintenanceWindowService.createMaintenanceWindow({ teamId, body: req.body });
|
||||
@@ -38,13 +37,13 @@ class MaintenanceWindowController {
|
||||
"createMaintenanceWindows"
|
||||
);
|
||||
|
||||
getMaintenanceWindowById = asyncHandler(
|
||||
getMaintenanceWindowById = this.asyncHandler(
|
||||
async (req, res) => {
|
||||
await getMaintenanceWindowByIdParamValidation.validateAsync(req.params);
|
||||
|
||||
const teamId = req.user.teamId;
|
||||
if (!teamId) {
|
||||
throw new Error("Team ID is required");
|
||||
throw this.errorService.createBadRequestError("Team ID is required");
|
||||
}
|
||||
|
||||
const maintenanceWindow = await this.maintenanceWindowService.getMaintenanceWindowById({ id: req.params.id, teamId });
|
||||
@@ -58,14 +57,14 @@ class MaintenanceWindowController {
|
||||
"getMaintenanceWindowById"
|
||||
);
|
||||
|
||||
getMaintenanceWindowsByTeamId = asyncHandler(
|
||||
getMaintenanceWindowsByTeamId = this.asyncHandler(
|
||||
async (req, res) => {
|
||||
await getMaintenanceWindowsByTeamIdQueryValidation.validateAsync(req.query);
|
||||
|
||||
const teamId = req?.user?.teamId;
|
||||
|
||||
if (!teamId) {
|
||||
throw new Error("Team ID is required");
|
||||
throw this.errorService.createBadRequestError("Team ID is required");
|
||||
}
|
||||
|
||||
const maintenanceWindows = await this.maintenanceWindowService.getMaintenanceWindowsByTeamId({ teamId, query: req.query });
|
||||
@@ -79,13 +78,13 @@ class MaintenanceWindowController {
|
||||
"getMaintenanceWindowsByTeamId"
|
||||
);
|
||||
|
||||
getMaintenanceWindowsByMonitorId = asyncHandler(
|
||||
getMaintenanceWindowsByMonitorId = this.asyncHandler(
|
||||
async (req, res) => {
|
||||
await getMaintenanceWindowsByMonitorIdParamValidation.validateAsync(req.params);
|
||||
|
||||
const teamId = req?.user?.teamId;
|
||||
if (!teamId) {
|
||||
throw new Error("Team ID is required");
|
||||
throw this.errorService.createBadRequestError("Team ID is required");
|
||||
}
|
||||
|
||||
const maintenanceWindows = await this.maintenanceWindowService.getMaintenanceWindowsByMonitorId({ monitorId: req.params.monitorId, teamId });
|
||||
@@ -99,13 +98,13 @@ class MaintenanceWindowController {
|
||||
"getMaintenanceWindowsByMonitorId"
|
||||
);
|
||||
|
||||
deleteMaintenanceWindow = asyncHandler(
|
||||
deleteMaintenanceWindow = this.asyncHandler(
|
||||
async (req, res) => {
|
||||
await deleteMaintenanceWindowByIdParamValidation.validateAsync(req.params);
|
||||
|
||||
const teamId = req?.user?.teamId;
|
||||
if (!teamId) {
|
||||
throw new Error("Team ID is required");
|
||||
throw this.errorService.createBadRequestError("Team ID is required");
|
||||
}
|
||||
|
||||
await this.maintenanceWindowService.deleteMaintenanceWindow({ id: req.params.id, teamId });
|
||||
@@ -118,14 +117,14 @@ class MaintenanceWindowController {
|
||||
"deleteMaintenanceWindow"
|
||||
);
|
||||
|
||||
editMaintenanceWindow = asyncHandler(
|
||||
editMaintenanceWindow = this.asyncHandler(
|
||||
async (req, res) => {
|
||||
await editMaintenanceWindowByIdParamValidation.validateAsync(req.params);
|
||||
await editMaintenanceByIdWindowBodyValidation.validateAsync(req.body);
|
||||
|
||||
const teamId = req.user.teamId;
|
||||
if (!teamId) {
|
||||
throw new Error("Team ID is required");
|
||||
throw this.errorService.createBadRequestError("Team ID is required");
|
||||
}
|
||||
|
||||
const editedMaintenanceWindow = await this.maintenanceWindowService.editMaintenanceWindow({ id: req.params.id, body: req.body, teamId });
|
||||
|
||||
@@ -13,16 +13,15 @@ import {
|
||||
getHardwareDetailsByIdQueryValidation,
|
||||
} from "../validation/joi.js";
|
||||
import sslChecker from "ssl-checker";
|
||||
import { asyncHandler } from "../utils/errorUtils.js";
|
||||
import { fetchMonitorCertificate } from "./controllerUtils.js";
|
||||
import BaseController from "./baseController.js";
|
||||
|
||||
const SERVICE_NAME = "monitorController";
|
||||
class MonitorController {
|
||||
constructor({ db, settingsService, jobQueue, stringService, emailService, monitorService }) {
|
||||
this.db = db;
|
||||
class MonitorController extends BaseController {
|
||||
constructor(commonDependencies, { settingsService, jobQueue, emailService, monitorService }) {
|
||||
super(commonDependencies);
|
||||
this.settingsService = settingsService;
|
||||
this.jobQueue = jobQueue;
|
||||
this.stringService = stringService;
|
||||
this.emailService = emailService;
|
||||
this.monitorService = monitorService;
|
||||
}
|
||||
@@ -30,13 +29,11 @@ class MonitorController {
|
||||
async verifyTeamAccess(teamId, monitorId) {
|
||||
const monitor = await this.db.getMonitorById(monitorId);
|
||||
if (!monitor.teamId.equals(teamId)) {
|
||||
const error = new Error("Unauthorized");
|
||||
error.status = 403;
|
||||
throw error;
|
||||
throw this.errorService.createAuthorizationError();
|
||||
}
|
||||
}
|
||||
|
||||
getAllMonitors = asyncHandler(
|
||||
getAllMonitors = this.asyncHandler(
|
||||
async (req, res) => {
|
||||
const monitors = await this.monitorService.getAllMonitors();
|
||||
return res.success({
|
||||
@@ -48,7 +45,7 @@ class MonitorController {
|
||||
"getAllMonitors"
|
||||
);
|
||||
|
||||
getUptimeDetailsById = asyncHandler(
|
||||
getUptimeDetailsById = this.asyncHandler(
|
||||
async (req, res) => {
|
||||
const monitorId = req?.params?.monitorId;
|
||||
const dateRange = req?.query?.dateRange;
|
||||
@@ -57,7 +54,7 @@ class MonitorController {
|
||||
const teamId = req?.user?.teamId;
|
||||
|
||||
if (!teamId) {
|
||||
throw new Error("Team ID is required");
|
||||
throw this.errorService.createBadRequestError("Team ID is required");
|
||||
}
|
||||
|
||||
const data = await this.monitorService.getUptimeDetailsById({
|
||||
@@ -75,7 +72,7 @@ class MonitorController {
|
||||
"getUptimeDetailsById"
|
||||
);
|
||||
|
||||
getMonitorStatsById = asyncHandler(
|
||||
getMonitorStatsById = this.asyncHandler(
|
||||
async (req, res) => {
|
||||
await getMonitorStatsByIdParamValidation.validateAsync(req.params);
|
||||
await getMonitorStatsByIdQueryValidation.validateAsync(req.query);
|
||||
@@ -85,7 +82,7 @@ class MonitorController {
|
||||
|
||||
const teamId = req?.user?.teamId;
|
||||
if (!teamId) {
|
||||
throw new Error("Team ID is required");
|
||||
throw this.errorService.createBadRequestError("Team ID is required");
|
||||
}
|
||||
|
||||
const monitorStats = await this.monitorService.getMonitorStatsById({
|
||||
@@ -116,7 +113,7 @@ class MonitorController {
|
||||
* @returns {Promise<Express.Response>}
|
||||
* @throws {Error} - Throws error if monitor not found or other database errors
|
||||
*/
|
||||
getHardwareDetailsById = asyncHandler(
|
||||
getHardwareDetailsById = this.asyncHandler(
|
||||
async (req, res) => {
|
||||
await getHardwareDetailsByIdParamValidation.validateAsync(req.params);
|
||||
await getHardwareDetailsByIdQueryValidation.validateAsync(req.query);
|
||||
@@ -125,7 +122,7 @@ class MonitorController {
|
||||
const dateRange = req?.query?.dateRange;
|
||||
const teamId = req?.user?.teamId;
|
||||
if (!teamId) {
|
||||
throw new Error("Team ID is required");
|
||||
throw this.errorService.createBadRequestError("Team ID is required");
|
||||
}
|
||||
|
||||
const monitor = await this.monitorService.getHardwareDetailsById({
|
||||
@@ -143,8 +140,8 @@ class MonitorController {
|
||||
"getHardwareDetailsById"
|
||||
);
|
||||
|
||||
getMonitorCertificate = asyncHandler(
|
||||
async (req, res, next) => {
|
||||
getMonitorCertificate = this.asyncHandler(
|
||||
async (req, res) => {
|
||||
await getCertificateParamValidation.validateAsync(req.params);
|
||||
|
||||
const { monitorId } = req.params;
|
||||
@@ -162,14 +159,14 @@ class MonitorController {
|
||||
"getMonitorCertificate"
|
||||
);
|
||||
|
||||
getMonitorById = asyncHandler(
|
||||
getMonitorById = this.asyncHandler(
|
||||
async (req, res) => {
|
||||
await getMonitorByIdParamValidation.validateAsync(req.params);
|
||||
await getMonitorByIdQueryValidation.validateAsync(req.query);
|
||||
|
||||
const teamId = req?.user?.teamId;
|
||||
if (!teamId) {
|
||||
throw new Error("Team ID is required");
|
||||
throw this.errorService.createBadRequestError("Team ID is required");
|
||||
}
|
||||
|
||||
const monitor = await this.monitorService.getMonitorById({ teamId, monitorId: req?.params?.monitorId });
|
||||
@@ -183,7 +180,7 @@ class MonitorController {
|
||||
"getMonitorById"
|
||||
);
|
||||
|
||||
createMonitor = asyncHandler(
|
||||
createMonitor = this.asyncHandler(
|
||||
async (req, res) => {
|
||||
await createMonitorBodyValidation.validateAsync(req.body);
|
||||
|
||||
@@ -201,30 +198,30 @@ class MonitorController {
|
||||
"createMonitor"
|
||||
);
|
||||
|
||||
createBulkMonitors = asyncHandler(
|
||||
createBulkMonitors = this.asyncHandler(
|
||||
async (req, res) => {
|
||||
if (!req.file) {
|
||||
throw new Error("No file uploaded");
|
||||
throw this.errorService.createBadRequestError("No file uploaded");
|
||||
}
|
||||
|
||||
if (!req.file.mimetype.includes("csv")) {
|
||||
throw new Error("File is not a CSV");
|
||||
throw this.errorService.createBadRequestError("File is not a CSV");
|
||||
}
|
||||
|
||||
if (req.file.size === 0) {
|
||||
throw new Error("File is empty");
|
||||
throw this.errorService.createBadRequestError("File is empty");
|
||||
}
|
||||
|
||||
const userId = req?.user?._id;
|
||||
const teamId = req?.user?.teamId;
|
||||
|
||||
if (!userId || !teamId) {
|
||||
throw new Error("Missing userId or teamId");
|
||||
throw this.errorService.createBadRequestError("Missing userId or teamId");
|
||||
}
|
||||
|
||||
const fileData = req?.file?.buffer?.toString("utf-8");
|
||||
if (!fileData) {
|
||||
throw new Error("Cannot get file from buffer");
|
||||
throw this.errorService.createBadRequestError("Cannot get file from buffer");
|
||||
}
|
||||
|
||||
const monitors = await this.monitorService.createBulkMonitors({ fileData, userId, teamId });
|
||||
@@ -238,13 +235,13 @@ class MonitorController {
|
||||
"createBulkMonitors"
|
||||
);
|
||||
|
||||
deleteMonitor = asyncHandler(
|
||||
deleteMonitor = this.asyncHandler(
|
||||
async (req, res) => {
|
||||
await getMonitorByIdParamValidation.validateAsync(req.params);
|
||||
const monitorId = req.params.monitorId;
|
||||
const teamId = req?.user?.teamId;
|
||||
if (!teamId) {
|
||||
throw new Error("Team ID is required");
|
||||
throw this.errorService.createBadRequestError("Team ID is required");
|
||||
}
|
||||
|
||||
const deletedMonitor = await this.monitorService.deleteMonitor({ teamId, monitorId });
|
||||
@@ -255,11 +252,11 @@ class MonitorController {
|
||||
"deleteMonitor"
|
||||
);
|
||||
|
||||
deleteAllMonitors = asyncHandler(
|
||||
deleteAllMonitors = this.asyncHandler(
|
||||
async (req, res) => {
|
||||
const teamId = req?.user?.teamId;
|
||||
if (!teamId) {
|
||||
throw new Error("Team ID is required");
|
||||
throw this.errorService.createBadRequestError("Team ID is required");
|
||||
}
|
||||
|
||||
const deletedCount = await this.monitorService.deleteAllMonitors({ teamId });
|
||||
@@ -270,7 +267,7 @@ class MonitorController {
|
||||
"deleteAllMonitors"
|
||||
);
|
||||
|
||||
editMonitor = asyncHandler(
|
||||
editMonitor = this.asyncHandler(
|
||||
async (req, res) => {
|
||||
await getMonitorByIdParamValidation.validateAsync(req.params);
|
||||
await editMonitorBodyValidation.validateAsync(req.body);
|
||||
@@ -278,7 +275,7 @@ class MonitorController {
|
||||
|
||||
const teamId = req?.user?.teamId;
|
||||
if (!teamId) {
|
||||
throw new Error("Team ID is required");
|
||||
throw this.errorService.createBadRequestError("Team ID is required");
|
||||
}
|
||||
|
||||
const editedMonitor = await this.monitorService.editMonitor({ teamId, monitorId, body: req.body });
|
||||
@@ -292,14 +289,14 @@ class MonitorController {
|
||||
"editMonitor"
|
||||
);
|
||||
|
||||
pauseMonitor = asyncHandler(
|
||||
pauseMonitor = this.asyncHandler(
|
||||
async (req, res) => {
|
||||
await pauseMonitorParamValidation.validateAsync(req.params);
|
||||
|
||||
const monitorId = req.params.monitorId;
|
||||
const teamId = req?.user?.teamId;
|
||||
if (!teamId) {
|
||||
throw new Error("Team ID is required");
|
||||
throw this.errorService.createBadRequestError("Team ID is required");
|
||||
}
|
||||
|
||||
const monitor = await this.monitorService.pauseMonitor({ teamId, monitorId });
|
||||
@@ -313,7 +310,7 @@ class MonitorController {
|
||||
"pauseMonitor"
|
||||
);
|
||||
|
||||
addDemoMonitors = asyncHandler(
|
||||
addDemoMonitors = this.asyncHandler(
|
||||
async (req, res) => {
|
||||
const { _id, teamId } = req.user;
|
||||
const demoMonitors = await this.monitorService.addDemoMonitors({ userId: _id, teamId });
|
||||
@@ -327,11 +324,11 @@ class MonitorController {
|
||||
"addDemoMonitors"
|
||||
);
|
||||
|
||||
sendTestEmail = asyncHandler(
|
||||
sendTestEmail = this.asyncHandler(
|
||||
async (req, res) => {
|
||||
const { to } = req.body;
|
||||
if (!to || typeof to !== "string") {
|
||||
throw new Error(this.stringService.errorForValidEmailAddress);
|
||||
throw this.errorService.createBadRequestError(this.stringService.errorForValidEmailAddress);
|
||||
}
|
||||
|
||||
const messageId = await this.monitorService.sendTestEmail({ to });
|
||||
@@ -344,8 +341,8 @@ class MonitorController {
|
||||
"sendTestEmail"
|
||||
);
|
||||
|
||||
getMonitorsByTeamId = asyncHandler(
|
||||
async (req, res, next) => {
|
||||
getMonitorsByTeamId = this.asyncHandler(
|
||||
async (req, res) => {
|
||||
await getMonitorsByTeamIdParamValidation.validateAsync(req.params);
|
||||
await getMonitorsByTeamIdQueryValidation.validateAsync(req.query);
|
||||
|
||||
@@ -363,7 +360,7 @@ class MonitorController {
|
||||
"getMonitorsByTeamId"
|
||||
);
|
||||
|
||||
getMonitorsAndSummaryByTeamId = asyncHandler(
|
||||
getMonitorsAndSummaryByTeamId = this.asyncHandler(
|
||||
async (req, res) => {
|
||||
await getMonitorsByTeamIdParamValidation.validateAsync(req.params);
|
||||
await getMonitorsByTeamIdQueryValidation.validateAsync(req.query);
|
||||
@@ -372,7 +369,7 @@ class MonitorController {
|
||||
const type = req?.query?.type;
|
||||
const teamId = req?.user?.teamId;
|
||||
if (!teamId) {
|
||||
throw new Error("Team ID is required");
|
||||
throw this.errorService.createBadRequestError("Team ID is required");
|
||||
}
|
||||
|
||||
const result = await this.monitorService.getMonitorsAndSummaryByTeamId({ teamId, type, explain });
|
||||
@@ -386,7 +383,7 @@ class MonitorController {
|
||||
"getMonitorsAndSummaryByTeamId"
|
||||
);
|
||||
|
||||
getMonitorsWithChecksByTeamId = asyncHandler(
|
||||
getMonitorsWithChecksByTeamId = this.asyncHandler(
|
||||
async (req, res) => {
|
||||
await getMonitorsByTeamIdParamValidation.validateAsync(req.params);
|
||||
await getMonitorsByTeamIdQueryValidation.validateAsync(req.query);
|
||||
@@ -395,7 +392,7 @@ class MonitorController {
|
||||
let { limit, type, page, rowsPerPage, filter, field, order } = req.query;
|
||||
const teamId = req?.user?.teamId;
|
||||
if (!teamId) {
|
||||
throw new Error("Team ID is required");
|
||||
throw this.errorService.createBadRequestError("Team ID is required");
|
||||
}
|
||||
|
||||
const monitors = await this.monitorService.getMonitorsWithChecksByTeamId({
|
||||
@@ -419,11 +416,11 @@ class MonitorController {
|
||||
"getMonitorsWithChecksByTeamId"
|
||||
);
|
||||
|
||||
exportMonitorsToCSV = asyncHandler(
|
||||
exportMonitorsToCSV = this.asyncHandler(
|
||||
async (req, res) => {
|
||||
const teamId = req?.user?.teamId;
|
||||
if (!teamId) {
|
||||
throw new Error("Team ID is required");
|
||||
throw this.errorService.createBadRequestError("Team ID is required");
|
||||
}
|
||||
|
||||
const csv = await this.monitorService.exportMonitorsToCSV({ teamId });
|
||||
|
||||
@@ -1,27 +1,23 @@
|
||||
import { createNotificationBodyValidation } from "../validation/joi.js";
|
||||
import { asyncHandler } from "../utils/errorUtils.js";
|
||||
import BaseController from "./baseController.js";
|
||||
|
||||
const SERVICE_NAME = "NotificationController";
|
||||
|
||||
class NotificationController {
|
||||
constructor({ notificationService, stringService, statusService, db }) {
|
||||
class NotificationController extends BaseController {
|
||||
constructor(commonDependencies, { notificationService, statusService }) {
|
||||
super(commonDependencies);
|
||||
this.notificationService = notificationService;
|
||||
this.stringService = stringService;
|
||||
this.statusService = statusService;
|
||||
this.db = db;
|
||||
}
|
||||
|
||||
testNotification = asyncHandler(
|
||||
async (req, res, next) => {
|
||||
testNotification = this.asyncHandler(
|
||||
async (req, res) => {
|
||||
const notification = req.body;
|
||||
|
||||
const success = await this.notificationService.sendTestNotification(notification);
|
||||
|
||||
if (!success) {
|
||||
return res.error({
|
||||
msg: "Sending notification failed",
|
||||
status: 400,
|
||||
});
|
||||
throw this.errorService.createServerError("Sending notification failed");
|
||||
}
|
||||
|
||||
return res.success({
|
||||
@@ -32,8 +28,8 @@ class NotificationController {
|
||||
"testNotification"
|
||||
);
|
||||
|
||||
createNotification = asyncHandler(
|
||||
async (req, res, next) => {
|
||||
createNotification = this.asyncHandler(
|
||||
async (req, res) => {
|
||||
await createNotificationBodyValidation.validateAsync(req.body, {
|
||||
abortEarly: false,
|
||||
});
|
||||
@@ -42,12 +38,12 @@ class NotificationController {
|
||||
|
||||
const teamId = req?.user?.teamId;
|
||||
if (!teamId) {
|
||||
throw new Error("No team ID in request");
|
||||
throw this.errorService.createBadRequestError("Team ID is required");
|
||||
}
|
||||
|
||||
const userId = req?.user?._id;
|
||||
if (!userId) {
|
||||
throw new Error("No user ID in request");
|
||||
throw this.errorService.createBadRequestError("User ID is required");
|
||||
}
|
||||
body.userId = userId;
|
||||
body.teamId = teamId;
|
||||
@@ -62,11 +58,11 @@ class NotificationController {
|
||||
"createNotification"
|
||||
);
|
||||
|
||||
getNotificationsByTeamId = asyncHandler(
|
||||
async (req, res, next) => {
|
||||
getNotificationsByTeamId = this.asyncHandler(
|
||||
async (req, res) => {
|
||||
const teamId = req?.user?.teamId;
|
||||
if (!teamId) {
|
||||
throw new Error("No team ID in request");
|
||||
throw this.errorService.createBadRequestError("Team ID is required");
|
||||
}
|
||||
|
||||
const notifications = await this.db.getNotificationsByTeamId(teamId);
|
||||
@@ -80,18 +76,16 @@ class NotificationController {
|
||||
"getNotificationsByTeamId"
|
||||
);
|
||||
|
||||
deleteNotification = asyncHandler(
|
||||
async (req, res, next) => {
|
||||
deleteNotification = this.asyncHandler(
|
||||
async (req, res) => {
|
||||
const teamId = req?.user?.teamId;
|
||||
if (!teamId) {
|
||||
throw new Error("No team ID in request");
|
||||
throw this.errorService.createBadRequestError("Team ID is required");
|
||||
}
|
||||
|
||||
const notification = await this.db.getNotificationById(req.params.id);
|
||||
if (!notification.teamId.equals(teamId)) {
|
||||
const error = new Error("Unauthorized");
|
||||
error.status = 403;
|
||||
throw error;
|
||||
throw this.errorService.createAuthorizationError();
|
||||
}
|
||||
|
||||
await this.db.deleteNotificationById(req.params.id);
|
||||
@@ -103,19 +97,17 @@ class NotificationController {
|
||||
"deleteNotification"
|
||||
);
|
||||
|
||||
getNotificationById = asyncHandler(
|
||||
async (req, res, next) => {
|
||||
getNotificationById = this.asyncHandler(
|
||||
async (req, res) => {
|
||||
const notification = await this.db.getNotificationById(req.params.id);
|
||||
|
||||
const teamId = req?.user?.teamId;
|
||||
if (!teamId) {
|
||||
throw new Error("No team ID in request");
|
||||
throw this.errorService.createBadRequestError("Team ID is required");
|
||||
}
|
||||
|
||||
if (!notification.teamId.equals(teamId)) {
|
||||
const error = new Error("Unauthorized");
|
||||
error.status = 403;
|
||||
throw error;
|
||||
throw this.errorService.createAuthorizationError();
|
||||
}
|
||||
return res.success({
|
||||
msg: "Notification fetched successfully",
|
||||
@@ -126,23 +118,21 @@ class NotificationController {
|
||||
"getNotificationById"
|
||||
);
|
||||
|
||||
editNotification = asyncHandler(
|
||||
async (req, res, next) => {
|
||||
editNotification = this.asyncHandler(
|
||||
async (req, res) => {
|
||||
await createNotificationBodyValidation.validateAsync(req.body, {
|
||||
abortEarly: false,
|
||||
});
|
||||
|
||||
const teamId = req?.user?.teamId;
|
||||
if (!teamId) {
|
||||
throw new Error("No team ID in request");
|
||||
throw this.errorService.createBadRequestError("Team ID is required");
|
||||
}
|
||||
|
||||
const notification = await this.db.getNotificationById(req.params.id);
|
||||
|
||||
if (!notification.teamId.equals(teamId)) {
|
||||
const error = new Error("Unauthorized");
|
||||
error.status = 403;
|
||||
throw error;
|
||||
throw this.errorService.createAuthorizationError();
|
||||
}
|
||||
|
||||
const editedNotification = await this.db.editNotification(req.params.id, req.body);
|
||||
@@ -155,26 +145,24 @@ class NotificationController {
|
||||
"editNotification"
|
||||
);
|
||||
|
||||
testAllNotifications = asyncHandler(
|
||||
async (req, res, next) => {
|
||||
testAllNotifications = this.asyncHandler(
|
||||
async (req, res) => {
|
||||
const monitorId = req.body.monitorId;
|
||||
const teamId = req?.user?.teamId;
|
||||
if (!teamId) {
|
||||
throw new Error("No team ID in request");
|
||||
throw this.errorService.createBadRequestError("Team ID is required");
|
||||
}
|
||||
|
||||
const monitor = await this.db.getMonitorById(monitorId);
|
||||
|
||||
if (!monitor.teamId.equals(teamId)) {
|
||||
const error = new Error("Unauthorized");
|
||||
error.status = 403;
|
||||
throw error;
|
||||
throw this.errorService.createAuthorizationError();
|
||||
}
|
||||
|
||||
const notifications = monitor.notifications;
|
||||
if (notifications.length === 0) throw new Error("No notifications");
|
||||
if (notifications.length === 0) throw this.errorService.createBadRequestError("No notifications");
|
||||
const result = await this.notificationService.testAllNotifications(notifications);
|
||||
if (!result) throw new Error("Failed to send all notifications");
|
||||
if (!result) throw this.errorService.createServerError("Failed to send all notifications");
|
||||
return res.success({
|
||||
msg: "All notifications sent successfully",
|
||||
});
|
||||
|
||||
@@ -1,15 +1,14 @@
|
||||
import { asyncHandler } from "../utils/errorUtils.js";
|
||||
|
||||
import BaseController from "./baseController.js";
|
||||
const SERVICE_NAME = "JobQueueController";
|
||||
|
||||
class JobQueueController {
|
||||
constructor(jobQueue, stringService) {
|
||||
class JobQueueController extends BaseController {
|
||||
constructor(commonDependencies, { jobQueue }) {
|
||||
super(commonDependencies);
|
||||
this.jobQueue = jobQueue;
|
||||
this.stringService = stringService;
|
||||
}
|
||||
|
||||
getMetrics = asyncHandler(
|
||||
async (req, res, next) => {
|
||||
getMetrics = this.asyncHandler(
|
||||
async (req, res) => {
|
||||
const metrics = await this.jobQueue.getMetrics();
|
||||
res.success({
|
||||
msg: this.stringService.queueGetMetrics,
|
||||
@@ -20,8 +19,8 @@ class JobQueueController {
|
||||
"getMetrics"
|
||||
);
|
||||
|
||||
getJobs = asyncHandler(
|
||||
async (req, res, next) => {
|
||||
getJobs = this.asyncHandler(
|
||||
async (req, res) => {
|
||||
const jobs = await this.jobQueue.getJobs();
|
||||
return res.success({
|
||||
msg: this.stringService.queueGetJobs,
|
||||
@@ -32,8 +31,8 @@ class JobQueueController {
|
||||
"getJobs"
|
||||
);
|
||||
|
||||
getAllMetrics = asyncHandler(
|
||||
async (req, res, next) => {
|
||||
getAllMetrics = this.asyncHandler(
|
||||
async (req, res) => {
|
||||
const jobs = await this.jobQueue.getJobs();
|
||||
const metrics = await this.jobQueue.getMetrics();
|
||||
return res.success({
|
||||
@@ -45,8 +44,8 @@ class JobQueueController {
|
||||
"getAllMetrics"
|
||||
);
|
||||
|
||||
addJob = asyncHandler(
|
||||
async (req, res, next) => {
|
||||
addJob = this.asyncHandler(
|
||||
async (req, res) => {
|
||||
await this.jobQueue.addJob(Math.random().toString(36).substring(7));
|
||||
return res.success({
|
||||
msg: this.stringService.queueAddJob,
|
||||
@@ -56,8 +55,8 @@ class JobQueueController {
|
||||
"addJob"
|
||||
);
|
||||
|
||||
flushQueue = asyncHandler(
|
||||
async (req, res, next) => {
|
||||
flushQueue = this.asyncHandler(
|
||||
async (req, res) => {
|
||||
const result = await this.jobQueue.flushQueues();
|
||||
return res.success({
|
||||
msg: this.stringService.jobQueueFlush,
|
||||
@@ -68,8 +67,8 @@ class JobQueueController {
|
||||
"flushQueue"
|
||||
);
|
||||
|
||||
checkQueueHealth = asyncHandler(
|
||||
async (req, res, next) => {
|
||||
checkQueueHealth = this.asyncHandler(
|
||||
async (req, res) => {
|
||||
const stuckQueues = await this.jobQueue.checkQueueHealth();
|
||||
return res.success({
|
||||
msg: this.stringService.queueHealthCheck,
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
import { updateAppSettingsBodyValidation } from "../validation/joi.js";
|
||||
import { sendTestEmailBodyValidation } from "../validation/joi.js";
|
||||
import { asyncHandler, createServerError } from "../utils/errorUtils.js";
|
||||
import BaseController from "./baseController.js";
|
||||
|
||||
const SERVICE_NAME = "SettingsController";
|
||||
|
||||
class SettingsController {
|
||||
constructor({ db, settingsService, stringService, emailService }) {
|
||||
this.db = db;
|
||||
class SettingsController extends BaseController {
|
||||
constructor(commonDependencies, { settingsService, emailService }) {
|
||||
super(commonDependencies);
|
||||
this.settingsService = settingsService;
|
||||
this.stringService = stringService;
|
||||
this.emailService = emailService;
|
||||
}
|
||||
|
||||
@@ -33,8 +32,8 @@ class SettingsController {
|
||||
return returnSettings;
|
||||
};
|
||||
|
||||
getAppSettings = asyncHandler(
|
||||
async (req, res, next) => {
|
||||
getAppSettings = this.asyncHandler(
|
||||
async (req, res) => {
|
||||
const dbSettings = await this.settingsService.getDBSettings();
|
||||
|
||||
const returnSettings = this.buildAppSettings(dbSettings);
|
||||
@@ -47,8 +46,8 @@ class SettingsController {
|
||||
"getAppSettings"
|
||||
);
|
||||
|
||||
updateAppSettings = asyncHandler(
|
||||
async (req, res, next) => {
|
||||
updateAppSettings = this.asyncHandler(
|
||||
async (req, res) => {
|
||||
await updateAppSettingsBodyValidation.validateAsync(req.body);
|
||||
|
||||
const updatedSettings = await this.db.updateAppSettings(req.body);
|
||||
@@ -62,14 +61,9 @@ class SettingsController {
|
||||
"updateAppSettings"
|
||||
);
|
||||
|
||||
sendTestEmail = asyncHandler(
|
||||
async (req, res, next) => {
|
||||
try {
|
||||
await sendTestEmailBodyValidation.validateAsync(req.body);
|
||||
} catch (error) {
|
||||
next(handleValidationError(error, SERVICE_NAME));
|
||||
return;
|
||||
}
|
||||
sendTestEmail = this.asyncHandler(
|
||||
async (req, res) => {
|
||||
await sendTestEmailBodyValidation.validateAsync(req.body);
|
||||
|
||||
const {
|
||||
to,
|
||||
@@ -107,7 +101,7 @@ class SettingsController {
|
||||
});
|
||||
|
||||
if (!messageId) {
|
||||
throw createServerError("Failed to send test email.");
|
||||
throw this.errorService.createServerError("Failed to send test email.");
|
||||
}
|
||||
|
||||
return res.success({
|
||||
|
||||
@@ -1,16 +1,15 @@
|
||||
import { createStatusPageBodyValidation, getStatusPageParamValidation, getStatusPageQueryValidation, imageValidation } from "../validation/joi.js";
|
||||
import { asyncHandler } from "../utils/errorUtils.js";
|
||||
import BaseController from "./baseController.js";
|
||||
|
||||
const SERVICE_NAME = "statusPageController";
|
||||
|
||||
class StatusPageController {
|
||||
constructor(db, stringService) {
|
||||
this.db = db;
|
||||
this.stringService = stringService;
|
||||
class StatusPageController extends BaseController {
|
||||
constructor(commonDependencies) {
|
||||
super(commonDependencies);
|
||||
}
|
||||
|
||||
createStatusPage = asyncHandler(
|
||||
async (req, res, next) => {
|
||||
createStatusPage = this.asyncHandler(
|
||||
async (req, res) => {
|
||||
await createStatusPageBodyValidation.validateAsync(req.body);
|
||||
await imageValidation.validateAsync(req.file);
|
||||
|
||||
@@ -30,16 +29,14 @@ class StatusPageController {
|
||||
"createStatusPage"
|
||||
);
|
||||
|
||||
updateStatusPage = asyncHandler(
|
||||
async (req, res, next) => {
|
||||
updateStatusPage = this.asyncHandler(
|
||||
async (req, res) => {
|
||||
await createStatusPageBodyValidation.validateAsync(req.body);
|
||||
await imageValidation.validateAsync(req.file);
|
||||
|
||||
const statusPage = await this.db.updateStatusPage(req.body, req.file);
|
||||
if (statusPage === null) {
|
||||
const error = new Error(this.stringService.statusPageNotFound);
|
||||
error.status = 404;
|
||||
throw error;
|
||||
throw this.errorService.createNotFoundError(this.stringService.statusPageNotFound);
|
||||
}
|
||||
return res.success({
|
||||
msg: this.stringService.statusPageUpdate,
|
||||
@@ -50,8 +47,8 @@ class StatusPageController {
|
||||
"updateStatusPage"
|
||||
);
|
||||
|
||||
getStatusPage = asyncHandler(
|
||||
async (req, res, next) => {
|
||||
getStatusPage = this.asyncHandler(
|
||||
async (req, res) => {
|
||||
const statusPage = await this.db.getStatusPage();
|
||||
return res.success({
|
||||
msg: this.stringService.statusPageByUrl,
|
||||
@@ -62,8 +59,8 @@ class StatusPageController {
|
||||
"getStatusPage"
|
||||
);
|
||||
|
||||
getStatusPageByUrl = asyncHandler(
|
||||
async (req, res, next) => {
|
||||
getStatusPageByUrl = this.asyncHandler(
|
||||
async (req, res) => {
|
||||
await getStatusPageParamValidation.validateAsync(req.params);
|
||||
await getStatusPageQueryValidation.validateAsync(req.query);
|
||||
|
||||
@@ -77,8 +74,8 @@ class StatusPageController {
|
||||
"getStatusPageByUrl"
|
||||
);
|
||||
|
||||
getStatusPagesByTeamId = asyncHandler(
|
||||
async (req, res, next) => {
|
||||
getStatusPagesByTeamId = this.asyncHandler(
|
||||
async (req, res) => {
|
||||
const teamId = req.user.teamId;
|
||||
const statusPages = await this.db.getStatusPagesByTeamId(teamId);
|
||||
|
||||
@@ -91,8 +88,8 @@ class StatusPageController {
|
||||
"getStatusPagesByTeamId"
|
||||
);
|
||||
|
||||
deleteStatusPage = asyncHandler(
|
||||
async (req, res, next) => {
|
||||
deleteStatusPage = this.asyncHandler(
|
||||
async (req, res) => {
|
||||
await this.db.deleteStatusPage(req.params.url);
|
||||
return res.success({
|
||||
msg: this.stringService.statusPageDelete,
|
||||
|
||||
@@ -3,16 +3,17 @@ import fs from "fs";
|
||||
import swaggerUi from "swagger-ui-express";
|
||||
import jwt from "jsonwebtoken";
|
||||
import papaparse from "papaparse";
|
||||
|
||||
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";
|
||||
@@ -58,6 +59,8 @@ import PulseQueueHelper from "./service/infrastructure/PulseQueue/PulseQueueHelp
|
||||
import SuperSimpleQueue from "./service/infrastructure/SuperSimpleQueue/SuperSimpleQueue.js";
|
||||
import SuperSimpleQueueHelper from "./service/infrastructure/SuperSimpleQueue/SuperSimpleQueueHelper.js";
|
||||
|
||||
import ErrorService from "./service/infrastructure/errorService.js";
|
||||
|
||||
// Business services
|
||||
import UserService from "./service/business/userService.js";
|
||||
import CheckService from "./service/business/checkService.js";
|
||||
@@ -186,6 +189,7 @@ const startApp = async () => {
|
||||
});
|
||||
|
||||
const redisService = new RedisService({ Redis: IORedis, logger });
|
||||
const errorService = new ErrorService();
|
||||
|
||||
// const jobQueueHelper = new JobQueueHelper({
|
||||
// redisService,
|
||||
@@ -240,11 +244,13 @@ const startApp = async () => {
|
||||
logger,
|
||||
stringService,
|
||||
jwt,
|
||||
errorService,
|
||||
});
|
||||
const checkService = new CheckService({
|
||||
db,
|
||||
settingsService,
|
||||
stringService,
|
||||
errorService,
|
||||
});
|
||||
const diagnosticService = new DiagnosticService();
|
||||
const inviteService = new InviteService({
|
||||
@@ -252,11 +258,13 @@ const startApp = async () => {
|
||||
settingsService,
|
||||
emailService,
|
||||
stringService,
|
||||
errorService,
|
||||
});
|
||||
const maintenanceWindowService = new MaintenanceWindowService({
|
||||
db,
|
||||
settingsService,
|
||||
stringService,
|
||||
errorService,
|
||||
});
|
||||
const monitorService = new MonitorService({
|
||||
db,
|
||||
@@ -266,11 +274,14 @@ const startApp = async () => {
|
||||
emailService,
|
||||
papaparse,
|
||||
logger,
|
||||
errorService,
|
||||
});
|
||||
// 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);
|
||||
ServiceRegistry.register(SettingsService.SERVICE_NAME, settingsService);
|
||||
ServiceRegistry.register(EmailService.SERVICE_NAME, emailService);
|
||||
@@ -285,6 +296,7 @@ const startApp = async () => {
|
||||
ServiceRegistry.register(InviteService.SERVICE_NAME, inviteService);
|
||||
ServiceRegistry.register(MaintenanceWindowService.SERVICE_NAME, maintenanceWindowService);
|
||||
ServiceRegistry.register(MonitorService.SERVICE_NAME, monitorService);
|
||||
ServiceRegistry.register(DiagnosticService.SERVICE_NAME, diagnosticService);
|
||||
|
||||
await translationService.initialize();
|
||||
|
||||
@@ -298,66 +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),
|
||||
});
|
||||
|
||||
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),
|
||||
});
|
||||
|
||||
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),
|
||||
});
|
||||
|
||||
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),
|
||||
});
|
||||
|
||||
const inviteController = new InviteController({
|
||||
stringService: ServiceRegistry.get(StringService.SERVICE_NAME),
|
||||
inviteService,
|
||||
const inviteController = new InviteController(commonDependencies, {
|
||||
inviteService: ServiceRegistry.get(InviteService.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),
|
||||
});
|
||||
|
||||
const queueController = new QueueController(ServiceRegistry.get(JobQueue.SERVICE_NAME), ServiceRegistry.get(StringService.SERVICE_NAME));
|
||||
|
||||
const logController = new LogController(logger);
|
||||
|
||||
const statusPageController = new StatusPageController(ServiceRegistry.get(MongoDB.SERVICE_NAME), ServiceRegistry.get(StringService.SERVICE_NAME));
|
||||
|
||||
const notificationController = new NotificationController({
|
||||
notificationService: ServiceRegistry.get(NotificationService.SERVICE_NAME),
|
||||
stringService: ServiceRegistry.get(StringService.SERVICE_NAME),
|
||||
statusService: ServiceRegistry.get(StatusService.SERVICE_NAME),
|
||||
db: ServiceRegistry.get(MongoDB.SERVICE_NAME),
|
||||
const queueController = new QueueController(commonDependencies, {
|
||||
jobQueue: ServiceRegistry.get(JobQueue.SERVICE_NAME),
|
||||
});
|
||||
|
||||
const diagnosticController = new DiagnosticController({
|
||||
diagnosticService,
|
||||
const logController = new LogController(commonDependencies);
|
||||
|
||||
const statusPageController = new StatusPageController(commonDependencies);
|
||||
|
||||
const notificationController = new NotificationController(commonDependencies, {
|
||||
notificationService: ServiceRegistry.get(NotificationService.SERVICE_NAME),
|
||||
statusService: ServiceRegistry.get(StatusService.SERVICE_NAME),
|
||||
});
|
||||
|
||||
const diagnosticController = new DiagnosticController(commonDependencies, {
|
||||
diagnosticService: ServiceRegistry.get(DiagnosticService.SERVICE_NAME),
|
||||
});
|
||||
|
||||
//Create routes
|
||||
@@ -370,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);
|
||||
@@ -415,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({
|
||||
|
||||
@@ -3,7 +3,6 @@ import ServiceRegistry from "../service/system/serviceRegistry.js";
|
||||
import StringService from "../service/system/stringService.js";
|
||||
|
||||
const handleErrors = (error, req, res, next) => {
|
||||
console.log("ERROR", error);
|
||||
const status = error.status || 500;
|
||||
const stringService = ServiceRegistry.get(StringService.SERVICE_NAME);
|
||||
const message = error.message || stringService.authIncorrectPassword;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
const SERVICE_NAME = "inviteService";
|
||||
import { createServerError } from "../../utils/errorUtils.js";
|
||||
|
||||
class InviteService {
|
||||
static SERVICE_NAME = SERVICE_NAME;
|
||||
|
||||
constructor({ db, settingsService, emailService, stringService }) {
|
||||
constructor({ db, settingsService, emailService, stringService, errorService }) {
|
||||
this.db = db;
|
||||
this.settingsService = settingsService;
|
||||
this.emailService = emailService;
|
||||
this.stringService = stringService;
|
||||
this.errorService = errorService;
|
||||
}
|
||||
|
||||
getInviteToken = async ({ invite, teamId }) => {
|
||||
@@ -27,7 +27,7 @@ class InviteService {
|
||||
});
|
||||
const result = await this.emailService.sendEmail(inviteRequest.email, "Welcome to Uptime Monitor", html);
|
||||
if (!result) {
|
||||
throw createServerError("Failed to send invite e-mail... Please verify your settings.");
|
||||
throw this.errorService.createServerError("Failed to send invite e-mail... Please verify your settings.");
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -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) => {
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
import { createMonitorsBodyValidation } from "../../validation/joi.js";
|
||||
import { createServerError } from "../../utils/errorUtils.js";
|
||||
import logger from "../../utils/logger.js";
|
||||
|
||||
const SERVICE_NAME = "MonitorService";
|
||||
class MonitorService {
|
||||
SERVICE_NAME = SERVICE_NAME;
|
||||
|
||||
constructor({ db, settingsService, jobQueue, stringService, emailService, papaparse }) {
|
||||
constructor({ db, settingsService, jobQueue, stringService, emailService, papaparse, logger, errorService }) {
|
||||
this.db = db;
|
||||
this.settingsService = settingsService;
|
||||
this.jobQueue = jobQueue;
|
||||
@@ -14,14 +12,13 @@ class MonitorService {
|
||||
this.emailService = emailService;
|
||||
this.papaparse = papaparse;
|
||||
this.logger = logger;
|
||||
this.errorService = errorService;
|
||||
}
|
||||
|
||||
verifyTeamAccess = async ({ teamId, monitorId }) => {
|
||||
const monitor = await this.db.getMonitorById(monitorId);
|
||||
if (!monitor?.teamId?.equals(teamId)) {
|
||||
const error = new Error("Unauthorized");
|
||||
error.status = 403;
|
||||
throw error;
|
||||
throw this.errorService.createAuthorizationError();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -93,7 +90,7 @@ class MonitorService {
|
||||
if (["port", "interval"].includes(header)) {
|
||||
const num = parseInt(value, 10);
|
||||
if (isNaN(num)) {
|
||||
throw new Error(`${header} should be a valid number, got: ${value}`);
|
||||
throw this.errorService.createBadRequestError(`${header} should be a valid number, got: ${value}`);
|
||||
}
|
||||
return num;
|
||||
}
|
||||
@@ -103,11 +100,11 @@ class MonitorService {
|
||||
complete: async ({ data, errors }) => {
|
||||
try {
|
||||
if (errors.length > 0) {
|
||||
throw createServerError("Error parsing CSV");
|
||||
throw this.errorService.createServerError("Error parsing CSV");
|
||||
}
|
||||
|
||||
if (!data || data.length === 0) {
|
||||
throw createServerError("CSV file contains no data rows");
|
||||
throw this.errorService.createServerError("CSV file contains no data rows");
|
||||
}
|
||||
|
||||
const enrichedData = data.map((monitor) => ({
|
||||
@@ -156,7 +153,7 @@ class MonitorService {
|
||||
await this.db.deletePageSpeedChecksByMonitorId(monitor._id);
|
||||
await this.db.deleteNotificationsByMonitorId(monitor._id);
|
||||
} catch (error) {
|
||||
logger.warn({
|
||||
this.logger.warn({
|
||||
message: `Error deleting associated records for monitor ${monitor._id} with name ${monitor.name}`,
|
||||
service: SERVICE_NAME,
|
||||
method: "deleteAllMonitors",
|
||||
@@ -195,7 +192,7 @@ class MonitorService {
|
||||
const messageId = await this.emailService.sendEmail(to, subject, html);
|
||||
|
||||
if (!messageId) {
|
||||
throw createServerError("Failed to send test email.");
|
||||
throw this.errorService.createServerError("Failed to send test email.");
|
||||
}
|
||||
|
||||
return messageId;
|
||||
@@ -240,10 +237,10 @@ class MonitorService {
|
||||
};
|
||||
|
||||
exportMonitorsToCSV = async ({ teamId }) => {
|
||||
const monitors = await this.monitorService.getMonitorsByTeamId({ teamId });
|
||||
const monitors = await this.db.getMonitorsByTeamId({ teamId });
|
||||
|
||||
if (!monitors || monitors.length === 0) {
|
||||
throw new Error("No monitors to export");
|
||||
throw this.errorService.createNotFoundError("No monitors to export");
|
||||
}
|
||||
|
||||
const csvData = monitors?.filteredMonitors?.map((monitor) => ({
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
const SERVICE_NAME = "userService";
|
||||
import { createAuthError, createError } from "../../utils/errorUtils.js";
|
||||
|
||||
class UserService {
|
||||
static SERVICE_NAME = SERVICE_NAME;
|
||||
constructor({ db, emailService, settingsService, logger, stringService, jwt }) {
|
||||
constructor({ db, emailService, settingsService, logger, stringService, jwt, errorService }) {
|
||||
this.db = db;
|
||||
this.emailService = emailService;
|
||||
this.settingsService = settingsService;
|
||||
this.logger = logger;
|
||||
this.stringService = stringService;
|
||||
this.jwt = jwt;
|
||||
this.errorService = errorService;
|
||||
}
|
||||
|
||||
issueToken = (payload, appSettings) => {
|
||||
@@ -80,7 +80,7 @@ class UserService {
|
||||
// Compare password
|
||||
const match = await user.comparePassword(password);
|
||||
if (match !== true) {
|
||||
throw createAuthError(this.stringService.authIncorrectPassword);
|
||||
throw this.errorService.createAuthenticationError(this.stringService.authIncorrectPassword);
|
||||
}
|
||||
|
||||
// Remove password from user object. Should this be abstracted to DB layer?
|
||||
@@ -109,7 +109,7 @@ class UserService {
|
||||
// If not a match, throw a 403
|
||||
// 403 instead of 401 to avoid triggering axios interceptor
|
||||
if (!match) {
|
||||
throw createError(this.stringService.authIncorrectPassword, 403);
|
||||
throw this.errorService.createAuthorizationError(this.stringService.authIncorrectPassword);
|
||||
}
|
||||
// If a match, update the password
|
||||
updates.password = updates.newPassword;
|
||||
@@ -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
|
||||
|
||||
98
server/service/infrastructure/errorService.js
Normal file
98
server/service/infrastructure/errorService.js
Normal file
@@ -0,0 +1,98 @@
|
||||
export class AppError extends Error {
|
||||
constructor(message, status = 500, service = null, method = null, details = null) {
|
||||
super(message);
|
||||
this.status = status;
|
||||
this.service = service;
|
||||
this.method = method;
|
||||
this.details = details;
|
||||
|
||||
Error.captureStackTrace(this, this.constructor);
|
||||
}
|
||||
}
|
||||
|
||||
class ValidationError extends AppError {
|
||||
constructor(message, details = null, service = null, method = null) {
|
||||
super(message, 422, service, method, details);
|
||||
}
|
||||
}
|
||||
|
||||
class AuthenticationError extends AppError {
|
||||
constructor(message, details = null, service = null, method = null) {
|
||||
super(message, 401, service, method, details);
|
||||
}
|
||||
}
|
||||
|
||||
class AuthorizationError extends AppError {
|
||||
constructor(message, details = null, service = null, method = null) {
|
||||
super(message, 403, service, method, details);
|
||||
}
|
||||
}
|
||||
|
||||
class NotFoundError extends AppError {
|
||||
constructor(message, details = null, service = null, method = null) {
|
||||
super(message, 404, service, method, details);
|
||||
}
|
||||
}
|
||||
|
||||
class ConflictError extends AppError {
|
||||
constructor(message, details = null, service = null, method = null) {
|
||||
super(message, 409, service, method, details);
|
||||
}
|
||||
}
|
||||
|
||||
class DatabaseError extends AppError {
|
||||
constructor(message, details = null, service = null, method = null) {
|
||||
super(message, 500, service, method, details);
|
||||
}
|
||||
}
|
||||
|
||||
class BadRequestError extends AppError {
|
||||
constructor(message, details = null, service = null, method = null) {
|
||||
super(message, 400, service, method, details);
|
||||
}
|
||||
}
|
||||
|
||||
const SERVICE_NAME = "ErrorService";
|
||||
class ErrorService {
|
||||
static SERVICE_NAME = SERVICE_NAME;
|
||||
|
||||
constructor() {}
|
||||
|
||||
createError = (message, status = 500, service = null, method = null, details = null) => {
|
||||
return new AppError(message, status, service, method, details);
|
||||
};
|
||||
|
||||
createValidationError = (message, details = null, service = null, method = null) => {
|
||||
return new ValidationError(message, details, service, method);
|
||||
};
|
||||
|
||||
createAuthenticationError = (message = "Unauthorized", details = null, service = null, method = null) => {
|
||||
return new AuthenticationError(message, details, service, method);
|
||||
};
|
||||
|
||||
createAuthorizationError = (message, details = null, service = null, method = null) => {
|
||||
return new AuthorizationError(message, details, service, method);
|
||||
};
|
||||
|
||||
createNotFoundError = (message, details = null, service = null, method = null) => {
|
||||
return new NotFoundError(message, details, service, method);
|
||||
};
|
||||
|
||||
createConflictError = (message, details = null, service = null, method = null) => {
|
||||
return new ConflictError(message, details, service, method);
|
||||
};
|
||||
|
||||
createDatabaseError = (message, details = null, service = null, method = null) => {
|
||||
return new DatabaseError(message, details, service, method);
|
||||
};
|
||||
|
||||
createServerError = (message, details = null, service = null, method = null) => {
|
||||
return this.createError(message, 500, service, method, details);
|
||||
};
|
||||
|
||||
createBadRequestError = (message = "BadRequest", details = null, service = null, method = null) => {
|
||||
return new BadRequestError(message, details, service, method);
|
||||
};
|
||||
}
|
||||
|
||||
export default ErrorService;
|
||||
@@ -1,81 +0,0 @@
|
||||
class AppError extends Error {
|
||||
constructor(message, status = 500, service = null, method = null, details = null) {
|
||||
super(message);
|
||||
this.status = status;
|
||||
this.service = service;
|
||||
this.method = method;
|
||||
this.details = details;
|
||||
|
||||
Error.captureStackTrace(this, this.constructor);
|
||||
}
|
||||
}
|
||||
|
||||
export const createError = (message, status = 500, service = null, method = null, details = null) => {
|
||||
return new AppError(message, status, service, method, details);
|
||||
};
|
||||
|
||||
export const createValidationError = (message, details = null, service = null, method = null) => {
|
||||
return createError(message, 422, service, method, details);
|
||||
};
|
||||
|
||||
export const createAuthError = (message, details = null, service = null, method = null) => {
|
||||
return createError(message, 401, service, method, details);
|
||||
};
|
||||
|
||||
export const createForbiddenError = (message, details = null, service = null, method = null) => {
|
||||
return createError(message, 403, service, method, details);
|
||||
};
|
||||
|
||||
export const createNotFoundError = (message, details = null, service = null, method = null) => {
|
||||
return createError(message, 404, service, method, details);
|
||||
};
|
||||
|
||||
export const createConflictError = (message, details = null, service = null, method = null) => {
|
||||
return createError(message, 409, service, method, details);
|
||||
};
|
||||
|
||||
export const createServerError = (message, details = null, service = null, method = null) => {
|
||||
return createError(message, 500, service, method, details);
|
||||
};
|
||||
|
||||
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 || error.name === "ValidationError") {
|
||||
const validationError = createValidationError(error.message, error.details, serviceName, methodName);
|
||||
return next(validationError);
|
||||
}
|
||||
|
||||
if (error instanceof AppError) {
|
||||
error.service = error.service || serviceName;
|
||||
error.method = error.method || methodName;
|
||||
return next(error);
|
||||
}
|
||||
|
||||
if (error.code === "23505") {
|
||||
const appError = createConflictError("Resource already exists", {
|
||||
originalError: error.message,
|
||||
code: error.code,
|
||||
});
|
||||
appError.service = serviceName;
|
||||
appError.method = methodName;
|
||||
return next(appError);
|
||||
}
|
||||
|
||||
if (error.status) {
|
||||
const appError = 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 = 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);
|
||||
}
|
||||
};
|
||||
};
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user