2025-04-20 11:29:53 -07:00
import {
registrationBodyValidation ,
loginValidation ,
editUserBodyValidation ,
recoveryValidation ,
recoveryTokenValidation ,
newPasswordValidation ,
2025-07-22 13:26:45 -07:00
getUserByIdParamValidation ,
editUserByIdParamValidation ,
editUserByIdBodyValidation ,
editSuperadminUserByIdBodyValidation ,
2025-04-20 11:29:53 -07:00
} from "../validation/joi.js" ;
import jwt from "jsonwebtoken" ;
import { getTokenFromHeaders } from "../utils/utils.js" ;
2025-07-23 11:46:59 -07:00
import { asyncHandler , createError } from "../utils/errorUtils.js" ;
2025-07-15 16:33:35 -07:00
2025-04-20 11:29:53 -07:00
const SERVICE_NAME = "authController" ;
class AuthController {
2025-07-23 11:46:59 -07:00
constructor ({ db , settingsService , emailService , jobQueue , stringService , logger , userService }) {
2025-04-20 11:29:53 -07:00
this . db = db ;
this . settingsService = settingsService ;
this . emailService = emailService ;
this . jobQueue = jobQueue ;
this . stringService = stringService ;
2025-06-19 09:44:24 +08:00
this . logger = logger ;
2025-07-23 11:46:59 -07:00
this . userService = userService ;
2025-04-20 11:29:53 -07:00
}
2025-07-15 16:33:35 -07:00
registerUser = asyncHandler (
async ( req , res , next ) => {
2025-04-28 10:34:32 -07:00
if ( req . body ? . email ) {
2025-04-20 11:29:53 -07:00
req . body . email = req . body . email ? . toLowerCase ();
2025-04-28 10:34:32 -07:00
}
2025-04-20 11:29:53 -07:00
await registrationBodyValidation . validateAsync ( req . body );
2025-07-23 11:46:59 -07:00
const { user , token } = await this . userService . registerUser ( req . body , req . file );
2025-04-20 11:29:53 -07:00
res . success ({
msg : this . stringService . authCreateUser ,
2025-07-23 11:46:59 -07:00
data : { user , token },
2025-04-20 11:29:53 -07:00
});
2025-07-15 16:33:35 -07:00
},
SERVICE_NAME ,
"registerUser"
);
2025-04-20 11:29:53 -07:00
2025-07-15 16:33:35 -07:00
loginUser = asyncHandler (
async ( req , res , next ) => {
2025-04-28 10:34:32 -07:00
if ( req . body ? . email ) {
req . body . email = req . body . email ? . toLowerCase ();
2025-04-20 11:29:53 -07:00
}
await loginValidation . validateAsync ( req . body );
2025-07-23 11:46:59 -07:00
const { user , token } = await this . userService . loginUser ( req . body . email , req . body . password );
2025-04-20 11:29:53 -07:00
return res . success ({
msg : this . stringService . authLoginUser ,
data : {
2025-07-23 11:46:59 -07:00
user ,
token ,
2025-04-20 11:29:53 -07:00
},
});
2025-07-15 16:33:35 -07:00
},
SERVICE_NAME ,
"loginUser"
);
2025-04-20 11:29:53 -07:00
2025-07-15 16:33:35 -07:00
editUser = asyncHandler (
async ( req , res , next ) => {
2025-04-20 11:29:53 -07:00
await editUserBodyValidation . validateAsync ( req . body );
2025-07-15 16:33:35 -07:00
2025-07-23 11:46:59 -07:00
const updatedUser = await this . userService . editUser ( req . body , req . file , req . user );
2025-04-20 11:29:53 -07:00
res . success ({
msg : this . stringService . authUpdateUser ,
data : updatedUser ,
});
2025-07-15 16:33:35 -07:00
},
SERVICE_NAME ,
"editUser"
);
2025-04-20 11:29:53 -07:00
2025-07-15 16:33:35 -07:00
checkSuperadminExists = asyncHandler (
async ( req , res , next ) => {
2025-07-23 11:48:47 -07:00
const superAdminExists = await this . userService . checkSuperadminExists ();
2025-04-20 11:29:53 -07:00
return res . success ({
msg : this . stringService . authAdminExists ,
data : superAdminExists ,
});
2025-07-15 16:33:35 -07:00
},
SERVICE_NAME ,
"checkSuperadminExists"
);
2025-07-23 11:48:47 -07:00
2025-07-15 16:33:35 -07:00
requestRecovery = asyncHandler (
async ( req , res , next ) => {
2025-04-20 11:29:53 -07:00
await recoveryValidation . validateAsync ( req . body );
const { email } = req . body ;
const user = await this . db . getUserByEmail ( email );
const recoveryToken = await this . db . requestRecoveryToken ( req , res );
const name = user . firstName ;
const { clientHost } = this . settingsService . getSettings ();
const url = ` ${ clientHost } /set-new-password/ ${ recoveryToken . token } ` ;
2025-06-18 10:44:59 +08:00
const html = await this . emailService . buildEmail ( "passwordResetTemplate" , {
name ,
email ,
url ,
});
2025-07-21 10:34:16 -07:00
const msgId = await this . emailService . sendEmail ( email , "Checkmate Password Reset" , html );
2025-04-20 11:29:53 -07:00
return res . success ({
msg : this . stringService . authCreateRecoveryToken ,
data : msgId ,
});
2025-07-15 16:33:35 -07:00
},
SERVICE_NAME ,
"requestRecovery"
);
2025-04-20 11:29:53 -07:00
/**
* Validates a recovery token. The recovery token is validated and if valid, a success message is returned.
* @async
* @param {Object} req - The Express request object.
* @property {Object} req.body - The body of the request.
* @property {string} req.body.token - The recovery token to be validated.
* @param {Object} res - The Express response object.
* @param {function} next - The next middleware function.
* @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).
*/
2025-07-15 16:33:35 -07:00
validateRecovery = asyncHandler (
async ( req , res , next ) => {
2025-04-20 11:29:53 -07:00
await recoveryTokenValidation . validateAsync ( req . body );
await this . db . validateRecoveryToken ( req , res );
return res . success ({
msg : this . stringService . authVerifyRecoveryToken ,
});
2025-07-15 16:33:35 -07:00
},
SERVICE_NAME ,
"validateRecovery"
);
2025-04-20 11:29:53 -07:00
/**
* 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.
* @async
* @param {Object} req - The Express request object.
* @property {Object} req.body - The body of the request.
* @property {string} req.body.token - The recovery token.
* @property {string} req.body.password - The new password of the user.
* @param {Object} res - The Express response object.
* @param {function} next - The next middleware function.
* @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).
*/
2025-07-15 16:33:35 -07:00
resetPassword = asyncHandler (
async ( req , res , next ) => {
2025-04-20 11:29:53 -07:00
await newPasswordValidation . validateAsync ( req . body );
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 },
});
2025-07-15 16:33:35 -07:00
},
SERVICE_NAME ,
"resetPassword"
);
2025-04-20 11:29:53 -07:00
/**
* Deletes a user and all associated monitors, checks, and alerts.
*
* @param {Object} req - The request object.
* @param {Object} res - The response object.
* @param {Function} next - The next middleware function.
* @returns {Object} The response object with success status and message.
* @throws {Error} If user validation fails or user is not found in the database.
*/
2025-07-15 16:33:35 -07:00
deleteUser = asyncHandler (
async ( req , res , next ) => {
2025-07-21 11:05:48 -07:00
const email = req ? . user ? . email ;
if ( ! email ) {
throw new Error ( "No email in request" );
}
2025-04-20 11:29:53 -07:00
2025-07-21 11:05:48 -07:00
const teamId = req ? . user ? . teamId ;
const userId = req ? . user ? . _id ;
if ( ! teamId ) {
throw new Error ( "No team ID in request" );
}
if ( ! userId ) {
throw new Error ( "No user ID in request" );
}
const roles = req . user . role ;
2025-07-21 15:05:17 -07:00
if ( roles . includes ( "demo" )) {
throw new Error ( "Demo user cannot be deleted" );
}
2025-07-21 11:05:48 -07:00
2025-07-21 15:05:17 -07:00
// 1. Find all the monitors associated with the team ID if superadmin
2025-04-20 11:29:53 -07:00
const result = await this . db . getMonitorsByTeamId ({
2025-07-21 11:05:48 -07:00
teamId : teamId ,
2025-04-20 11:29:53 -07:00
});
2025-07-21 11:05:48 -07:00
if ( roles . includes ( "superadmin" )) {
// 2. Remove all jobs, delete checks and alerts
2025-04-20 11:29:53 -07:00
result ? . monitors . length > 0 &&
( await Promise . all (
result . monitors . map ( async ( monitor ) => {
await this . jobQueue . deleteJob ( monitor );
})
));
}
// 6. Delete the user by id
2025-07-21 11:05:48 -07:00
await this . db . deleteUser ( userId );
2025-04-20 11:29:53 -07:00
return res . success ({
msg : this . stringService . authDeleteUser ,
});
2025-07-15 16:33:35 -07:00
},
SERVICE_NAME ,
"deleteUser"
);
2025-04-20 11:29:53 -07:00
2025-07-15 16:33:35 -07:00
getAllUsers = asyncHandler (
async ( req , res , next ) => {
2025-04-20 11:29:53 -07:00
const allUsers = await this . db . getAllUsers ( req , res );
return res . success ({
msg : this . stringService . authGetAllUsers ,
data : allUsers ,
});
2025-07-15 16:33:35 -07:00
},
SERVICE_NAME ,
"getAllUsers"
);
2025-07-22 12:27:33 -07:00
getUserById = asyncHandler (
async ( req , res , next ) => {
2025-07-22 13:26:45 -07:00
await getUserByIdParamValidation . validateAsync ( req . params );
2025-07-22 12:27:33 -07:00
const userId = req ? . params ? . userId ;
const roles = req ? . user ? . role ;
if ( ! userId ) {
throw new Error ( "No user ID in request" );
}
if ( ! roles || roles . length === 0 ) {
throw new Error ( "No roles in request" );
}
const user = await this . db . getUserById ( roles , userId );
return res . success ({ msg : "ok" , data : user });
},
SERVICE_NAME ,
"getUserById"
);
editUserById = asyncHandler (
async ( req , res , next ) => {
const roles = req ? . user ? . role ;
if ( ! roles . includes ( "superadmin" )) {
throw createError ( "Unauthorized" , 403 );
}
2025-07-22 13:26:45 -07:00
const userId = req . params . userId ;
const user = { ... req . body };
await editUserByIdParamValidation . validateAsync ( req . params );
// If this is superadmin self edit, allow "superadmin" role
if ( userId === req . user . _id ) {
await editSuperadminUserByIdBodyValidation . validateAsync ( req . body );
} else {
await editUserByIdBodyValidation . validateAsync ( req . body );
}
2025-07-22 12:27:33 -07:00
await this . db . editUserById ( userId , user );
return res . success ({ msg : "ok" });
},
SERVICE_NAME ,
"editUserById"
);
2025-04-20 11:29:53 -07:00
}
export default AuthController ;