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/serviceRegistry.js"; import StringService from "../../../service/stringService.js"; const SERVICE_NAME = "userModule"; /** * Insert a User * @async * @param {Express.Request} req * @param {Express.Response} res * @returns {Promise} * @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, }; // 2. Get the avatar sized image const avatar = await generateAvatarImage(imageFile); userData.avatarImage = avatar; } // 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(); } 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; } 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} * @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); } 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} * @throws {Error} */ const updateUser = async ( req, res, parseBoolean = ParseBoolean, generateAvatarImage = GenerateAvatarImage ) => { const candidateUserId = req.params.userId; try { const candidateUser = { ...req.body }; // ****************************************** // Handle profile image // ****************************************** if (parseBoolean(candidateUser.deleteProfileImage) === true) { candidateUser.profileImage = null; candidateUser.avatarImage = null; } else if (req.file) { // 1. Save the full size image candidateUser.profileImage = { data: req.file.buffer, contentType: req.file.mimetype, }; // 2. Get the avatar sized image const avatar = await generateAvatarImage(req.file); candidateUser.avatarImage = avatar; } // ****************************************** // End handling profile image // ****************************************** const updatedUser = await UserModel.findByIdAndUpdate( candidateUserId, 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} * @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); } 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 (req, res) => { 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; } }; export { insertUser, getUserByEmail, updateUser, deleteUser, deleteTeam, deleteAllOtherUsers, getAllUsers, logoutUser, };