import authLogs, { IAuthLogs, authLogger } from './auth.logs'; import { formatString } from '../../utils/Strings'; import { generateToken } from '../../utils/Jwt'; import { HttpCodes } from '../../config/Errors'; import { ErrorResponseC, SuccessResponseC } from '../services.response'; import { Response } from 'express'; import { db } from '../../settings'; import bcrypt from 'bcrypt'; import { ResultSetHeader, RowDataPacket } from 'mysql2'; import { Optimize } from '../../utils/Function'; export class AuthServices { /** * @description Login a user * @param email - String * @param password - String * @returns ResponseT */ static executeLogin = async ( res: Response, email: string, password: string ): Promise => { try { const sqlquery = 'SELECT * FROM users WHERE email = ?'; const [[user]] = await db.query(sqlquery, [email]); if (user) { const isPasswordMatch = bcrypt.compareSync(password, user.password); if (isPasswordMatch) { // query additional info base on role const additionalInfo = await this.getAdditionalInfo( user.role, user.user_id ); if (additionalInfo.isErr) { const msg = formatString(authLogs.LOGIN_ERROR_GENERIC.message, { error: (additionalInfo.err as Error)?.message || '', email, }); authLogger.error(msg, additionalInfo.err as Error); return new ErrorResponseC( authLogs.LOGIN_ERROR_GENERIC.type, HttpCodes.InternalServerError.code, msg ); } generateToken(res, { user_id: user.user_id.toString(), role: user.role, }); const resp: ICode = authLogs.LOGIN_SUCCESS; const msg = formatString(resp.message, { email: user.email, lastName: user.lastName, firstName: user.firstName, }); authLogger.info(msg, { type: resp.type }); return new SuccessResponseC( resp.type, { ...Optimize(user), ...additionalInfo.data }, msg, HttpCodes.Accepted.code ); } const msg = formatString( authLogs.LOGIN_ERROR_INCORRECT_PASSWORD_FOUND.message, { email } ); authLogger.error(msg); return new ErrorResponseC( authLogs.LOGIN_ERROR_INCORRECT_PASSWORD_FOUND.type, HttpCodes.Unauthorized.code, msg ); } const msg = formatString(authLogs.LOGIN_ERROR_EMAIL_NOT_FOUND.message, { email, }); authLogger.error(msg); return new ErrorResponseC( authLogs.LOGIN_ERROR_EMAIL_NOT_FOUND.type, HttpCodes.NotFound.code, msg ); } catch (err) { const msg = formatString(authLogs.LOGIN_ERROR_GENERIC.message, { error: (err as Error)?.message || '', email, }); authLogger.error(msg, err as Error); return new ErrorResponseC( authLogs.LOGIN_ERROR_GENERIC.type, HttpCodes.InternalServerError.code, msg ); } }; /** * @description Register a user * @returns {ResponseT} */ static executeRegister = async ( res: Response, email: string, password: string, firstName: string, lastName: string, role: string, phone: string, additionalInfo: any ): Promise => { try { const sqlquery = 'SELECT * FROM users WHERE email = ?'; const [[userExist]] = await db.query(sqlquery, [email]); if (userExist) { const msg = formatString(authLogs.REGISTER_ERROR_EMAIL_EXIST.message, { email, }); authLogger.error(msg); return new ErrorResponseC( authLogs.REGISTER_ERROR_EMAIL_EXIST.type, HttpCodes.Conflict.code, msg ); } const hashedPassword = bcrypt.hashSync(password, 10); const sqlInsertQuery = 'INSERT INTO users (email, password, firstName, lastName, role, phone) VALUES (?, ?, ?, ?, ?, ?)'; const result: any = await db.query(sqlInsertQuery, [ email, hashedPassword, firstName, lastName, role, phone, ]); const userId = result[0].insertId; const user = { user_id: userId, email, firstName, lastName, role, isActive: 1, phone, }; // const sqlquery2 = 'SELECT * FROM users WHERE user_id = ?'; // const [[user]] = await db.query(sqlquery2, [userId]); // insert additional info on table base on role if (role !== 'admin' && role !== 'super_admin' && role !== 'inst_admin') { const insertAdditionalInfo = await this.insertAdditionalInfo( role, userId.toString(), firstName, lastName, additionalInfo ); if (insertAdditionalInfo.isErr) { const sqlDeleteQuery = 'DELETE FROM users WHERE user_id = ?'; await db.query(sqlDeleteQuery, [userId]); const msg = formatString(authLogs.REGISTER_ERROR_GENERIC.message, { error: (insertAdditionalInfo.err as Error)?.message || '', email, }); authLogger.error(msg, insertAdditionalInfo.err as Error); return new ErrorResponseC( authLogs.REGISTER_ERROR_GENERIC.type, HttpCodes.InternalServerError.code, msg ); } additionalInfo = { [`${role}_id`]: insertAdditionalInfo.insertId, ...additionalInfo, }; } // generateToken(res, { // user_id: userId.toString(), // role, // }); const resp: ICode = authLogs.REGISTER_SUCCESS; const msg = formatString(resp.message, { email, lastName, firstName, }); authLogger.info(msg, { type: resp.type }); return new SuccessResponseC( resp.type, { ...Optimize(user), ...additionalInfo, }, msg, HttpCodes.Created.code ); } catch (err) { const msg = formatString(authLogs.REGISTER_ERROR_GENERIC.message, { error: (err as Error)?.message || '', email, }); authLogger.error(msg, err as Error); return new ErrorResponseC( authLogs.REGISTER_ERROR_GENERIC.type, HttpCodes.InternalServerError.code, msg ); } }; static executeAuthBack = async (user_id: number): Promise => { try { const sqlquery = 'SELECT * FROM users WHERE user_id = ?'; const [[user]] = await db.query(sqlquery, [user_id]); if (!user) { const msg = formatString(authLogs.USER_NOT_FOUND.message, { userId: user_id, }); authLogger.error(msg); return new ErrorResponseC( authLogs.USER_NOT_FOUND.type, HttpCodes.NotFound.code, msg ); } // query additional info base on role const additionalInfo = await this.getAdditionalInfo( user.role, user.user_id ); if (additionalInfo.isErr) { const msg = formatString(authLogs.AUTH_ERROR_GENERIC.message, { email: user.email, error: (additionalInfo.err as Error)?.message || '', }); authLogger.error(msg, additionalInfo.err as Error); return new ErrorResponseC( authLogs.AUTH_ERROR_GENERIC.type, HttpCodes.InternalServerError.code, msg ); } const resp: ICode = authLogs.AUTH_BACK; const msg = formatString(resp.message, { email: user.email, username: user.firstName + ' ' + user.lastName, }); authLogger.info(msg, { type: resp.type }); return new SuccessResponseC( resp.type, { ...Optimize(user), ...additionalInfo.data }, msg, HttpCodes.Accepted.code ); } catch (err) { const msg = formatString(authLogs.AUTH_ERROR_GENERIC.message, { error: (err as Error)?.message || '', user_id, }); authLogger.error(msg, err as Error); return new ErrorResponseC( authLogs.AUTH_ERROR_GENERIC.type, HttpCodes.InternalServerError.code, msg ); } }; static executeLogout = async (res: Response): Promise => { try { res.clearCookie('token'); const resp: ICode = authLogs.LOGOUT_SUCCESS; const msg = resp.message; authLogger.info(msg, { type: resp.type }); return new SuccessResponseC( resp.type, null, msg, HttpCodes.Accepted.code ); } catch (err) { const msg = formatString(authLogs.LOGOUT_ERROR_GENERIC.message, { error: (err as Error)?.message || '', }); authLogger.error(msg, err as Error); return new ErrorResponseC( authLogs.LOGOUT_ERROR_GENERIC.type, HttpCodes.InternalServerError.code, msg ); } }; static executeResetPassword = async ( userId: number, newPassword: string ): Promise => { try { const hashedPassword = bcrypt.hashSync(newPassword, 10); const sqlquery = 'UPDATE users SET password = ? WHERE user_id = ?'; await db.query(sqlquery, [hashedPassword, userId]); const resp: ICode = authLogs.RESET_PASSWORD_SUCCESS; const msg = formatString(resp.message, { user: userId, }); authLogger.info(msg, { type: resp.type }); return new SuccessResponseC( resp.type, null, msg, HttpCodes.Accepted.code ); } catch (err) { const msg = formatString(authLogs.RESET_ERROR_GENERIC.message, { error: (err as Error)?.message || '', email: userId, }); authLogger.error(msg, err as Error); return new ErrorResponseC( authLogs.RESET_ERROR_GENERIC.type, HttpCodes.InternalServerError.code, msg ); } }; static executeResetTeacherPassword = async ( teacherId: number, newPassword: string ): Promise => { try { const sqlTeacherQuery = 'SELECT * FROM teachers WHERE teacher_id = ?'; const [[teacher]] = await db.query(sqlTeacherQuery, [ teacherId, ]); if (!teacher) { const msg = formatString(authLogs.USER_NOT_FOUND.message, { userId: teacherId, }); authLogger.error(msg); return new ErrorResponseC( authLogs.USER_NOT_FOUND.type, HttpCodes.NotFound.code, msg ); } const hashedPassword = bcrypt.hashSync(newPassword, 10); const sqlquery = 'UPDATE users SET password = ? WHERE user_id = ?'; await db.query(sqlquery, [ hashedPassword, teacher.user_id, ]); const resp: ICode = authLogs.RESET_PASSWORD_SUCCESS; const msg = formatString(resp.message, { user: teacherId, }); authLogger.info(msg, { type: resp.type }); return new SuccessResponseC( resp.type, null, msg, HttpCodes.Accepted.code ); } catch (err) { const msg = formatString(authLogs.RESET_ERROR_GENERIC.message, { error: (err as Error)?.message || '', email: teacherId, }); authLogger.error(msg, err as Error); return new ErrorResponseC( authLogs.RESET_ERROR_GENERIC.type, HttpCodes.InternalServerError.code, msg ); } }; static getAdditionalInfo = async ( role: string, user_id: number ): Promise<{ data: any; isErr: boolean; err: any; }> => { if (role !== 'admin' && role !== 'super_admin' && role !== 'inst_admin') { const tableName = role === 'teacher' ? 'teachers' : role === 'school' ? 'schools' : 'inst_designers'; const sqlquery = `SELECT * FROM ${tableName} WHERE user_id = ?`; try { const [[additionalInfo]]: any = await db.query( sqlquery, user_id ); return { data: additionalInfo, isErr: false, err: null, }; } catch (err) { return { data: null, isErr: true, err, }; } } return { isErr: false, err: null, data: null, }; }; static insertAdditionalInfo = async ( role: string, user_id: number, firstName: string, lastName: string, additionalInfo: any ): Promise<{ insertId: number | null; isErr: boolean; err: any; }> => { const tableName = role === 'teacher' ? 'teachers' : role === 'school' ? 'schools' : 'inst_designers'; const additionalInfoKeys = Object.keys(additionalInfo); const additionalInfoValues = Object.values(additionalInfo); additionalInfoKeys.push('user_id'); additionalInfoValues.push(user_id); if (role === 'inst_designer') { additionalInfoKeys.push('firstName'); additionalInfoValues.push(firstName); additionalInfoKeys.push('lastName'); additionalInfoValues.push(lastName); } const additionalInfoColumns = additionalInfoKeys.join(','); try { const sqlquery = `INSERT INTO ${tableName} (${additionalInfoColumns}) VALUES (?)`; const [result]: any = await db.query(sqlquery, [ additionalInfoValues, ]); return { insertId: result.insertId, isErr: false, err: null }; } catch (err) { return { insertId: null, isErr: true, err }; } }; }