Compare commits

..

4 Commits

Author SHA1 Message Date
abaeafea8a
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>
2024-05-02 14:18:28 +02:00
cb41c68f77
feat(utils): add debug state utility
This commit includes the creation of a new utility file for checking if the app is running in debugging mode. It exports `isDebugMode()` function that checks the `DEBUG` environment variable's value.

Signed-off-by: Mathis <yidhra@tuta.io>
2024-05-02 14:11:48 +02:00
e98729c9e5
style(app): Remove newline from memory usage logging
This commit modifies the logging of memory and heap usage in the app.ts file. The redundant newline code `\n` at the end of the log message has been removed to enhance readability and neatness of the logs.

Signed-off-by: Mathis <yidhra@tuta.io>
2024-05-02 13:31:56 +02:00
cd09a0c61b
build(others): update TypeScript compiler options in tsconfig.json
This commit updates the TypeScript compilerOptions in tsconfig.json. The changes include higher module resolution targets, adjustments in various compiler constraints, and the modification of paths for baseUrl. This will help improve the code's compile-time and run-time behavior.

Signed-off-by: Mathis <yidhra@tuta.io>
2024-05-02 13:12:54 +02:00
14 changed files with 69 additions and 63 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -2,6 +2,7 @@ import type { IDbCategory } from "@interfaces/database/IDbCategory";
import MysqlService from "@services/mysql.service";
import { Logger } from "tslog";
import { v4 as uuidv4 } from "uuid";
import {isDebugMode} from "@utils/debugState";
const DbHandler = new MysqlService.Handler("CategoryService");
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 = {
create: createCategory,

View File

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

View File

@ -2,6 +2,7 @@ import type IDbModel from "@interfaces/database/IDbModel";
import MysqlService from "@services/mysql.service";
import { Logger } from "tslog";
import { v4 as uuidv4 } from "uuid";
import {isDebugMode} from "@utils/debugState";
const DbHandler = new MysqlService.Handler("ModelService");
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.

View File

@ -12,7 +12,7 @@ import type { IDbVehicle } from "@interfaces/database/IDbVehicle";
import type { IUserUpdate } from "@interfaces/services/IUserUpdate";
import mysql, { type Connection, type ConnectionOptions } from "mysql2";
import { Logger } from "tslog";
import { v4 } from "uuid";
import {isDebugMode} from "@utils/debugState";
const access: ConnectionOptions = {
host: `${process.env["MYSQL_HOST"]}`,
@ -35,10 +35,10 @@ class MysqlHandler {
this.Connection = mysql.createConnection(access);
this.Connection.connect((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);
}
this.Logger.info(
if (isDebugMode()) this.Logger.debug(
`\n\n> Connected to MySQL database (${access.database})\n`,
);
});
@ -51,7 +51,7 @@ class MysqlHandler {
return new Promise((resolve, reject) => {
this.Connection.query(queryString, (err, results) => {
if (err) {
this.Logger.error(`Error executing query: ${err}`);
this.Logger.error(`\n\n> Error executing query: \n${err}\n`);
reject(err);
} else {
resolve(results);
@ -82,12 +82,12 @@ class MysqlHandler {
(key: string) => `${key}`,
);
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`,
);
const sqlQueryKeys = _sqlQueryKeys.join(", ");
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);
}
@ -176,19 +176,6 @@ const MySqlService = {
if (!data.id) return reject("Id is undefined");
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
.factorize({
values: data,

View File

@ -7,6 +7,7 @@ import JwtService from "@services/jwt.service";
import MySqlService from "@services/mysql.service";
import { Logger } from "tslog";
import { v4 } from "uuid";
import {isDebugMode} from "@utils/debugState";
const logger = new Logger({
name: "UserService",
@ -63,14 +64,19 @@ async function getUserFromIdService(id: string): Promise<IDbUser | ISError> {
};
}
const dbUser = await MySqlService.User.getById(DbHandler, id);
if (dbUser === undefined) {
if (dbUser.length === 0) {
logger.info(`User not found (${id})`);
return {
error: ErrorType.NotFound,
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) {
return {
error: ErrorType.DatabaseError,
@ -123,13 +129,7 @@ async function register(inputData: IReqRegister): Promise<ISError | string> {
const dbUserIfExist: IDbUser | ISError = await getUserByEmail(
inputData.email,
);
if ("error" in dbUserIfExist) {
return {
error: dbUserIfExist.error,
message: dbUserIfExist.message,
};
}
if (dbUserIfExist.id) {
if ("username" in dbUserIfExist) {
logger.info(
`\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_email_verified: false,
});
if ("error" in NewUser || NewUser.affectedRows === 0) {
if ("error" in NewUser && NewUser.affectedRows === 0) {
return {
error: ErrorType.DatabaseError,
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 = {
register: register,

9
src/utils/debugState.ts Normal file
View File

@ -0,0 +1,9 @@
import process from "node:process";
export function isDebugMode() {
if (process.env["DEBUG"] === 'true') {
return true;
}
return false;
}

View File

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

View File

@ -1,10 +1,22 @@
{
"compilerOptions": {
"target": "es2017",
"module": "es6",
"rootDir": "./src",
"moduleResolution": "node",
"module": "commonjs",
"declaration": true,
"removeComments": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"allowSyntheticDefaultImports": true,
"target": "ES2021",
"sourceMap": true,
"outDir": "./dist",
"baseUrl": "./",
"incremental": true,
"skipLibCheck": true,
"strictNullChecks": true,
"noImplicitAny": true,
"strictBindCallApply": true,
"forceConsistentCasingInFileNames": false,
"noFallthroughCasesInSwitch": true,
"paths": {
"@services/*": [
"src/services/*"
@ -26,19 +38,11 @@
]
},
"resolveJsonModule": true,
"declaration": true,
"declarationMap": true,
"sourceMap": true,
"outDir": "./dist",
"removeComments": false,
"noEmitOnError": true,
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"strict": true,
"noImplicitAny": true,
"strictNullChecks": true,
"strictFunctionTypes": true,
"strictBindCallApply": true,
"strictPropertyInitialization": true,
"noImplicitThis": true,
"useUnknownInCatchVariables": true,
@ -47,12 +51,10 @@
"noUnusedParameters": true,
"exactOptionalPropertyTypes": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
"noUncheckedIndexedAccess": true,
"noImplicitOverride": true,
"noPropertyAccessFromIndexSignature": true,
"allowUnusedLabels": true,
"allowUnreachableCode": true,
"skipLibCheck": true
"allowUnreachableCode": true
},
}