Changes applied to several services and controllers files: - Enhanced logging messages with line breaks in `brand.controller.ts`, `auth.controller.ts`, `model.controller.ts`, and `category.controller.ts`. - Adjusted logging in `mysql.service.ts`, `brand.service.ts`, `user.service.ts`, `category.service.ts`, `model.service.ts` and `jwt.service.ts` to commence on a new line for better readability. Signed-off-by: Mathis <yidhra@tuta.io>
300 lines
7.6 KiB
TypeScript
300 lines
7.6 KiB
TypeScript
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 { v4 } from "uuid";
|
|
|
|
const logger = new Logger({
|
|
name: "UserService",
|
|
});
|
|
|
|
const DbHandler = new MySqlService.Handler("UserService");
|
|
|
|
/**
|
|
* Retrieves a user from the database by the given email address.
|
|
*
|
|
* @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 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: ErrorType.NotFound,
|
|
message: "The user was not fund.",
|
|
};
|
|
}
|
|
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(inputData: IReqRegister): Promise<ISError | string> {
|
|
if (inputData.password.length < 6) {
|
|
return {
|
|
error: ErrorType.InvalidData,
|
|
message: "Password must be at least 6 characters long.",
|
|
};
|
|
}
|
|
//TODO check Object content keys
|
|
const passwordHash = await CredentialService.hash(`${inputData.password}`);
|
|
|
|
// Does the new user has accepted GDPR ?
|
|
if (inputData.gdpr !== true) {
|
|
return {
|
|
error: ErrorType.InvalidData,
|
|
message: "GDPR acceptance is required.",
|
|
};
|
|
}
|
|
const currentDate = new Date();
|
|
|
|
// Check if exist and return
|
|
const dbUserIfExist: IDbUser | ISError = await getUserByEmail(
|
|
inputData.email,
|
|
);
|
|
if ("error" in dbUserIfExist) {
|
|
return {
|
|
error: dbUserIfExist.error,
|
|
message: dbUserIfExist.message,
|
|
};
|
|
}
|
|
if (dbUserIfExist.id) {
|
|
logger.info(
|
|
`User already exist for email "${inputData.email}".\n(${dbUserIfExist.username}::${dbUserIfExist.id})\n`,
|
|
);
|
|
return {
|
|
error: ErrorType.UnAuthorized,
|
|
message: "User already exists.",
|
|
};
|
|
}
|
|
const currentId = v4();
|
|
const NewUser = await MySqlService.User.insert(DbHandler, {
|
|
id: currentId,
|
|
email: inputData.email,
|
|
username: inputData.username,
|
|
firstname: inputData.firstName,
|
|
lastname: inputData.lastName,
|
|
dob: inputData.dob,
|
|
hash: passwordHash,
|
|
gdpr: currentDate,
|
|
is_admin: false,
|
|
is_mail_verified: false,
|
|
});
|
|
if ("error" in NewUser || NewUser.affectedRows === 0) {
|
|
return {
|
|
error: ErrorType.DatabaseError,
|
|
message: "Error when inserting user in database.",
|
|
};
|
|
}
|
|
logger.info(`New user created ! (${inputData.username}::${currentId})`);
|
|
|
|
// JWT
|
|
const token = await JwtService.sign(
|
|
{
|
|
sub: NewUser.id,
|
|
},
|
|
{
|
|
alg: "HS512",
|
|
},
|
|
"1d",
|
|
"user",
|
|
);
|
|
return token;
|
|
}
|
|
|
|
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: <explanation>
|
|
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<user>, 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: <explanation>
|
|
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<boolean>} - 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;
|
|
}
|
|
}
|
|
|
|
logger.debug("\nService loaded.");
|
|
|
|
const UserService = {
|
|
register: register,
|
|
login: login,
|
|
getAll: getAllUsersService,
|
|
getFromId: getUserFromIdService,
|
|
edit: editUserService,
|
|
delete: deleteUserService,
|
|
};
|
|
|
|
export default UserService;
|