mirror of
https://github.com/bluewave-labs/Checkmate.git
synced 2026-01-23 18:19:51 -06:00
refactor module into class
This commit is contained in:
@@ -34,7 +34,10 @@ import crypto from "crypto";
|
||||
|
||||
// DB Modules
|
||||
import { NormalizeData } from "../utils/dataUtils.js";
|
||||
import { GenerateAvatarImage } from "../utils/imageProcessing.js";
|
||||
import { ParseBoolean } from "../utils/utils.js";
|
||||
|
||||
// Models
|
||||
import Check from "../db/models/Check.js";
|
||||
import HardwareCheck from "../db/models/HardwareCheck.js";
|
||||
import PageSpeedCheck from "../db/models/PageSpeedCheck.js";
|
||||
@@ -42,10 +45,12 @@ import Monitor from "../db/models/Monitor.js";
|
||||
import User from "../db/models/User.js";
|
||||
import InviteToken from "../db/models/InviteToken.js";
|
||||
import StatusPage from "../db/models/StatusPage.js";
|
||||
import Team from "../db/models/Team.js";
|
||||
|
||||
import InviteModule from "../db/mongo/modules/inviteModule.js";
|
||||
import CheckModule from "../db/mongo/modules/checkModule.js";
|
||||
import StatusPageModule from "../db/mongo/modules/statusPageModule.js";
|
||||
import UserModule from "../db/mongo/modules/userModule.js";
|
||||
|
||||
export const initializeServices = async ({ logger, envSettings, settingsService }) => {
|
||||
const serviceRegistry = new ServiceRegistry({ logger });
|
||||
@@ -60,7 +65,9 @@ export const initializeServices = async ({ logger, envSettings, settingsService
|
||||
const checkModule = new CheckModule({ logger, Check, HardwareCheck, PageSpeedCheck, Monitor, User });
|
||||
const inviteModule = new InviteModule({ InviteToken, crypto, stringService });
|
||||
const statusPageModule = new StatusPageModule({ StatusPage, NormalizeData, stringService });
|
||||
const db = new MongoDB({ logger, envSettings, checkModule, inviteModule, statusPageModule });
|
||||
const userModule = new UserModule({ User, Team, GenerateAvatarImage, ParseBoolean, stringService });
|
||||
const db = new MongoDB({ logger, envSettings, checkModule, inviteModule, statusPageModule, userModule });
|
||||
|
||||
await db.connect();
|
||||
|
||||
const networkService = new NetworkService(axios, ping, logger, http, Docker, net, stringService, settingsService);
|
||||
|
||||
@@ -1,12 +1,6 @@
|
||||
import mongoose from "mongoose";
|
||||
import AppSettings from "../models/AppSettings.js";
|
||||
|
||||
//****************************************
|
||||
// User Operations
|
||||
//****************************************
|
||||
|
||||
import * as userModule from "./modules/userModule.js";
|
||||
|
||||
//****************************************
|
||||
// Recovery Operations
|
||||
//****************************************
|
||||
@@ -52,10 +46,10 @@ import * as diagnosticModule from "./modules/diagnosticModule.js";
|
||||
class MongoDB {
|
||||
static SERVICE_NAME = "MongoDB";
|
||||
|
||||
constructor({ logger, envSettings, checkModule, inviteModule, statusPageModule }) {
|
||||
constructor({ logger, envSettings, checkModule, inviteModule, statusPageModule, userModule }) {
|
||||
this.logger = logger;
|
||||
this.envSettings = envSettings;
|
||||
Object.assign(this, userModule);
|
||||
this.userModule = userModule;
|
||||
this.inviteModule = inviteModule;
|
||||
Object.assign(this, recoveryModule);
|
||||
Object.assign(this, monitorModule);
|
||||
|
||||
@@ -1,261 +1,171 @@
|
||||
import UserModel from "../../models/User.js";
|
||||
import TeamModel from "../../models/Team.js";
|
||||
import { GenerateAvatarImage } from "../../../utils/imageProcessing.js";
|
||||
|
||||
const DUPLICATE_KEY_CODE = 11000; // MongoDB error code for duplicate key
|
||||
import { ParseBoolean } from "../../../utils/utils.js";
|
||||
import ServiceRegistry from "../../../service/system/serviceRegistry.js";
|
||||
import StringService from "../../../service/system/stringService.js";
|
||||
const SERVICE_NAME = "userModule";
|
||||
const DUPLICATE_KEY_CODE = 11000; // MongoDB error code for duplicate key
|
||||
|
||||
const checkSuperadmin = async () => {
|
||||
const superAdmin = await UserModel.findOne({ role: "superadmin" });
|
||||
if (superAdmin !== null) {
|
||||
return true;
|
||||
class UserModule {
|
||||
constructor({ User, Team, GenerateAvatarImage, ParseBoolean, stringService }) {
|
||||
this.User = User;
|
||||
this.Team = Team;
|
||||
this.GenerateAvatarImage = GenerateAvatarImage;
|
||||
this.ParseBoolean = ParseBoolean;
|
||||
this.stringService = stringService;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Insert a User
|
||||
* @async
|
||||
* @param {Express.Request} req
|
||||
* @param {Express.Response} res
|
||||
* @returns {Promise<UserModel>}
|
||||
* @throws {Error}
|
||||
*/
|
||||
const insertUser = async (userData, imageFile, generateAvatarImage = GenerateAvatarImage) => {
|
||||
const stringService = ServiceRegistry.get(StringService.SERVICE_NAME);
|
||||
try {
|
||||
if (imageFile) {
|
||||
// 1. Save the full size image
|
||||
userData.profileImage = {
|
||||
data: imageFile.buffer,
|
||||
contentType: imageFile.mimetype,
|
||||
};
|
||||
checkSuperadmin = async () => {
|
||||
try {
|
||||
const superAdmin = await this.User.findOne({ role: "superadmin" });
|
||||
if (superAdmin !== null) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "checkSuperadmin";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
// 2. Get the avatar sized image
|
||||
const avatar = await generateAvatarImage(imageFile);
|
||||
userData.avatarImage = avatar;
|
||||
insertUser = async (userData, imageFile) => {
|
||||
try {
|
||||
if (imageFile) {
|
||||
// 1. Save the full size image
|
||||
userData.profileImage = {
|
||||
data: imageFile.buffer,
|
||||
contentType: imageFile.mimetype,
|
||||
};
|
||||
|
||||
// 2. Get the avatar sized image
|
||||
const avatar = await this.GenerateAvatarImage(imageFile);
|
||||
userData.avatarImage = avatar;
|
||||
}
|
||||
|
||||
// Handle creating team if superadmin
|
||||
if (userData.role.includes("superadmin")) {
|
||||
const team = new this.Team({
|
||||
email: userData.email,
|
||||
});
|
||||
userData.teamId = team._id;
|
||||
userData.checkTTL = 60 * 60 * 24 * 30;
|
||||
await team.save();
|
||||
}
|
||||
|
||||
const newUser = new this.User(userData);
|
||||
await newUser.save();
|
||||
return await this.User.findOne({ _id: newUser._id }).select("-password").select("-profileImage"); // .select() doesn't work with create, need to save then find
|
||||
} catch (error) {
|
||||
if (error.code === DUPLICATE_KEY_CODE) {
|
||||
error.message = this.stringService.dbUserExists;
|
||||
}
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "insertUser";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
getUserByEmail = async (email) => {
|
||||
try {
|
||||
// Need the password to be able to compare, removed .select()
|
||||
// We can strip the hash before returning the user
|
||||
const user = await this.User.findOne({ email: email }).select("-profileImage");
|
||||
if (!user) {
|
||||
throw new Error(this.stringService.dbUserNotFound);
|
||||
}
|
||||
return user;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "getUserByEmail";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
updateUser = async ({ userId, user, file }) => {
|
||||
if (!userId) {
|
||||
throw new Error("No user in request");
|
||||
}
|
||||
|
||||
// Handle creating team if superadmin
|
||||
if (userData.role.includes("superadmin")) {
|
||||
const team = new TeamModel({
|
||||
email: userData.email,
|
||||
});
|
||||
userData.teamId = team._id;
|
||||
userData.checkTTL = 60 * 60 * 24 * 30;
|
||||
await team.save();
|
||||
try {
|
||||
const candidateUser = { ...user };
|
||||
|
||||
if (this.ParseBoolean(candidateUser.deleteProfileImage) === true) {
|
||||
candidateUser.profileImage = null;
|
||||
candidateUser.avatarImage = null;
|
||||
} else if (file) {
|
||||
// 1. Save the full size image
|
||||
candidateUser.profileImage = {
|
||||
data: file.buffer,
|
||||
contentType: file.mimetype,
|
||||
};
|
||||
|
||||
// 2. Get the avatar sized image
|
||||
const avatar = await this.GenerateAvatarImage(file);
|
||||
candidateUser.avatarImage = avatar;
|
||||
}
|
||||
|
||||
const updatedUser = await this.User.findByIdAndUpdate(
|
||||
userId,
|
||||
candidateUser,
|
||||
{ new: true } // Returns updated user instead of pre-update user
|
||||
)
|
||||
.select("-password")
|
||||
.select("-profileImage");
|
||||
return updatedUser;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "updateUser";
|
||||
throw error;
|
||||
}
|
||||
|
||||
const newUser = new UserModel(userData);
|
||||
await newUser.save();
|
||||
return await UserModel.findOne({ _id: newUser._id }).select("-password").select("-profileImage"); // .select() doesn't work with create, need to save then find
|
||||
} catch (error) {
|
||||
if (error.code === DUPLICATE_KEY_CODE) {
|
||||
error.message = stringService.dbUserExists;
|
||||
};
|
||||
deleteUser = async (userId) => {
|
||||
try {
|
||||
const deletedUser = await this.User.findByIdAndDelete(userId);
|
||||
if (!deletedUser) {
|
||||
throw new Error(this.stringService.dbUserNotFound);
|
||||
}
|
||||
return deletedUser;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "deleteUser";
|
||||
throw error;
|
||||
}
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "insertUser";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Get User by Email
|
||||
* Gets a user by Email. Not sure if we'll ever need this except for login.
|
||||
* If not needed except for login, we can move password comparison here
|
||||
* Throws error if user not found
|
||||
* @async
|
||||
* @param {Express.Request} req
|
||||
* @param {Express.Response} res
|
||||
* @returns {Promise<UserModel>}
|
||||
* @throws {Error}
|
||||
*/
|
||||
const getUserByEmail = async (email) => {
|
||||
const stringService = ServiceRegistry.get(StringService.SERVICE_NAME);
|
||||
|
||||
try {
|
||||
// Need the password to be able to compare, removed .select()
|
||||
// We can strip the hash before returning the user
|
||||
const user = await UserModel.findOne({ email: email }).select("-profileImage");
|
||||
if (!user) {
|
||||
throw new Error(stringService.dbUserNotFound);
|
||||
getAllUsers = async () => {
|
||||
try {
|
||||
const users = await this.User.find().select("-password").select("-profileImage");
|
||||
return users;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "getAllUsers";
|
||||
throw error;
|
||||
}
|
||||
return user;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "getUserByEmail";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Update a user by ID
|
||||
* @async
|
||||
* @param {Express.Request} req
|
||||
* @param {Express.Response} res
|
||||
* @returns {Promise<UserModel>}
|
||||
* @throws {Error}
|
||||
*/
|
||||
getUserById = async (roles, userId) => {
|
||||
try {
|
||||
if (!roles.includes("superadmin")) {
|
||||
throw new Error("User is not a superadmin");
|
||||
}
|
||||
|
||||
const updateUser = async ({ userId, user, file }) => {
|
||||
if (!userId) {
|
||||
throw new Error("No user in request");
|
||||
}
|
||||
const user = await this.User.findById(userId).select("-password").select("-profileImage");
|
||||
if (!user) {
|
||||
throw new Error("User not found");
|
||||
}
|
||||
|
||||
try {
|
||||
const candidateUser = { ...user };
|
||||
|
||||
if (ParseBoolean(candidateUser.deleteProfileImage) === true) {
|
||||
candidateUser.profileImage = null;
|
||||
candidateUser.avatarImage = null;
|
||||
} else if (file) {
|
||||
// 1. Save the full size image
|
||||
candidateUser.profileImage = {
|
||||
data: file.buffer,
|
||||
contentType: file.mimetype,
|
||||
};
|
||||
|
||||
// 2. Get the avatar sized image
|
||||
const avatar = await GenerateAvatarImage(file);
|
||||
candidateUser.avatarImage = avatar;
|
||||
return user;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "getUserById";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
// ******************************************
|
||||
// End handling profile image
|
||||
// ******************************************
|
||||
|
||||
const updatedUser = await UserModel.findByIdAndUpdate(
|
||||
userId,
|
||||
candidateUser,
|
||||
{ new: true } // Returns updated user instead of pre-update user
|
||||
)
|
||||
.select("-password")
|
||||
.select("-profileImage");
|
||||
return updatedUser;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "updateUser";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Delete a user by ID
|
||||
* @async
|
||||
* @param {Express.Request} req
|
||||
* @param {Express.Response} res
|
||||
* @returns {Promise<UserModel>}
|
||||
* @throws {Error}
|
||||
*/
|
||||
const deleteUser = async (userId) => {
|
||||
const stringService = ServiceRegistry.get(StringService.SERVICE_NAME);
|
||||
|
||||
try {
|
||||
const deletedUser = await UserModel.findByIdAndDelete(userId);
|
||||
if (!deletedUser) {
|
||||
throw new Error(stringService.dbUserNotFound);
|
||||
editUserById = async (userId, user) => {
|
||||
try {
|
||||
await this.User.findByIdAndUpdate(userId, user, { new: true }).select("-password").select("-profileImage");
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "editUserById";
|
||||
throw error;
|
||||
}
|
||||
return deletedUser;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "deleteUser";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a user by ID
|
||||
* @async
|
||||
* @param {string} teamId
|
||||
* @returns {void}
|
||||
* @throws {Error}
|
||||
*/
|
||||
const deleteTeam = async (teamId) => {
|
||||
try {
|
||||
await TeamModel.findByIdAndDelete(teamId);
|
||||
return true;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "deleteTeam";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
const deleteAllOtherUsers = async () => {
|
||||
try {
|
||||
await UserModel.deleteMany({ role: { $ne: "superadmin" } });
|
||||
return true;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "deleteAllOtherUsers";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
const getAllUsers = async () => {
|
||||
try {
|
||||
const users = await UserModel.find().select("-password").select("-profileImage");
|
||||
return users;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "getAllUsers";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
const logoutUser = async (userId) => {
|
||||
try {
|
||||
await UserModel.updateOne({ _id: userId }, { $unset: { authToken: null } });
|
||||
return true;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "logoutUser";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
const getUserById = async (roles, userId) => {
|
||||
try {
|
||||
if (!roles.includes("superadmin")) {
|
||||
throw new Error("User is not a superadmin");
|
||||
}
|
||||
|
||||
const user = await UserModel.findById(userId).select("-password").select("-profileImage");
|
||||
if (!user) {
|
||||
throw new Error("User not found");
|
||||
}
|
||||
|
||||
return user;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "getUserById";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
const editUserById = async (userId, user) => {
|
||||
try {
|
||||
await UserModel.findByIdAndUpdate(userId, user, { new: true }).select("-password").select("-profileImage");
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "editUserById";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
export {
|
||||
checkSuperadmin,
|
||||
insertUser,
|
||||
getUserByEmail,
|
||||
updateUser,
|
||||
deleteUser,
|
||||
deleteTeam,
|
||||
deleteAllOtherUsers,
|
||||
getAllUsers,
|
||||
logoutUser,
|
||||
getUserById,
|
||||
editUserById,
|
||||
};
|
||||
export default UserModule;
|
||||
|
||||
@@ -29,7 +29,7 @@ class UserService {
|
||||
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();
|
||||
const superAdminExists = await this.db.userModule.checkSuperadmin();
|
||||
if (superAdminExists) {
|
||||
const invitedUser = await this.db.inviteModule.getInviteTokenAndDelete(user.inviteToken);
|
||||
user.role = invitedUser.role;
|
||||
@@ -40,7 +40,7 @@ class UserService {
|
||||
await this.db.updateAppSettings({ jwtSecret });
|
||||
}
|
||||
|
||||
const newUser = await this.db.insertUser({ ...user }, file);
|
||||
const newUser = await this.db.userModule.insertUser({ ...user }, file);
|
||||
|
||||
this.logger.debug({
|
||||
message: "New user created",
|
||||
@@ -83,7 +83,7 @@ class UserService {
|
||||
|
||||
loginUser = async (email, password) => {
|
||||
// Check if user exists
|
||||
const user = await this.db.getUserByEmail(email);
|
||||
const user = await this.db.userModule.getUserByEmail(email);
|
||||
// Compare password
|
||||
const match = await user.comparePassword(password);
|
||||
if (match !== true) {
|
||||
@@ -110,7 +110,7 @@ class UserService {
|
||||
// Add user email to body for DB operation
|
||||
updates.email = currentUser.email;
|
||||
// Get user
|
||||
const user = await this.db.getUserByEmail(currentUser.email);
|
||||
const user = await this.db.userModule.getUserByEmail(currentUser.email);
|
||||
// Compare passwords
|
||||
const match = await user.comparePassword(updates?.password);
|
||||
// If not a match, throw a 403
|
||||
@@ -122,17 +122,17 @@ class UserService {
|
||||
updates.password = updates.newPassword;
|
||||
}
|
||||
|
||||
const updatedUser = await this.db.updateUser({ userId: currentUser?._id, user: updates, file: file });
|
||||
const updatedUser = await this.db.userModule.updateUser({ userId: currentUser?._id, user: updates, file: file });
|
||||
return updatedUser;
|
||||
};
|
||||
|
||||
checkSuperadminExists = async () => {
|
||||
const superAdminExists = await this.db.checkSuperadmin();
|
||||
const superAdminExists = await this.db.userModule.checkSuperadmin();
|
||||
return superAdminExists;
|
||||
};
|
||||
|
||||
requestRecovery = async (email) => {
|
||||
const user = await this.db.getUserByEmail(email);
|
||||
const user = await this.db.userModule.getUserByEmail(email);
|
||||
const recoveryToken = await this.db.requestRecoveryToken(email);
|
||||
const name = user.firstName;
|
||||
const { clientHost } = this.settingsService.getSettings();
|
||||
@@ -195,21 +195,21 @@ class UserService {
|
||||
));
|
||||
}
|
||||
// 6. Delete the user by id
|
||||
await this.db.deleteUser(userId);
|
||||
await this.db.userModule.deleteUser(userId);
|
||||
};
|
||||
|
||||
getAllUsers = async () => {
|
||||
const users = await this.db.getAllUsers();
|
||||
const users = await this.db.userModule.getAllUsers();
|
||||
return users;
|
||||
};
|
||||
|
||||
getUserById = async (roles, userId) => {
|
||||
const user = await this.db.getUserById(roles, userId);
|
||||
const user = await this.db.userModule.getUserById(roles, userId);
|
||||
return user;
|
||||
};
|
||||
|
||||
editUserById = async (userId, user) => {
|
||||
await this.db.editUserById(userId, user);
|
||||
await this.db.userModule.editUserById(userId, user);
|
||||
};
|
||||
}
|
||||
export default UserService;
|
||||
|
||||
Reference in New Issue
Block a user