feat(services): improve UserService with enhanced error handling and data validation
This commit includes updates to `user.service.ts` that enhance error handling and data validation. The `getUserFromUsername` function has been renamed to `getUserByEmail` reflecting the new identifier being used to retrieve users. Both `getUserByEmail` and `getUserFromIdService` have been revised to include error handling and logging capability. The `register` function now incorporates stronger validation checks and proper GDPR verification. It is also updated to return JWT token on successful registration. Issue: #18 Signed-off-by: Mathis <yidhra@tuta.io>
This commit is contained in:
parent
9225337e95
commit
3232e5fac1
@ -1,10 +1,14 @@
|
||||
import type { IReqLogin } from "@interfaces/requests/IReqLogin";
|
||||
import type { IReqRegister } from "@interfaces/requests/IReqRegister";
|
||||
import type {IDbUser} from "@interfaces/database/IDbUser";
|
||||
import type {IReqLogin} from "@interfaces/requests/IReqLogin";
|
||||
import type {IReqRegister} from "@interfaces/requests/IReqRegister";
|
||||
import {ErrorType, type ISError} from "@interfaces/services/ISError";
|
||||
import CredentialService from "@services/credential.service";
|
||||
import JwtService from "@services/jwt.service";
|
||||
import MySqlService from "@services/mysql.service";
|
||||
import MysqlService from "@services/mysql.service";
|
||||
import { Logger } from "tslog";
|
||||
import {Logger} from "tslog";
|
||||
import {v4} from "uuid";
|
||||
|
||||
|
||||
const logger = new Logger({
|
||||
name: "UserService",
|
||||
@ -13,92 +17,124 @@ const logger = new Logger({
|
||||
const DbHandler = new MySqlService.Handler("UserService");
|
||||
|
||||
/**
|
||||
* Retrieves a user object from the database based on the given username.
|
||||
* Retrieves a user from the database by the given email address.
|
||||
*
|
||||
* @param {string} username - The username of the user to retrieve.
|
||||
* @returns {Promise<Object | null>} - The user object if found, or null if not found.
|
||||
* @param {string} targetEmail - The email address of the user to retrieve.
|
||||
* @returns {Promise<IDbUser | ISError>}
|
||||
* - A promise that resolves with the user.
|
||||
* - If the user is not found, an error object is returned.
|
||||
* - If an error occurs during the database operation, an error object is returned.
|
||||
*/
|
||||
async function getUserFromUsername(username: string): Promise<object | null> {
|
||||
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})`);
|
||||
async function getUserByEmail(targetEmail: string): Promise<IDbUser | ISError> {
|
||||
try {
|
||||
const dbUser = await MySqlService.User.getByEmail(DbHandler, targetEmail);
|
||||
if (dbUser === undefined) {
|
||||
logger.info(`User not found (${targetEmail})`);
|
||||
return {
|
||||
error: "invalidPassword",
|
||||
error: ErrorType.NotFound,
|
||||
message: "The user was not fund.",
|
||||
};
|
||||
}
|
||||
const passwordHash = await CredentialService.hash(ReqData.password);
|
||||
return dbUser;
|
||||
} catch (err) {
|
||||
logger.error(err);
|
||||
return {
|
||||
error: ErrorType.DatabaseError,
|
||||
message: "An unknown error occurred.",
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a user from the database based on the provided ID.
|
||||
*
|
||||
* @param {string} id - The ID of the user to retrieve.
|
||||
* @returns {Promise<IDbUser | ISError>} - A promise that resolves with the user object if found, or an error object if not.
|
||||
*/
|
||||
async function getUserFromIdService(id: string): Promise<IDbUser | ISError> {
|
||||
try {
|
||||
if (!id || id.length !== 36) {
|
||||
logger.info(`Invalid ID (${id})`);
|
||||
return {
|
||||
error: ErrorType.InvalidData,
|
||||
message: "Invalid ID length.",
|
||||
};
|
||||
}
|
||||
const dbUser = await MySqlService.User.getById(DbHandler, id);
|
||||
if (dbUser === undefined) {
|
||||
logger.info(`User not found (${id})`);
|
||||
return {
|
||||
error: ErrorType.NotFound,
|
||||
message: "The user was not found.",
|
||||
};
|
||||
}
|
||||
return dbUser;
|
||||
} catch (err) {
|
||||
return {
|
||||
error: ErrorType.DatabaseError,
|
||||
message: "An unknown error occurred.",
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
async function register(ReqData: IReqRegister): Promise<ISError | string> {
|
||||
if (ReqData.password.length < 6) {
|
||||
return {
|
||||
error: ErrorType.InvalidData,
|
||||
message: "Password must be at least 6 characters long.",
|
||||
};
|
||||
}
|
||||
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",
|
||||
error: ErrorType.InvalidData,
|
||||
message: "GDPR acceptance is required.",
|
||||
};
|
||||
}
|
||||
|
||||
// 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)
|
||||
// Check if exist and return
|
||||
const dbUserIfExist: IDbUser | ISError = await getUserByEmail(ReqData.email);
|
||||
if ("error" in dbUserIfExist) {
|
||||
return {
|
||||
error: dbUserIfExist.error,
|
||||
message: dbUserIfExist.message,
|
||||
};
|
||||
}
|
||||
|
||||
const NewUser = new User(
|
||||
ReqData.username,
|
||||
ReqData.displayName,
|
||||
passwordHash,
|
||||
currentDate,
|
||||
);
|
||||
NewUser.setFirstName(ReqData.firstName);
|
||||
NewUser.setLastName(ReqData.lastName);
|
||||
const NewUser = await MySqlService.User.insert(DbHandler, {
|
||||
id: v4(),
|
||||
email: ReqData.email,
|
||||
username: ReqData.username,
|
||||
firstname: ReqData.firstName,
|
||||
lastname: ReqData.lastName,
|
||||
dob: ReqData.dob,
|
||||
hash: passwordHash,
|
||||
gdpr: currentDate,
|
||||
is_admin: false,
|
||||
is_mail_verified: false,
|
||||
});
|
||||
if ("error" in NewUser || !NewUser.id) {
|
||||
return {
|
||||
error: ErrorType.DatabaseError,
|
||||
message: 'Error when inserting user in database.'
|
||||
};
|
||||
}
|
||||
|
||||
// JWT
|
||||
|
||||
const alg = "HS512";
|
||||
const token = await JwtService.sign(
|
||||
{
|
||||
sub: NewUser.id,
|
||||
},
|
||||
alg,
|
||||
{
|
||||
alg: "HS512",
|
||||
},
|
||||
"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;
|
||||
return token;
|
||||
}
|
||||
|
||||
async function login(ReqData: IReqLogin) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user