import {Logger} from "tslog"; import MySqlService from "@services/mysql.service"; import CredentialService from "@services/credential.service"; import JwtService from "@services/jwt.service"; import MysqlService from "@services/mysql.service"; import type {IReqRegister} from "@interfaces/requests/IReqRegister"; import {IReqLogin} from "@interfaces/requests/IReqLogin"; const logger = new Logger({ name: "UserService" }); const DbHandler = new MySqlService.Handler('UserService') /** * Retrieves a user object from the database based on the given username. * * @param {string} username - The username of the user to retrieve. * @returns {Promise} - The user object if found, or null if not found. */ async function getUserFromUsername(username: string): Promise { const dbUser = await MySqlService.User.getByUsername(DbHandler, username) if (dbUser === undefined) return null; return dbUser; } async function getUserFromIdService(id: string | undefined) { const dbUser = await MySqlService.User.getById(DbHandler, id); if (dbUser === undefined) return null; return dbUser; } async function register(ReqData: IReqRegister) { if (ReqData.password.length < 6) { logger.info(`REGISTER :> Invalid password (${ReqData.username})`) return { error: "invalidPassword" }; } const passwordHash = await CredentialService.hash(ReqData.password) // Does the new user has accepted GDPR ? if (ReqData.gdpr !== true) { logger.info(`REGISTER :> GDPR not validated (${ReqData.username})`) return { error: "gdprNotApproved" } } // Check if exist and return const dbUserIfExist = await getUserFromUsername(ReqData.username) if (dbUserIfExist) { logger.info(`REGISTER :> User exist (${dbUserIfExist.username})\n ID:${dbUserIfExist.id}`) return { error: "exist" } } const currentDate = new Date(); // New UserService (class) const NewUser = new User(ReqData.username, ReqData.displayName, passwordHash, currentDate); NewUser.setFirstName(ReqData.firstName); NewUser.setLastName(ReqData.lastName); // JWT const alg = 'HS512' const token = await JwtService.sign({ sub: NewUser.id }, alg, '1d', 'user') const userData = { error: "none", jwt: token, user: { id: NewUser.id, username: NewUser.username, displayName: NewUser.displayName, firstName: NewUser.firstName, lastName: NewUser.lastName }}; logger.info(userData) await Db.collection("users").insertOne(NewUser); logger.info(`REGISTER :> Inserted new user (${NewUser.username})`) return userData } async function login(ReqData: IReqLogin) { //const passwordHash = await getHashFromPassword(sanitizedData.password); const dbUser = await MysqlService.User.getByUsername(DbHandler, ReqData.username); if (!dbUser) { console.log(`LoginService :> User does not exist (${ReqData.username})`); return { error: "userNotFound" }; } if (ReqData.password.length < 6) { console.log('X') console.log(`LoginService :> Invalid password (${ReqData.username})`); return { error: "invalidPassword" }; } const isPasswordValid = await CredentialService.compare(ReqData.password, dbUser.hash) if (!isPasswordValid) { console.log(isPasswordValid) console.log(`LoginService :> Invalid password (${ReqData.username})`); return { error: "invalidPassword" }; } // biome-ignore lint/style/useConst: let userData = { error: "none", jwt: '', user: { id: dbUser.id, username: dbUser.username, displayName: dbUser.displayName, } }; userData.jwt = await JwtService.sign({sub: dbUser.id}, {alg: 'HS512'}, '7d', 'user') console.log("USERDATA :>"); console.log(userData); return userData; } /** * Retrieves all users from the database. * * @async * @function getAllUsersService * @returns {Promise<{iat: number, users: Array, length: number}>} - The response object containing the users array and its length. */ async function getAllUsersService() { const users = await Db.collection("users").find().toArray(); // biome-ignore lint/complexity/noForEach: users.forEach(user => { delete user.passwordHash delete user._id delete user.gdpr }); logger.info(`Query ${users.length} user(s)`) return { iat: Date.now(), users: users, length: users.length } } /** * Edits a user in the database. * * @param {string} targetId - The ID of the user to be edited. * @param {object} sanitizedData - The sanitized data to update the user with. * @returns {object} - An object indicating the result of the operation. * If the user is not found, the error property will be a string "userNotFound". * Otherwise, the error property will be a string "none". */ async function editUserService(targetId, sanitizedData) { if (sanitizedData.password) { const passwordHash = await getHashFromPassword(sanitizedData.password) delete sanitizedData.password logger.info(`Changing password for user "${targetId}"`) sanitizedData.passwordHash = passwordHash } const updatedUserResult = await Db.collection("users").updateOne({id: targetId}, {$set: sanitizedData}); if (updatedUserResult.modifiedCount === 0) { logger.info(`EDIT :> User not found (${targetId})`); return { error: "userNotFound" }; } logger.info(`EDIT :> User updated (${targetId})`); return { error: "none" }; } /** * Delete a user from the database. * * @param {string} targetId - The ID of the user to be deleted. * @return {Promise} - A promise that resolves to true if the user is successfully deleted, or false if an error occurs. */ async function deleteUserService(targetId) { logger.info(`Deleting user ${targetId}`) try { await Db.collection("users").deleteOne({id: targetId}); return true } catch (e) { logger.warn(e) return false } } const UserService = { register: register, login: login, getAll: getAllUsersService, getFromId: getUserFromIdService, edit: editUserService, delete: deleteUserService } export default UserService;