feat(services-controllers): implement debug mode and refactor error handling

- Implement debug mode for logging in services and controllers.
- Improve the precision of memory usage information in the app by rounding.
- Refactor error handling in services, notably in user service register function for better error checking.
- Include a condition in the AdminGuard validator to check if the token is valid.
- Fix issue in auth controller that was misidentifying user registration error as a successful registration.
- Refactor log display in Mysql service error throwing for better readability.
- Refactor memory usage information display in app for better readability.

These changes aim to improve the clarity of logs and accuracy of error reporting for a better debugging experience. The implementation of debug mode gives more control over the information displayed in the development phase. Several fixes in error handling also contribute to a more robust system.

Signed-off-by: Mathis <yidhra@tuta.io>
This commit is contained in:
Mathis H (Avnyr) 2024-05-02 14:18:28 +02:00
parent cb41c68f77
commit abaeafea8a
Signed by: Mathis
GPG Key ID: DD9E0666A747D126
12 changed files with 42 additions and 47 deletions

View File

@ -43,7 +43,7 @@ try {
app.use("/auth", AuthRouter); app.use("/auth", AuthRouter);
app.use("/catalog", CatalogRouter); app.use("/catalog", CatalogRouter);
app.use("/rent", RentRouter); app.use("/rent", RentRouter);
logger.info("Routers loaded !\n"); logger.info("Routers loaded !");
} catch (err) { } catch (err) {
logger.error(err); logger.error(err);
throw null; throw null;
@ -53,9 +53,9 @@ try {
app.listen(process.env["APP_PORT"]); app.listen(process.env["APP_PORT"]);
logger.info( logger.info(
`Server is running !\n >> Memory total: ${ `Server is running !\n >> Memory total: ${
process.memoryUsage().rss / 1_000_000 Math.round(process.memoryUsage().rss / 1_000_000)
} Mio\n >> Memory heap usage: ${ } Mio\n >> Memory heap: ${
process.memoryUsage().heapUsed / 1_000_000 Math.round(process.memoryUsage().heapUsed / 1_000_000)
} Mio\n`, } Mio\n`,
); );
} catch (error) { } catch (error) {

View File

@ -6,6 +6,7 @@ import UserService from "@services/user.service";
import { isEmail } from "@utils/validators/email"; import { isEmail } from "@utils/validators/email";
import type { Request, Response } from "express"; import type { Request, Response } from "express";
import { Logger } from "tslog"; import { Logger } from "tslog";
import {isDebugMode} from "@utils/debugState";
const logger = new Logger({ const logger = new Logger({
name: "AuthController", name: "AuthController",
@ -61,8 +62,8 @@ async function registerUser(req: Request, res: Response): Promise<unknown> {
message: "GDPR not accepted.", message: "GDPR not accepted.",
}); });
} }
if (RegisterServiceResult.error === "exist") { if (typeof RegisterServiceResult !== 'string' && RegisterServiceResult.error === 5) {
logger.warn(`The user already exists (${req.ip})`); logger.warn(`The user already exists (${sanitizeData.email})`);
return res.type("application/json").status(400).json({ return res.type("application/json").status(400).json({
error: RegisterServiceResult.error, error: RegisterServiceResult.error,
message: "The user already exists.", message: "The user already exists.",
@ -70,8 +71,8 @@ async function registerUser(req: Request, res: Response): Promise<unknown> {
} }
// SUCCESS // SUCCESS
logger.info(`User registered successfully (${sanitizeData.username})`); //logger.info(`User registered successfully (${sanitizeData.username})`);
return res.type("application/json").status(201).json(RegisterServiceResult); return res.type("application/json").status(201).json({token:RegisterServiceResult});
} }
/** /**
@ -393,7 +394,7 @@ async function getSelf(req: Request, res: Response) {
}); });
} }
logger.debug("\nController loaded."); if (isDebugMode()) logger.debug("\nController loaded.");
const AuthController = { const AuthController = {
register: registerUser, register: registerUser,

View File

@ -3,6 +3,7 @@ import { Logger } from "tslog";
import type IDbBrand from "@interfaces/database/IDbBrand"; import type IDbBrand from "@interfaces/database/IDbBrand";
import BrandService from "@services/brand.service"; import BrandService from "@services/brand.service";
import {isDebugMode} from "@utils/debugState";
//import {body} from "express-validator"; //import {body} from "express-validator";
const logger = new Logger({ const logger = new Logger({
@ -171,7 +172,7 @@ async function deleteBrand(req: Request, res: Response): Promise<Response> {
//TODO get models of the brand //TODO get models of the brand
logger.debug("\nController loaded."); if (isDebugMode()) logger.debug("\nController loaded.");
const BrandController = { const BrandController = {
create: createBrand, create: createBrand,

View File

@ -2,6 +2,7 @@ import type IDbCategory from "@interfaces/database/IDbCategory";
import CategoryService from "@services/category.service"; import CategoryService from "@services/category.service";
import type { Request, Response } from "express"; import type { Request, Response } from "express";
import { Logger } from "tslog"; import { Logger } from "tslog";
import {isDebugMode} from "@utils/debugState";
//import {validationResult} from "express-validator"; //import {validationResult} from "express-validator";
const logger = new Logger({ const logger = new Logger({
@ -177,7 +178,7 @@ async function getBySlugCategory(
}); });
} }
logger.debug("\nController loaded."); if (isDebugMode()) logger.debug("\nController loaded.");
const CategoryController = { const CategoryController = {
create: createCategory, create: createCategory,

View File

@ -3,6 +3,7 @@ import CategoryService from "@services/category.service";
import ModelService from "@services/model.service"; import ModelService from "@services/model.service";
import type { Request, Response } from "express"; import type { Request, Response } from "express";
import { Logger } from "tslog"; import { Logger } from "tslog";
import {isDebugMode} from "@utils/debugState";
//import {validationResult} from "express-validator"; //import {validationResult} from "express-validator";
const logger = new Logger({ const logger = new Logger({
@ -131,7 +132,7 @@ async function deleteModel(req: Request, res: Response): Promise<Response> {
//TODO get model with vehicle available. //TODO get model with vehicle available.
logger.debug("\nController loaded."); if (isDebugMode()) logger.debug("\nController loaded.");
const ModelController = { const ModelController = {
create: createModel, create: createModel,

View File

@ -2,6 +2,7 @@ import type IDbBrand from "@interfaces/database/IDbBrand";
import MysqlService from "@services/mysql.service"; import MysqlService from "@services/mysql.service";
import { Logger } from "tslog"; import { Logger } from "tslog";
import { v4 as uuidv4 } from "uuid"; import { v4 as uuidv4 } from "uuid";
import {isDebugMode} from "@utils/debugState";
const DbHandler = new MysqlService.Handler("BrandService"); const DbHandler = new MysqlService.Handler("BrandService");
const logger = new Logger({ const logger = new Logger({
@ -168,7 +169,7 @@ async function deleteBrand(brandId: string): Promise<boolean> {
return true; return true;
} }
logger.debug("\nService loaded."); if (isDebugMode()) logger.debug("\nService loaded.");
const BrandService = { const BrandService = {
create: createBrand, create: createBrand,

View File

@ -2,6 +2,7 @@ import type { IDbCategory } from "@interfaces/database/IDbCategory";
import MysqlService from "@services/mysql.service"; import MysqlService from "@services/mysql.service";
import { Logger } from "tslog"; import { Logger } from "tslog";
import { v4 as uuidv4 } from "uuid"; import { v4 as uuidv4 } from "uuid";
import {isDebugMode} from "@utils/debugState";
const DbHandler = new MysqlService.Handler("CategoryService"); const DbHandler = new MysqlService.Handler("CategoryService");
const logger = new Logger({ const logger = new Logger({
@ -127,7 +128,7 @@ async function deleteCategory(id: string): Promise<unknown> {
} }
} }
logger.debug("\nService loaded."); if (isDebugMode()) logger.debug("\nService loaded.");
const CategoryService = { const CategoryService = {
create: createCategory, create: createCategory,

View File

@ -5,6 +5,7 @@ import {
jwtVerify, jwtVerify,
} from "jose"; } from "jose";
import { Logger } from "tslog"; import { Logger } from "tslog";
import {isDebugMode} from "@utils/debugState";
const logger = new Logger({ const logger = new Logger({
name: "JwtService", name: "JwtService",
@ -64,7 +65,7 @@ async function JwtSignService(
.sign(new TextEncoder().encode(`${process.env["JWT_SECRET"]}`)); .sign(new TextEncoder().encode(`${process.env["JWT_SECRET"]}`));
} }
logger.debug("\nService loaded."); if (isDebugMode()) logger.debug("\nService loaded.");
const JwtService = { const JwtService = {
verify: JwtVerifyService, verify: JwtVerifyService,

View File

@ -2,6 +2,7 @@ import type IDbModel from "@interfaces/database/IDbModel";
import MysqlService from "@services/mysql.service"; import MysqlService from "@services/mysql.service";
import { Logger } from "tslog"; import { Logger } from "tslog";
import { v4 as uuidv4 } from "uuid"; import { v4 as uuidv4 } from "uuid";
import {isDebugMode} from "@utils/debugState";
const DbHandler = new MysqlService.Handler("ModelService"); const DbHandler = new MysqlService.Handler("ModelService");
const logger = new Logger({ const logger = new Logger({
@ -135,7 +136,7 @@ async function getAllModels(): Promise<IDbModel[] | null> {
} }
} }
logger.debug("\nService loaded."); if (isDebugMode()) logger.debug("\nService loaded.");
/** /**
* ModelService is responsible for managing models. * ModelService is responsible for managing models.

View File

@ -12,7 +12,7 @@ import type { IDbVehicle } from "@interfaces/database/IDbVehicle";
import type { IUserUpdate } from "@interfaces/services/IUserUpdate"; import type { IUserUpdate } from "@interfaces/services/IUserUpdate";
import mysql, { type Connection, type ConnectionOptions } from "mysql2"; import mysql, { type Connection, type ConnectionOptions } from "mysql2";
import { Logger } from "tslog"; import { Logger } from "tslog";
import { v4 } from "uuid"; import {isDebugMode} from "@utils/debugState";
const access: ConnectionOptions = { const access: ConnectionOptions = {
host: `${process.env["MYSQL_HOST"]}`, host: `${process.env["MYSQL_HOST"]}`,
@ -35,10 +35,10 @@ class MysqlHandler {
this.Connection = mysql.createConnection(access); this.Connection = mysql.createConnection(access);
this.Connection.connect((err) => { this.Connection.connect((err) => {
if (err) { if (err) {
this.Logger.error(`Error connecting to MySQL: ${err}`); this.Logger.error(`\n\n> Error connecting to MySQL: \n${err}\n`);
process.exit(1); process.exit(1);
} }
this.Logger.info( if (isDebugMode()) this.Logger.debug(
`\n\n> Connected to MySQL database (${access.database})\n`, `\n\n> Connected to MySQL database (${access.database})\n`,
); );
}); });
@ -51,7 +51,7 @@ class MysqlHandler {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
this.Connection.query(queryString, (err, results) => { this.Connection.query(queryString, (err, results) => {
if (err) { if (err) {
this.Logger.error(`Error executing query: ${err}`); this.Logger.error(`\n\n> Error executing query: \n${err}\n`);
reject(err); reject(err);
} else { } else {
resolve(results); resolve(results);
@ -82,12 +82,12 @@ class MysqlHandler {
(key: string) => `${key}`, (key: string) => `${key}`,
); );
const values = Object.values(data.values).map((val) => val); const values = Object.values(data.values).map((val) => val);
this.Logger.debug( if (isDebugMode()) this.Logger.debug(
`\n\n>-> Factorized ${_sqlQueryKeys.length} keys for a prepare Query.\n>-> Action: ${data.actionName}\n`, `\n\n>-> Factorized ${_sqlQueryKeys.length} keys for a prepare Query.\n>-> Action: ${data.actionName}\n`,
); );
const sqlQueryKeys = _sqlQueryKeys.join(", "); const sqlQueryKeys = _sqlQueryKeys.join(", ");
if (_id && _id.length > 2) { if (_id && _id.length > 2) {
this.Logger.trace(`Id post-pushed in factorized data`); if (isDebugMode()) this.Logger.trace(`\n\n> Id post-pushed in factorized data.\n ${_id}`);
values.push(_id); values.push(_id);
} }
@ -176,19 +176,6 @@ const MySqlService = {
if (!data.id) return reject("Id is undefined"); if (!data.id) return reject("Id is undefined");
if (data.id.length !== 36) return reject("Id invalid"); if (data.id.length !== 36) return reject("Id invalid");
// const _values = [
// data.id,
// data.username,
// data.firstname,
// data.lastname,
// data.dob,
// data.email,
// data.is_mail_verified,
// data.is_admin,
// data.gdpr,
// data.hash,
// ];
handler handler
.factorize({ .factorize({
values: data, values: data,

View File

@ -7,6 +7,7 @@ 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"; import { v4 } from "uuid";
import {isDebugMode} from "@utils/debugState";
const logger = new Logger({ const logger = new Logger({
name: "UserService", name: "UserService",
@ -63,14 +64,19 @@ async function getUserFromIdService(id: string): Promise<IDbUser | ISError> {
}; };
} }
const dbUser = await MySqlService.User.getById(DbHandler, id); const dbUser = await MySqlService.User.getById(DbHandler, id);
if (dbUser === undefined) { if (dbUser.length === 0) {
logger.info(`User not found (${id})`); logger.info(`User not found (${id})`);
return { return {
error: ErrorType.NotFound, error: ErrorType.NotFound,
message: "The user was not found.", message: "The user was not found.",
}; };
} }
return dbUser; const firstUser = dbUser[0];
if (firstUser) return firstUser;
return {
error: ErrorType.ServiceError,
message: "No user found.",
};
} catch (err) { } catch (err) {
return { return {
error: ErrorType.DatabaseError, error: ErrorType.DatabaseError,
@ -123,13 +129,7 @@ async function register(inputData: IReqRegister): Promise<ISError | string> {
const dbUserIfExist: IDbUser | ISError = await getUserByEmail( const dbUserIfExist: IDbUser | ISError = await getUserByEmail(
inputData.email, inputData.email,
); );
if ("error" in dbUserIfExist) { if ("username" in dbUserIfExist) {
return {
error: dbUserIfExist.error,
message: dbUserIfExist.message,
};
}
if (dbUserIfExist.id) {
logger.info( logger.info(
`\n\n> 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`,
); );
@ -152,7 +152,7 @@ async function register(inputData: IReqRegister): Promise<ISError | string> {
is_admin: false, is_admin: false,
is_email_verified: false, is_email_verified: false,
}); });
if ("error" in NewUser || NewUser.affectedRows === 0) { if ("error" in NewUser && NewUser.affectedRows === 0) {
return { return {
error: ErrorType.DatabaseError, error: ErrorType.DatabaseError,
message: "Error when inserting user in database.", message: "Error when inserting user in database.",
@ -326,7 +326,7 @@ async function deleteUserService(targetId) {
} }
} }
logger.debug("\nService loaded."); if (isDebugMode()) logger.debug("\nService loaded.");
const UserService = { const UserService = {
register: register, register: register,

View File

@ -33,7 +33,7 @@ async function AdminGuard(req: Request, res: Response, next: NextFunction) {
const token = await JwtService.verify(bearerToken); const token = await JwtService.verify(bearerToken);
if (token) { if (token && token.sub) {
// @ts-ignore // @ts-ignore
const isSourceAdmin = await MysqlService.User.getAdminStateForId( const isSourceAdmin = await MysqlService.User.getAdminStateForId(
DbHandler, DbHandler,