2025-04-20 11:29:53 -07:00
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<UserModel>}
* @throws {Error}
*/
2025-07-21 10:34:16 -07:00
const insertUser = async ( userData , imageFile , generateAvatarImage = GenerateAvatarImage ) => {
2025-04-20 11:29:53 -07:00
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 ();
2025-07-21 10:34:16 -07:00
return await UserModel . findOne ({ _id : newUser . _id }). select ( "-password" ). select ( "-profileImage" ); // .select() doesn't work with create, need to save then find
2025-04-20 11:29:53 -07:00
} 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<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 );
}
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}
*/
2025-07-21 11:05:48 -07:00
const updateUser = async ({ userId , user , file }) => {
if ( ! userId ) {
throw new Error ( "No user in request" );
}
2025-04-20 11:29:53 -07:00
try {
2025-07-21 11:05:48 -07:00
const candidateUser = { ... user };
2025-04-20 11:29:53 -07:00
2025-07-21 11:05:48 -07:00
if ( ParseBoolean ( candidateUser . deleteProfileImage ) === true ) {
2025-04-20 11:29:53 -07:00
candidateUser . profileImage = null ;
candidateUser . avatarImage = null ;
2025-07-21 11:05:48 -07:00
} else if ( file ) {
2025-04-20 11:29:53 -07:00
// 1. Save the full size image
candidateUser . profileImage = {
2025-07-21 11:05:48 -07:00
data : file . buffer ,
contentType : file . mimetype ,
2025-04-20 11:29:53 -07:00
};
// 2. Get the avatar sized image
2025-07-21 11:05:48 -07:00
const avatar = await GenerateAvatarImage ( file );
2025-04-20 11:29:53 -07:00
candidateUser . avatarImage = avatar ;
}
// ******************************************
// End handling profile image
// ******************************************
const updatedUser = await UserModel . findByIdAndUpdate (
2025-07-21 11:05:48 -07:00
userId ,
2025-04-20 11:29:53 -07:00
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 );
}
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 ;
}
};
2025-07-21 10:34:16 -07:00
export { insertUser , getUserByEmail , updateUser , deleteUser , deleteTeam , deleteAllOtherUsers , getAllUsers , logoutUser };