mirror of
https://github.com/bluewave-labs/Checkmate.git
synced 2026-01-23 02:00:20 -06:00
Merge remote-tracking branch 'upstream/develop' into develop
This commit is contained in:
2
Server/.gitignore
vendored
2
Server/.gitignore
vendored
@@ -2,3 +2,5 @@ node_modules
|
||||
.env
|
||||
*.log
|
||||
*.sh
|
||||
.nyc_output
|
||||
coverage
|
||||
8
Server/.mocharc.js
Normal file
8
Server/.mocharc.js
Normal file
@@ -0,0 +1,8 @@
|
||||
module.exports = {
|
||||
require: ["chai/register-expect.js"], // Include Chai's "expect" interface globally
|
||||
spec: "tests/**/*.test.js", // Specify test files
|
||||
timeout: 5000, // Set test-case timeout in milliseconds
|
||||
recursive: true, // Include subdirectories
|
||||
reporter: "spec", // Use the "spec" reporter
|
||||
exit: true, // Force Mocha to quit after tests complete
|
||||
};
|
||||
8
Server/.nycrc
Normal file
8
Server/.nycrc
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"all": true,
|
||||
"include": ["**/*.js"],
|
||||
"exclude": ["**/*.test.js"],
|
||||
"reporter": ["html", "text", "lcov"],
|
||||
"sourceMap": false,
|
||||
"instrument": true
|
||||
}
|
||||
@@ -1,19 +1,20 @@
|
||||
const {
|
||||
registrationBodyValidation,
|
||||
loginValidation,
|
||||
editUserParamValidation,
|
||||
editUserBodyValidation,
|
||||
recoveryValidation,
|
||||
recoveryTokenValidation,
|
||||
newPasswordValidation,
|
||||
registrationBodyValidation,
|
||||
loginValidation,
|
||||
editUserParamValidation,
|
||||
editUserBodyValidation,
|
||||
recoveryValidation,
|
||||
recoveryTokenValidation,
|
||||
newPasswordValidation,
|
||||
} = require("../validation/joi");
|
||||
const logger = require("../utils/logger");
|
||||
require("dotenv").config();
|
||||
const {errorMessages, successMessages} = require("../utils/messages");
|
||||
const { errorMessages, successMessages } = require("../utils/messages");
|
||||
const jwt = require("jsonwebtoken");
|
||||
const SERVICE_NAME = "AuthController";
|
||||
const {getTokenFromHeaders} = require("../utils/utils");
|
||||
const { getTokenFromHeaders } = require("../utils/utils");
|
||||
const crypto = require("crypto");
|
||||
const { handleValidationError, handleError } = require("./controllerUtils");
|
||||
|
||||
/**
|
||||
* Creates and returns JWT token with an arbitrary payload
|
||||
@@ -24,14 +25,12 @@ const crypto = require("crypto");
|
||||
* @throws {Error}
|
||||
*/
|
||||
const issueToken = (payload, appSettings) => {
|
||||
try {
|
||||
const tokenTTL = appSettings.jwtTTL ? appSettings.jwtTTL : "2h";
|
||||
return jwt.sign(payload, appSettings.jwtSecret, {expiresIn: tokenTTL});
|
||||
} catch (error) {
|
||||
error.service === undefined ? (error.service = SERVICE_NAME) : null;
|
||||
error.method === undefined ? (error.method = "issueToken") : null;
|
||||
throw error;
|
||||
}
|
||||
try {
|
||||
const tokenTTL = appSettings.jwtTTL ? appSettings.jwtTTL : "2h";
|
||||
return jwt.sign(payload, appSettings.jwtSecret, { expiresIn: tokenTTL });
|
||||
} catch (error) {
|
||||
throw handleError(error, SERVICE_NAME, "issueToken");
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -47,56 +46,61 @@ const issueToken = (payload, appSettings) => {
|
||||
* @throws {Error} If there is an error during the process, especially if there is a validation error (422).
|
||||
*/
|
||||
const registerUser = async (req, res, next) => {
|
||||
// joi validation
|
||||
try {
|
||||
await registrationBodyValidation.validateAsync(req.body);
|
||||
} catch (error) {
|
||||
error.status = 422;
|
||||
error.service = SERVICE_NAME;
|
||||
error.message = error.details?.[0]?.message || error.message || "Validation Error";
|
||||
next(error);
|
||||
return;
|
||||
// joi validation
|
||||
try {
|
||||
await registrationBodyValidation.validateAsync(req.body);
|
||||
} catch (error) {
|
||||
const validationError = handleValidationError(error, SERVICE_NAME);
|
||||
next(validationError);
|
||||
return;
|
||||
}
|
||||
// Create a new user
|
||||
try {
|
||||
const { inviteToken } = req.body;
|
||||
// If superAdmin exists, a token should be attached to all further register requests
|
||||
const superAdminExists = await req.db.checkSuperadmin(req, res);
|
||||
if (superAdminExists) {
|
||||
await req.db.getInviteTokenAndDelete(inviteToken);
|
||||
} else {
|
||||
// This is the first account, create JWT secret to use if one is not supplied by env
|
||||
const jwtSecret = crypto.randomBytes(64).toString("hex");
|
||||
await req.db.updateAppSettings({ jwtSecret });
|
||||
}
|
||||
// Create a new user
|
||||
try {
|
||||
const {inviteToken} = req.body;
|
||||
// If superAdmin exists, a token should be attached to all further register requests
|
||||
const superAdminExists = await req.db.checkSuperadmin(req, res);
|
||||
if (superAdminExists) {
|
||||
await req.db.getInviteTokenAndDelete(inviteToken);
|
||||
} else {
|
||||
// This is the first account, create JWT secret to use if one is not supplied by env
|
||||
const jwtSecret = crypto.randomBytes(64).toString("hex");
|
||||
await req.db.updateAppSettings({jwtSecret});
|
||||
}
|
||||
|
||||
const newUser = await req.db.insertUser({...req.body}, req.file);
|
||||
logger.info(successMessages.AUTH_CREATE_USER, {
|
||||
service: SERVICE_NAME, userId: newUser._id,
|
||||
const newUser = await req.db.insertUser({ ...req.body }, req.file);
|
||||
logger.info(successMessages.AUTH_CREATE_USER, {
|
||||
service: SERVICE_NAME,
|
||||
userId: newUser._id,
|
||||
});
|
||||
|
||||
const userForToken = { ...newUser._doc };
|
||||
delete userForToken.profileImage;
|
||||
delete userForToken.avatarImage;
|
||||
|
||||
const appSettings = await req.settingsService.getSettings();
|
||||
const token = issueToken(userForToken, appSettings);
|
||||
req.emailService
|
||||
.buildAndSendEmail(
|
||||
"welcomeEmailTemplate",
|
||||
{ name: newUser.firstName },
|
||||
newUser.email,
|
||||
"Welcome to Uptime Monitor"
|
||||
)
|
||||
.catch((error) => {
|
||||
logger.error("Error sending welcome email", {
|
||||
service: SERVICE_NAME,
|
||||
error: error.message,
|
||||
});
|
||||
});
|
||||
|
||||
const userForToken = {...newUser._doc};
|
||||
delete userForToken.profileImage;
|
||||
delete userForToken.avatarImage;
|
||||
|
||||
const appSettings = await req.settingsService.getSettings();
|
||||
const token = issueToken(userForToken, appSettings);
|
||||
req.emailService
|
||||
.buildAndSendEmail("welcomeEmailTemplate", {name: newUser.firstName}, newUser.email, "Welcome to Uptime Monitor")
|
||||
.catch((error) => {
|
||||
logger.error("Error sending welcome email", {
|
||||
service: SERVICE_NAME, error: error.message,
|
||||
});
|
||||
});
|
||||
|
||||
return res.status(200).json({
|
||||
success: true, msg: successMessages.AUTH_CREATE_USER, data: {user: newUser, token: token},
|
||||
});
|
||||
} catch (error) {
|
||||
error.service === undefined ? (error.service = SERVICE_NAME) : null;
|
||||
error.method === undefined ? (error.method = "registerController") : null;
|
||||
next(error);
|
||||
}
|
||||
return res.status(200).json({
|
||||
success: true,
|
||||
msg: successMessages.AUTH_CREATE_USER,
|
||||
data: { user: newUser, token: token },
|
||||
});
|
||||
} catch (error) {
|
||||
next(handleError(error, SERVICE_NAME, "registerController"));
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -112,47 +116,44 @@ const registerUser = async (req, res, next) => {
|
||||
* @throws {Error} If there is an error during the process, especially if there is a validation error (422) or the password is incorrect.
|
||||
*/
|
||||
const loginUser = async (req, res, next) => {
|
||||
try {
|
||||
await loginValidation.validateAsync(req.body);
|
||||
} catch (error) {
|
||||
error.status = 422;
|
||||
error.service = SERVICE_NAME;
|
||||
error.message = error.details?.[0]?.message || error.message || "Validation Error";
|
||||
next(error);
|
||||
return;
|
||||
try {
|
||||
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
|
||||
const user = await req.db.getUserByEmail(email);
|
||||
|
||||
// Compare password
|
||||
const match = await user.comparePassword(password);
|
||||
if (match !== true) {
|
||||
next(new Error(errorMessages.AUTH_INCORRECT_PASSWORD));
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const {email, password} = req.body;
|
||||
// Check if user exists
|
||||
const user = await req.db.getUserByEmail(email);
|
||||
|
||||
// Compare password
|
||||
const match = await user.comparePassword(password);
|
||||
if (match !== true) {
|
||||
next(new Error(errorMessages.AUTH_INCORRECT_PASSWORD));
|
||||
return;
|
||||
}
|
||||
// Remove password from user object. Should this be abstracted to DB layer?
|
||||
const userWithoutPassword = { ...user._doc };
|
||||
delete userWithoutPassword.password;
|
||||
delete userWithoutPassword.avatarImage;
|
||||
|
||||
// Remove password from user object. Should this be abstracted to DB layer?
|
||||
const userWithoutPassword = {...user._doc};
|
||||
delete userWithoutPassword.password;
|
||||
delete userWithoutPassword.avatarImage;
|
||||
// Happy path, return token
|
||||
const appSettings = await req.settingsService.getSettings();
|
||||
const token = issueToken(userWithoutPassword, appSettings);
|
||||
// reset avatar image
|
||||
userWithoutPassword.avatarImage = user.avatarImage;
|
||||
|
||||
// Happy path, return token
|
||||
const appSettings = req.settingsService.getSettings();
|
||||
const token = issueToken(userWithoutPassword, appSettings);
|
||||
// reset avatar image
|
||||
userWithoutPassword.avatarImage = user.avatarImage;
|
||||
|
||||
return res.status(200).json({
|
||||
success: true, msg: successMessages.AUTH_LOGIN_USER, data: {user: userWithoutPassword, token: token},
|
||||
});
|
||||
} catch (error) {
|
||||
error.status = 500;
|
||||
error.service === undefined ? (error.service = SERVICE_NAME) : null;
|
||||
error.method === undefined ? (error.method = "loginController") : null;
|
||||
next(error);
|
||||
}
|
||||
return res.status(200).json({
|
||||
success: true,
|
||||
msg: successMessages.AUTH_LOGIN_USER,
|
||||
data: { user: userWithoutPassword, token: token },
|
||||
});
|
||||
} catch (error) {
|
||||
next(handleError(error, SERVICE_NAME, "loginController"));
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -170,60 +171,58 @@ const loginUser = async (req, res, next) => {
|
||||
* @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).
|
||||
*/
|
||||
const editUser = async (req, res, next) => {
|
||||
try {
|
||||
await editUserParamValidation.validateAsync(req.params);
|
||||
await editUserBodyValidation.validateAsync(req.body);
|
||||
} catch (error) {
|
||||
error.status = 422;
|
||||
error.service = SERVICE_NAME;
|
||||
error.message = error.details?.[0]?.message || error.message || "Validation Error";
|
||||
try {
|
||||
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(errorMessages.AUTH_UNAUTHORIZED);
|
||||
error.status = 401;
|
||||
error.service = SERVICE_NAME;
|
||||
next(error);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// Change Password check
|
||||
if (req.body.password && req.body.newPassword) {
|
||||
// Get token from headers
|
||||
const token = getTokenFromHeaders(req.headers);
|
||||
// Get email from token
|
||||
const { jwtSecret } = req.settingsService.getSettings();
|
||||
const { email } = jwt.verify(token, jwtSecret);
|
||||
// Add user email to body for DB operation
|
||||
req.body.email = email;
|
||||
// Get user
|
||||
const user = await req.db.getUserByEmail(email);
|
||||
// Compare passwords
|
||||
const match = await user.comparePassword(req.body.password);
|
||||
// If not a match, throw a 403
|
||||
if (!match) {
|
||||
const error = new Error(errorMessages.AUTH_INCORRECT_PASSWORD);
|
||||
error.status = 403;
|
||||
next(error);
|
||||
return;
|
||||
}
|
||||
// If a match, update the password
|
||||
req.body.password = req.body.newPassword;
|
||||
}
|
||||
|
||||
// TODO is this neccessary any longer? Verify ownership middleware should handle this
|
||||
if (req.params.userId !== req.user._id.toString()) {
|
||||
const error = new Error(errorMessages.AUTH_UNAUTHORIZED);
|
||||
error.status = 401;
|
||||
error.service = SERVICE_NAME;
|
||||
next(error);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// Change Password check
|
||||
if (req.body.password && req.body.newPassword) {
|
||||
// Get token from headers
|
||||
const token = getTokenFromHeaders(req.headers);
|
||||
// Get email from token
|
||||
const {jwtSecret} = req.settingsService.getSettings();
|
||||
const {email} = jwt.verify(token, jwtSecret);
|
||||
// Add user email to body for DB operation
|
||||
req.body.email = email;
|
||||
// Get user
|
||||
const user = await req.db.getUserByEmail(email);
|
||||
// Compare passwords
|
||||
const match = await user.comparePassword(req.body.password);
|
||||
// If not a match, throw a 403
|
||||
if (!match) {
|
||||
const error = new Error(errorMessages.AUTH_INCORRECT_PASSWORD);
|
||||
error.status = 403;
|
||||
next(error)
|
||||
return;
|
||||
}
|
||||
// If a match, update the password
|
||||
req.body.password = req.body.newPassword;
|
||||
}
|
||||
|
||||
const updatedUser = await req.db.updateUser(req, res);
|
||||
return res.status(200).json({
|
||||
success: true, msg: successMessages.AUTH_UPDATE_USER, data: updatedUser,
|
||||
});
|
||||
} catch (error) {
|
||||
error.service === undefined ? (error.service = SERVICE_NAME) : null;
|
||||
error.method === undefined ? (error.method = "userEditController") : null;
|
||||
next(error);
|
||||
}
|
||||
const updatedUser = await req.db.updateUser(req, res);
|
||||
return res.status(200).json({
|
||||
success: true,
|
||||
msg: successMessages.AUTH_UPDATE_USER,
|
||||
data: updatedUser,
|
||||
});
|
||||
} catch (error) {
|
||||
next(handleError(error, SERVICE_NAME, "userEditController"));
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -236,16 +235,16 @@ const editUser = async (req, res, next) => {
|
||||
* @throws {Error} If there is an error during the process.
|
||||
*/
|
||||
const checkSuperAdminExists = async (req, res, next) => {
|
||||
try {
|
||||
const superAdminExists = await req.db.checkSuperadmin(req, res);
|
||||
return res.status(200).json({
|
||||
success: true, msg: successMessages.AUTH_ADMIN_EXISTS, data: superAdminExists,
|
||||
});
|
||||
} catch (error) {
|
||||
error.service === undefined ? (error.service = SERVICE_NAME) : null;
|
||||
error.method === undefined ? (error.method = "checkSuperadminController") : null;
|
||||
next(error);
|
||||
}
|
||||
try {
|
||||
const superAdminExists = await req.db.checkSuperadmin(req, res);
|
||||
return res.status(200).json({
|
||||
success: true,
|
||||
msg: successMessages.AUTH_ADMIN_EXISTS,
|
||||
data: superAdminExists,
|
||||
});
|
||||
} catch (error) {
|
||||
next(handleError(error, SERVICE_NAME, "checkSuperadminController"));
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -260,41 +259,44 @@ const checkSuperAdminExists = async (req, res, next) => {
|
||||
* @throws {Error} If there is an error during the process, especially if there is a validation error (422).
|
||||
*/
|
||||
const requestRecovery = async (req, res, next) => {
|
||||
try {
|
||||
await recoveryValidation.validateAsync(req.body);
|
||||
} catch (error) {
|
||||
error.status = 422;
|
||||
error.service = SERVICE_NAME;
|
||||
error.message = error.details?.[0]?.message || error.message || "Validation Error";
|
||||
next(error);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const {email} = req.body;
|
||||
const user = await req.db.getUserByEmail(email);
|
||||
if (user) {
|
||||
const recoveryToken = await req.db.requestRecoveryToken(req, res);
|
||||
const name = user.firstName;
|
||||
const email = req.body.email;
|
||||
const {clientHost} = req.settingsService.getSettings();
|
||||
const url = `${clientHost}/set-new-password/${recoveryToken.token}`;
|
||||
|
||||
const msgId = await req.emailService.buildAndSendEmail("passwordResetTemplate", {
|
||||
name,
|
||||
email,
|
||||
url
|
||||
}, email, "Bluewave Uptime Password Reset");
|
||||
|
||||
return res.status(200).json({
|
||||
success: true, msg: successMessages.AUTH_CREATE_RECOVERY_TOKEN, data: msgId,
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
error.service === undefined ? (error.service = SERVICE_NAME) : null;
|
||||
error.method === undefined ? (error.method = "recoveryRequestController") : null;
|
||||
next(error);
|
||||
try {
|
||||
await recoveryValidation.validateAsync(req.body);
|
||||
} catch (error) {
|
||||
const validationError = handleValidationError(error, SERVICE_NAME);
|
||||
next(validationError);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const { email } = req.body;
|
||||
const user = await req.db.getUserByEmail(email);
|
||||
if (user) {
|
||||
const recoveryToken = await req.db.requestRecoveryToken(req, res);
|
||||
const name = user.firstName;
|
||||
const email = req.body.email;
|
||||
const { clientHost } = req.settingsService.getSettings();
|
||||
const url = `${clientHost}/set-new-password/${recoveryToken.token}`;
|
||||
|
||||
const msgId = await req.emailService.buildAndSendEmail(
|
||||
"passwordResetTemplate",
|
||||
{
|
||||
name,
|
||||
email,
|
||||
url,
|
||||
},
|
||||
email,
|
||||
"Bluewave Uptime Password Reset"
|
||||
);
|
||||
|
||||
return res.status(200).json({
|
||||
success: true,
|
||||
msg: successMessages.AUTH_CREATE_RECOVERY_TOKEN,
|
||||
data: msgId,
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
next(handleError(error, SERVICE_NAME, "recoveryRequestController"));
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -309,26 +311,23 @@ const requestRecovery = async (req, res, next) => {
|
||||
* @throws {Error} If there is an error during the process, especially if there is a validation error (422).
|
||||
*/
|
||||
const validateRecovery = async (req, res, next) => {
|
||||
try {
|
||||
await recoveryTokenValidation.validateAsync(req.body);
|
||||
} catch (error) {
|
||||
error.status = 422;
|
||||
error.service = SERVICE_NAME;
|
||||
error.message = error.details?.[0]?.message || error.message || "Validation Error";
|
||||
next(error);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
await recoveryTokenValidation.validateAsync(req.body);
|
||||
} catch (error) {
|
||||
const validationError = handleValidationError(error, SERVICE_NAME);
|
||||
next(validationError);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await req.db.validateRecoveryToken(req, res);
|
||||
return res.status(200).json({
|
||||
success: true, msg: successMessages.AUTH_VERIFY_RECOVERY_TOKEN,
|
||||
});
|
||||
} catch (error) {
|
||||
error.service === undefined ? (error.service = SERVICE_NAME) : null;
|
||||
error.method === undefined ? (error.method = "validateRecoveryTokenController") : null;
|
||||
next(error);
|
||||
}
|
||||
try {
|
||||
await req.db.validateRecoveryToken(req, res);
|
||||
return res.status(200).json({
|
||||
success: true,
|
||||
msg: successMessages.AUTH_VERIFY_RECOVERY_TOKEN,
|
||||
});
|
||||
} catch (error) {
|
||||
next(handleError(error, SERVICE_NAME, "validateRecoveryTokenController"));
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -344,28 +343,26 @@ const validateRecovery = async (req, res, next) => {
|
||||
* @throws {Error} If there is an error during the process, especially if there is a validation error (422).
|
||||
*/
|
||||
const resetPassword = async (req, res, next) => {
|
||||
try {
|
||||
await newPasswordValidation.validateAsync(req.body);
|
||||
} catch (error) {
|
||||
error.status = 422;
|
||||
error.service = SERVICE_NAME;
|
||||
error.message = error.details?.[0]?.message || error.message || "Validation Error";
|
||||
next(error);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const user = await req.db.resetPassword(req, res);
|
||||
try {
|
||||
await newPasswordValidation.validateAsync(req.body);
|
||||
} catch (error) {
|
||||
const validationError = handleValidationError(error, SERVICE_NAME);
|
||||
next(validationError);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const user = await req.db.resetPassword(req, res);
|
||||
|
||||
const appSettings = await req.settingsService.getSettings();
|
||||
const token = issueToken(user._doc, appSettings);
|
||||
res.status(200).json({
|
||||
success: true, msg: successMessages.AUTH_RESET_PASSWORD, data: {user, token},
|
||||
});
|
||||
} catch (error) {
|
||||
error.service === undefined ? (error.service = SERVICE_NAME) : null;
|
||||
error.method === undefined ? (error.method = "resetPasswordController") : null;
|
||||
next(error);
|
||||
}
|
||||
const appSettings = await req.settingsService.getSettings();
|
||||
const token = issueToken(user._doc, appSettings);
|
||||
res.status(200).json({
|
||||
success: true,
|
||||
msg: successMessages.AUTH_RESET_PASSWORD,
|
||||
data: { user, token },
|
||||
});
|
||||
} catch (error) {
|
||||
next(handleError(error, SERVICE_NAME, "resetPasswordController"));
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -378,73 +375,73 @@ const resetPassword = async (req, res, next) => {
|
||||
* @throws {Error} If user validation fails or user is not found in the database.
|
||||
*/
|
||||
const deleteUser = async (req, res, next) => {
|
||||
try {
|
||||
const token = getTokenFromHeaders(req.headers);
|
||||
const decodedToken = jwt.decode(token);
|
||||
const {email} = decodedToken;
|
||||
try {
|
||||
const token = getTokenFromHeaders(req.headers);
|
||||
const decodedToken = jwt.decode(token);
|
||||
const { email } = decodedToken;
|
||||
|
||||
// Check if the user exists
|
||||
const user = await req.db.getUserByEmail(email);
|
||||
if (!user) {
|
||||
next(new Error(errorMessages.DB_USER_NOT_FOUND));
|
||||
return;
|
||||
}
|
||||
|
||||
// 1. Find all the monitors associated with the team ID if superadmin
|
||||
|
||||
const result = await req.db.getMonitorsByTeamId({
|
||||
params: {teamId: user.teamId},
|
||||
});
|
||||
if (user.role.includes("superadmin")) {
|
||||
//2. Remove all jobs, delete checks and alerts
|
||||
result?.monitors.length > 0 && (await Promise.all(result.monitors.map(async (monitor) => {
|
||||
await req.jobQueue.deleteJob(monitor);
|
||||
await req.db.deleteChecks(monitor._id);
|
||||
await req.db.deletePageSpeedChecksByMonitorId(monitor._id);
|
||||
await req.db.deleteNotificationsByMonitorId(monitor._id);
|
||||
})));
|
||||
|
||||
// 3. Delete team
|
||||
await req.db.deleteTeam(user.teamId);
|
||||
// 4. Delete all other team members
|
||||
await req.db.deleteAllOtherUsers();
|
||||
// 5. Delete each monitor
|
||||
await req.db.deleteMonitorsByUserId(user._id);
|
||||
}
|
||||
// 6. Delete the user by id
|
||||
await req.db.deleteUser(user._id);
|
||||
|
||||
return res.status(200).json({
|
||||
success: true, msg: successMessages.AUTH_DELETE_USER,
|
||||
});
|
||||
} catch (error) {
|
||||
error.service === undefined ? (error.service = SERVICE_NAME) : null;
|
||||
error.method === undefined ? (error.method = "deleteUserController") : null;
|
||||
next(error);
|
||||
// Check if the user exists
|
||||
const user = await req.db.getUserByEmail(email);
|
||||
if (!user) {
|
||||
next(new Error(errorMessages.DB_USER_NOT_FOUND));
|
||||
return;
|
||||
}
|
||||
|
||||
// 1. Find all the monitors associated with the team ID if superadmin
|
||||
|
||||
const result = await req.db.getMonitorsByTeamId({
|
||||
params: { teamId: user.teamId },
|
||||
});
|
||||
|
||||
if (user.role.includes("superadmin")) {
|
||||
//2. Remove all jobs, delete checks and alerts
|
||||
result?.monitors.length > 0 &&
|
||||
(await Promise.all(
|
||||
result.monitors.map(async (monitor) => {
|
||||
await req.jobQueue.deleteJob(monitor);
|
||||
await req.db.deleteChecks(monitor._id);
|
||||
await req.db.deletePageSpeedChecksByMonitorId(monitor._id);
|
||||
await req.db.deleteNotificationsByMonitorId(monitor._id);
|
||||
})
|
||||
));
|
||||
|
||||
// 3. Delete team
|
||||
await req.db.deleteTeam(user.teamId);
|
||||
// 4. Delete all other team members
|
||||
await req.db.deleteAllOtherUsers();
|
||||
// 5. Delete each monitor
|
||||
await req.db.deleteMonitorsByUserId(user._id);
|
||||
}
|
||||
// 6. Delete the user by id
|
||||
await req.db.deleteUser(user._id);
|
||||
return res.status(200).json({
|
||||
success: true,
|
||||
msg: successMessages.AUTH_DELETE_USER,
|
||||
});
|
||||
} catch (error) {
|
||||
next(handleError(error, SERVICE_NAME, "deleteUserController"));
|
||||
}
|
||||
};
|
||||
|
||||
const getAllUsers = async (req, res) => {
|
||||
try {
|
||||
const allUsers = await req.db.getAllUsers(req, res);
|
||||
res
|
||||
.status(200)
|
||||
.json({success: true, msg: "Got all users", data: allUsers});
|
||||
} catch (error) {
|
||||
error.service === undefined ? (error.service = SERVICE_NAME) : null;
|
||||
error.method === undefined ? (error.method = "getAllUsersController") : null;
|
||||
next(error);
|
||||
}
|
||||
try {
|
||||
const allUsers = await req.db.getAllUsers(req, res);
|
||||
res
|
||||
.status(200)
|
||||
.json({ success: true, msg: "Got all users", data: allUsers });
|
||||
} catch (error) {
|
||||
next(handleError(error, SERVICE_NAME, "getAllUsersController"));
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
registerUser,
|
||||
loginUser,
|
||||
editUser,
|
||||
checkSuperadminExists: checkSuperAdminExists,
|
||||
requestRecovery,
|
||||
validateRecovery,
|
||||
resetPassword,
|
||||
deleteUser,
|
||||
getAllUsers,
|
||||
registerUser,
|
||||
loginUser,
|
||||
editUser,
|
||||
checkSuperadminExists: checkSuperAdminExists,
|
||||
requestRecovery,
|
||||
validateRecovery,
|
||||
resetPassword,
|
||||
deleteUser,
|
||||
getAllUsers,
|
||||
};
|
||||
|
||||
@@ -13,17 +13,14 @@ const { successMessages } = require("../utils/messages");
|
||||
const jwt = require("jsonwebtoken");
|
||||
const { getTokenFromHeaders } = require("../utils/utils");
|
||||
const SERVICE_NAME = "checkController";
|
||||
const { handleValidationError, handleError } = require("./controllerUtils");
|
||||
|
||||
const createCheck = async (req, res, next) => {
|
||||
try {
|
||||
await createCheckParamValidation.validateAsync(req.params);
|
||||
await createCheckBodyValidation.validateAsync(req.body);
|
||||
} catch (error) {
|
||||
error.status = 422;
|
||||
error.service = SERVICE_NAME;
|
||||
error.message =
|
||||
error.details?.[0]?.message || error.message || "Validation Error";
|
||||
next(error);
|
||||
next(handleValidationError(error, SERVICE_NAME));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -34,9 +31,7 @@ const createCheck = async (req, res, next) => {
|
||||
.status(200)
|
||||
.json({ success: true, msg: successMessages.CHECK_CREATE, data: check });
|
||||
} catch (error) {
|
||||
error.service === undefined ? (error.service = SERVICE_NAME) : null;
|
||||
error.method === undefined ? (error.method = "createCheck") : null;
|
||||
next(error);
|
||||
next(handleError(error, SERVICE_NAME, "createCheck"));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -45,11 +40,7 @@ const getChecks = async (req, res, next) => {
|
||||
await getChecksParamValidation.validateAsync(req.params);
|
||||
await getChecksQueryValidation.validateAsync(req.query);
|
||||
} catch (error) {
|
||||
error.status = 422;
|
||||
error.service = SERVICE_NAME;
|
||||
error.message =
|
||||
error.details?.[0]?.message || error.message || "Validation Error";
|
||||
next(error);
|
||||
next(handleValidationError(error, SERVICE_NAME));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -62,9 +53,7 @@ const getChecks = async (req, res, next) => {
|
||||
data: { checksCount, checks },
|
||||
});
|
||||
} catch (error) {
|
||||
error.service === undefined ? (error.service = SERVICE_NAME) : null;
|
||||
error.method === undefined ? (error.method = "getChecks") : null;
|
||||
next(error);
|
||||
next(handleError(error, SERVICE_NAME, "getChecks"));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -73,11 +62,7 @@ const getTeamChecks = async (req, res, next) => {
|
||||
await getTeamChecksParamValidation.validateAsync(req.params);
|
||||
await getTeamChecksQueryValidation.validateAsync(req.query);
|
||||
} catch (error) {
|
||||
error.status = 422;
|
||||
error.service = SERVICE_NAME;
|
||||
error.message =
|
||||
error.details?.[0]?.message || error.message || "Validation Error";
|
||||
next(error);
|
||||
next(handleValidationError(error, SERVICE_NAME));
|
||||
return;
|
||||
}
|
||||
try {
|
||||
@@ -88,9 +73,7 @@ const getTeamChecks = async (req, res, next) => {
|
||||
data: checkData,
|
||||
});
|
||||
} catch (error) {
|
||||
error.service === undefined ? (error.service = SERVICE_NAME) : null;
|
||||
error.method === undefined ? (error.method = "getTeamChecks") : null;
|
||||
next(error);
|
||||
next(handleError(error, SERVICE_NAME, "getTeamChecks"));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -98,11 +81,7 @@ const deleteChecks = async (req, res, next) => {
|
||||
try {
|
||||
await deleteChecksParamValidation.validateAsync(req.params);
|
||||
} catch (error) {
|
||||
error.status = 422;
|
||||
error.service = SERVICE_NAME;
|
||||
error.message =
|
||||
error.details?.[0]?.message || error.message || "Validation Error";
|
||||
next(error);
|
||||
next(handleValidationError(error, SERVICE_NAME));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -114,9 +93,7 @@ const deleteChecks = async (req, res, next) => {
|
||||
data: { deletedCount },
|
||||
});
|
||||
} catch (error) {
|
||||
error.service === undefined ? (error.service = SERVICE_NAME) : null;
|
||||
error.method === undefined ? (error.method = "deleteChecks") : null;
|
||||
next(error);
|
||||
next(handleError(error, SERVICE_NAME, "deleteChecks"));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -124,10 +101,7 @@ const deleteChecksByTeamId = async (req, res, next) => {
|
||||
try {
|
||||
await deleteChecksByTeamIdParamValidation.validateAsync(req.params);
|
||||
} catch (error) {
|
||||
error.service === undefined ? (error.service = SERVICE_NAME) : null;
|
||||
error.method === undefined ? (error.method = "deleteChecksByTeam") : null;
|
||||
error.status = 422;
|
||||
next(error);
|
||||
next(handleValidationError(error, SERVICE_NAME));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -139,9 +113,7 @@ const deleteChecksByTeamId = async (req, res, next) => {
|
||||
data: { deletedCount },
|
||||
});
|
||||
} catch (error) {
|
||||
error.service === undefined ? (error.service = SERVICE_NAME) : null;
|
||||
error.method === undefined ? (error.method = "deleteChecksByTeamId") : null;
|
||||
next(error);
|
||||
next(handleError(error, SERVICE_NAME, "deleteChecksByTeamId"));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -151,12 +123,7 @@ const updateChecksTTL = async (req, res, next) => {
|
||||
try {
|
||||
await updateChecksTTLBodyValidation.validateAsync(req.body);
|
||||
} catch (error) {
|
||||
error.status = 422;
|
||||
error.service === undefined ? (error.service = SERVICE_NAME) : null;
|
||||
error.method === undefined ? (error.method = "updateChecksTTL") : null;
|
||||
error.message =
|
||||
error.details?.[0]?.message || error.message || "Validation Error";
|
||||
next(error);
|
||||
next(handleValidationError(error, SERVICE_NAME));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -172,9 +139,7 @@ const updateChecksTTL = async (req, res, next) => {
|
||||
msg: successMessages.CHECK_UPDATE_TTL,
|
||||
});
|
||||
} catch (error) {
|
||||
error.service === undefined ? (error.service = SERVICE_NAME) : null;
|
||||
error.method === undefined ? (error.method = "updateTTL") : null;
|
||||
next(error);
|
||||
next(handleError(error, SERVICE_NAME, "updateTTL"));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
19
Server/controllers/controllerUtils.js
Normal file
19
Server/controllers/controllerUtils.js
Normal file
@@ -0,0 +1,19 @@
|
||||
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, code = 500) => {
|
||||
error.code === undefined ? (error.code = code) : null;
|
||||
error.service === undefined ? (error.service = serviceName) : null;
|
||||
error.method === undefined ? (error.method = method) : null;
|
||||
return error;
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
handleValidationError,
|
||||
handleError,
|
||||
};
|
||||
@@ -1,22 +1,23 @@
|
||||
const {
|
||||
inviteRoleValidation,
|
||||
inviteBodyValidation,
|
||||
inviteVerificationBodyValidation,
|
||||
inviteRoleValidation,
|
||||
inviteBodyValidation,
|
||||
inviteVerificationBodyValidation,
|
||||
} = require("../validation/joi");
|
||||
const logger = require("../utils/logger");
|
||||
require("dotenv").config();
|
||||
const jwt = require("jsonwebtoken");
|
||||
const { handleError, handleValidationError } = require("./controllerUtils");
|
||||
const SERVICE_NAME = "inviteController";
|
||||
|
||||
const getTokenFromHeaders = (headers) => {
|
||||
const authorizationHeader = headers.authorization;
|
||||
if (!authorizationHeader) throw new Error("No auth headers");
|
||||
const authorizationHeader = headers.authorization;
|
||||
if (!authorizationHeader) throw new Error("No auth headers");
|
||||
|
||||
const parts = authorizationHeader.split(" ");
|
||||
if (parts.length !== 2 || parts[0] !== "Bearer")
|
||||
throw new Error("Invalid auth headers");
|
||||
const parts = authorizationHeader.split(" ");
|
||||
if (parts.length !== 2 || parts[0] !== "Bearer")
|
||||
throw new Error("Invalid auth headers");
|
||||
|
||||
return parts[1];
|
||||
return parts[1];
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -33,79 +34,65 @@ const getTokenFromHeaders = (headers) => {
|
||||
* @throws {Error} If there is an error during the process, especially if there is a validation error (422).
|
||||
*/
|
||||
const issueInvitation = async (req, res, next) => {
|
||||
try {
|
||||
// Only admins can invite
|
||||
const token = getTokenFromHeaders(req.headers);
|
||||
const { role, firstname, teamId } = jwt.decode(token);
|
||||
req.body.teamId = teamId;
|
||||
try {
|
||||
// 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) {
|
||||
error.status = 422;
|
||||
error.service = SERVICE_NAME;
|
||||
error.message =
|
||||
error.details?.[0]?.message || error.message || "Validation Error";
|
||||
next(error);
|
||||
return;
|
||||
}
|
||||
|
||||
const inviteToken = await req.db.requestInviteToken({...req.body});
|
||||
const {clientHost} = req.settingsService.getSettings();
|
||||
req.emailService
|
||||
.buildAndSendEmail(
|
||||
"employeeActivationTemplate",
|
||||
{
|
||||
name: firstname,
|
||||
link: `${clientHost}/register/${inviteToken.token}`,
|
||||
},
|
||||
req.body.email,
|
||||
"Welcome to Uptime Monitor"
|
||||
)
|
||||
.catch((error) => {
|
||||
logger.error("Error sending invite email", {
|
||||
service: SERVICE_NAME,
|
||||
error: error.message,
|
||||
});
|
||||
});
|
||||
|
||||
return res
|
||||
.status(200)
|
||||
.json({success: true, msg: "Invite sent", data: inviteToken});
|
||||
await inviteRoleValidation.validateAsync({ roles: role });
|
||||
await inviteBodyValidation.validateAsync(req.body);
|
||||
} catch (error) {
|
||||
error.service === undefined ? (error.service = SERVICE_NAME) : null;
|
||||
error.method === undefined ? (error.method = "inviteController") : null;
|
||||
next(error);
|
||||
next(handleValidationError(error, SERVICE_NAME));
|
||||
return;
|
||||
}
|
||||
|
||||
const inviteToken = await req.db.requestInviteToken({ ...req.body });
|
||||
const { clientHost } = req.settingsService.getSettings();
|
||||
req.emailService
|
||||
.buildAndSendEmail(
|
||||
"employeeActivationTemplate",
|
||||
{
|
||||
name: firstname,
|
||||
link: `${clientHost}/register/${inviteToken.token}`,
|
||||
},
|
||||
req.body.email,
|
||||
"Welcome to Uptime Monitor"
|
||||
)
|
||||
.catch((error) => {
|
||||
logger.error("Error sending invite email", {
|
||||
service: SERVICE_NAME,
|
||||
error: error.message,
|
||||
});
|
||||
});
|
||||
|
||||
return res
|
||||
.status(200)
|
||||
.json({ success: true, msg: "Invite sent", data: inviteToken });
|
||||
} catch (error) {
|
||||
next(handleError(error, SERVICE_NAME, "inviteController"));
|
||||
}
|
||||
};
|
||||
|
||||
const inviteVerifyController = async (req, res, next) => {
|
||||
try {
|
||||
await inviteVerificationBodyValidation.validateAsync(req.body);
|
||||
} catch (error) {
|
||||
error.status = 422;
|
||||
error.service = SERVICE_NAME;
|
||||
error.message =
|
||||
error.details?.[0]?.message || error.message || "Validation Error";
|
||||
next(error);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
await inviteVerificationBodyValidation.validateAsync(req.body);
|
||||
} catch (error) {
|
||||
next(handleValidationError(error, SERVICE_NAME));
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const invite = await req.db.getInviteToken(req.body.token);
|
||||
res
|
||||
.status(200)
|
||||
.json({status: "success", msg: "Invite verified", data: invite});
|
||||
} catch (error) {
|
||||
error.service === undefined ? (error.service = SERVICE_NAME) : null;
|
||||
error.method === undefined
|
||||
? (error.method = "inviteVerifyController")
|
||||
: null;
|
||||
next(error);
|
||||
}
|
||||
try {
|
||||
const invite = await req.db.getInviteToken(req.body.token);
|
||||
res
|
||||
.status(200)
|
||||
.json({ status: "success", msg: "Invite verified", data: invite });
|
||||
} catch (error) {
|
||||
next(handleError(error, SERVICE_NAME, "inviteVerifyController"));
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
inviteController: issueInvitation,
|
||||
inviteVerifyController,
|
||||
inviteController: issueInvitation,
|
||||
inviteVerifyController,
|
||||
};
|
||||
|
||||
@@ -10,17 +10,14 @@ const {
|
||||
const jwt = require("jsonwebtoken");
|
||||
const { getTokenFromHeaders } = require("../utils/utils");
|
||||
const { successMessages } = require("../utils/messages");
|
||||
const { handleValidationError, handleError } = require("./controllerUtils");
|
||||
const SERVICE_NAME = "maintenanceWindowController";
|
||||
|
||||
const createMaintenanceWindows = async (req, res, next) => {
|
||||
try {
|
||||
await createMaintenanceWindowBodyValidation.validateAsync(req.body);
|
||||
} catch (error) {
|
||||
error.status = 422;
|
||||
error.service = SERVICE_NAME;
|
||||
error.message =
|
||||
error.details?.[0]?.message || error.message || "Validation Error";
|
||||
next(error);
|
||||
next(handleValidationError(error, SERVICE_NAME));
|
||||
return;
|
||||
}
|
||||
try {
|
||||
@@ -45,11 +42,7 @@ const createMaintenanceWindows = async (req, res, next) => {
|
||||
msg: successMessages.MAINTENANCE_WINDOW_CREATE,
|
||||
});
|
||||
} catch (error) {
|
||||
error.service === undefined ? (error.service = SERVICE_NAME) : null;
|
||||
error.method === undefined
|
||||
? (error.method = "createMaintenanceWindow")
|
||||
: null;
|
||||
next(error);
|
||||
next(handleError(error, SERVICE_NAME, "createMaintenanceWindow"));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -57,11 +50,7 @@ const getMaintenanceWindowById = async (req, res, next) => {
|
||||
try {
|
||||
await getMaintenanceWindowByIdParamValidation.validateAsync(req.params);
|
||||
} catch (error) {
|
||||
error.status = 422;
|
||||
error.service = SERVICE_NAME;
|
||||
error.message =
|
||||
error.details?.[0]?.message || error.message || "Validation Error";
|
||||
next(error);
|
||||
next(handleValidationError(error, SERVICE_NAME));
|
||||
return;
|
||||
}
|
||||
try {
|
||||
@@ -74,11 +63,7 @@ const getMaintenanceWindowById = async (req, res, next) => {
|
||||
data: maintenanceWindow,
|
||||
});
|
||||
} catch (error) {
|
||||
error.service === undefined ? (error.service = SERVICE_NAME) : null;
|
||||
error.method === undefined
|
||||
? (error.method = "getMaintenanceWindowById")
|
||||
: null;
|
||||
next(error);
|
||||
next(handleError(error, SERVICE_NAME, "getMaintenanceWindowById"));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -86,11 +71,7 @@ const getMaintenanceWindowsByTeamId = async (req, res, next) => {
|
||||
try {
|
||||
await getMaintenanceWindowsByTeamIdQueryValidation.validateAsync(req.query);
|
||||
} catch (error) {
|
||||
error.status = 422;
|
||||
error.service = SERVICE_NAME;
|
||||
error.message =
|
||||
error.details?.[0]?.message || error.message || "Validation Error";
|
||||
next(error);
|
||||
next(handleValidationError(error, SERVICE_NAME));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -109,11 +90,7 @@ const getMaintenanceWindowsByTeamId = async (req, res, next) => {
|
||||
data: maintenanceWindows,
|
||||
});
|
||||
} catch (error) {
|
||||
error.service === undefined ? (error.service = SERVICE_NAME) : null;
|
||||
error.method === undefined
|
||||
? (error.method = "getMaintenanceWindowsByUserId")
|
||||
: null;
|
||||
next(error);
|
||||
next(handleError(error, SERVICE_NAME, "getMaintenanceWindowsByUserId"));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -123,11 +100,7 @@ const getMaintenanceWindowsByMonitorId = async (req, res, next) => {
|
||||
req.params
|
||||
);
|
||||
} catch (error) {
|
||||
error.status = 422;
|
||||
error.service = SERVICE_NAME;
|
||||
error.message =
|
||||
error.details?.[0]?.message || error.message || "Validation Error";
|
||||
next(error);
|
||||
next(handleValidationError(error, SERVICE_NAME));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -142,11 +115,7 @@ const getMaintenanceWindowsByMonitorId = async (req, res, next) => {
|
||||
data: maintenanceWindows,
|
||||
});
|
||||
} catch (error) {
|
||||
error.service === undefined ? (error.service = SERVICE_NAME) : null;
|
||||
error.method === undefined
|
||||
? (error.method = "getMaintenanceWindowsByMonitorId")
|
||||
: null;
|
||||
next(error);
|
||||
next(handleError(error, SERVICE_NAME, "getMaintenanceWindowsByMonitorId"));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -154,11 +123,7 @@ const deleteMaintenanceWindow = async (req, res, next) => {
|
||||
try {
|
||||
await deleteMaintenanceWindowByIdParamValidation.validateAsync(req.params);
|
||||
} catch (error) {
|
||||
error.status = 422;
|
||||
error.service = SERVICE_NAME;
|
||||
error.message =
|
||||
error.details?.[0]?.message || error.message || "Validation Error";
|
||||
next(error);
|
||||
next(handleValidationError(error, SERVICE_NAME));
|
||||
return;
|
||||
}
|
||||
try {
|
||||
@@ -168,10 +133,7 @@ const deleteMaintenanceWindow = async (req, res, next) => {
|
||||
msg: successMessages.MAINTENANCE_WINDOW_DELETE,
|
||||
});
|
||||
} catch (error) {
|
||||
error.service === undefined ? (error.service = SERVICE_NAME) : null;
|
||||
error.method === undefined
|
||||
? (error.method = "deleteMaintenanceWindow")
|
||||
: null;
|
||||
next(handleError(error, SERVICE_NAME, "deleteMaintenanceWindow"));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -180,11 +142,7 @@ const editMaintenanceWindow = async (req, res, next) => {
|
||||
await editMaintenanceWindowByIdParamValidation.validateAsync(req.params);
|
||||
await editMaintenanceByIdWindowBodyValidation.validateAsync(req.body);
|
||||
} catch (error) {
|
||||
error.status = 422;
|
||||
error.service = SERVICE_NAME;
|
||||
error.message =
|
||||
error.details?.[0]?.message || error.message || "Validation Error";
|
||||
next(error);
|
||||
next(handleValidationError(error, SERVICE_NAME));
|
||||
return;
|
||||
}
|
||||
try {
|
||||
@@ -198,10 +156,7 @@ const editMaintenanceWindow = async (req, res, next) => {
|
||||
data: editedMaintenanceWindow,
|
||||
});
|
||||
} catch (error) {
|
||||
error.service === undefined ? (error.service = SERVICE_NAME) : null;
|
||||
error.method === undefined
|
||||
? (error.method = "editMaintenanceWindow")
|
||||
: null;
|
||||
next(handleError(error, SERVICE_NAME, "editMaintenanceWindow"));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ const { errorMessages, successMessages } = require("../utils/messages");
|
||||
const jwt = require("jsonwebtoken");
|
||||
const { getTokenFromHeaders } = require("../utils/utils");
|
||||
const logger = require("../utils/logger");
|
||||
const { handleError, handleValidationError } = require("./controllerUtils");
|
||||
|
||||
/**
|
||||
* Returns all monitors
|
||||
@@ -38,9 +39,7 @@ const getAllMonitors = async (req, res, next) => {
|
||||
data: monitors,
|
||||
});
|
||||
} catch (error) {
|
||||
error.service === undefined ? (error.service = SERVICE_NAME) : null;
|
||||
error.method === undefined ? (error.method = "getAllMonitors") : null;
|
||||
next(error);
|
||||
next(handleError(error, SERVICE_NAME, "getAllMonitors"));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -58,10 +57,7 @@ const getMonitorStatsById = async (req, res, next) => {
|
||||
await getMonitorStatsByIdParamValidation.validateAsync(req.params);
|
||||
await getMonitorStatsByIdQueryValidation.validateAsync(req.query);
|
||||
} catch (error) {
|
||||
error.status = 422;
|
||||
error.message =
|
||||
error.details?.[0]?.message || error.message || "Validation Error";
|
||||
next(error);
|
||||
next(handleValidationError(error, SERVICE_NAME));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -73,9 +69,7 @@ const getMonitorStatsById = async (req, res, next) => {
|
||||
data: monitorStats,
|
||||
});
|
||||
} catch (error) {
|
||||
error.service === undefined ? (error.service = SERVICE_NAME) : null;
|
||||
error.method === undefined ? (error.method = "getMonitorStatsById") : null;
|
||||
next(error);
|
||||
next(handleError(error, SERVICE_NAME, "getMonitorStatsById"));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -83,10 +77,7 @@ const getMonitorCertificate = async (req, res, next) => {
|
||||
try {
|
||||
await getCertificateParamValidation.validateAsync(req.params);
|
||||
} catch (error) {
|
||||
error.status = 422;
|
||||
error.message =
|
||||
error.details?.[0]?.message || error.message || "Validation Error";
|
||||
next(error);
|
||||
next(handleValidationError(error, SERVICE_NAME));
|
||||
}
|
||||
|
||||
try {
|
||||
@@ -110,11 +101,7 @@ const getMonitorCertificate = async (req, res, next) => {
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
error.service === undefined ? (error.service = SERVICE_NAME) : null;
|
||||
error.method === undefined
|
||||
? (error.method = "getMonitorCertificate")
|
||||
: null;
|
||||
next(error);
|
||||
next(handleError(error, SERVICE_NAME, "getMonitorCertificate"));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -134,10 +121,7 @@ const getMonitorById = async (req, res, next) => {
|
||||
await getMonitorByIdParamValidation.validateAsync(req.params);
|
||||
await getMonitorByIdQueryValidation.validateAsync(req.query);
|
||||
} catch (error) {
|
||||
error.status = 422;
|
||||
error.message =
|
||||
error.details?.[0]?.message || error.message || "Validation Error";
|
||||
next(error);
|
||||
next(handleValidationError(error, SERVICE_NAME));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -154,9 +138,7 @@ const getMonitorById = async (req, res, next) => {
|
||||
data: monitor,
|
||||
});
|
||||
} catch (error) {
|
||||
error.service === undefined ? (error.service = SERVICE_NAME) : null;
|
||||
error.method === undefined ? (error.method = "getMonitorById") : null;
|
||||
next(error);
|
||||
next(handleError(error, SERVICE_NAME, "getMonitorById"));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -179,14 +161,8 @@ const getMonitorsAndSummaryByTeamId = async (req, res, next) => {
|
||||
req.params
|
||||
);
|
||||
await getMonitorsAndSummaryByTeamIdQueryValidation.validateAsync(req.query);
|
||||
//validation
|
||||
} catch (error) {
|
||||
error.status = 422;
|
||||
error.service = SERVICE_NAME;
|
||||
error.method === undefined? error.method = "getMonitorsAndSummaryByTeamId": null;
|
||||
error.message =
|
||||
error.details?.[0]?.message || error.message || "Validation Error";
|
||||
next(error);
|
||||
next(handleValidationError(error, SERVICE_NAME));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -203,11 +179,7 @@ const getMonitorsAndSummaryByTeamId = async (req, res, next) => {
|
||||
data: monitorsSummary,
|
||||
});
|
||||
} catch (error) {
|
||||
error.service === undefined ? (error.service = SERVICE_NAME) : null;
|
||||
error.method === undefined
|
||||
? (error.method = "getMonitorsAndSummaryByTeamId")
|
||||
: null;
|
||||
next(error);
|
||||
next(handleError(error, SERVICE_NAME, "getMonitorsAndSummaryByTeamId"));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -228,11 +200,7 @@ const getMonitorsByTeamId = async (req, res, next) => {
|
||||
await getMonitorsByTeamIdValidation.validateAsync(req.params);
|
||||
await getMonitorsByTeamIdQueryValidation.validateAsync(req.query);
|
||||
} catch (error) {
|
||||
error.status = 422;
|
||||
error.service = SERVICE_NAME;
|
||||
error.message =
|
||||
error.details?.[0]?.message || error.message || "Validation Error";
|
||||
next(error);
|
||||
next(handleValidationError(error, SERVICE_NAME));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -245,8 +213,7 @@ const getMonitorsByTeamId = async (req, res, next) => {
|
||||
data: monitors,
|
||||
});
|
||||
} catch (error) {
|
||||
error.service === undefined ? (error.service = SERVICE_NAME) : null;
|
||||
error.method === undefined ? (error.method = "getMonitorsByTeamId") : null;
|
||||
next(handleError(error, SERVICE_NAME, "getMonitorsByTeamId"));
|
||||
next(error);
|
||||
}
|
||||
};
|
||||
@@ -266,12 +233,7 @@ const createMonitor = async (req, res, next) => {
|
||||
try {
|
||||
await createMonitorBodyValidation.validateAsync(req.body);
|
||||
} catch (error) {
|
||||
error.status = 422;
|
||||
error.service = SERVICE_NAME;
|
||||
error.message =
|
||||
error.details?.[0]?.message || error.message || "Validation Error";
|
||||
|
||||
next(error);
|
||||
next(handleValidationError(error, SERVICE_NAME));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -281,10 +243,10 @@ const createMonitor = async (req, res, next) => {
|
||||
|
||||
if (notifications && notifications.length !== 0) {
|
||||
monitor.notifications = await Promise.all(
|
||||
notifications.map(async (notification) => {
|
||||
notification.monitorId = monitor._id;
|
||||
await req.db.createNotification(notification);
|
||||
})
|
||||
notifications.map(async (notification) => {
|
||||
notification.monitorId = monitor._id;
|
||||
await req.db.createNotification(notification);
|
||||
})
|
||||
);
|
||||
await monitor.save();
|
||||
}
|
||||
@@ -296,9 +258,7 @@ const createMonitor = async (req, res, next) => {
|
||||
data: monitor,
|
||||
});
|
||||
} catch (error) {
|
||||
error.service === undefined ? (error.service = SERVICE_NAME) : null;
|
||||
error.method === undefined ? (error.method = "createMonitor") : null;
|
||||
next(error);
|
||||
next(handleError(error, SERVICE_NAME, "createMonitor"));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -317,11 +277,7 @@ const deleteMonitor = async (req, res, next) => {
|
||||
try {
|
||||
await getMonitorByIdParamValidation.validateAsync(req.params);
|
||||
} catch (error) {
|
||||
error.status = 422;
|
||||
error.service = SERVICE_NAME;
|
||||
error.message =
|
||||
error.details?.[0]?.message || error.message || "Validation Error";
|
||||
next(error);
|
||||
next(handleValidationError(error, SERVICE_NAME));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -347,9 +303,7 @@ const deleteMonitor = async (req, res, next) => {
|
||||
.status(200)
|
||||
.json({ success: true, msg: successMessages.MONITOR_DELETE });
|
||||
} catch (error) {
|
||||
error.service === undefined ? (error.service = SERVICE_NAME) : null;
|
||||
error.method === undefined ? (error.method = "deleteMonitor") : null;
|
||||
next(error);
|
||||
next(handleError(error, SERVICE_NAME, "deleteMonitor"));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -382,9 +336,7 @@ const deleteAllMonitors = async (req, res, next) => {
|
||||
.status(200)
|
||||
.json({ success: true, msg: `Deleted ${deletedCount} monitors` });
|
||||
} catch (error) {
|
||||
error.service === undefined ? (error.service = SERVICE_NAME) : null;
|
||||
error.method === undefined ? (error.method = "deleteAllMonitors") : null;
|
||||
next(error);
|
||||
next(handleError(error, SERVICE_NAME, "deleteAllMonitors"));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -406,11 +358,7 @@ const editMonitor = async (req, res, next) => {
|
||||
await getMonitorByIdParamValidation.validateAsync(req.params);
|
||||
await editMonitorBodyValidation.validateAsync(req.body);
|
||||
} catch (error) {
|
||||
error.status = 422;
|
||||
error.service = SERVICE_NAME;
|
||||
error.message =
|
||||
error.details?.[0]?.message || error.message || "Validation Error";
|
||||
next(error);
|
||||
next(handleValidationError(error, SERVICE_NAME));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -444,9 +392,7 @@ const editMonitor = async (req, res, next) => {
|
||||
data: editedMonitor,
|
||||
});
|
||||
} catch (error) {
|
||||
error.service === undefined ? (error.service = SERVICE_NAME) : null;
|
||||
error.method === undefined ? (error.method = "editMonitor") : null;
|
||||
next(error);
|
||||
next(handleError(error, SERVICE_NAME, "editMonitor"));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -465,11 +411,7 @@ const pauseMonitor = async (req, res, next) => {
|
||||
try {
|
||||
await pauseMonitorParamValidation.validateAsync(req.params);
|
||||
} catch (error) {
|
||||
error.status = 422;
|
||||
error.service = SERVICE_NAME;
|
||||
error.message =
|
||||
error.details?.[0]?.message || error.message || "Validation Error";
|
||||
next(error);
|
||||
next(handleValidationError(error, SERVICE_NAME));
|
||||
}
|
||||
|
||||
try {
|
||||
@@ -490,9 +432,7 @@ const pauseMonitor = async (req, res, next) => {
|
||||
data: monitor,
|
||||
});
|
||||
} catch (error) {
|
||||
error.service === undefined ? (error.service = SERVICE_NAME) : null;
|
||||
error.method === undefined ? (error.method = "pauseMonitor") : null;
|
||||
next(error);
|
||||
next(handleError(error, SERVICE_NAME, "pauseMonitor"));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -511,21 +451,19 @@ const addDemoMonitors = async (req, res, next) => {
|
||||
try {
|
||||
const token = getTokenFromHeaders(req.headers);
|
||||
const { jwtSecret } = req.settingsService.getSettings();
|
||||
|
||||
const { _id, teamId } = jwt.verify(token, jwtSecret);
|
||||
const demoMonitors = await req.db.addDemoMonitors(_id, teamId);
|
||||
await demoMonitors.forEach(async (monitor) => {
|
||||
await req.jobQueue.addJob(monitor._id, monitor);
|
||||
});
|
||||
await Promise.all(
|
||||
demoMonitors.map((monitor) => req.jobQueue.addJob(monitor._id, monitor))
|
||||
);
|
||||
|
||||
return res.status(200).json({
|
||||
success: true,
|
||||
message: successMessages.MONITOR_DEMO_ADDED,
|
||||
data: demoMonitors.length,
|
||||
});
|
||||
} catch (error) {
|
||||
error.service === undefined ? (error.service = SERVICE_NAME) : null;
|
||||
error.method === undefined ? (error.method = "addDemoMonitors") : null;
|
||||
next(error);
|
||||
next(handleError(error, SERVICE_NAME, "addDemoMonitors"));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
const { handleError } = require("./controllerUtils");
|
||||
|
||||
const SERVICE_NAME = "JobQueueController";
|
||||
|
||||
const getMetrics = async (req, res, next) => {
|
||||
@@ -7,9 +9,7 @@ const getMetrics = async (req, res, next) => {
|
||||
.status(200)
|
||||
.json({ success: true, msg: "Metrics retrieved", data: metrics });
|
||||
} catch (error) {
|
||||
error.service === undefined ? (error.service = SERVICE_NAME) : null;
|
||||
error.method === undefined ? (error.method = "getMetrics") : null;
|
||||
next(error);
|
||||
next(handleError(error, SERVICE_NAME, "getMetrics"));
|
||||
return;
|
||||
}
|
||||
};
|
||||
@@ -19,9 +19,7 @@ const getJobs = async (req, res, next) => {
|
||||
const jobs = await req.jobQueue.getJobStats();
|
||||
return res.status(200).json({ jobs });
|
||||
} catch (error) {
|
||||
error.service === undefined ? (error.service = SERVICE_NAME) : null;
|
||||
error.method === undefined ? (error.method = "getJobs") : null;
|
||||
next(error);
|
||||
next(handleError(error, SERVICE_NAME, "getJobs"));
|
||||
return;
|
||||
}
|
||||
};
|
||||
@@ -31,21 +29,17 @@ const addJob = async (req, res, next) => {
|
||||
await req.jobQueue.addJob(Math.random().toString(36).substring(7));
|
||||
return res.send("Added job");
|
||||
} catch (error) {
|
||||
error.service === undefined ? (error.service = SERVICE_NAME) : null;
|
||||
error.method === undefined ? (error.method = "addJob") : null;
|
||||
next(error);
|
||||
next(handleError(error, SERVICE_NAME, "addJob"));
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
const obliterateQueue = async (req, res, next) => {
|
||||
try {
|
||||
const obliterated = await req.jobQueue.obliterate();
|
||||
await req.jobQueue.obliterate();
|
||||
return res.status(200).send("Obliterated queue");
|
||||
} catch (error) {
|
||||
error.service === undefined ? (error.service = SERVICE_NAME) : null;
|
||||
error.method === undefined ? (error.method = "obliterateQueue") : null;
|
||||
next(error);
|
||||
next(handleError(error, SERVICE_NAME, "obliterateQueue"));
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
const { successMessages } = require("../utils/messages");
|
||||
const SERVICE_NAME = "SettingsController";
|
||||
const { updateAppSettingsBodyValidation } = require("../validation/joi");
|
||||
const { handleValidationError, handleError } = require("./controllerUtils");
|
||||
|
||||
const getAppSettings = async (req, res, next) => {
|
||||
try {
|
||||
@@ -12,9 +13,7 @@ const getAppSettings = async (req, res, next) => {
|
||||
data: settings,
|
||||
});
|
||||
} catch (error) {
|
||||
error.service === undefined ? (error.service = SERVICE_NAME) : null;
|
||||
error.method === undefined ? (error.method = "getAppSettings") : null;
|
||||
next(error);
|
||||
next(handleError(error, SERVICE_NAME, "getAppSettings"));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -22,11 +21,7 @@ const updateAppSettings = async (req, res, next) => {
|
||||
try {
|
||||
await updateAppSettingsBodyValidation.validateAsync(req.body);
|
||||
} catch (error) {
|
||||
error.status = 422;
|
||||
error.service = SERVICE_NAME;
|
||||
error.message =
|
||||
error.details?.[0]?.message || error.message || "Validation Error";
|
||||
next(error);
|
||||
next(handleValidationError(error, SERVICE_NAME));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -40,9 +35,7 @@ const updateAppSettings = async (req, res, next) => {
|
||||
data: updatedSettings,
|
||||
});
|
||||
} catch (error) {
|
||||
error.service === undefined ? (error.service = SERVICE_NAME) : null;
|
||||
error.method === undefined ? (error.method = "updateAppSettings") : null;
|
||||
next(error);
|
||||
next(handleError(error, SERVICE_NAME, "updateAppSettings"));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
const jwt = require("jsonwebtoken");
|
||||
const logger = require("../utils/logger");
|
||||
const SERVICE_NAME = "verifyJWT";
|
||||
const TOKEN_PREFIX = "Bearer ";
|
||||
const { errorMessages } = require("../utils/messages");
|
||||
const { parse } = require("path");
|
||||
const User = require("../db/models/User");
|
||||
/**
|
||||
* Verifies the JWT token
|
||||
* @function
|
||||
@@ -38,16 +35,14 @@ const verifyJWT = (req, res, next) => {
|
||||
const { jwtSecret } = req.settingsService.getSettings();
|
||||
jwt.verify(parsedToken, jwtSecret, (err, decoded) => {
|
||||
if (err) {
|
||||
if (err.name === "TokenExpiredError") {
|
||||
res
|
||||
.status(401)
|
||||
.json({ success: false, msg: errorMessages.EXPIRED_AUTH_TOKEN });
|
||||
}
|
||||
return res
|
||||
.status(401)
|
||||
.json({ success: false, msg: errorMessages.INVALID_AUTH_TOKEN });
|
||||
const errorMessage =
|
||||
err.name === "TokenExpiredError"
|
||||
? errorMessages.EXPIRED_AUTH_TOKEN
|
||||
: errorMessages.INVALID_AUTH_TOKEN;
|
||||
return res.status(401).json({ success: false, msg: errorMessage });
|
||||
}
|
||||
//Add the user to the request object for use in the route
|
||||
|
||||
// Add the user to the request object for use in the route
|
||||
req.user = decoded;
|
||||
next();
|
||||
});
|
||||
|
||||
2225
Server/package-lock.json
generated
2225
Server/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -4,7 +4,7 @@
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1",
|
||||
"test": "nyc mocha",
|
||||
"dev": "nodemon index.js"
|
||||
},
|
||||
"keywords": [],
|
||||
@@ -15,6 +15,7 @@
|
||||
"axios": "^1.7.2",
|
||||
"bcrypt": "^5.1.1",
|
||||
"bullmq": "5.7.15",
|
||||
"chai": "5.1.1",
|
||||
"cors": "^2.8.5",
|
||||
"dotenv": "^16.4.5",
|
||||
"express": "^4.19.2",
|
||||
@@ -24,6 +25,7 @@
|
||||
"jsonwebtoken": "9.0.2",
|
||||
"mailersend": "^2.2.0",
|
||||
"mjml": "^5.0.0-alpha.4",
|
||||
"mocha": "10.7.3",
|
||||
"mongoose": "^8.3.3",
|
||||
"multer": "1.4.5-lts.1",
|
||||
"nodemailer": "^6.9.14",
|
||||
@@ -34,6 +36,8 @@
|
||||
"winston": "^3.13.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"nodemon": "3.1.0"
|
||||
"nodemon": "3.1.0",
|
||||
"nyc": "17.1.0",
|
||||
"sinon": "19.0.2"
|
||||
}
|
||||
}
|
||||
|
||||
609
Server/tests/controllers/authController.test.js
Normal file
609
Server/tests/controllers/authController.test.js
Normal file
@@ -0,0 +1,609 @@
|
||||
const {
|
||||
registerUser,
|
||||
loginUser,
|
||||
editUser,
|
||||
checkSuperadminExists,
|
||||
requestRecovery,
|
||||
validateRecovery,
|
||||
resetPassword,
|
||||
deleteUser,
|
||||
getAllUsers,
|
||||
} = require("../../controllers/authController");
|
||||
const jwt = require("jsonwebtoken");
|
||||
const { errorMessages, successMessages } = require("../../utils/messages");
|
||||
const sinon = require("sinon");
|
||||
|
||||
describe("Auth Controller - registerUser", () => {
|
||||
// Set up test
|
||||
beforeEach(() => {
|
||||
req = {
|
||||
db: {
|
||||
checkSuperadmin: sinon.stub(),
|
||||
getInviteTokenAndDelete: sinon.stub(),
|
||||
updateAppSettings: sinon.stub(),
|
||||
insertUser: sinon.stub(),
|
||||
},
|
||||
settingsService: {
|
||||
getSettings: sinon.stub().resolves({
|
||||
jwtSecret: "my_secret",
|
||||
}),
|
||||
},
|
||||
emailService: {
|
||||
buildAndSendEmail: sinon.stub().returns(Promise.resolve()),
|
||||
},
|
||||
file: {},
|
||||
};
|
||||
res = {
|
||||
status: sinon.stub().returnsThis(),
|
||||
json: sinon.stub(),
|
||||
};
|
||||
next = sinon.stub();
|
||||
});
|
||||
|
||||
it("should register a valid user", async () => {
|
||||
req.body = {
|
||||
firstName: "John",
|
||||
lastName: "Doe",
|
||||
email: "john.doe@example.com",
|
||||
password: "Uptime1!",
|
||||
inviteToken: "someToken",
|
||||
role: ["user"],
|
||||
teamId: "123",
|
||||
};
|
||||
req.db.checkSuperadmin.resolves(false);
|
||||
req.db.insertUser.resolves({
|
||||
_id: "123",
|
||||
_doc: {
|
||||
firstName: "John",
|
||||
lastName: "Doe",
|
||||
email: "john.doe@example.com",
|
||||
},
|
||||
});
|
||||
|
||||
await registerUser(req, res, next);
|
||||
expect(res.status.calledWith(200)).to.be.true;
|
||||
expect(
|
||||
res.json.calledWith(
|
||||
sinon.match({
|
||||
success: true,
|
||||
msg: sinon.match.string,
|
||||
data: {
|
||||
user: sinon.match.object,
|
||||
token: sinon.match.string,
|
||||
},
|
||||
})
|
||||
)
|
||||
).to.be.true;
|
||||
expect(next.notCalled).to.be.true;
|
||||
});
|
||||
it("should reject a user with an invalid password", async () => {
|
||||
req.body = {
|
||||
firstName: "John",
|
||||
lastName: "Doe",
|
||||
email: "john.doe@example.com",
|
||||
password: "bad_password",
|
||||
inviteToken: "someToken",
|
||||
role: ["user"],
|
||||
teamId: "123",
|
||||
};
|
||||
await registerUser(req, res, next);
|
||||
expect(next.firstCall.args[0]).to.be.an("error");
|
||||
expect(next.firstCall.args[0].status).to.equal(422);
|
||||
});
|
||||
it("should reject a user with an invalid role", async () => {
|
||||
req.body = {
|
||||
firstName: "John",
|
||||
lastName: "Doe",
|
||||
email: "john.doe@example.com",
|
||||
password: "Uptime1!",
|
||||
inviteToken: "someToken",
|
||||
role: ["superman"],
|
||||
teamId: "123",
|
||||
};
|
||||
await registerUser(req, res, next);
|
||||
expect(next.firstCall.args[0]).to.be.an("error");
|
||||
expect(next.firstCall.args[0].status).to.equal(422);
|
||||
});
|
||||
});
|
||||
|
||||
describe("Auth Controller - loginUser", () => {
|
||||
beforeEach(() => {
|
||||
req = {
|
||||
body: { email: "test@example.com", password: "Password123!" },
|
||||
db: {
|
||||
getUserByEmail: sinon.stub(),
|
||||
},
|
||||
settingsService: {
|
||||
getSettings: sinon.stub().resolves({
|
||||
jwtSecret: "my_secret",
|
||||
}),
|
||||
},
|
||||
};
|
||||
res = {
|
||||
status: sinon.stub().returnsThis(),
|
||||
json: sinon.stub(),
|
||||
};
|
||||
next = sinon.stub();
|
||||
user = {
|
||||
_doc: {
|
||||
email: "test@example.com",
|
||||
},
|
||||
comparePassword: sinon.stub(),
|
||||
};
|
||||
});
|
||||
it("should login user successfully", async () => {
|
||||
req.db.getUserByEmail.resolves(user);
|
||||
user.comparePassword.resolves(true);
|
||||
await loginUser(req, res, next);
|
||||
expect(res.status.calledWith(200)).to.be.true;
|
||||
expect(
|
||||
res.json.calledWith({
|
||||
success: true,
|
||||
msg: successMessages.AUTH_LOGIN_USER,
|
||||
data: {
|
||||
user: {
|
||||
email: "test@example.com",
|
||||
avatarImage: undefined,
|
||||
},
|
||||
token: sinon.match.string,
|
||||
},
|
||||
})
|
||||
).to.be.true;
|
||||
expect(next.notCalled).to.be.true;
|
||||
});
|
||||
it("should reject a user with an incorrect password", async () => {
|
||||
req.body = {
|
||||
email: "test@test.com",
|
||||
password: "Password123!",
|
||||
};
|
||||
req.db.getUserByEmail.resolves(user);
|
||||
user.comparePassword.resolves(false);
|
||||
await loginUser(req, res, next);
|
||||
expect(next.firstCall.args[0]).to.be.an("error");
|
||||
expect(next.firstCall.args[0].message).to.equal(
|
||||
errorMessages.AUTH_INCORRECT_PASSWORD
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("Auth Controller - editUser", async () => {
|
||||
beforeEach(() => {
|
||||
req = {
|
||||
params: { userId: "123" },
|
||||
body: { password: "Password1!", newPassword: "Password2!" },
|
||||
headers: { authorization: "Bearer token" },
|
||||
user: { _id: "123" },
|
||||
settingsService: {
|
||||
getSettings: sinon.stub().returns({ jwtSecret: "my_secret" }),
|
||||
},
|
||||
db: {
|
||||
getUserByEmail: sinon.stub(),
|
||||
updateUser: sinon.stub(),
|
||||
},
|
||||
};
|
||||
res = {
|
||||
status: sinon.stub().returnsThis(),
|
||||
json: sinon.stub(),
|
||||
};
|
||||
next = sinon.stub();
|
||||
});
|
||||
|
||||
it("should edit a user if it receives a proper request", async () => {
|
||||
sinon.stub(jwt, "verify").returns({ email: "test@example.com" });
|
||||
const user = {
|
||||
comparePassword: sinon.stub().resolves(true),
|
||||
};
|
||||
req.db.getUserByEmail.resolves(user);
|
||||
req.db.updateUser.resolves({ email: "test@example.com" });
|
||||
|
||||
await editUser(req, res, next);
|
||||
|
||||
expect(req.db.getUserByEmail.calledOnce).to.be.true;
|
||||
expect(req.db.updateUser.calledOnce).to.be.true;
|
||||
expect(res.status.calledWith(200)).to.be.true;
|
||||
expect(
|
||||
res.json.calledWith({
|
||||
success: true,
|
||||
msg: successMessages.AUTH_UPDATE_USER,
|
||||
data: { email: "test@example.com" },
|
||||
})
|
||||
).to.be.true;
|
||||
expect(next.notCalled).to.be.true;
|
||||
});
|
||||
|
||||
it("should reject an edit request if password format is incorrect", async () => {
|
||||
req.body = { password: "bad_password", newPassword: "bad_password" };
|
||||
const user = {
|
||||
comparePassword: sinon.stub().resolves(true),
|
||||
};
|
||||
req.db.getUserByEmail.resolves(user);
|
||||
|
||||
await editUser(req, res, next);
|
||||
|
||||
expect(next.firstCall.args[0]).to.be.an("error");
|
||||
expect(next.firstCall.args[0].status).to.equal(422);
|
||||
});
|
||||
});
|
||||
|
||||
describe("Auth Controller - checkSuperadminExists", async () => {
|
||||
beforeEach(() => {
|
||||
req = {
|
||||
db: {
|
||||
checkSuperadmin: sinon.stub(),
|
||||
},
|
||||
};
|
||||
res = {
|
||||
status: sinon.stub().returnsThis(),
|
||||
json: sinon.stub(),
|
||||
};
|
||||
next = sinon.stub();
|
||||
});
|
||||
|
||||
it("should return true if a superadmin exists", async () => {
|
||||
req.db.checkSuperadmin.resolves(true);
|
||||
await checkSuperadminExists(req, res, next);
|
||||
expect(res.status.calledWith(200)).to.be.true;
|
||||
expect(
|
||||
res.json.calledWith({
|
||||
success: true,
|
||||
msg: successMessages.AUTH_SUPERADMIN_EXISTS,
|
||||
data: true,
|
||||
})
|
||||
).to.be.true;
|
||||
expect(next.notCalled).to.be.true;
|
||||
});
|
||||
|
||||
it("should return false if a superadmin does not exist", async () => {
|
||||
req.db.checkSuperadmin.resolves(false);
|
||||
await checkSuperadminExists(req, res, next);
|
||||
expect(res.status.calledWith(200)).to.be.true;
|
||||
expect(
|
||||
res.json.calledWith({
|
||||
success: true,
|
||||
msg: successMessages.AUTH_SUPERADMIN_EXISTS,
|
||||
data: false,
|
||||
})
|
||||
).to.be.true;
|
||||
expect(next.notCalled).to.be.true;
|
||||
});
|
||||
});
|
||||
|
||||
describe("Auth Controller - requestRecovery", async () => {
|
||||
beforeEach(() => {
|
||||
req = {
|
||||
body: { email: "test@test.com" },
|
||||
db: {
|
||||
getUserByEmail: sinon.stub(),
|
||||
requestRecoveryToken: sinon.stub(),
|
||||
},
|
||||
settingsService: {
|
||||
getSettings: sinon.stub().returns({ clientHost: "http://localhost" }),
|
||||
},
|
||||
emailService: {
|
||||
buildAndSendEmail: sinon.stub(),
|
||||
},
|
||||
};
|
||||
res = {
|
||||
status: sinon.stub().returnsThis(),
|
||||
json: sinon.stub(),
|
||||
};
|
||||
next = sinon.stub();
|
||||
});
|
||||
it("should throw an error if the email is not provided", async () => {
|
||||
req.body = {};
|
||||
await requestRecovery(req, res, next);
|
||||
expect(next.firstCall.args[0]).to.be.an("error");
|
||||
expect(next.firstCall.args[0].status).to.equal(422);
|
||||
});
|
||||
it("should return a success message if the email is provided", async () => {
|
||||
const user = { firstName: "John" };
|
||||
const recoveryToken = { token: "recovery-token" };
|
||||
const msgId = "message-id";
|
||||
req.db.getUserByEmail.resolves(user);
|
||||
req.db.requestRecoveryToken.resolves(recoveryToken);
|
||||
req.emailService.buildAndSendEmail.resolves(msgId);
|
||||
await requestRecovery(req, res, next);
|
||||
expect(req.db.getUserByEmail.calledOnceWith("test@test.com")).to.be.true;
|
||||
expect(req.db.requestRecoveryToken.calledOnceWith(req, res)).to.be.true;
|
||||
expect(
|
||||
req.emailService.buildAndSendEmail.calledOnceWith(
|
||||
"passwordResetTemplate",
|
||||
{
|
||||
name: "John",
|
||||
email: "test@test.com",
|
||||
url: "http://localhost/set-new-password/recovery-token",
|
||||
},
|
||||
"test@test.com",
|
||||
"Bluewave Uptime Password Reset"
|
||||
)
|
||||
).to.be.true;
|
||||
expect(res.status.calledOnceWith(200)).to.be.true;
|
||||
expect(
|
||||
res.json.calledOnceWith({
|
||||
success: true,
|
||||
msg: successMessages.AUTH_CREATE_RECOVERY_TOKEN,
|
||||
data: msgId,
|
||||
})
|
||||
).to.be.true;
|
||||
expect(next.notCalled).to.be.true;
|
||||
});
|
||||
});
|
||||
|
||||
describe("Auth Controller - validateRecovery", async () => {
|
||||
beforeEach(() => {
|
||||
req = {
|
||||
body: { recoveryToken: "recovery-token" },
|
||||
db: {
|
||||
validateRecoveryToken: sinon.stub(),
|
||||
},
|
||||
};
|
||||
res = {
|
||||
status: sinon.stub().returnsThis(),
|
||||
json: sinon.stub(),
|
||||
};
|
||||
next = sinon.stub();
|
||||
});
|
||||
|
||||
it("should call next with a validation error if the token is invalid", async () => {
|
||||
req = {
|
||||
body: {},
|
||||
};
|
||||
await validateRecovery(req, res, next);
|
||||
expect(next.firstCall.args[0]).to.be.an("error");
|
||||
expect(next.firstCall.args[0].status).to.equal(422);
|
||||
});
|
||||
|
||||
it("should return a success message if the token is valid", async () => {
|
||||
req.db.validateRecoveryToken.resolves();
|
||||
await validateRecovery(req, res, next);
|
||||
expect(res.status.calledOnceWith(200)).to.be.true;
|
||||
expect(
|
||||
res.json.calledOnceWith({
|
||||
success: true,
|
||||
msg: successMessages.AUTH_VERIFY_RECOVERY_TOKEN,
|
||||
})
|
||||
).to.be.true;
|
||||
expect(next.notCalled).to.be.true;
|
||||
});
|
||||
});
|
||||
|
||||
describe("Auth Controller - resetPassword", async () => {
|
||||
beforeEach(() => {
|
||||
req = {
|
||||
body: {
|
||||
recoveryToken: "recovery-token",
|
||||
password: "Password1!",
|
||||
},
|
||||
db: {
|
||||
resetPassword: sinon.stub(),
|
||||
},
|
||||
settingsService: {
|
||||
getSettings: sinon.stub(),
|
||||
},
|
||||
};
|
||||
res = {
|
||||
status: sinon.stub().returnsThis(),
|
||||
json: sinon.stub(),
|
||||
};
|
||||
next = sinon.stub();
|
||||
newPasswordValidation = {
|
||||
validateAsync: sinon.stub(),
|
||||
};
|
||||
handleValidationError = sinon.stub();
|
||||
handleError = sinon.stub();
|
||||
issueToken = sinon.stub();
|
||||
});
|
||||
it("should call next with a validation error if the password is invalid", async () => {
|
||||
req.body = { password: "bad_password" };
|
||||
await resetPassword(req, res, next);
|
||||
expect(next.firstCall.args[0]).to.be.an("error");
|
||||
expect(next.firstCall.args[0].status).to.equal(422);
|
||||
});
|
||||
it("should reset password successfully", async () => {
|
||||
const user = { _doc: {} };
|
||||
const appSettings = { jwtSecret: "my_secret" };
|
||||
const token = "token";
|
||||
|
||||
newPasswordValidation.validateAsync.resolves();
|
||||
req.db.resetPassword.resolves(user);
|
||||
req.settingsService.getSettings.resolves(appSettings);
|
||||
issueToken.returns(token);
|
||||
|
||||
await resetPassword(req, res, next);
|
||||
|
||||
expect(req.db.resetPassword.calledOnceWith(req, res)).to.be.true;
|
||||
expect(req.settingsService.getSettings.calledOnce).to.be.true;
|
||||
expect(res.status.calledOnceWith(200)).to.be.true;
|
||||
expect(
|
||||
res.json.calledOnceWith({
|
||||
success: true,
|
||||
msg: successMessages.AUTH_RESET_PASSWORD,
|
||||
data: { user: sinon.match.object, token: sinon.match.string },
|
||||
})
|
||||
).to.be.true;
|
||||
expect(next.notCalled).to.be.true;
|
||||
});
|
||||
});
|
||||
|
||||
describe("Auth Controller - deleteUser", async () => {
|
||||
beforeEach(() => {
|
||||
req = {
|
||||
headers: {
|
||||
authorization: "Bearer token",
|
||||
},
|
||||
db: {
|
||||
getUserByEmail: sinon.stub(),
|
||||
getMonitorsByTeamId: sinon.stub(),
|
||||
deleteJob: sinon.stub(),
|
||||
deleteChecks: sinon.stub(),
|
||||
deletePageSpeedChecksByMonitorId: sinon.stub(),
|
||||
deleteNotificationsByMonitorId: sinon.stub(),
|
||||
deleteTeam: sinon.stub(),
|
||||
deleteAllOtherUsers: sinon.stub(),
|
||||
deleteMonitorsByUserId: sinon.stub(),
|
||||
deleteUser: sinon.stub(),
|
||||
},
|
||||
jobQueue: {
|
||||
deleteJob: sinon.stub(),
|
||||
},
|
||||
};
|
||||
res = {
|
||||
status: sinon.stub().returnsThis(),
|
||||
json: sinon.stub(),
|
||||
};
|
||||
next = sinon.stub();
|
||||
|
||||
sinon.stub(jwt, "decode");
|
||||
|
||||
handleError = sinon.stub();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
sinon.restore();
|
||||
});
|
||||
it("should return 404 if user is not found", async () => {
|
||||
jwt.decode.returns({ email: "test@example.com" });
|
||||
req.db.getUserByEmail.resolves(null);
|
||||
|
||||
await deleteUser(req, res, next);
|
||||
|
||||
expect(req.db.getUserByEmail.calledOnceWith("test@example.com")).to.be.true;
|
||||
expect(next.calledOnce).to.be.true;
|
||||
expect(next.firstCall.args[0].message).to.equal(
|
||||
errorMessages.DB_USER_NOT_FOUND
|
||||
);
|
||||
expect(res.status.notCalled).to.be.true;
|
||||
expect(res.json.notCalled).to.be.true;
|
||||
});
|
||||
|
||||
it("should delete user and associated data if user is superadmin", async () => {
|
||||
const user = {
|
||||
_id: "user_id",
|
||||
email: "test@example.com",
|
||||
role: ["superadmin"],
|
||||
teamId: "team_id",
|
||||
};
|
||||
const monitors = [{ _id: "monitor_id" }];
|
||||
|
||||
jwt.decode.returns({ email: "test@example.com" });
|
||||
req.db.getUserByEmail.resolves(user);
|
||||
req.db.getMonitorsByTeamId.resolves({ monitors });
|
||||
|
||||
await deleteUser(req, res, next);
|
||||
|
||||
expect(req.db.getUserByEmail.calledOnceWith("test@example.com")).to.be.true;
|
||||
expect(
|
||||
req.db.getMonitorsByTeamId.calledOnceWith({
|
||||
params: { teamId: "team_id" },
|
||||
})
|
||||
).to.be.true;
|
||||
expect(req.jobQueue.deleteJob.calledOnceWith(monitors[0])).to.be.true;
|
||||
expect(req.db.deleteChecks.calledOnceWith("monitor_id")).to.be.true;
|
||||
expect(req.db.deletePageSpeedChecksByMonitorId.calledOnceWith("monitor_id"))
|
||||
.to.be.true;
|
||||
expect(req.db.deleteNotificationsByMonitorId.calledOnceWith("monitor_id"))
|
||||
.to.be.true;
|
||||
expect(req.db.deleteTeam.calledOnceWith("team_id")).to.be.true;
|
||||
expect(req.db.deleteAllOtherUsers.calledOnce).to.be.true;
|
||||
expect(req.db.deleteMonitorsByUserId.calledOnceWith("user_id")).to.be.true;
|
||||
expect(req.db.deleteUser.calledOnceWith("user_id")).to.be.true;
|
||||
expect(res.status.calledOnceWith(200)).to.be.true;
|
||||
expect(
|
||||
res.json.calledOnceWith({
|
||||
success: true,
|
||||
msg: successMessages.AUTH_DELETE_USER,
|
||||
})
|
||||
).to.be.true;
|
||||
expect(next.notCalled).to.be.true;
|
||||
});
|
||||
|
||||
it("should delete user if user is not superadmin", async () => {
|
||||
const user = {
|
||||
_id: "user_id",
|
||||
email: "test@example.com",
|
||||
role: ["user"],
|
||||
teamId: "team_id",
|
||||
};
|
||||
|
||||
jwt.decode.returns({ email: "test@example.com" });
|
||||
req.db.getUserByEmail.resolves(user);
|
||||
|
||||
await deleteUser(req, res, next);
|
||||
|
||||
expect(req.db.getUserByEmail.calledOnceWith("test@example.com")).to.be.true;
|
||||
expect(
|
||||
req.db.getMonitorsByTeamId.calledOnceWith({
|
||||
params: { teamId: "team_id" },
|
||||
})
|
||||
).to.be.true;
|
||||
expect(req.db.deleteUser.calledOnceWith("user_id")).to.be.true;
|
||||
expect(res.status.calledOnceWith(200)).to.be.true;
|
||||
expect(
|
||||
res.json.calledOnceWith({
|
||||
success: true,
|
||||
msg: successMessages.AUTH_DELETE_USER,
|
||||
})
|
||||
).to.be.true;
|
||||
expect(next.notCalled).to.be.true;
|
||||
});
|
||||
|
||||
it("should handle errors", async () => {
|
||||
const error = new Error("Something went wrong");
|
||||
const SERVICE_NAME = "AuthController";
|
||||
jwt.decode.returns({ email: "test@example.com" });
|
||||
req.db.getUserByEmail.rejects(error);
|
||||
await deleteUser(req, res, next);
|
||||
expect(next.calledOnce).to.be.true;
|
||||
expect(next.firstCall.args[0].message).to.equal("Something went wrong");
|
||||
expect(res.status.notCalled).to.be.true;
|
||||
expect(res.json.notCalled).to.be.true;
|
||||
});
|
||||
});
|
||||
|
||||
describe("Auth Controller - getAllUsers", async () => {
|
||||
beforeEach(() => {
|
||||
req = {
|
||||
db: {
|
||||
getAllUsers: sinon.stub(),
|
||||
},
|
||||
};
|
||||
res = {
|
||||
status: sinon.stub().returnsThis(),
|
||||
json: sinon.stub(),
|
||||
};
|
||||
next = sinon.stub();
|
||||
handleError = sinon.stub();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
sinon.restore(); // Restore the original methods after each test
|
||||
});
|
||||
|
||||
it("should return 200 and all users", async () => {
|
||||
const allUsers = [{ id: 1, name: "John Doe" }];
|
||||
req.db.getAllUsers.resolves(allUsers);
|
||||
|
||||
await getAllUsers(req, res, next);
|
||||
|
||||
expect(req.db.getAllUsers.calledOnce).to.be.true;
|
||||
expect(res.status.calledOnceWith(200)).to.be.true;
|
||||
expect(
|
||||
res.json.calledOnceWith({
|
||||
success: true,
|
||||
msg: "Got all users",
|
||||
data: allUsers,
|
||||
})
|
||||
).to.be.true;
|
||||
expect(next.notCalled).to.be.true;
|
||||
});
|
||||
|
||||
it("should call next with error when an exception occurs", async () => {
|
||||
const error = new Error("Something went wrong");
|
||||
req.db.getAllUsers.rejects(error);
|
||||
await getAllUsers(req, res, next);
|
||||
expect(req.db.getAllUsers.calledOnce).to.be.true;
|
||||
expect(next.calledOnce).to.be.true;
|
||||
expect(res.status.notCalled).to.be.true;
|
||||
expect(res.json.notCalled).to.be.true;
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user