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<ResponseT> => {
try {
const sqlquery = 'SELECT * FROM users WHERE email = ?';
const [[user]] = await db.query<UserI[]>(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<IAuthLogs> = 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<ResponseT> => {
try {
const sqlquery = 'SELECT * FROM users WHERE email = ?';
const [[userExist]] = await db.query<UserI[]>(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<ResultSetHeader[]>(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<RowDataPacket[]>(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<RowDataPacket[]>(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<IAuthLogs> = 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<ResponseT> => {
try {
const sqlquery = 'SELECT * FROM users WHERE user_id = ?';
const [[user]] = await db.query<UserI[]>(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<IAuthLogs> = 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<ResponseT> => {
try {
res.clearCookie('token');
const resp: ICode<IAuthLogs> = 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<ResponseT> => {
try {
const hashedPassword = bcrypt.hashSync(newPassword, 10);
const sqlquery = 'UPDATE users SET password = ? WHERE user_id = ?';
await db.query<RowDataPacket[]>(sqlquery, [hashedPassword, userId]);
const resp: ICode<IAuthLogs> = 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<ResponseT> => {
try {
const sqlTeacherQuery = 'SELECT * FROM teachers WHERE teacher_id = ?';
const [[teacher]] = await db.query<Teacher[]>(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<RowDataPacket[]>(sqlquery, [
hashedPassword,
teacher.user_id,
]);
const resp: ICode<IAuthLogs> = 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<RowDataPacket[]>(
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<RowDataPacket[]>(sqlquery, [
additionalInfoValues,
]);
return { insertId: result.insertId, isErr: false, err: null };
} catch (err) {
return { insertId: null, isErr: true, err };
}
};
}