Compare commits
5 Commits
0635c512cc
...
bc12f94e41
Author | SHA1 | Date | |
---|---|---|---|
bc12f94e41 | |||
df28d3aa52 | |||
3d5ea6ac30 | |||
2fb6cd6e83 | |||
34f028ef9f |
@ -397,6 +397,8 @@ async function getSelf(req: Request, res: Response) {
|
||||
});
|
||||
}
|
||||
|
||||
logger.debug("\nController loaded.");
|
||||
|
||||
const AuthController = {
|
||||
register: registerUser,
|
||||
login: loginUser,
|
||||
|
@ -171,6 +171,8 @@ async function deleteBrand(req: Request, res: Response): Promise<Response> {
|
||||
|
||||
//TODO get models of the brand
|
||||
|
||||
logger.debug("\nController loaded.");
|
||||
|
||||
const BrandController = {
|
||||
create: createBrand,
|
||||
update: updateBrand,
|
||||
|
@ -177,6 +177,8 @@ async function getBySlugCategory(
|
||||
});
|
||||
}
|
||||
|
||||
logger.debug("\nController loaded.");
|
||||
|
||||
const CategoryController = {
|
||||
create: createCategory,
|
||||
update: updateCategory,
|
||||
|
@ -131,6 +131,8 @@ async function deleteModel(req: Request, res: Response): Promise<Response> {
|
||||
|
||||
//TODO get model with vehicle available.
|
||||
|
||||
logger.debug("\nController loaded.");
|
||||
|
||||
const ModelController = {
|
||||
create: createModel,
|
||||
update: updateModel,
|
||||
|
48
src/interfaces/database/IDbFactorize.ts
Normal file
48
src/interfaces/database/IDbFactorize.ts
Normal file
@ -0,0 +1,48 @@
|
||||
/**
|
||||
* Represents the output of the factorization function.
|
||||
*/
|
||||
export interface IDbFactorizeOutput {
|
||||
/**
|
||||
* Description: The variable `_valuesArray` is an array that can contain values of type `string`, `boolean`, `number`, or `Date`.
|
||||
* (The value associated with the keys of `_keysTemplate`)
|
||||
*
|
||||
* @type {Array<string | boolean | number | Date>}
|
||||
*/
|
||||
_valuesArray: Array<string | boolean | number | Date>;
|
||||
/**
|
||||
* Represents the SQL Query template for the keys.
|
||||
* @type {string}
|
||||
*/
|
||||
_keysTemplate: string;
|
||||
/**
|
||||
* The total number of fields.
|
||||
*
|
||||
* @type {number}
|
||||
*/
|
||||
totalFields: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface IDbFactorizeInput represents the input required to factorize a SQL query.
|
||||
*/
|
||||
export interface IDbFactorizeInput {
|
||||
/**
|
||||
* An object containing values that will be in a SQL Query.
|
||||
*
|
||||
* @type {Array<string | boolean | number | Date>}
|
||||
*/
|
||||
values: object;
|
||||
/**
|
||||
* Represents the name of the action that will result of the prepared SQL Query.
|
||||
*
|
||||
* @type {string}
|
||||
*/
|
||||
actionName: string;
|
||||
/**
|
||||
* Indicates whether an error should be thrown when encountering an error.
|
||||
* If set to true, an error will be thrown. If set to false or not provided, the error will not be thrown.
|
||||
*
|
||||
* @type {boolean}
|
||||
*/
|
||||
throwOnError?: true;
|
||||
}
|
8
src/interfaces/services/IUserUpdate.ts
Normal file
8
src/interfaces/services/IUserUpdate.ts
Normal file
@ -0,0 +1,8 @@
|
||||
export interface IUserUpdate {
|
||||
id?: string;
|
||||
username?: string;
|
||||
firstname?: string;
|
||||
lastname?: string;
|
||||
dob?: Date;
|
||||
gdpr?: Date;
|
||||
}
|
@ -168,7 +168,7 @@ async function deleteBrand(brandId: string): Promise<boolean> {
|
||||
return true;
|
||||
}
|
||||
|
||||
logger.debug("Service loaded.");
|
||||
logger.debug("\nService loaded.");
|
||||
|
||||
const BrandService = {
|
||||
create: createBrand,
|
||||
|
@ -127,7 +127,7 @@ async function deleteCategory(id: string): Promise<unknown> {
|
||||
}
|
||||
}
|
||||
|
||||
logger.debug("Service loaded.");
|
||||
logger.debug("\nService loaded.");
|
||||
|
||||
const CategoryService = {
|
||||
create: createCategory,
|
||||
|
@ -64,7 +64,7 @@ async function JwtSignService(
|
||||
.sign(new TextEncoder().encode(`${process.env["JWT_SECRET"]}`));
|
||||
}
|
||||
|
||||
logger.debug("Service loaded.");
|
||||
logger.debug("\nService loaded.");
|
||||
|
||||
const JwtService = {
|
||||
verify: JwtVerifyService,
|
||||
|
@ -135,7 +135,7 @@ async function getAllModels(): Promise<IDbModel[] | null> {
|
||||
}
|
||||
}
|
||||
|
||||
logger.debug("Service loaded.");
|
||||
logger.debug("\nService loaded.");
|
||||
|
||||
/**
|
||||
* ModelService is responsible for managing models.
|
||||
|
@ -7,6 +7,8 @@ import type { IDbUser } from "@interfaces/database/IDbUser";
|
||||
import type { IDbVehicle } from "@interfaces/database/IDbVehicle";
|
||||
import mysql, { type Connection, type ConnectionOptions } from "mysql2";
|
||||
import { Logger } from "tslog";
|
||||
import {IUserUpdate} from "@interfaces/services/IUserUpdate";
|
||||
import {IDbFactorizeInput, IDbFactorizeOutput} from "@interfaces/database/IDbFactorize";
|
||||
|
||||
const access: ConnectionOptions = {
|
||||
host: `${process.env["MYSQL_HOST"]}`,
|
||||
@ -32,7 +34,7 @@ class MysqlHandler {
|
||||
this.Logger.error(`Error connecting to MySQL: ${err}`);
|
||||
process.exit(1);
|
||||
}
|
||||
this.Logger.info(`Connected to MySQL database (${access.database})`);
|
||||
this.Logger.info(`\n\n> Connected to MySQL database (${access.database})\n`);
|
||||
});
|
||||
}
|
||||
closeConnection() {
|
||||
@ -52,6 +54,40 @@ class MysqlHandler {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Factorize the input data values into a database query.
|
||||
*
|
||||
* @param {IDbFactorizeInput} data - The input data containing values to factorize.
|
||||
* @return {Promise<IDbFactorizeOutput>} - A promise resolving to the factorized output.
|
||||
*/
|
||||
factorize(data: IDbFactorizeInput): Promise<IDbFactorizeOutput> {
|
||||
return new Promise((resolve, reject)=>{
|
||||
try {
|
||||
const _sqlQueryKeys = Object.keys(data.values).map((key: string) => `\'${key}\' = ?`)
|
||||
const values = Object.values(data.values).map((val)=>val)
|
||||
this.Logger.debug(`\n\n>-> Factorized ${_sqlQueryKeys.length} keys for a prepare Query.\n>-> Action: ${data.actionName}\n`)
|
||||
const sqlQueryKeys = _sqlQueryKeys.join(', ')
|
||||
|
||||
const factorizedOutput: IDbFactorizeOutput = {
|
||||
_keysTemplate: sqlQueryKeys,
|
||||
totalFields: _sqlQueryKeys.length,
|
||||
_valuesArray: values
|
||||
}
|
||||
resolve(factorizedOutput);
|
||||
} catch (err) {
|
||||
if (data.throwOnError) throw new Error(`${err}`)
|
||||
this.Logger.error(`\n|\n${err}\n|`)
|
||||
reject(`${err}`)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes a query using the provided queryString and values.
|
||||
* @param {string} queryString - The SQL query string to execute.
|
||||
* @param {Array<string | boolean | Date | number>} values - The values to be inserted into the query.
|
||||
* @returns {Promise<unknown>} - A promise that resolves with the query results or rejects with an error.
|
||||
*/
|
||||
execute(
|
||||
queryString: string,
|
||||
values: Array<string | boolean | Date | number>,
|
||||
@ -138,38 +174,25 @@ const MySqlService = {
|
||||
/**
|
||||
* Updates a user in the database.
|
||||
* @param {MysqlHandler} handler - The MySQL handler object.
|
||||
* @param {IDbUser} data - The updated user data.
|
||||
* @param {IUserUpdate} data - The updated user data.
|
||||
* @returns {Promise<IDbStatusResult>} - A promise that resolves to the result of the update operation.
|
||||
* @throws {Error} If an error occurs during the update operation.
|
||||
*/
|
||||
update(handler: MysqlHandler, data: IDbUser): Promise<IDbStatusResult> {
|
||||
update(handler: MysqlHandler, data: IUserUpdate): Promise<IDbStatusResult> {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (!data.id) return reject("Id is undefined");
|
||||
if (data.id.length !== 36) return reject("Id invalid");
|
||||
|
||||
if (data.gdpr && typeof data.gdpr !== typeof Date) {
|
||||
return reject("Invalid gdpr date.")
|
||||
}
|
||||
try {
|
||||
const _values = [];
|
||||
const _template = `
|
||||
${data.username ? "`username` = ?," : null}
|
||||
${data.username ? "`username` = ?," && _values.push(data.username) as unknown as void : null}
|
||||
${data.firstname ? "`firstname` = ?," : null}
|
||||
${data.lastname ? "`lastname` = ?," : null}
|
||||
${data.dob ? "`dob` = ?," : null}
|
||||
${data.email ? "`email` = ?," : null}
|
||||
${data.is_mail_verified ? "`is_mail_verified` = ?," : null}
|
||||
${data.is_admin ? "`is_admin` = ?," : null}
|
||||
${data.gdpr ? "`gdpr` = ?," : null}
|
||||
${data.hash ? "`hash` = ?" : null}`;
|
||||
const _values = [
|
||||
data.username,
|
||||
data.firstname,
|
||||
data.lastname,
|
||||
data.dob,
|
||||
data.email,
|
||||
data.is_mail_verified,
|
||||
data.is_admin,
|
||||
data.gdpr,
|
||||
data.hash,
|
||||
data.id,
|
||||
];
|
||||
${data.gdpr ? "`gdpr` = ?," : null}`
|
||||
|
||||
const _sql = `UPDATE "users" SET ${_template} WHERE 'id' = ?`;
|
||||
handler.execute(_sql, _values).then((result) => {
|
||||
|
@ -5,7 +5,6 @@ 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";
|
||||
|
||||
@ -28,7 +27,7 @@ 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})`);
|
||||
logger.info(`\n\n> User not found (${targetEmail})\n`);
|
||||
return {
|
||||
error: ErrorType.NotFound,
|
||||
message: "The user was not fund.",
|
||||
@ -53,7 +52,7 @@ async function getUserByEmail(targetEmail: string): Promise<IDbUser | ISError> {
|
||||
async function getUserFromIdService(id: string): Promise<IDbUser | ISError> {
|
||||
try {
|
||||
if (!id || id.length !== 36) {
|
||||
logger.info(`Invalid ID (${id})`);
|
||||
logger.info(`\n\n> Invalid ID (${id})\n`);
|
||||
return {
|
||||
error: ErrorType.InvalidData,
|
||||
message: "Invalid ID length.",
|
||||
@ -76,14 +75,22 @@ async function getUserFromIdService(id: string): Promise<IDbUser | ISError> {
|
||||
}
|
||||
}
|
||||
|
||||
//ToTest
|
||||
/**
|
||||
* Registers a new user.
|
||||
*
|
||||
* @param {IReqRegister} inputData - The input data for registration.
|
||||
* @return {Promise<ISError | string>} - A Promise that resolves to either an error or a token.
|
||||
*/
|
||||
async function register(inputData: IReqRegister): Promise<ISError | string> {
|
||||
try {
|
||||
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 ?
|
||||
@ -107,7 +114,7 @@ async function register(inputData: IReqRegister): Promise<ISError | string> {
|
||||
}
|
||||
if (dbUserIfExist.id) {
|
||||
logger.info(
|
||||
`User already exist for email "${inputData.email}".\n(${dbUserIfExist.username}::${dbUserIfExist.id})\n`,
|
||||
`\n\n> User already exist for email "${inputData.email}".\n(${dbUserIfExist.username}::${dbUserIfExist.id})\n`,
|
||||
);
|
||||
return {
|
||||
error: ErrorType.UnAuthorized,
|
||||
@ -133,12 +140,12 @@ async function register(inputData: IReqRegister): Promise<ISError | string> {
|
||||
message: "Error when inserting user in database.",
|
||||
};
|
||||
}
|
||||
logger.info(`New user created ! (${inputData.username}::${currentId})`);
|
||||
logger.info(`\n\n> New user created ! (${inputData.username}::${currentId})\n`);
|
||||
|
||||
// JWT
|
||||
const token = await JwtService.sign(
|
||||
{
|
||||
sub: NewUser.id,
|
||||
sub: currentId,
|
||||
},
|
||||
{
|
||||
alg: "HS512",
|
||||
@ -147,124 +154,123 @@ async function register(inputData: IReqRegister): Promise<ISError | string> {
|
||||
"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})`);
|
||||
} catch (err) {
|
||||
logger.error(`\n\n${err}\n`);
|
||||
return {
|
||||
error: "userNotFound",
|
||||
error: ErrorType.DatabaseError,
|
||||
message: "An unknown error occurred.",
|
||||
};
|
||||
}
|
||||
if (ReqData.password.length < 6) {
|
||||
console.log("X");
|
||||
console.log(`LoginService :> Invalid password (${ReqData.username})`);
|
||||
}
|
||||
|
||||
//ToTest
|
||||
/**
|
||||
* Logs in a user with the provided input data.
|
||||
*
|
||||
* @param inputData - The input data for the login operation.
|
||||
* @property email - The email of the user.
|
||||
* @property password - The password of the user.
|
||||
* @returns A promise that resolves to either an error or a token string.
|
||||
* @throws {ISError} - If an error occurs in the login process.
|
||||
* @throws {string} - If the login was successful, returns a token string.
|
||||
*/
|
||||
async function login(inputData: IReqLogin): Promise<ISError | string> {
|
||||
try {
|
||||
const dbUser = await getUserByEmail(inputData.email);
|
||||
if ("error" in dbUser) {
|
||||
return {
|
||||
error: "invalidPassword",
|
||||
error: dbUser.error,
|
||||
message: dbUser.message,
|
||||
};
|
||||
}
|
||||
if (!dbUser.id) {
|
||||
return {
|
||||
error: ErrorType.NotFound,
|
||||
message: "User not found.",
|
||||
};
|
||||
}
|
||||
const isPasswordValid = await CredentialService.compare(
|
||||
ReqData.password,
|
||||
dbUser.hash,
|
||||
inputData.password,
|
||||
dbUser.hash
|
||||
);
|
||||
if (!isPasswordValid) {
|
||||
console.log(isPasswordValid);
|
||||
console.log(`LoginService :> Invalid password (${ReqData.username})`);
|
||||
return {
|
||||
error: "invalidPassword",
|
||||
error: ErrorType.UnAuthorized,
|
||||
message: "Invalid password.",
|
||||
};
|
||||
}
|
||||
// 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(
|
||||
const token = await JwtService.sign(
|
||||
{
|
||||
sub: dbUser.id,
|
||||
p: [{
|
||||
isAdmin: dbUser.is_admin,
|
||||
gdpr: dbUser.gdpr
|
||||
}]
|
||||
},
|
||||
{
|
||||
alg: "HS512",
|
||||
},
|
||||
"7d",
|
||||
"user",
|
||||
"1d",
|
||||
"user"
|
||||
);
|
||||
|
||||
console.log("USERDATA :>");
|
||||
console.log(userData);
|
||||
return userData;
|
||||
return token;
|
||||
} catch (err) {
|
||||
logger.error(`\n\n${err}\n`);
|
||||
return {
|
||||
error: ErrorType.DatabaseError,
|
||||
message: "An unknown error occurred.",
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
//TOTest
|
||||
/**
|
||||
* 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.
|
||||
* @returns {Promise<Array<IDbUser> | ISError>} The list of users, or an error object if an error occurred.
|
||||
*/
|
||||
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)`);
|
||||
async function getAllUsersService(): Promise<Array<IDbUser> | ISError> {
|
||||
try {
|
||||
const allUsers = await MySqlService.User.getAll(DbHandler);
|
||||
if (allUsers === undefined) {
|
||||
logger.error(`Error retrieving all users.`);
|
||||
return {
|
||||
iat: Date.now(),
|
||||
users: users,
|
||||
length: users.length,
|
||||
error: ErrorType.DatabaseError,
|
||||
message: "An unknown error occurred.",
|
||||
};
|
||||
}
|
||||
return allUsers;
|
||||
} catch (err) {
|
||||
logger.error(`\n\n${err}\n`);
|
||||
return {
|
||||
error: ErrorType.DatabaseError,
|
||||
message: "An unknown error occurred.",
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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})`);
|
||||
async function editUserService(targetId, inputData: IDbUser): Promise<ISError | boolean> {
|
||||
if (!targetId || targetId.length !== 36) {
|
||||
logger.info(`\n\n> Invalid ID (${targetId})\n`);
|
||||
return {
|
||||
error: "userNotFound",
|
||||
error: ErrorType.InvalidData,
|
||||
message: "Invalid ID length.",
|
||||
};
|
||||
}
|
||||
|
||||
logger.info(`EDIT :> User updated (${targetId})`);
|
||||
const dbUser = await MySqlService.User.getById(DbHandler, targetId)
|
||||
if (!dbUser.id) {
|
||||
return {
|
||||
error: "none",
|
||||
error: ErrorType.NotFound,
|
||||
message: "User not found.",
|
||||
};
|
||||
}
|
||||
const result = await MySqlService.User.update(DbHandler, {
|
||||
username: inputData.username,
|
||||
firstname: inputData.firstname,
|
||||
lastname: inputData.lastname,
|
||||
dob: inputData.dob,
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a user from the database.
|
||||
@ -285,7 +291,8 @@ async function deleteUserService(targetId) {
|
||||
}
|
||||
}
|
||||
|
||||
logger.debug("Service loaded.");
|
||||
logger.debug("\nService loaded.");
|
||||
|
||||
const UserService = {
|
||||
register: register,
|
||||
login: login,
|
||||
|
Loading…
x
Reference in New Issue
Block a user