mirror of
https://github.com/bluewave-labs/Checkmate.git
synced 2026-01-08 02:39:44 -06:00
add error handling util, update middleware and controllers
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import { createAnnouncementValidation } from "../validation/joi.js";
|
||||
import { handleError } from "./controllerUtils.js";
|
||||
import { asyncHandler } from "../utils/errorUtils.js";
|
||||
|
||||
const SERVICE_NAME = "announcementController";
|
||||
|
||||
@@ -28,16 +28,10 @@ class AnnouncementController {
|
||||
*
|
||||
* @returns {Promise<void>} A promise that resolves once the response is sent.
|
||||
*/
|
||||
createAnnouncement = async (req, res, next) => {
|
||||
try {
|
||||
createAnnouncement = asyncHandler(
|
||||
async (req, res, next) => {
|
||||
await createAnnouncementValidation.validateAsync(req.body);
|
||||
} catch (error) {
|
||||
return next(handleError(error, SERVICE_NAME)); // Handle Joi validation errors
|
||||
}
|
||||
|
||||
const { title, message } = req.body;
|
||||
|
||||
try {
|
||||
const { title, message } = req.body;
|
||||
const announcementData = {
|
||||
title: title.trim(),
|
||||
message: message.trim(),
|
||||
@@ -49,10 +43,10 @@ class AnnouncementController {
|
||||
msg: this.stringService.createAnnouncement,
|
||||
data: newAnnouncement,
|
||||
});
|
||||
} catch (error) {
|
||||
next(handleError(error, SERVICE_NAME, "createAnnouncement"));
|
||||
}
|
||||
};
|
||||
},
|
||||
SERVICE_NAME,
|
||||
"createAnnouncement"
|
||||
);
|
||||
|
||||
/**
|
||||
* Handles retrieving announcements with pagination.
|
||||
@@ -63,17 +57,17 @@ class AnnouncementController {
|
||||
* - `msg`: A message about the success of the request.
|
||||
* @param {Function} next - The next middleware function in the stack for error handling.
|
||||
*/
|
||||
getAnnouncement = async (req, res, next) => {
|
||||
try {
|
||||
getAnnouncement = asyncHandler(
|
||||
async (req, res, next) => {
|
||||
const allAnnouncements = await this.db.getAnnouncements();
|
||||
return res.success({
|
||||
msg: this.stringService.getAnnouncement,
|
||||
data: allAnnouncements,
|
||||
});
|
||||
} catch (error) {
|
||||
next(handleError(error, SERVICE_NAME, "getAnnouncement"));
|
||||
}
|
||||
};
|
||||
},
|
||||
SERVICE_NAME,
|
||||
"getAnnouncement"
|
||||
);
|
||||
}
|
||||
|
||||
export default AnnouncementController;
|
||||
|
||||
@@ -10,7 +10,8 @@ import {
|
||||
import jwt from "jsonwebtoken";
|
||||
import { getTokenFromHeaders } from "../utils/utils.js";
|
||||
import crypto from "crypto";
|
||||
import { handleValidationError, handleError } from "./controllerUtils.js";
|
||||
import { asyncHandler, createAuthError, createError } from "../utils/errorUtils.js";
|
||||
|
||||
const SERVICE_NAME = "authController";
|
||||
|
||||
class AuthController {
|
||||
@@ -32,15 +33,10 @@ class AuthController {
|
||||
* @throws {Error}
|
||||
*/
|
||||
issueToken = (payload, appSettings) => {
|
||||
try {
|
||||
const tokenTTL = appSettings?.jwtTTL ?? "2h";
|
||||
const tokenSecret = appSettings?.jwtSecret;
|
||||
const payloadData = payload;
|
||||
|
||||
return jwt.sign(payloadData, tokenSecret, { expiresIn: tokenTTL });
|
||||
} catch (error) {
|
||||
throw handleError(error, SERVICE_NAME, "issueToken");
|
||||
}
|
||||
const tokenTTL = appSettings?.jwtTTL ?? "2h";
|
||||
const tokenSecret = appSettings?.jwtSecret;
|
||||
const payloadData = payload;
|
||||
return jwt.sign(payloadData, tokenSecret, { expiresIn: tokenTTL });
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -55,19 +51,14 @@ class AuthController {
|
||||
* @returns {Object} The response object with a success status, a message indicating the creation of the user, the created user data, and a JWT token.
|
||||
* @throws {Error} If there is an error during the process, especially if there is a validation error (422).
|
||||
*/
|
||||
registerUser = async (req, res, next) => {
|
||||
try {
|
||||
registerUser = asyncHandler(
|
||||
async (req, res, next) => {
|
||||
if (req.body?.email) {
|
||||
req.body.email = req.body.email?.toLowerCase();
|
||||
}
|
||||
await registrationBodyValidation.validateAsync(req.body);
|
||||
} catch (error) {
|
||||
const validationError = handleValidationError(error, SERVICE_NAME);
|
||||
next(validationError);
|
||||
return;
|
||||
}
|
||||
// Create a new user
|
||||
try {
|
||||
|
||||
// Create a new user
|
||||
const user = req.body;
|
||||
// If superAdmin exists, a token should be attached to all further register requests
|
||||
const superAdminExists = await this.db.checkSuperadmin(req, res);
|
||||
@@ -123,10 +114,10 @@ class AuthController {
|
||||
msg: this.stringService.authCreateUser,
|
||||
data: { user: newUser, token: token },
|
||||
});
|
||||
} catch (error) {
|
||||
next(handleError(error, SERVICE_NAME, "registerController"));
|
||||
}
|
||||
};
|
||||
},
|
||||
SERVICE_NAME,
|
||||
"registerUser"
|
||||
);
|
||||
|
||||
/**
|
||||
* Logs in a user by validating the user's credentials and issuing a JWT token.
|
||||
@@ -140,18 +131,13 @@ class AuthController {
|
||||
* @returns {Object} The response object with a success status, a message indicating the login of the user, the user data (without password and avatar image), and a JWT token.
|
||||
* @throws {Error} If there is an error during the process, especially if there is a validation error (422) or the password is incorrect.
|
||||
*/
|
||||
loginUser = async (req, res, next) => {
|
||||
try {
|
||||
loginUser = asyncHandler(
|
||||
async (req, res, next) => {
|
||||
if (req.body?.email) {
|
||||
req.body.email = req.body.email?.toLowerCase();
|
||||
}
|
||||
await loginValidation.validateAsync(req.body);
|
||||
} catch (error) {
|
||||
const validationError = handleValidationError(error, SERVICE_NAME);
|
||||
next(validationError);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
|
||||
const { email, password } = req.body;
|
||||
|
||||
// Check if user exists
|
||||
@@ -160,10 +146,7 @@ class AuthController {
|
||||
// Compare password
|
||||
const match = await user.comparePassword(password);
|
||||
if (match !== true) {
|
||||
const error = new Error(this.stringService.authIncorrectPassword);
|
||||
error.status = 401;
|
||||
next(error);
|
||||
return;
|
||||
throw createAuthError(this.stringService.authIncorrectPassword);
|
||||
}
|
||||
|
||||
// Remove password from user object. Should this be abstracted to DB layer?
|
||||
@@ -184,11 +167,10 @@ class AuthController {
|
||||
token: token,
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
error.status = 401;
|
||||
next(handleError(error, SERVICE_NAME, "loginUser"));
|
||||
}
|
||||
};
|
||||
},
|
||||
SERVICE_NAME,
|
||||
"loginUser"
|
||||
);
|
||||
|
||||
/**
|
||||
* Edits a user's information. If the user wants to change their password, the current password is checked before updating to the new password.
|
||||
@@ -204,26 +186,16 @@ class AuthController {
|
||||
* @returns {Object} The response object with a success status, a message indicating the update of the user, and the updated user data.
|
||||
* @throws {Error} If there is an error during the process, especially if there is a validation error (422), the user is unauthorized (401), or the password is incorrect (403).
|
||||
*/
|
||||
editUser = async (req, res, next) => {
|
||||
try {
|
||||
editUser = asyncHandler(
|
||||
async (req, res, next) => {
|
||||
await editUserParamValidation.validateAsync(req.params);
|
||||
await editUserBodyValidation.validateAsync(req.body);
|
||||
} catch (error) {
|
||||
const validationError = handleValidationError(error, SERVICE_NAME);
|
||||
next(validationError);
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO is this neccessary any longer? Verify ownership middleware should handle this
|
||||
if (req.params.userId !== req.user._id.toString()) {
|
||||
const error = new Error(this.stringService.unauthorized);
|
||||
error.status = 401;
|
||||
error.service = SERVICE_NAME;
|
||||
next(error);
|
||||
return;
|
||||
}
|
||||
// TODO is this neccessary any longer? Verify ownership middleware should handle this
|
||||
if (req.params.userId !== req.user._id.toString()) {
|
||||
throw createAuthError(this.stringService.unauthorized);
|
||||
}
|
||||
|
||||
try {
|
||||
// Change Password check
|
||||
if (req.body.password && req.body.newPassword) {
|
||||
// Get token from headers
|
||||
@@ -240,10 +212,7 @@ class AuthController {
|
||||
// If not a match, throw a 403
|
||||
// 403 instead of 401 to avoid triggering axios interceptor
|
||||
if (!match) {
|
||||
const error = new Error(this.stringService.authIncorrectPassword);
|
||||
error.status = 403;
|
||||
next(error);
|
||||
return;
|
||||
throw createError(this.stringService.authIncorrectPassword, 403);
|
||||
}
|
||||
// If a match, update the password
|
||||
req.body.password = req.body.newPassword;
|
||||
@@ -254,10 +223,10 @@ class AuthController {
|
||||
msg: this.stringService.authUpdateUser,
|
||||
data: updatedUser,
|
||||
});
|
||||
} catch (error) {
|
||||
next(handleError(error, SERVICE_NAME, "userEditController"));
|
||||
}
|
||||
};
|
||||
},
|
||||
SERVICE_NAME,
|
||||
"editUser"
|
||||
);
|
||||
|
||||
/**
|
||||
* Checks if a superadmin account exists in the database.
|
||||
@@ -268,18 +237,17 @@ class AuthController {
|
||||
* @returns {Object} The response object with a success status, a message indicating the existence of a superadmin, and a boolean indicating the existence of a superadmin.
|
||||
* @throws {Error} If there is an error during the process.
|
||||
*/
|
||||
checkSuperadminExists = async (req, res, next) => {
|
||||
try {
|
||||
checkSuperadminExists = asyncHandler(
|
||||
async (req, res, next) => {
|
||||
const superAdminExists = await this.db.checkSuperadmin(req, res);
|
||||
|
||||
return res.success({
|
||||
msg: this.stringService.authAdminExists,
|
||||
data: superAdminExists,
|
||||
});
|
||||
} catch (error) {
|
||||
next(handleError(error, SERVICE_NAME, "checkSuperadminController"));
|
||||
}
|
||||
};
|
||||
},
|
||||
SERVICE_NAME,
|
||||
"checkSuperadminExists"
|
||||
);
|
||||
/**
|
||||
* Requests a recovery token for a user. The user's email is validated and a recovery token is created and sent via email.
|
||||
* @async
|
||||
@@ -291,16 +259,9 @@ class AuthController {
|
||||
* @returns {Object} The response object with a success status, a message indicating the creation of the recovery token, and the message ID of the sent email.
|
||||
* @throws {Error} If there is an error during the process, especially if there is a validation error (422).
|
||||
*/
|
||||
requestRecovery = async (req, res, next) => {
|
||||
try {
|
||||
requestRecovery = asyncHandler(
|
||||
async (req, res, next) => {
|
||||
await recoveryValidation.validateAsync(req.body);
|
||||
} catch (error) {
|
||||
const validationError = handleValidationError(error, SERVICE_NAME);
|
||||
next(validationError);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const { email } = req.body;
|
||||
const user = await this.db.getUserByEmail(email);
|
||||
const recoveryToken = await this.db.requestRecoveryToken(req, res);
|
||||
@@ -323,10 +284,10 @@ class AuthController {
|
||||
msg: this.stringService.authCreateRecoveryToken,
|
||||
data: msgId,
|
||||
});
|
||||
} catch (error) {
|
||||
next(handleError(error, SERVICE_NAME, "recoveryRequestController"));
|
||||
}
|
||||
};
|
||||
},
|
||||
SERVICE_NAME,
|
||||
"requestRecovery"
|
||||
);
|
||||
/**
|
||||
* Validates a recovery token. The recovery token is validated and if valid, a success message is returned.
|
||||
* @async
|
||||
@@ -338,25 +299,17 @@ class AuthController {
|
||||
* @returns {Object} The response object with a success status and a message indicating the validation of the recovery token.
|
||||
* @throws {Error} If there is an error during the process, especially if there is a validation error (422).
|
||||
*/
|
||||
validateRecovery = async (req, res, next) => {
|
||||
try {
|
||||
validateRecovery = asyncHandler(
|
||||
async (req, res, next) => {
|
||||
await recoveryTokenValidation.validateAsync(req.body);
|
||||
} catch (error) {
|
||||
const validationError = handleValidationError(error, SERVICE_NAME);
|
||||
next(validationError);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await this.db.validateRecoveryToken(req, res);
|
||||
|
||||
return res.success({
|
||||
msg: this.stringService.authVerifyRecoveryToken,
|
||||
});
|
||||
} catch (error) {
|
||||
next(handleError(error, SERVICE_NAME, "validateRecoveryTokenController"));
|
||||
}
|
||||
};
|
||||
},
|
||||
SERVICE_NAME,
|
||||
"validateRecovery"
|
||||
);
|
||||
|
||||
/**
|
||||
* Resets a user's password. The new password is validated and if valid, the user's password is updated in the database and a new JWT token is issued.
|
||||
@@ -370,27 +323,20 @@ class AuthController {
|
||||
* @returns {Object} The response object with a success status, a message indicating the reset of the password, the updated user data (without password and avatar image), and a new JWT token.
|
||||
* @throws {Error} If there is an error during the process, especially if there is a validation error (422).
|
||||
*/
|
||||
resetPassword = async (req, res, next) => {
|
||||
try {
|
||||
resetPassword = asyncHandler(
|
||||
async (req, res, next) => {
|
||||
await newPasswordValidation.validateAsync(req.body);
|
||||
} catch (error) {
|
||||
const validationError = handleValidationError(error, SERVICE_NAME);
|
||||
next(validationError);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const user = await this.db.resetPassword(req, res);
|
||||
const appSettings = await this.settingsService.getSettings();
|
||||
const token = this.issueToken(user._doc, appSettings);
|
||||
|
||||
return res.success({
|
||||
msg: this.stringService.authResetPassword,
|
||||
data: { user, token },
|
||||
});
|
||||
} catch (error) {
|
||||
next(handleError(error, SERVICE_NAME, "resetPasswordController"));
|
||||
}
|
||||
};
|
||||
},
|
||||
SERVICE_NAME,
|
||||
"resetPassword"
|
||||
);
|
||||
|
||||
/**
|
||||
* Deletes a user and all associated monitors, checks, and alerts.
|
||||
@@ -401,8 +347,8 @@ class AuthController {
|
||||
* @returns {Object} The response object with success status and message.
|
||||
* @throws {Error} If user validation fails or user is not found in the database.
|
||||
*/
|
||||
deleteUser = async (req, res, next) => {
|
||||
try {
|
||||
deleteUser = asyncHandler(
|
||||
async (req, res, next) => {
|
||||
const token = getTokenFromHeaders(req.headers);
|
||||
const decodedToken = jwt.decode(token);
|
||||
const { email } = decodedToken;
|
||||
@@ -430,23 +376,22 @@ class AuthController {
|
||||
return res.success({
|
||||
msg: this.stringService.authDeleteUser,
|
||||
});
|
||||
} catch (error) {
|
||||
next(handleError(error, SERVICE_NAME, "deleteUserController"));
|
||||
}
|
||||
};
|
||||
},
|
||||
SERVICE_NAME,
|
||||
"deleteUser"
|
||||
);
|
||||
|
||||
getAllUsers = async (req, res, next) => {
|
||||
try {
|
||||
getAllUsers = asyncHandler(
|
||||
async (req, res, next) => {
|
||||
const allUsers = await this.db.getAllUsers(req, res);
|
||||
|
||||
return res.success({
|
||||
msg: this.stringService.authGetAllUsers,
|
||||
data: allUsers,
|
||||
});
|
||||
} catch (error) {
|
||||
next(handleError(error, SERVICE_NAME, "getAllUsersController"));
|
||||
}
|
||||
};
|
||||
},
|
||||
SERVICE_NAME,
|
||||
"getAllUsers"
|
||||
);
|
||||
}
|
||||
|
||||
export default AuthController;
|
||||
|
||||
@@ -12,9 +12,7 @@ import {
|
||||
ackAllChecksParamValidation,
|
||||
ackAllChecksBodyValidation,
|
||||
} from "../validation/joi.js";
|
||||
import jwt from "jsonwebtoken";
|
||||
import { getTokenFromHeaders } from "../utils/utils.js";
|
||||
import { handleValidationError, handleError } from "./controllerUtils.js";
|
||||
import { asyncHandler } from "../utils/errorUtils.js";
|
||||
|
||||
const SERVICE_NAME = "checkController";
|
||||
|
||||
@@ -25,16 +23,11 @@ class CheckController {
|
||||
this.stringService = stringService;
|
||||
}
|
||||
|
||||
createCheck = async (req, res, next) => {
|
||||
try {
|
||||
createCheck = asyncHandler(
|
||||
async (req, res, next) => {
|
||||
await createCheckParamValidation.validateAsync(req.params);
|
||||
await createCheckBodyValidation.validateAsync(req.body);
|
||||
} catch (error) {
|
||||
next(handleValidationError(error, SERVICE_NAME));
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const checkData = { ...req.body };
|
||||
const check = await this.db.createCheck(checkData);
|
||||
|
||||
@@ -42,21 +35,16 @@ class CheckController {
|
||||
msg: this.stringService.checkCreate,
|
||||
data: check,
|
||||
});
|
||||
} catch (error) {
|
||||
next(handleError(error, SERVICE_NAME, "createCheck"));
|
||||
}
|
||||
};
|
||||
},
|
||||
SERVICE_NAME,
|
||||
"createCheck"
|
||||
);
|
||||
|
||||
getChecksByMonitor = async (req, res, next) => {
|
||||
try {
|
||||
getChecksByMonitor = asyncHandler(
|
||||
async (req, res, next) => {
|
||||
await getChecksParamValidation.validateAsync(req.params);
|
||||
await getChecksQueryValidation.validateAsync(req.query);
|
||||
} catch (error) {
|
||||
next(handleValidationError(error, SERVICE_NAME));
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const { monitorId } = req.params;
|
||||
let { type, sortOrder, dateRange, filter, ack, page, rowsPerPage, status } =
|
||||
req.query;
|
||||
@@ -76,20 +64,16 @@ class CheckController {
|
||||
msg: this.stringService.checkGet,
|
||||
data: result,
|
||||
});
|
||||
} catch (error) {
|
||||
next(handleError(error, SERVICE_NAME, "getChecks"));
|
||||
}
|
||||
};
|
||||
},
|
||||
SERVICE_NAME,
|
||||
"getChecksByMonitor"
|
||||
);
|
||||
|
||||
getChecksByTeam = async (req, res, next) => {
|
||||
try {
|
||||
getChecksByTeam = asyncHandler(
|
||||
async (req, res, next) => {
|
||||
await getTeamChecksParamValidation.validateAsync(req.params);
|
||||
await getTeamChecksQueryValidation.validateAsync(req.query);
|
||||
} catch (error) {
|
||||
next(handleValidationError(error, SERVICE_NAME));
|
||||
return;
|
||||
}
|
||||
try {
|
||||
|
||||
let { sortOrder, dateRange, filter, ack, page, rowsPerPage } = req.query;
|
||||
const { teamId } = req.user;
|
||||
|
||||
@@ -106,33 +90,28 @@ class CheckController {
|
||||
msg: this.stringService.checkGet,
|
||||
data: checkData,
|
||||
});
|
||||
} catch (error) {
|
||||
next(handleError(error, SERVICE_NAME, "getTeamChecks"));
|
||||
}
|
||||
};
|
||||
},
|
||||
SERVICE_NAME,
|
||||
"getChecksByTeam"
|
||||
);
|
||||
|
||||
getChecksSummaryByTeamId = async (req, res, next) => {
|
||||
try {
|
||||
getChecksSummaryByTeamId = asyncHandler(
|
||||
async (req, res, next) => {
|
||||
const { teamId } = req.user;
|
||||
const summary = await this.db.getChecksSummaryByTeamId({ teamId });
|
||||
return res.success({
|
||||
msg: this.stringService.checkGetSummary,
|
||||
data: summary,
|
||||
});
|
||||
} catch (error) {
|
||||
next(handleError(error, SERVICE_NAME, "getChecksSummaryByTeamId"));
|
||||
}
|
||||
};
|
||||
},
|
||||
SERVICE_NAME,
|
||||
"getChecksSummaryByTeamId"
|
||||
);
|
||||
|
||||
ackCheck = async (req, res, next) => {
|
||||
try {
|
||||
ackCheck = asyncHandler(
|
||||
async (req, res, next) => {
|
||||
await ackCheckBodyValidation.validateAsync(req.body);
|
||||
} catch (error) {
|
||||
next(handleValidationError(error, SERVICE_NAME));
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const { checkId } = req.params;
|
||||
const { ack } = req.body;
|
||||
const { teamId } = req.user;
|
||||
@@ -143,21 +122,16 @@ class CheckController {
|
||||
msg: this.stringService.checkUpdateStatus,
|
||||
data: updatedCheck,
|
||||
});
|
||||
} catch (error) {
|
||||
next(handleError(error, SERVICE_NAME, "ackCheck"));
|
||||
}
|
||||
};
|
||||
},
|
||||
SERVICE_NAME,
|
||||
"ackCheck"
|
||||
);
|
||||
|
||||
ackAllChecks = async (req, res, next) => {
|
||||
try {
|
||||
ackAllChecks = asyncHandler(
|
||||
async (req, res, next) => {
|
||||
await ackAllChecksParamValidation.validateAsync(req.params);
|
||||
await ackAllChecksBodyValidation.validateAsync(req.body);
|
||||
} catch (error) {
|
||||
next(handleValidationError(error, SERVICE_NAME));
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const { monitorId, path } = req.params;
|
||||
const { ack } = req.body;
|
||||
const { teamId } = req.user;
|
||||
@@ -168,40 +142,30 @@ class CheckController {
|
||||
msg: this.stringService.checkUpdateStatus,
|
||||
data: updatedChecks,
|
||||
});
|
||||
} catch (error) {
|
||||
next(handleError(error, SERVICE_NAME, "ackAllChecks"));
|
||||
}
|
||||
};
|
||||
},
|
||||
SERVICE_NAME,
|
||||
"ackAllChecks"
|
||||
);
|
||||
|
||||
deleteChecks = async (req, res, next) => {
|
||||
try {
|
||||
deleteChecks = asyncHandler(
|
||||
async (req, res, next) => {
|
||||
await deleteChecksParamValidation.validateAsync(req.params);
|
||||
} catch (error) {
|
||||
next(handleValidationError(error, SERVICE_NAME));
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const deletedCount = await this.db.deleteChecks(req.params.monitorId);
|
||||
|
||||
return res.success({
|
||||
msg: this.stringService.checkDelete,
|
||||
data: { deletedCount },
|
||||
});
|
||||
} catch (error) {
|
||||
next(handleError(error, SERVICE_NAME, "deleteChecks"));
|
||||
}
|
||||
};
|
||||
},
|
||||
SERVICE_NAME,
|
||||
"deleteChecks"
|
||||
);
|
||||
|
||||
deleteChecksByTeamId = async (req, res, next) => {
|
||||
try {
|
||||
deleteChecksByTeamId = asyncHandler(
|
||||
async (req, res, next) => {
|
||||
await deleteChecksByTeamIdParamValidation.validateAsync(req.params);
|
||||
} catch (error) {
|
||||
next(handleValidationError(error, SERVICE_NAME));
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const { teamId } = req.user;
|
||||
const deletedCount = await this.db.deleteChecksByTeamId(teamId);
|
||||
|
||||
@@ -209,23 +173,17 @@ class CheckController {
|
||||
msg: this.stringService.checkDelete,
|
||||
data: { deletedCount },
|
||||
});
|
||||
} catch (error) {
|
||||
next(handleError(error, SERVICE_NAME, "deleteChecksByTeamId"));
|
||||
}
|
||||
};
|
||||
},
|
||||
SERVICE_NAME,
|
||||
"deleteChecksByTeamId"
|
||||
);
|
||||
|
||||
updateChecksTTL = async (req, res, next) => {
|
||||
const SECONDS_PER_DAY = 86400;
|
||||
updateChecksTTL = asyncHandler(
|
||||
async (req, res, next) => {
|
||||
const SECONDS_PER_DAY = 86400;
|
||||
|
||||
try {
|
||||
await updateChecksTTLBodyValidation.validateAsync(req.body);
|
||||
} catch (error) {
|
||||
next(handleValidationError(error, SERVICE_NAME));
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// Get user's teamId
|
||||
const { teamId } = req.user;
|
||||
const ttl = parseInt(req.body.ttl, 10) * SECONDS_PER_DAY;
|
||||
await this.db.updateChecksTTL(teamId, ttl);
|
||||
@@ -233,9 +191,9 @@ class CheckController {
|
||||
return res.success({
|
||||
msg: this.stringService.checkUpdateTTL,
|
||||
});
|
||||
} catch (error) {
|
||||
next(handleError(error, SERVICE_NAME, "updateTTL"));
|
||||
}
|
||||
};
|
||||
},
|
||||
SERVICE_NAME,
|
||||
"updateChecksTtl"
|
||||
);
|
||||
}
|
||||
export default CheckController;
|
||||
|
||||
@@ -1,16 +1,4 @@
|
||||
const handleValidationError = (error, serviceName) => {
|
||||
error.status = 422;
|
||||
error.service = serviceName;
|
||||
error.message = error.details?.[0]?.message || error.message || "Validation Error";
|
||||
return error;
|
||||
};
|
||||
|
||||
const handleError = (error, serviceName, method, status = 500) => {
|
||||
error.status === undefined ? (error.status = status) : null;
|
||||
error.service === undefined ? (error.service = serviceName) : null;
|
||||
error.method === undefined ? (error.method = method) : null;
|
||||
return error;
|
||||
};
|
||||
import { createServerError } from "../utils/errorUtils.js";
|
||||
|
||||
const fetchMonitorCertificate = async (sslChecker, monitor) => {
|
||||
const monitorUrl = new URL(monitor.url);
|
||||
@@ -18,9 +6,9 @@ const fetchMonitorCertificate = async (sslChecker, monitor) => {
|
||||
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 new Error("Certificate not found");
|
||||
throw createServerError("Certificate not found");
|
||||
}
|
||||
return cert;
|
||||
};
|
||||
|
||||
export { handleValidationError, handleError, fetchMonitorCertificate };
|
||||
export { fetchMonitorCertificate };
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { handleError } from "./controllerUtils.js";
|
||||
import v8 from "v8";
|
||||
import os from "os";
|
||||
import { asyncHandler } from "../utils/errorUtils.js";
|
||||
|
||||
const SERVICE_NAME = "diagnosticController";
|
||||
|
||||
@@ -17,20 +17,20 @@ class DiagnosticController {
|
||||
this.getDbStats = this.getDbStats.bind(this);
|
||||
}
|
||||
|
||||
async getMonitorsByTeamIdExecutionStats(req, res, next) {
|
||||
try {
|
||||
getMonitorsByTeamIdExecutionStats = asyncHandler(
|
||||
async (req, res, next) => {
|
||||
const data = await this.db.getMonitorsByTeamIdExecutionStats(req);
|
||||
return res.success({
|
||||
msg: "OK",
|
||||
data,
|
||||
});
|
||||
} catch (error) {
|
||||
next(handleError(error, SERVICE_NAME, "getMonitorsByTeamIdExecutionStats"));
|
||||
}
|
||||
}
|
||||
},
|
||||
SERVICE_NAME,
|
||||
"getMonitorsByTeamIdExecutionStats"
|
||||
);
|
||||
|
||||
async getDbStats(req, res, next) {
|
||||
try {
|
||||
getDbStats = asyncHandler(
|
||||
async (req, res, next) => {
|
||||
const { methodName, args = [] } = req.body;
|
||||
if (!methodName || !this.db[methodName]) {
|
||||
return res.error({
|
||||
@@ -48,13 +48,13 @@ class DiagnosticController {
|
||||
msg: "OK",
|
||||
data: stats,
|
||||
});
|
||||
} catch (error) {
|
||||
next(handleError(error, SERVICE_NAME, "getDbStats"));
|
||||
}
|
||||
}
|
||||
},
|
||||
SERVICE_NAME,
|
||||
"getDbStats"
|
||||
);
|
||||
|
||||
async getCPUUsage() {
|
||||
try {
|
||||
getCPUUsage = asyncHandler(
|
||||
async (req, res, next) => {
|
||||
const startUsage = process.cpuUsage();
|
||||
const timingPeriod = 1000; // measured in ms
|
||||
await new Promise((resolve) => setTimeout(resolve, timingPeriod));
|
||||
@@ -65,16 +65,13 @@ class DiagnosticController {
|
||||
usagePercentage: ((endUsage.user + endUsage.system) / 1000 / timingPeriod) * 100,
|
||||
};
|
||||
return cpuUsage;
|
||||
} catch (error) {
|
||||
return {
|
||||
userUsageMs: 0,
|
||||
systemUsageMs: 0,
|
||||
};
|
||||
}
|
||||
}
|
||||
},
|
||||
SERVICE_NAME,
|
||||
"getCPUUsage"
|
||||
);
|
||||
|
||||
getSystemStats = async (req, res, next) => {
|
||||
try {
|
||||
getSystemStats = asyncHandler(
|
||||
async (req, res, next) => {
|
||||
// Memory Usage
|
||||
const totalMemory = os.totalmem();
|
||||
const freeMemory = os.freemem();
|
||||
@@ -129,9 +126,9 @@ class DiagnosticController {
|
||||
msg: "OK",
|
||||
data: diagnostics,
|
||||
});
|
||||
} catch (error) {
|
||||
next(handleError(error, SERVICE_NAME, "getMemoryUsage"));
|
||||
}
|
||||
};
|
||||
},
|
||||
SERVICE_NAME,
|
||||
"getSystemStats"
|
||||
);
|
||||
}
|
||||
export default DiagnosticController;
|
||||
|
||||
@@ -3,10 +3,9 @@ import {
|
||||
inviteBodyValidation,
|
||||
inviteVerificationBodyValidation,
|
||||
} from "../validation/joi.js";
|
||||
import logger from "../utils/logger.js";
|
||||
import jwt from "jsonwebtoken";
|
||||
import { handleError, handleValidationError } from "./controllerUtils.js";
|
||||
import { getTokenFromHeaders } from "../utils/utils.js";
|
||||
import { asyncHandler, createServerError } from "../utils/errorUtils.js";
|
||||
|
||||
const SERVICE_NAME = "inviteController";
|
||||
|
||||
@@ -31,99 +30,73 @@ class InviteController {
|
||||
* @returns {Object} The response object with a success status, a message indicating the sending of the invitation, and the invitation token.
|
||||
* @throws {Error} If there is an error during the process, especially if there is a validation error (422).
|
||||
*/
|
||||
getInviteToken = async (req, res, next) => {
|
||||
try {
|
||||
getInviteToken = asyncHandler(
|
||||
async (req, res, next) => {
|
||||
// Only admins can invite
|
||||
const token = getTokenFromHeaders(req.headers);
|
||||
const { role, teamId } = jwt.decode(token);
|
||||
req.body.teamId = teamId;
|
||||
try {
|
||||
await inviteRoleValidation.validateAsync({ roles: role });
|
||||
await inviteBodyValidation.validateAsync(req.body);
|
||||
} catch (error) {
|
||||
next(handleValidationError(error, SERVICE_NAME));
|
||||
return;
|
||||
}
|
||||
await inviteRoleValidation.validateAsync({ roles: role });
|
||||
await inviteBodyValidation.validateAsync(req.body);
|
||||
|
||||
const inviteToken = await this.db.requestInviteToken({ ...req.body });
|
||||
return res.success({
|
||||
msg: this.stringService.inviteIssued,
|
||||
data: inviteToken,
|
||||
});
|
||||
} catch (error) {
|
||||
next(handleError(error, SERVICE_NAME, "inviteController"));
|
||||
}
|
||||
};
|
||||
},
|
||||
SERVICE_NAME,
|
||||
"getInviteToken"
|
||||
);
|
||||
|
||||
sendInviteEmail = async (req, res, next) => {
|
||||
try {
|
||||
sendInviteEmail = asyncHandler(
|
||||
async (req, res, next) => {
|
||||
// Only admins can invite
|
||||
const token = getTokenFromHeaders(req.headers);
|
||||
const { role, firstname, teamId } = jwt.decode(token);
|
||||
req.body.teamId = teamId;
|
||||
try {
|
||||
await inviteRoleValidation.validateAsync({ roles: role });
|
||||
await inviteBodyValidation.validateAsync(req.body);
|
||||
} catch (error) {
|
||||
next(handleValidationError(error, SERVICE_NAME));
|
||||
return;
|
||||
}
|
||||
await inviteRoleValidation.validateAsync({ roles: role });
|
||||
await inviteBodyValidation.validateAsync(req.body);
|
||||
|
||||
const inviteToken = await this.db.requestInviteToken({ ...req.body });
|
||||
const { clientHost } = this.settingsService.getSettings();
|
||||
|
||||
try {
|
||||
const html = await this.emailService.buildEmail("employeeActivationTemplate", {
|
||||
name: firstname,
|
||||
link: `${clientHost}/register/${inviteToken.token}`,
|
||||
});
|
||||
const result = await this.emailService.sendEmail(
|
||||
req.body.email,
|
||||
"Welcome to Uptime Monitor",
|
||||
html
|
||||
const html = await this.emailService.buildEmail("employeeActivationTemplate", {
|
||||
name: firstname,
|
||||
link: `${clientHost}/register/${inviteToken.token}`,
|
||||
});
|
||||
const result = await this.emailService.sendEmail(
|
||||
req.body.email,
|
||||
"Welcome to Uptime Monitor",
|
||||
html
|
||||
);
|
||||
if (!result) {
|
||||
throw createServerError(
|
||||
"Failed to send invite e-mail... Please verify your settings."
|
||||
);
|
||||
if (!result) {
|
||||
return res.error({
|
||||
msg: "Failed to send invite e-mail... Please verify your settings.",
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
logger.warn({
|
||||
message: error.message,
|
||||
service: SERVICE_NAME,
|
||||
method: "sendInviteEmail",
|
||||
stack: error.stack,
|
||||
});
|
||||
}
|
||||
|
||||
return res.success({
|
||||
msg: this.stringService.inviteIssued,
|
||||
data: inviteToken,
|
||||
});
|
||||
} catch (error) {
|
||||
next(handleError(error, SERVICE_NAME, "inviteController"));
|
||||
}
|
||||
};
|
||||
},
|
||||
SERVICE_NAME,
|
||||
"sendInviteEmail"
|
||||
);
|
||||
|
||||
inviteVerifyController = async (req, res, next) => {
|
||||
try {
|
||||
inviteVerifyController = asyncHandler(
|
||||
async (req, res, next) => {
|
||||
await inviteVerificationBodyValidation.validateAsync(req.body);
|
||||
} catch (error) {
|
||||
next(handleValidationError(error, SERVICE_NAME));
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const invite = await this.db.getInviteToken(req.body.token);
|
||||
|
||||
return res.success({
|
||||
msg: this.stringService.inviteVerified,
|
||||
data: invite,
|
||||
});
|
||||
} catch (error) {
|
||||
next(handleError(error, SERVICE_NAME, "inviteVerifyController"));
|
||||
}
|
||||
};
|
||||
},
|
||||
SERVICE_NAME,
|
||||
"inviteVerifyController"
|
||||
);
|
||||
}
|
||||
|
||||
export default InviteController;
|
||||
|
||||
@@ -1,23 +1,22 @@
|
||||
import { handleError } from "./controllerUtils.js";
|
||||
import { asyncHandler } from "../utils/errorUtils.js";
|
||||
|
||||
const SERVICE_NAME = "JobQueueController";
|
||||
const SERVICE_NAME = "LogController";
|
||||
|
||||
class LogController {
|
||||
constructor(logger) {
|
||||
this.logger = logger;
|
||||
}
|
||||
|
||||
getLogs = async (req, res, next) => {
|
||||
try {
|
||||
getLogs = asyncHandler(
|
||||
async (req, res, next) => {
|
||||
const logs = await this.logger.getLogs();
|
||||
res.success({
|
||||
msg: "Logs fetched successfully",
|
||||
data: logs,
|
||||
});
|
||||
} catch (error) {
|
||||
next(handleError(error, SERVICE_NAME, "getLogs"));
|
||||
return;
|
||||
}
|
||||
};
|
||||
},
|
||||
SERVICE_NAME,
|
||||
"getLogs"
|
||||
);
|
||||
}
|
||||
export default LogController;
|
||||
|
||||
@@ -7,7 +7,7 @@ import {
|
||||
getMaintenanceWindowsByTeamIdQueryValidation,
|
||||
deleteMaintenanceWindowByIdParamValidation,
|
||||
} from "../validation/joi.js";
|
||||
import { handleValidationError, handleError } from "./controllerUtils.js";
|
||||
import { asyncHandler } from "../utils/errorUtils.js";
|
||||
|
||||
const SERVICE_NAME = "maintenanceWindowController";
|
||||
|
||||
@@ -18,14 +18,10 @@ class MaintenanceWindowController {
|
||||
this.stringService = stringService;
|
||||
}
|
||||
|
||||
createMaintenanceWindows = async (req, res, next) => {
|
||||
try {
|
||||
createMaintenanceWindows = asyncHandler(
|
||||
async (req, res, next) => {
|
||||
await createMaintenanceWindowBodyValidation.validateAsync(req.body);
|
||||
} catch (error) {
|
||||
next(handleValidationError(error, SERVICE_NAME));
|
||||
return;
|
||||
}
|
||||
try {
|
||||
|
||||
const { teamId } = req.user;
|
||||
const monitorIds = req.body.monitors;
|
||||
const dbTransactions = monitorIds.map((monitorId) => {
|
||||
@@ -44,39 +40,28 @@ class MaintenanceWindowController {
|
||||
return res.success({
|
||||
msg: this.stringService.maintenanceWindowCreate,
|
||||
});
|
||||
} catch (error) {
|
||||
next(handleError(error, SERVICE_NAME, "createMaintenanceWindow"));
|
||||
}
|
||||
};
|
||||
},
|
||||
SERVICE_NAME,
|
||||
"createMaintenanceWindows"
|
||||
);
|
||||
|
||||
getMaintenanceWindowById = async (req, res, next) => {
|
||||
try {
|
||||
getMaintenanceWindowById = asyncHandler(
|
||||
async (req, res, next) => {
|
||||
await getMaintenanceWindowByIdParamValidation.validateAsync(req.params);
|
||||
} catch (error) {
|
||||
next(handleValidationError(error, SERVICE_NAME));
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const maintenanceWindow = await this.db.getMaintenanceWindowById(req.params.id);
|
||||
|
||||
return res.success({
|
||||
msg: this.stringService.maintenanceWindowGetById,
|
||||
data: maintenanceWindow,
|
||||
});
|
||||
} catch (error) {
|
||||
next(handleError(error, SERVICE_NAME, "getMaintenanceWindowById"));
|
||||
}
|
||||
};
|
||||
},
|
||||
SERVICE_NAME,
|
||||
"getMaintenanceWindowById"
|
||||
);
|
||||
|
||||
getMaintenanceWindowsByTeamId = async (req, res, next) => {
|
||||
try {
|
||||
getMaintenanceWindowsByTeamId = asyncHandler(
|
||||
async (req, res, next) => {
|
||||
await getMaintenanceWindowsByTeamIdQueryValidation.validateAsync(req.query);
|
||||
} catch (error) {
|
||||
next(handleValidationError(error, SERVICE_NAME));
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const { teamId } = req.user;
|
||||
const maintenanceWindows = await this.db.getMaintenanceWindowsByTeamId(
|
||||
teamId,
|
||||
@@ -87,20 +72,15 @@ class MaintenanceWindowController {
|
||||
msg: this.stringService.maintenanceWindowGetByTeam,
|
||||
data: maintenanceWindows,
|
||||
});
|
||||
} catch (error) {
|
||||
next(handleError(error, SERVICE_NAME, "getMaintenanceWindowsByUserId"));
|
||||
}
|
||||
};
|
||||
},
|
||||
SERVICE_NAME,
|
||||
"getMaintenanceWindowsByTeamId"
|
||||
);
|
||||
|
||||
getMaintenanceWindowsByMonitorId = async (req, res, next) => {
|
||||
try {
|
||||
getMaintenanceWindowsByMonitorId = asyncHandler(
|
||||
async (req, res, next) => {
|
||||
await getMaintenanceWindowsByMonitorIdParamValidation.validateAsync(req.params);
|
||||
} catch (error) {
|
||||
next(handleValidationError(error, SERVICE_NAME));
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const maintenanceWindows = await this.db.getMaintenanceWindowsByMonitorId(
|
||||
req.params.monitorId
|
||||
);
|
||||
@@ -109,37 +89,27 @@ class MaintenanceWindowController {
|
||||
msg: this.stringService.maintenanceWindowGetByUser,
|
||||
data: maintenanceWindows,
|
||||
});
|
||||
} catch (error) {
|
||||
next(handleError(error, SERVICE_NAME, "getMaintenanceWindowsByMonitorId"));
|
||||
}
|
||||
};
|
||||
},
|
||||
SERVICE_NAME,
|
||||
"getMaintenanceWindowsByMonitorId"
|
||||
);
|
||||
|
||||
deleteMaintenanceWindow = async (req, res, next) => {
|
||||
try {
|
||||
deleteMaintenanceWindow = asyncHandler(
|
||||
async (req, res, next) => {
|
||||
await deleteMaintenanceWindowByIdParamValidation.validateAsync(req.params);
|
||||
} catch (error) {
|
||||
next(handleValidationError(error, SERVICE_NAME));
|
||||
return;
|
||||
}
|
||||
try {
|
||||
await this.db.deleteMaintenanceWindowById(req.params.id);
|
||||
return res.success({
|
||||
msg: this.stringService.maintenanceWindowDelete,
|
||||
});
|
||||
} catch (error) {
|
||||
next(handleError(error, SERVICE_NAME, "deleteMaintenanceWindow"));
|
||||
}
|
||||
};
|
||||
},
|
||||
SERVICE_NAME,
|
||||
"deleteMaintenanceWindow"
|
||||
);
|
||||
|
||||
editMaintenanceWindow = async (req, res, next) => {
|
||||
try {
|
||||
editMaintenanceWindow = asyncHandler(
|
||||
async (req, res, next) => {
|
||||
await editMaintenanceWindowByIdParamValidation.validateAsync(req.params);
|
||||
await editMaintenanceByIdWindowBodyValidation.validateAsync(req.body);
|
||||
} catch (error) {
|
||||
next(handleValidationError(error, SERVICE_NAME));
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const editedMaintenanceWindow = await this.db.editMaintenanceWindowById(
|
||||
req.params.id,
|
||||
req.body
|
||||
@@ -148,10 +118,10 @@ class MaintenanceWindowController {
|
||||
msg: this.stringService.maintenanceWindowEdit,
|
||||
data: editedMaintenanceWindow,
|
||||
});
|
||||
} catch (error) {
|
||||
next(handleError(error, SERVICE_NAME, "editMaintenanceWindow"));
|
||||
}
|
||||
};
|
||||
},
|
||||
SERVICE_NAME,
|
||||
"editMaintenanceWindow"
|
||||
);
|
||||
}
|
||||
|
||||
export default MaintenanceWindowController;
|
||||
|
||||
@@ -16,12 +16,12 @@ import {
|
||||
} from "../validation/joi.js";
|
||||
import sslChecker from "ssl-checker";
|
||||
import logger from "../utils/logger.js";
|
||||
import { handleError, handleValidationError } from "./controllerUtils.js";
|
||||
import axios from "axios";
|
||||
import seedDb from "../db/mongo/utils/seedDb.js";
|
||||
const SERVICE_NAME = "monitorController";
|
||||
import pkg from "papaparse";
|
||||
import { asyncHandler, createServerError } from "../utils/errorUtils.js";
|
||||
|
||||
const SERVICE_NAME = "monitorController";
|
||||
class MonitorController {
|
||||
constructor(db, settingsService, jobQueue, stringService, emailService) {
|
||||
this.db = db;
|
||||
@@ -40,17 +40,17 @@ class MonitorController {
|
||||
* @returns {Promise<Express.Response>}
|
||||
* @throws {Error}
|
||||
*/
|
||||
getAllMonitors = async (req, res, next) => {
|
||||
try {
|
||||
getAllMonitors = asyncHandler(
|
||||
async (req, res, next) => {
|
||||
const monitors = await this.db.getAllMonitors();
|
||||
return res.success({
|
||||
msg: this.stringService.monitorGetAll,
|
||||
data: monitors,
|
||||
});
|
||||
} catch (error) {
|
||||
next(handleError(error, SERVICE_NAME, "getAllMonitors"));
|
||||
}
|
||||
};
|
||||
},
|
||||
SERVICE_NAME,
|
||||
"getAllMonitors"
|
||||
);
|
||||
|
||||
/**
|
||||
* Returns all monitors with uptime stats for 1,7,30, and 90 days
|
||||
@@ -61,20 +61,20 @@ class MonitorController {
|
||||
* @returns {Promise<Express.Response>}
|
||||
* @throws {Error}
|
||||
*/
|
||||
getAllMonitorsWithUptimeStats = async (req, res, next) => {
|
||||
try {
|
||||
getAllMonitorsWithUptimeStats = asyncHandler(
|
||||
async (req, res, next) => {
|
||||
const monitors = await this.db.getAllMonitorsWithUptimeStats();
|
||||
return res.success({
|
||||
msg: this.stringService.monitorGetAll,
|
||||
data: monitors,
|
||||
});
|
||||
} catch (error) {
|
||||
next(handleError(error, SERVICE_NAME, "getAllMonitorsWithUptimeStats"));
|
||||
}
|
||||
};
|
||||
},
|
||||
SERVICE_NAME,
|
||||
"getAllMonitorsWithUptimeStats"
|
||||
);
|
||||
|
||||
getUptimeDetailsById = async (req, res, next) => {
|
||||
try {
|
||||
getUptimeDetailsById = asyncHandler(
|
||||
async (req, res, next) => {
|
||||
const { monitorId } = req.params;
|
||||
const { dateRange, normalize } = req.query;
|
||||
|
||||
@@ -87,10 +87,10 @@ class MonitorController {
|
||||
msg: this.stringService.monitorGetByIdSuccess,
|
||||
data,
|
||||
});
|
||||
} catch (error) {
|
||||
next(handleError(error, SERVICE_NAME, "getMonitorDetailsById"));
|
||||
}
|
||||
};
|
||||
},
|
||||
SERVICE_NAME,
|
||||
"getUptimeDetailsById"
|
||||
);
|
||||
|
||||
/**
|
||||
* Returns monitor stats for monitor with matching ID
|
||||
@@ -101,16 +101,11 @@ class MonitorController {
|
||||
* @returns {Promise<Express.Response>}
|
||||
* @throws {Error}
|
||||
*/
|
||||
getMonitorStatsById = async (req, res, next) => {
|
||||
try {
|
||||
getMonitorStatsById = asyncHandler(
|
||||
async (req, res, next) => {
|
||||
await getMonitorStatsByIdParamValidation.validateAsync(req.params);
|
||||
await getMonitorStatsByIdQueryValidation.validateAsync(req.query);
|
||||
} catch (error) {
|
||||
next(handleValidationError(error, SERVICE_NAME));
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
let { limit, sortOrder, dateRange, numToDisplay, normalize } = req.query;
|
||||
const { monitorId } = req.params;
|
||||
|
||||
@@ -126,10 +121,10 @@ class MonitorController {
|
||||
msg: this.stringService.monitorStatsById,
|
||||
data: monitorStats,
|
||||
});
|
||||
} catch (error) {
|
||||
next(handleError(error, SERVICE_NAME, "getMonitorStatsById"));
|
||||
}
|
||||
};
|
||||
},
|
||||
SERVICE_NAME,
|
||||
"getMonitorStatsById"
|
||||
);
|
||||
|
||||
/**
|
||||
* Get hardware details for a specific monitor by ID
|
||||
@@ -140,15 +135,11 @@ class MonitorController {
|
||||
* @returns {Promise<Express.Response>}
|
||||
* @throws {Error} - Throws error if monitor not found or other database errors
|
||||
*/
|
||||
getHardwareDetailsById = async (req, res, next) => {
|
||||
try {
|
||||
getHardwareDetailsById = asyncHandler(
|
||||
async (req, res, next) => {
|
||||
await getHardwareDetailsByIdParamValidation.validateAsync(req.params);
|
||||
await getHardwareDetailsByIdQueryValidation.validateAsync(req.query);
|
||||
} catch (error) {
|
||||
next(handleValidationError(error, SERVICE_NAME));
|
||||
return;
|
||||
}
|
||||
try {
|
||||
|
||||
const { monitorId } = req.params;
|
||||
const { dateRange } = req.query;
|
||||
const monitor = await this.db.getHardwareDetailsById({ monitorId, dateRange });
|
||||
@@ -156,19 +147,15 @@ class MonitorController {
|
||||
msg: this.stringService.monitorGetByIdSuccess,
|
||||
data: monitor,
|
||||
});
|
||||
} catch (error) {
|
||||
next(handleError(error, SERVICE_NAME, "getHardwareDetailsById"));
|
||||
}
|
||||
};
|
||||
},
|
||||
SERVICE_NAME,
|
||||
"getHardwareDetailsById"
|
||||
);
|
||||
|
||||
getMonitorCertificate = async (req, res, next, fetchMonitorCertificate) => {
|
||||
try {
|
||||
getMonitorCertificate = asyncHandler(
|
||||
async (req, res, next, fetchMonitorCertificate) => {
|
||||
await getCertificateParamValidation.validateAsync(req.params);
|
||||
} catch (error) {
|
||||
next(handleValidationError(error, SERVICE_NAME));
|
||||
}
|
||||
|
||||
try {
|
||||
const { monitorId } = req.params;
|
||||
const monitor = await this.db.getMonitorById(monitorId);
|
||||
const certificate = await fetchMonitorCertificate(sslChecker, monitor);
|
||||
@@ -179,10 +166,10 @@ class MonitorController {
|
||||
certificateDate: new Date(certificate.validTo),
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
next(handleError(error, SERVICE_NAME, "getMonitorCertificate"));
|
||||
}
|
||||
};
|
||||
},
|
||||
SERVICE_NAME,
|
||||
"getMonitorCertificate"
|
||||
);
|
||||
|
||||
/**
|
||||
* Retrieves a monitor by its ID.
|
||||
@@ -195,25 +182,20 @@ class MonitorController {
|
||||
* @returns {Object} The response object with a success status, a message, and the retrieved monitor data.
|
||||
* @throws {Error} If there is an error during the process, especially if the monitor is not found (404) or if there is a validation error (422).
|
||||
*/
|
||||
getMonitorById = async (req, res, next) => {
|
||||
try {
|
||||
getMonitorById = asyncHandler(
|
||||
async (req, res, next) => {
|
||||
await getMonitorByIdParamValidation.validateAsync(req.params);
|
||||
await getMonitorByIdQueryValidation.validateAsync(req.query);
|
||||
} catch (error) {
|
||||
next(handleValidationError(error, SERVICE_NAME));
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const monitor = await this.db.getMonitorById(req.params.monitorId);
|
||||
return res.success({
|
||||
msg: this.stringService.monitorGetByIdSuccess,
|
||||
data: monitor,
|
||||
});
|
||||
} catch (error) {
|
||||
next(handleError(error, SERVICE_NAME, "getMonitorById"));
|
||||
}
|
||||
};
|
||||
},
|
||||
SERVICE_NAME,
|
||||
"getMonitorById"
|
||||
);
|
||||
|
||||
/**
|
||||
* Creates a new monitor and adds it to the job queue.
|
||||
@@ -226,15 +208,10 @@ class MonitorController {
|
||||
* @returns {Object} The response object with a success status, a message indicating the creation of the monitor, and the created monitor data.
|
||||
* @throws {Error} If there is an error during the process, especially if there is a validation error (422).
|
||||
*/
|
||||
createMonitor = async (req, res, next) => {
|
||||
try {
|
||||
createMonitor = asyncHandler(
|
||||
async (req, res, next) => {
|
||||
await createMonitorBodyValidation.validateAsync(req.body);
|
||||
} catch (error) {
|
||||
next(handleValidationError(error, SERVICE_NAME));
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const { _id, teamId } = req.user;
|
||||
const monitor = await this.db.createMonitor({
|
||||
body: req.body,
|
||||
@@ -248,10 +225,10 @@ class MonitorController {
|
||||
msg: this.stringService.monitorCreate,
|
||||
data: monitor,
|
||||
});
|
||||
} catch (error) {
|
||||
next(handleError(error, SERVICE_NAME, "createMonitor"));
|
||||
}
|
||||
};
|
||||
},
|
||||
SERVICE_NAME,
|
||||
"createMonitor"
|
||||
);
|
||||
|
||||
/**
|
||||
* Creates bulk monitors and adds them to the job queue after parsing CSV.
|
||||
@@ -263,8 +240,8 @@ class MonitorController {
|
||||
* @returns {Object} The response object with a success status and message.
|
||||
* @throws {Error} If there is an error during the process, especially if there is a validation error (422).
|
||||
*/
|
||||
createBulkMonitors = async (req, res, next) => {
|
||||
try {
|
||||
createBulkMonitors = asyncHandler(
|
||||
async (req, res, next) => {
|
||||
const { parse } = pkg;
|
||||
|
||||
// validate the file
|
||||
@@ -310,51 +287,43 @@ class MonitorController {
|
||||
return value;
|
||||
},
|
||||
complete: async ({ data, errors }) => {
|
||||
try {
|
||||
if (errors.length > 0) {
|
||||
throw new Error("Error parsing CSV");
|
||||
}
|
||||
|
||||
if (!data || data.length === 0) {
|
||||
throw new Error("CSV file contains no data rows");
|
||||
}
|
||||
|
||||
const enrichedData = data.map((monitor) => ({
|
||||
userId: _id,
|
||||
teamId,
|
||||
...monitor,
|
||||
description: monitor.description || monitor.name || monitor.url,
|
||||
name: monitor.name || monitor.url,
|
||||
type: monitor.type || "http",
|
||||
}));
|
||||
|
||||
await createMonitorsBodyValidation.validateAsync(enrichedData);
|
||||
|
||||
try {
|
||||
const monitors = await this.db.createBulkMonitors(enrichedData);
|
||||
|
||||
await Promise.all(
|
||||
monitors.map(async (monitor, index) => {
|
||||
this.jobQueue.addJob(monitor._id, monitor);
|
||||
})
|
||||
);
|
||||
|
||||
return res.success({
|
||||
msg: this.stringService.bulkMonitorsCreate,
|
||||
data: monitors,
|
||||
});
|
||||
} catch (error) {
|
||||
next(handleError(error, SERVICE_NAME, "createBulkMonitors"));
|
||||
}
|
||||
} catch (error) {
|
||||
next(handleError(error, SERVICE_NAME, "createBulkMonitors"));
|
||||
if (errors.length > 0) {
|
||||
throw createServerError("Error parsing CSV");
|
||||
}
|
||||
|
||||
if (!data || data.length === 0) {
|
||||
throw createServerError("CSV file contains no data rows");
|
||||
}
|
||||
|
||||
const enrichedData = data.map((monitor) => ({
|
||||
userId: _id,
|
||||
teamId,
|
||||
...monitor,
|
||||
description: monitor.description || monitor.name || monitor.url,
|
||||
name: monitor.name || monitor.url,
|
||||
type: monitor.type || "http",
|
||||
}));
|
||||
|
||||
await createMonitorsBodyValidation.validateAsync(enrichedData);
|
||||
|
||||
const monitors = await this.db.createBulkMonitors(enrichedData);
|
||||
|
||||
await Promise.all(
|
||||
monitors.map(async (monitor, index) => {
|
||||
this.jobQueue.addJob(monitor._id, monitor);
|
||||
})
|
||||
);
|
||||
|
||||
return res.success({
|
||||
msg: this.stringService.bulkMonitorsCreate,
|
||||
data: monitors,
|
||||
});
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
return next(handleError(error, SERVICE_NAME, "createBulkMonitors"));
|
||||
}
|
||||
};
|
||||
},
|
||||
SERVICE_NAME,
|
||||
"createBulkMonitors"
|
||||
);
|
||||
/**
|
||||
* Checks if the endpoint can be resolved
|
||||
* @async
|
||||
@@ -365,15 +334,9 @@ class MonitorController {
|
||||
* @returns {Object} The response object with a success status, a message, and the resolution result.
|
||||
* @throws {Error} If there is an error during the process, especially if there is a validation error (422).
|
||||
*/
|
||||
checkEndpointResolution = async (req, res, next) => {
|
||||
try {
|
||||
checkEndpointResolution = asyncHandler(
|
||||
async (req, res, next) => {
|
||||
await getMonitorURLByQueryValidation.validateAsync(req.query);
|
||||
} catch (error) {
|
||||
next(handleValidationError(error, SERVICE_NAME));
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const { monitorURL } = req.query;
|
||||
const parsedUrl = new URL(monitorURL);
|
||||
const response = await axios.get(parsedUrl, {
|
||||
@@ -384,10 +347,10 @@ class MonitorController {
|
||||
status: response.status,
|
||||
msg: response.statusText,
|
||||
});
|
||||
} catch (error) {
|
||||
next(handleError(error, SERVICE_NAME, "checkEndpointResolution"));
|
||||
}
|
||||
};
|
||||
},
|
||||
SERVICE_NAME,
|
||||
"checkEndpointResolution"
|
||||
);
|
||||
|
||||
/**
|
||||
* Deletes a monitor by its ID and also deletes associated checks, alerts, and notifications.
|
||||
@@ -400,24 +363,18 @@ class MonitorController {
|
||||
* @returns {Object} The response object with a success status and a message indicating the deletion of the monitor.
|
||||
* @throws {Error} If there is an error during the process, especially if there is a validation error (422) or an error in deleting associated records.
|
||||
*/
|
||||
deleteMonitor = async (req, res, next) => {
|
||||
try {
|
||||
deleteMonitor = asyncHandler(
|
||||
async (req, res, next) => {
|
||||
await getMonitorByIdParamValidation.validateAsync(req.params);
|
||||
} catch (error) {
|
||||
next(handleValidationError(error, SERVICE_NAME));
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const monitorId = req.params.monitorId;
|
||||
const monitor = await this.db.deleteMonitor({ monitorId });
|
||||
await this.jobQueue.deleteJob(monitor);
|
||||
await this.db.deleteStatusPagesByMonitorId(monitor._id);
|
||||
return res.success({ msg: this.stringService.monitorDelete });
|
||||
} catch (error) {
|
||||
next(handleError(error, SERVICE_NAME, "deleteMonitor"));
|
||||
}
|
||||
};
|
||||
},
|
||||
SERVICE_NAME,
|
||||
"deleteMonitor"
|
||||
);
|
||||
|
||||
/**
|
||||
* Deletes all monitors associated with a team.
|
||||
@@ -430,8 +387,8 @@ class MonitorController {
|
||||
* @returns {Object} The response object with a success status and a message indicating the number of deleted monitors.
|
||||
* @throws {Error} If there is an error during the deletion process.
|
||||
*/
|
||||
deleteAllMonitors = async (req, res, next) => {
|
||||
try {
|
||||
deleteAllMonitors = asyncHandler(
|
||||
async (req, res, next) => {
|
||||
const { teamId } = req.user;
|
||||
const { monitors, deletedCount } = await this.db.deleteAllMonitors(teamId);
|
||||
await Promise.all(
|
||||
@@ -442,7 +399,7 @@ class MonitorController {
|
||||
await this.db.deletePageSpeedChecksByMonitorId(monitor._id);
|
||||
await this.db.deleteNotificationsByMonitorId(monitor._id);
|
||||
} catch (error) {
|
||||
logger.error({
|
||||
logger.warn({
|
||||
message: `Error deleting associated records for monitor ${monitor._id} with name ${monitor.name}`,
|
||||
service: SERVICE_NAME,
|
||||
method: "deleteAllMonitors",
|
||||
@@ -452,10 +409,10 @@ class MonitorController {
|
||||
})
|
||||
);
|
||||
return res.success({ msg: `Deleted ${deletedCount} monitors` });
|
||||
} catch (error) {
|
||||
next(handleError(error, SERVICE_NAME, "deleteAllMonitors"));
|
||||
}
|
||||
};
|
||||
},
|
||||
SERVICE_NAME,
|
||||
"deleteAllMonitors"
|
||||
);
|
||||
|
||||
/**
|
||||
* Edits a monitor by its ID, updates its notifications, and updates its job in the job queue.
|
||||
@@ -470,16 +427,10 @@ class MonitorController {
|
||||
* @returns {Object} The response object with a success status, a message indicating the editing of the monitor, and the edited monitor data.
|
||||
* @throws {Error} If there is an error during the process, especially if there is a validation error (422).
|
||||
*/
|
||||
editMonitor = async (req, res, next) => {
|
||||
try {
|
||||
editMonitor = asyncHandler(
|
||||
async (req, res, next) => {
|
||||
await getMonitorByIdParamValidation.validateAsync(req.params);
|
||||
await editMonitorBodyValidation.validateAsync(req.body);
|
||||
} catch (error) {
|
||||
next(handleValidationError(error, SERVICE_NAME));
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const { monitorId } = req.params;
|
||||
|
||||
const editedMonitor = await this.db.editMonitor(monitorId, req.body);
|
||||
@@ -490,10 +441,10 @@ class MonitorController {
|
||||
msg: this.stringService.monitorEdit,
|
||||
data: editedMonitor,
|
||||
});
|
||||
} catch (error) {
|
||||
next(handleError(error, SERVICE_NAME, "editMonitor"));
|
||||
}
|
||||
};
|
||||
},
|
||||
SERVICE_NAME,
|
||||
"editMonitor"
|
||||
);
|
||||
|
||||
/**
|
||||
* Pauses or resumes a monitor based on its current state.
|
||||
@@ -506,14 +457,10 @@ class MonitorController {
|
||||
* @returns {Object} The response object with a success status, a message indicating the new state of the monitor, and the updated monitor data.
|
||||
* @throws {Error} If there is an error during the process.
|
||||
*/
|
||||
pauseMonitor = async (req, res, next) => {
|
||||
try {
|
||||
pauseMonitor = asyncHandler(
|
||||
async (req, res, next) => {
|
||||
await pauseMonitorParamValidation.validateAsync(req.params);
|
||||
} catch (error) {
|
||||
next(handleValidationError(error, SERVICE_NAME));
|
||||
}
|
||||
|
||||
try {
|
||||
const monitorId = req.params.monitorId;
|
||||
const monitor = await this.db.pauseMonitor({ monitorId });
|
||||
monitor.isActive === true
|
||||
@@ -526,10 +473,10 @@ class MonitorController {
|
||||
: this.stringService.monitorPause,
|
||||
data: monitor,
|
||||
});
|
||||
} catch (error) {
|
||||
next(handleError(error, SERVICE_NAME, "pauseMonitor"));
|
||||
}
|
||||
};
|
||||
},
|
||||
SERVICE_NAME,
|
||||
"pauseMonitor"
|
||||
);
|
||||
|
||||
/**
|
||||
* Adds demo monitors for a team.
|
||||
@@ -542,8 +489,8 @@ class MonitorController {
|
||||
* @returns {Object} The response object with a success status, a message indicating the addition of demo monitors, and the number of demo monitors added.
|
||||
* @throws {Error} If there is an error during the process.
|
||||
*/
|
||||
addDemoMonitors = async (req, res, next) => {
|
||||
try {
|
||||
addDemoMonitors = asyncHandler(
|
||||
async (req, res, next) => {
|
||||
const { _id, teamId } = req.user;
|
||||
const demoMonitors = await this.db.addDemoMonitors(_id, teamId);
|
||||
await Promise.all(
|
||||
@@ -554,10 +501,10 @@ class MonitorController {
|
||||
msg: this.stringService.monitorDemoAdded,
|
||||
data: demoMonitors.length,
|
||||
});
|
||||
} catch (error) {
|
||||
next(handleError(error, SERVICE_NAME, "addDemoMonitors"));
|
||||
}
|
||||
};
|
||||
},
|
||||
SERVICE_NAME,
|
||||
"addDemoMonitors"
|
||||
);
|
||||
|
||||
/**
|
||||
* Sends a test email to verify email delivery functionality.
|
||||
@@ -570,8 +517,8 @@ class MonitorController {
|
||||
* @returns {Object} The response object with a success status and the email delivery message ID.
|
||||
* @throws {Error} If there is an error while sending the test email.
|
||||
*/
|
||||
sendTestEmail = async (req, res, next) => {
|
||||
try {
|
||||
sendTestEmail = asyncHandler(
|
||||
async (req, res, next) => {
|
||||
const { to } = req.body;
|
||||
if (!to || typeof to !== "string") {
|
||||
throw new Error(this.stringService.errorForValidEmailAddress);
|
||||
@@ -584,29 +531,23 @@ class MonitorController {
|
||||
const messageId = await this.emailService.sendEmail(to, subject, html);
|
||||
|
||||
if (!messageId) {
|
||||
return res.error({
|
||||
msg: "Failed to send test email.",
|
||||
});
|
||||
throw createServerError("Failed to send test email.");
|
||||
}
|
||||
|
||||
return res.success({
|
||||
msg: this.stringService.sendTestEmail,
|
||||
data: { messageId },
|
||||
});
|
||||
} catch (error) {
|
||||
next(handleError(error, SERVICE_NAME, "sendTestEmail"));
|
||||
}
|
||||
};
|
||||
},
|
||||
SERVICE_NAME,
|
||||
"sendTestEmail"
|
||||
);
|
||||
|
||||
getMonitorsByTeamId = async (req, res, next) => {
|
||||
try {
|
||||
getMonitorsByTeamId = asyncHandler(
|
||||
async (req, res, next) => {
|
||||
await getMonitorsByTeamIdParamValidation.validateAsync(req.params);
|
||||
await getMonitorsByTeamIdQueryValidation.validateAsync(req.query);
|
||||
} catch (error) {
|
||||
next(handleValidationError(error, SERVICE_NAME));
|
||||
}
|
||||
|
||||
try {
|
||||
let { limit, type, page, rowsPerPage, filter, field, order } = req.query;
|
||||
const teamId = req.user.teamId;
|
||||
|
||||
@@ -624,20 +565,16 @@ class MonitorController {
|
||||
msg: this.stringService.monitorGetByTeamId,
|
||||
data: monitors,
|
||||
});
|
||||
} catch (error) {
|
||||
next(handleError(error, SERVICE_NAME, "getMonitorsByTeamId"));
|
||||
}
|
||||
};
|
||||
},
|
||||
SERVICE_NAME,
|
||||
"getMonitorsByTeamId"
|
||||
);
|
||||
|
||||
getMonitorsAndSummaryByTeamId = async (req, res, next) => {
|
||||
try {
|
||||
getMonitorsAndSummaryByTeamId = asyncHandler(
|
||||
async (req, res, next) => {
|
||||
await getMonitorsByTeamIdParamValidation.validateAsync(req.params);
|
||||
await getMonitorsByTeamIdQueryValidation.validateAsync(req.query);
|
||||
} catch (error) {
|
||||
return next(handleValidationError(error, SERVICE_NAME));
|
||||
}
|
||||
|
||||
try {
|
||||
const { explain } = req;
|
||||
const { type } = req.query;
|
||||
const { teamId } = req.user;
|
||||
@@ -651,20 +588,16 @@ class MonitorController {
|
||||
msg: "OK", // TODO
|
||||
data: result,
|
||||
});
|
||||
} catch (error) {
|
||||
return next(handleError(error, SERVICE_NAME, "getMonitorsAndSummaryByTeamId"));
|
||||
}
|
||||
};
|
||||
},
|
||||
SERVICE_NAME,
|
||||
"getMonitorsAndSummaryByTeamId"
|
||||
);
|
||||
|
||||
getMonitorsWithChecksByTeamId = async (req, res, next) => {
|
||||
try {
|
||||
getMonitorsWithChecksByTeamId = asyncHandler(
|
||||
async (req, res, next) => {
|
||||
await getMonitorsByTeamIdParamValidation.validateAsync(req.params);
|
||||
await getMonitorsByTeamIdQueryValidation.validateAsync(req.query);
|
||||
} catch (error) {
|
||||
return next(handleValidationError(error, SERVICE_NAME));
|
||||
}
|
||||
|
||||
try {
|
||||
const { explain } = req;
|
||||
let { limit, type, page, rowsPerPage, filter, field, order } = req.query;
|
||||
const { teamId } = req.user;
|
||||
@@ -684,23 +617,23 @@ class MonitorController {
|
||||
msg: "OK",
|
||||
data: result,
|
||||
});
|
||||
} catch (error) {
|
||||
return next(handleError(error, SERVICE_NAME, "getMonitorsWithChecksByTeamId"));
|
||||
}
|
||||
};
|
||||
},
|
||||
SERVICE_NAME,
|
||||
"getMonitorsWithChecksByTeamId"
|
||||
);
|
||||
|
||||
seedDb = async (req, res, next) => {
|
||||
try {
|
||||
seedDb = asyncHandler(
|
||||
async (req, res, next) => {
|
||||
const { _id, teamId } = req.user;
|
||||
await seedDb(_id, teamId);
|
||||
res.success({ msg: "Database seeded" });
|
||||
} catch (error) {
|
||||
next(handleError(error, SERVICE_NAME, "seedDb"));
|
||||
}
|
||||
};
|
||||
},
|
||||
SERVICE_NAME,
|
||||
"seedDb"
|
||||
);
|
||||
|
||||
exportMonitorsToCSV = async (req, res, next) => {
|
||||
try {
|
||||
exportMonitorsToCSV = asyncHandler(
|
||||
async (req, res, next) => {
|
||||
const { teamId } = req.user;
|
||||
|
||||
const monitors = await this.db.getMonitorsByTeamId({ teamId });
|
||||
@@ -730,10 +663,10 @@ class MonitorController {
|
||||
"Content-Disposition": "attachment; filename=monitors.csv",
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
next(handleError(error, SERVICE_NAME, "exportMonitorsToCSV"));
|
||||
}
|
||||
};
|
||||
},
|
||||
SERVICE_NAME,
|
||||
"exportMonitorsToCSV"
|
||||
);
|
||||
}
|
||||
|
||||
export default MonitorController;
|
||||
|
||||
@@ -1,22 +1,8 @@
|
||||
import {
|
||||
triggerNotificationBodyValidation,
|
||||
createNotificationBodyValidation,
|
||||
} from "../validation/joi.js";
|
||||
import { handleError, handleValidationError } from "./controllerUtils.js";
|
||||
import { createNotificationBodyValidation } from "../validation/joi.js";
|
||||
import { asyncHandler } from "../utils/errorUtils.js";
|
||||
|
||||
const SERVICE_NAME = "NotificationController";
|
||||
|
||||
const NOTIFICATION_TYPES = {
|
||||
WEBHOOK: "webhook",
|
||||
TELEGRAM: "telegram",
|
||||
};
|
||||
|
||||
const PLATFORMS = {
|
||||
SLACK: "slack",
|
||||
DISCORD: "discord",
|
||||
TELEGRAM: "telegram",
|
||||
};
|
||||
|
||||
class NotificationController {
|
||||
constructor({ notificationService, stringService, statusService, db }) {
|
||||
this.notificationService = notificationService;
|
||||
@@ -25,8 +11,8 @@ class NotificationController {
|
||||
this.db = db;
|
||||
}
|
||||
|
||||
testNotification = async (req, res, next) => {
|
||||
try {
|
||||
testNotification = asyncHandler(
|
||||
async (req, res, next) => {
|
||||
const notification = req.body;
|
||||
|
||||
const success = await this.notificationService.sendTestNotification(notification);
|
||||
@@ -41,22 +27,17 @@ class NotificationController {
|
||||
return res.success({
|
||||
msg: "Notification sent successfully",
|
||||
});
|
||||
} catch (error) {
|
||||
next(handleError(error, SERVICE_NAME, "testWebhook"));
|
||||
}
|
||||
};
|
||||
},
|
||||
SERVICE_NAME,
|
||||
"testNotification"
|
||||
);
|
||||
|
||||
createNotification = async (req, res, next) => {
|
||||
try {
|
||||
createNotification = asyncHandler(
|
||||
async (req, res, next) => {
|
||||
await createNotificationBodyValidation.validateAsync(req.body, {
|
||||
abortEarly: false,
|
||||
});
|
||||
} catch (error) {
|
||||
next(handleValidationError(error, SERVICE_NAME));
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const body = req.body;
|
||||
const { _id, teamId } = req.user;
|
||||
body.userId = _id;
|
||||
@@ -66,69 +47,64 @@ class NotificationController {
|
||||
msg: "Notification created successfully",
|
||||
data: notification,
|
||||
});
|
||||
} catch (error) {
|
||||
next(handleError(error, SERVICE_NAME, "createNotification"));
|
||||
}
|
||||
};
|
||||
},
|
||||
SERVICE_NAME,
|
||||
"createNotification"
|
||||
);
|
||||
|
||||
getNotificationsByTeamId = async (req, res, next) => {
|
||||
try {
|
||||
getNotificationsByTeamId = asyncHandler(
|
||||
async (req, res, next) => {
|
||||
const notifications = await this.db.getNotificationsByTeamId(req.user.teamId);
|
||||
return res.success({
|
||||
msg: "Notifications fetched successfully",
|
||||
data: notifications,
|
||||
});
|
||||
} catch (error) {
|
||||
next(handleError(error, SERVICE_NAME, "getNotificationsByTeamId"));
|
||||
}
|
||||
};
|
||||
},
|
||||
SERVICE_NAME,
|
||||
"getNotificationsByTeamId"
|
||||
);
|
||||
|
||||
deleteNotification = async (req, res, next) => {
|
||||
try {
|
||||
deleteNotification = asyncHandler(
|
||||
async (req, res, next) => {
|
||||
await this.db.deleteNotificationById(req.params.id);
|
||||
return res.success({
|
||||
msg: "Notification deleted successfully",
|
||||
});
|
||||
} catch (error) {
|
||||
next(handleError(error, SERVICE_NAME, "deleteNotification"));
|
||||
}
|
||||
};
|
||||
},
|
||||
SERVICE_NAME,
|
||||
"deleteNotification"
|
||||
);
|
||||
|
||||
getNotificationById = async (req, res, next) => {
|
||||
try {
|
||||
getNotificationById = asyncHandler(
|
||||
async (req, res, next) => {
|
||||
const notification = await this.db.getNotificationById(req.params.id);
|
||||
return res.success({
|
||||
msg: "Notification fetched successfully",
|
||||
data: notification,
|
||||
});
|
||||
} catch (error) {
|
||||
next(handleError(error, SERVICE_NAME, "getNotificationById"));
|
||||
}
|
||||
};
|
||||
},
|
||||
SERVICE_NAME,
|
||||
"getNotificationById"
|
||||
);
|
||||
|
||||
editNotification = async (req, res, next) => {
|
||||
try {
|
||||
editNotification = asyncHandler(
|
||||
async (req, res, next) => {
|
||||
await createNotificationBodyValidation.validateAsync(req.body, {
|
||||
abortEarly: false,
|
||||
});
|
||||
} catch (error) {
|
||||
next(handleValidationError(error, SERVICE_NAME));
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const notification = await this.db.editNotification(req.params.id, req.body);
|
||||
return res.success({
|
||||
msg: "Notification updated successfully",
|
||||
data: notification,
|
||||
});
|
||||
} catch (error) {
|
||||
next(handleError(error, SERVICE_NAME, "editNotification"));
|
||||
}
|
||||
};
|
||||
},
|
||||
SERVICE_NAME,
|
||||
"editNotification"
|
||||
);
|
||||
|
||||
testAllNotifications = async (req, res, next) => {
|
||||
try {
|
||||
testAllNotifications = asyncHandler(
|
||||
async (req, res, next) => {
|
||||
const { monitorId } = req.body;
|
||||
const monitor = await this.db.getMonitorById(monitorId);
|
||||
const notifications = monitor.notifications;
|
||||
@@ -138,10 +114,10 @@ class NotificationController {
|
||||
return res.success({
|
||||
msg: "All notifications sent successfully",
|
||||
});
|
||||
} catch (error) {
|
||||
next(handleError(error, SERVICE_NAME, "testAllNotifications"));
|
||||
}
|
||||
};
|
||||
},
|
||||
SERVICE_NAME,
|
||||
"testAllNotifications"
|
||||
);
|
||||
}
|
||||
|
||||
export default NotificationController;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { handleError } from "./controllerUtils.js";
|
||||
import { asyncHandler } from "../utils/errorUtils.js";
|
||||
|
||||
const SERVICE_NAME = "JobQueueController";
|
||||
|
||||
@@ -8,82 +8,76 @@ class JobQueueController {
|
||||
this.stringService = stringService;
|
||||
}
|
||||
|
||||
getMetrics = async (req, res, next) => {
|
||||
try {
|
||||
getMetrics = asyncHandler(
|
||||
async (req, res, next) => {
|
||||
const metrics = await this.jobQueue.getMetrics();
|
||||
res.success({
|
||||
msg: this.stringService.queueGetMetrics,
|
||||
data: metrics,
|
||||
});
|
||||
} catch (error) {
|
||||
next(handleError(error, SERVICE_NAME, "getMetrics"));
|
||||
return;
|
||||
}
|
||||
};
|
||||
},
|
||||
SERVICE_NAME,
|
||||
"getMetrics"
|
||||
);
|
||||
|
||||
getJobs = async (req, res, next) => {
|
||||
try {
|
||||
getJobs = asyncHandler(
|
||||
async (req, res, next) => {
|
||||
const jobs = await this.jobQueue.getJobs();
|
||||
return res.success({
|
||||
msg: this.stringService.queueGetJobs,
|
||||
data: jobs,
|
||||
});
|
||||
} catch (error) {
|
||||
next(handleError(error, SERVICE_NAME, "getJobs"));
|
||||
return;
|
||||
}
|
||||
};
|
||||
},
|
||||
SERVICE_NAME,
|
||||
"getJobs"
|
||||
);
|
||||
|
||||
getAllMetrics = async (req, res, next) => {
|
||||
try {
|
||||
getAllMetrics = asyncHandler(
|
||||
async (req, res, next) => {
|
||||
const jobs = await this.jobQueue.getJobs();
|
||||
const metrics = await this.jobQueue.getMetrics();
|
||||
return res.success({
|
||||
msg: this.stringService.queueGetAllMetrics,
|
||||
data: { jobs, metrics },
|
||||
});
|
||||
} catch (error) {
|
||||
next(handleError(error, SERVICE_NAME, "getAllMetrics"));
|
||||
return;
|
||||
}
|
||||
};
|
||||
},
|
||||
SERVICE_NAME,
|
||||
"getAllMetrics"
|
||||
);
|
||||
|
||||
addJob = async (req, res, next) => {
|
||||
try {
|
||||
addJob = asyncHandler(
|
||||
async (req, res, next) => {
|
||||
await this.jobQueue.addJob(Math.random().toString(36).substring(7));
|
||||
return res.success({
|
||||
msg: this.stringService.queueAddJob,
|
||||
});
|
||||
} catch (error) {
|
||||
next(handleError(error, SERVICE_NAME, "addJob"));
|
||||
return;
|
||||
}
|
||||
};
|
||||
},
|
||||
SERVICE_NAME,
|
||||
"addJob"
|
||||
);
|
||||
|
||||
flushQueue = async (req, res, next) => {
|
||||
try {
|
||||
flushQueue = asyncHandler(
|
||||
async (req, res, next) => {
|
||||
const result = await this.jobQueue.flushQueues();
|
||||
return res.success({
|
||||
msg: this.stringService.jobQueueFlush,
|
||||
data: result,
|
||||
});
|
||||
} catch (error) {
|
||||
next(handleError(error, SERVICE_NAME, "flushQueue"));
|
||||
return;
|
||||
}
|
||||
};
|
||||
},
|
||||
SERVICE_NAME,
|
||||
"flushQueue"
|
||||
);
|
||||
|
||||
checkQueueHealth = async (req, res, next) => {
|
||||
try {
|
||||
checkQueueHealth = asyncHandler(
|
||||
async (req, res, next) => {
|
||||
const stuckQueues = await this.jobQueue.checkQueueHealth();
|
||||
return res.success({
|
||||
msg: this.stringService.queueHealthCheck,
|
||||
data: stuckQueues,
|
||||
});
|
||||
} catch (error) {
|
||||
next(handleError(error, SERVICE_NAME, "checkQueueHealth"));
|
||||
return;
|
||||
}
|
||||
};
|
||||
},
|
||||
SERVICE_NAME,
|
||||
"checkQueueHealth"
|
||||
);
|
||||
}
|
||||
export default JobQueueController;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { updateAppSettingsBodyValidation } from "../validation/joi.js";
|
||||
import { handleValidationError, handleError } from "./controllerUtils.js";
|
||||
import { sendTestEmailBodyValidation } from "../validation/joi.js";
|
||||
import { asyncHandler, createServerError } from "../utils/errorUtils.js";
|
||||
|
||||
const SERVICE_NAME = "SettingsController";
|
||||
|
||||
class SettingsController {
|
||||
@@ -32,45 +33,44 @@ class SettingsController {
|
||||
return returnSettings;
|
||||
};
|
||||
|
||||
getAppSettings = async (req, res, next) => {
|
||||
const dbSettings = await this.settingsService.getDBSettings();
|
||||
getAppSettings = asyncHandler(
|
||||
async (req, res, next) => {
|
||||
const dbSettings = await this.settingsService.getDBSettings();
|
||||
|
||||
const returnSettings = this.buildAppSettings(dbSettings);
|
||||
return res.success({
|
||||
msg: this.stringService.getAppSettings,
|
||||
data: returnSettings,
|
||||
});
|
||||
};
|
||||
const returnSettings = this.buildAppSettings(dbSettings);
|
||||
return res.success({
|
||||
msg: this.stringService.getAppSettings,
|
||||
data: returnSettings,
|
||||
});
|
||||
},
|
||||
SERVICE_NAME,
|
||||
"getAppSettings"
|
||||
);
|
||||
|
||||
updateAppSettings = async (req, res, next) => {
|
||||
try {
|
||||
updateAppSettings = asyncHandler(
|
||||
async (req, res, next) => {
|
||||
await updateAppSettingsBodyValidation.validateAsync(req.body);
|
||||
} catch (error) {
|
||||
next(handleValidationError(error, SERVICE_NAME));
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const updatedSettings = await this.db.updateAppSettings(req.body);
|
||||
const returnSettings = this.buildAppSettings(updatedSettings);
|
||||
return res.success({
|
||||
msg: this.stringService.updateAppSettings,
|
||||
data: returnSettings,
|
||||
});
|
||||
} catch (error) {
|
||||
next(handleError(error, SERVICE_NAME, "updateAppSettings"));
|
||||
}
|
||||
};
|
||||
},
|
||||
SERVICE_NAME,
|
||||
"updateAppSettings"
|
||||
);
|
||||
|
||||
sendTestEmail = async (req, res, next) => {
|
||||
try {
|
||||
await sendTestEmailBodyValidation.validateAsync(req.body);
|
||||
} catch (error) {
|
||||
next(handleValidationError(error, SERVICE_NAME));
|
||||
return;
|
||||
}
|
||||
sendTestEmail = asyncHandler(
|
||||
async (req, res, next) => {
|
||||
try {
|
||||
await sendTestEmailBodyValidation.validateAsync(req.body);
|
||||
} catch (error) {
|
||||
next(handleValidationError(error, SERVICE_NAME));
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const {
|
||||
to,
|
||||
systemEmailHost,
|
||||
@@ -107,20 +107,17 @@ class SettingsController {
|
||||
});
|
||||
|
||||
if (!messageId) {
|
||||
return res.error({
|
||||
msg: "Failed to send test email.",
|
||||
});
|
||||
throw createServerError("Failed to send test email.");
|
||||
}
|
||||
|
||||
return res.success({
|
||||
msg: this.stringService.sendTestEmail,
|
||||
data: { messageId },
|
||||
});
|
||||
} catch (error) {
|
||||
next(handleError(error, SERVICE_NAME));
|
||||
return;
|
||||
}
|
||||
};
|
||||
},
|
||||
SERVICE_NAME,
|
||||
"sendTestEmail"
|
||||
);
|
||||
}
|
||||
|
||||
export default SettingsController;
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { handleError, handleValidationError } from "./controllerUtils.js";
|
||||
import {
|
||||
createStatusPageBodyValidation,
|
||||
getStatusPageParamValidation,
|
||||
getStatusPageQueryValidation,
|
||||
imageValidation,
|
||||
} from "../validation/joi.js";
|
||||
import { asyncHandler } from "../utils/errorUtils.js";
|
||||
|
||||
const SERVICE_NAME = "statusPageController";
|
||||
|
||||
@@ -14,16 +14,11 @@ class StatusPageController {
|
||||
this.stringService = stringService;
|
||||
}
|
||||
|
||||
createStatusPage = async (req, res, next) => {
|
||||
try {
|
||||
createStatusPage = asyncHandler(
|
||||
async (req, res, next) => {
|
||||
await createStatusPageBodyValidation.validateAsync(req.body);
|
||||
await imageValidation.validateAsync(req.file);
|
||||
} catch (error) {
|
||||
next(handleValidationError(error, SERVICE_NAME));
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const { _id, teamId } = req.user;
|
||||
const statusPage = await this.db.createStatusPage({
|
||||
statusPageData: req.body,
|
||||
@@ -35,21 +30,16 @@ class StatusPageController {
|
||||
msg: this.stringService.statusPageCreate,
|
||||
data: statusPage,
|
||||
});
|
||||
} catch (error) {
|
||||
next(handleError(error, SERVICE_NAME, "createStatusPage"));
|
||||
}
|
||||
};
|
||||
},
|
||||
SERVICE_NAME,
|
||||
"createStatusPage"
|
||||
);
|
||||
|
||||
updateStatusPage = async (req, res, next) => {
|
||||
try {
|
||||
updateStatusPage = asyncHandler(
|
||||
async (req, res, next) => {
|
||||
await createStatusPageBodyValidation.validateAsync(req.body);
|
||||
await imageValidation.validateAsync(req.file);
|
||||
} catch (error) {
|
||||
next(handleValidationError(error, SERVICE_NAME));
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const statusPage = await this.db.updateStatusPage(req.body, req.file);
|
||||
if (statusPage === null) {
|
||||
const error = new Error(this.stringService.statusPageNotFound);
|
||||
@@ -60,45 +50,40 @@ class StatusPageController {
|
||||
msg: this.stringService.statusPageUpdate,
|
||||
data: statusPage,
|
||||
});
|
||||
} catch (error) {
|
||||
next(handleError(error, SERVICE_NAME, "updateStatusPage"));
|
||||
}
|
||||
};
|
||||
},
|
||||
SERVICE_NAME,
|
||||
"updateStatusPage"
|
||||
);
|
||||
|
||||
getStatusPage = async (req, res, next) => {
|
||||
try {
|
||||
getStatusPage = asyncHandler(
|
||||
async (req, res, next) => {
|
||||
const statusPage = await this.db.getStatusPage();
|
||||
return res.success({
|
||||
msg: this.stringService.statusPageByUrl,
|
||||
data: statusPage,
|
||||
});
|
||||
} catch (error) {
|
||||
next(handleError(error, SERVICE_NAME, "getStatusPage"));
|
||||
}
|
||||
};
|
||||
},
|
||||
SERVICE_NAME,
|
||||
"getStatusPage"
|
||||
);
|
||||
|
||||
getStatusPageByUrl = async (req, res, next) => {
|
||||
try {
|
||||
getStatusPageByUrl = asyncHandler(
|
||||
async (req, res, next) => {
|
||||
await getStatusPageParamValidation.validateAsync(req.params);
|
||||
await getStatusPageQueryValidation.validateAsync(req.query);
|
||||
} catch (error) {
|
||||
next(handleValidationError(error, SERVICE_NAME));
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const statusPage = await this.db.getStatusPageByUrl(req.params.url, req.query.type);
|
||||
return res.success({
|
||||
msg: this.stringService.statusPageByUrl,
|
||||
data: statusPage,
|
||||
});
|
||||
} catch (error) {
|
||||
next(handleError(error, SERVICE_NAME, "getStatusPageByUrl"));
|
||||
}
|
||||
};
|
||||
},
|
||||
SERVICE_NAME,
|
||||
"getStatusPageByUrl"
|
||||
);
|
||||
|
||||
getStatusPagesByTeamId = async (req, res, next) => {
|
||||
try {
|
||||
getStatusPagesByTeamId = asyncHandler(
|
||||
async (req, res, next) => {
|
||||
const teamId = req.user.teamId;
|
||||
const statusPages = await this.db.getStatusPagesByTeamId(teamId);
|
||||
|
||||
@@ -106,21 +91,21 @@ class StatusPageController {
|
||||
msg: this.stringService.statusPageByTeamId,
|
||||
data: statusPages,
|
||||
});
|
||||
} catch (error) {
|
||||
next(handleError(error, SERVICE_NAME, "getStatusPageByTeamId"));
|
||||
}
|
||||
};
|
||||
},
|
||||
SERVICE_NAME,
|
||||
"getStatusPagesByTeamId"
|
||||
);
|
||||
|
||||
deleteStatusPage = async (req, res, next) => {
|
||||
try {
|
||||
deleteStatusPage = asyncHandler(
|
||||
async (req, res, next) => {
|
||||
await this.db.deleteStatusPage(req.params.url);
|
||||
return res.success({
|
||||
msg: this.stringService.statusPageDelete,
|
||||
});
|
||||
} catch (error) {
|
||||
next(handleError(error, SERVICE_NAME, "deleteStatusPage"));
|
||||
}
|
||||
};
|
||||
},
|
||||
SERVICE_NAME,
|
||||
"deleteStatusPage"
|
||||
);
|
||||
}
|
||||
|
||||
export default StatusPageController;
|
||||
|
||||
@@ -3,6 +3,7 @@ import ServiceRegistry from "../service/serviceRegistry.js";
|
||||
import StringService from "../service/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;
|
||||
|
||||
121
server/utils/errorUtils.js
Normal file
121
server/utils/errorUtils.js
Normal file
@@ -0,0 +1,121 @@
|
||||
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);
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
};
|
||||
};
|
||||
Reference in New Issue
Block a user