mirror of
https://github.com/bluewave-labs/Checkmate.git
synced 2026-01-20 08:39:43 -06:00
209 lines
6.1 KiB
JavaScript
209 lines
6.1 KiB
JavaScript
const SERVICE_NAME = "userService";
|
|
import { createAuthError, createError } from "../utils/errorUtils.js";
|
|
|
|
class UserService {
|
|
static SERVICE_NAME = SERVICE_NAME;
|
|
constructor({ db, emailService, settingsService, logger, stringService, jwt }) {
|
|
this.db = db;
|
|
this.emailService = emailService;
|
|
this.settingsService = settingsService;
|
|
this.logger = logger;
|
|
this.stringService = stringService;
|
|
this.jwt = jwt;
|
|
}
|
|
|
|
issueToken = (payload, appSettings) => {
|
|
const tokenTTL = appSettings?.jwtTTL ?? "2h";
|
|
const tokenSecret = appSettings?.jwtSecret;
|
|
const payloadData = payload;
|
|
return this.jwt.sign(payloadData, tokenSecret, { expiresIn: tokenTTL });
|
|
};
|
|
|
|
registerUser = async (user, file) => {
|
|
// Create a new user
|
|
// If superAdmin exists, a token should be attached to all further register requests
|
|
const superAdminExists = await this.db.checkSuperadmin();
|
|
if (superAdminExists) {
|
|
const invitedUser = await this.db.getInviteTokenAndDelete(user.inviteToken);
|
|
user.role = invitedUser.role;
|
|
user.teamId = invitedUser.teamId;
|
|
} 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 this.db.updateAppSettings({ jwtSecret });
|
|
}
|
|
|
|
const newUser = await this.db.insertUser({ ...user }, file);
|
|
|
|
this.logger.debug({
|
|
message: "New user created",
|
|
service: SERVICE_NAME,
|
|
method: "registerUser",
|
|
details: newUser._id,
|
|
});
|
|
|
|
const userForToken = { ...newUser._doc };
|
|
delete userForToken.profileImage;
|
|
delete userForToken.avatarImage;
|
|
|
|
const appSettings = await this.settingsService.getSettings();
|
|
|
|
const token = this.issueToken(userForToken, appSettings);
|
|
|
|
try {
|
|
const html = await this.emailService.buildEmail("welcomeEmailTemplate", {
|
|
name: newUser.firstName,
|
|
});
|
|
this.emailService.sendEmail(newUser.email, "Welcome to Uptime Monitor", html).catch((error) => {
|
|
this.logger.warn({
|
|
message: error.message,
|
|
service: SERVICE_NAME,
|
|
method: "registerUser",
|
|
stack: error.stack,
|
|
});
|
|
});
|
|
} catch (error) {
|
|
this.logger.warn({
|
|
message: error.message,
|
|
service: SERVICE_NAME,
|
|
method: "registerUser",
|
|
stack: error.stack,
|
|
});
|
|
}
|
|
|
|
return { user: newUser, token };
|
|
};
|
|
|
|
loginUser = async (email, password) => {
|
|
// Check if user exists
|
|
const user = await this.db.getUserByEmail(email);
|
|
// Compare password
|
|
const match = await user.comparePassword(password);
|
|
if (match !== true) {
|
|
throw createAuthError(this.stringService.authIncorrectPassword);
|
|
}
|
|
|
|
// 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 this.settingsService.getSettings();
|
|
const token = this.issueToken(userWithoutPassword, appSettings);
|
|
// reset avatar image
|
|
userWithoutPassword.avatarImage = user.avatarImage;
|
|
return { user: userWithoutPassword, token };
|
|
};
|
|
|
|
editUser = async (updates, file, currentUser) => {
|
|
// Change Password check
|
|
if (updates?.password && updates?.newPassword) {
|
|
// Get user's email
|
|
// Add user email to body for DB operation
|
|
updates.email = currentUser.email;
|
|
// Get user
|
|
const user = await this.db.getUserByEmail(currentUser.email);
|
|
// Compare passwords
|
|
const match = await user.comparePassword(updates?.password);
|
|
// If not a match, throw a 403
|
|
// 403 instead of 401 to avoid triggering axios interceptor
|
|
if (!match) {
|
|
throw createError(this.stringService.authIncorrectPassword, 403);
|
|
}
|
|
// If a match, update the password
|
|
updates.password = updates.newPassword;
|
|
}
|
|
|
|
const updatedUser = await this.db.updateUser({ userId: currentUser?._id, user: updates, file: file });
|
|
return updatedUser;
|
|
};
|
|
|
|
checkSuperadminExists = async () => {
|
|
const superAdminExists = await this.db.checkSuperadmin();
|
|
return superAdminExists;
|
|
};
|
|
|
|
requestRecovery = async (email) => {
|
|
const user = await this.db.getUserByEmail(email);
|
|
const recoveryToken = await this.db.requestRecoveryToken(email);
|
|
const name = user.firstName;
|
|
const { clientHost } = this.settingsService.getSettings();
|
|
const url = `${clientHost}/set-new-password/${recoveryToken.token}`;
|
|
|
|
const html = await this.emailService.buildEmail("passwordResetTemplate", {
|
|
name,
|
|
email,
|
|
url,
|
|
});
|
|
const msgId = await this.emailService.sendEmail(email, "Checkmate Password Reset", html);
|
|
return msgId;
|
|
};
|
|
|
|
validateRecovery = async (recoveryToken) => {
|
|
await this.db.validateRecoveryToken(recoveryToken);
|
|
};
|
|
|
|
resetPassword = async (password, recoveryToken) => {
|
|
const user = await this.db.resetPassword(password, recoveryToken);
|
|
const appSettings = await this.settingsService.getSettings();
|
|
const token = this.issueToken(user._doc, appSettings);
|
|
return { user, token };
|
|
};
|
|
|
|
deleteUser = async (user) => {
|
|
const email = user?.email;
|
|
if (!email) {
|
|
throw new Error("No email in request");
|
|
}
|
|
|
|
const teamId = user?.teamId;
|
|
const userId = user?._id;
|
|
|
|
if (!teamId) {
|
|
throw new Error("No team ID in request");
|
|
}
|
|
|
|
if (!userId) {
|
|
throw new Error("No user ID in request");
|
|
}
|
|
|
|
const roles = user?.role;
|
|
if (roles.includes("demo")) {
|
|
throw new Error("Demo user cannot be deleted");
|
|
}
|
|
|
|
// 1. Find all the monitors associated with the team ID if superadmin
|
|
const result = await this.db.getMonitorsByTeamId({
|
|
teamId: teamId,
|
|
});
|
|
|
|
if (roles.includes("superadmin")) {
|
|
// 2. Remove all jobs, delete checks and alerts
|
|
result?.monitors.length > 0 &&
|
|
(await Promise.all(
|
|
result.monitors.map(async (monitor) => {
|
|
await this.jobQueue.deleteJob(monitor);
|
|
})
|
|
));
|
|
}
|
|
// 6. Delete the user by id
|
|
await this.db.deleteUser(userId);
|
|
};
|
|
|
|
getAllUsers = async () => {
|
|
const users = await this.db.getAllUsers();
|
|
return users;
|
|
};
|
|
|
|
getUserById = async (roles, userId) => {
|
|
const user = await this.db.getUserById(roles, userId);
|
|
return user;
|
|
};
|
|
|
|
editUserById = async (userId, user) => {
|
|
await this.db.editUserById(userId, user);
|
|
};
|
|
}
|
|
export default UserService;
|