feat(app): update debug syntax, refactor HTTP status codes

- Updated import and usage of `isDebugMode` function across multiple controllers and services for better readability
- Improved the use of HTTP status codes in `auth.controller.ts` for more accurate responses
- Refactored HTTP status codes `HttpStatusCode` enum to include detailed comments

Signed-off-by: Mathis <yidhra@tuta.io>
This commit is contained in:
Mathis H (Avnyr) 2024-05-02 15:29:19 +02:00
parent 8644b5add6
commit 1cbc771251
Signed by: Mathis
GPG Key ID: DD9E0666A747D126
12 changed files with 287 additions and 67 deletions

View File

@ -52,11 +52,11 @@ try {
try {
app.listen(process.env["APP_PORT"]);
logger.info(
`Server is running !\n >> Memory total: ${
Math.round(process.memoryUsage().rss / 1_000_000)
} Mio\n >> Memory heap: ${
Math.round(process.memoryUsage().heapUsed / 1_000_000)
} Mio\n`,
`Server is running !\n >> Memory total: ${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

@ -1,22 +1,31 @@
import JwtService from "@services/jwt.service";
import type { IReqEditUserData } from "@interfaces/IReqEditUserData";
import { HttpStatusCode } from "@interfaces/requests/HttpStatusCode";
import type { IReqRegister } from "@interfaces/requests/IReqRegister";
import UserService from "@services/user.service";
import { isDebugMode } from "@utils/debugState";
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",
});
async function registerUser(req: Request, res: Response): Promise<unknown> {
/**
* Registers a user.
*
* @param {Request} req - The request object.
* @param {Response} res - The response object.
*
* @return {Promise} - A promise that resolves with the registration result or rejects with an error.
*/
async function registerUser(req: Request, res: Response): Promise<Response> {
const body: IReqRegister = req.body;
if (!body) {
logger.warn(`Invalid input data (${req.ip})`);
return res.type("application/json").status(400).json({
return res.type("application/json").status(HttpStatusCode.BadRequest).json({
error: "Invalid input data",
});
}
@ -28,14 +37,14 @@ async function registerUser(req: Request, res: Response): Promise<unknown> {
!body.email
) {
logger.warn(`Field(s) missing (${req.ip})`);
return res.type("application/json").status(400).json({
return res.type("application/json").status(HttpStatusCode.BadRequest).json({
error: "Field(s) missing",
});
}
if (!isEmail(body.email)) {
logger.warn(`Invalid email format (${req.ip})`);
return res.type("application/json").status(400).json({
return res.type("application/json").status(HttpStatusCode.BadRequest).json({
error: "Invalid email format",
});
}
@ -55,16 +64,19 @@ async function registerUser(req: Request, res: Response): Promise<unknown> {
const RegisterServiceResult = await UserService.register(sanitizeData);
if (RegisterServiceResult.error === "gdprNotApproved") {
if (typeof RegisterServiceResult !== 'string' && RegisterServiceResult.message === "GDPR acceptance is required.") {
logger.warn(`GDPR not approved (${req.ip})`);
return res.status(400).json({
return res.status(HttpStatusCode.BadRequest).json({
error: RegisterServiceResult.error,
message: "GDPR not accepted.",
});
}
if (typeof RegisterServiceResult !== 'string' && RegisterServiceResult.error === 5) {
if (
typeof RegisterServiceResult !== "string" &&
RegisterServiceResult.error === 5
) {
logger.warn(`The user already exists (${sanitizeData.email})`);
return res.type("application/json").status(400).json({
return res.type("application/json").status(HttpStatusCode.Conflict).json({
error: RegisterServiceResult.error,
message: "The user already exists.",
});
@ -72,7 +84,10 @@ 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({token:RegisterServiceResult});
return res
.type("application/json")
.status(HttpStatusCode.Ok)
.json({ token: RegisterServiceResult });
}
/**
@ -86,19 +101,19 @@ async function registerUser(req: Request, res: Response): Promise<unknown> {
async function loginUser(req: Request, res: Response): Promise<void> {
const body = req.body;
if (!body) {
res.type("application/json").status(400).json({
res.type("application/json").status(HttpStatusCode.BadRequest).json({
error: "Invalid input data",
});
}
if (!body.password || !body.username) {
if (!body.password || !body.email) {
logger.warn(`Field(s) missing (${req.ip})`);
res.type("application/json").status(400).json({
res.type("application/json").status(HttpStatusCode.BadRequest).json({
error: "Field(s) missing",
});
}
const loginData = {
username: `${body.username}`,
email: `${body.email}`,
password: `${body.password}`,
};
console.log(body);
@ -107,13 +122,13 @@ async function loginUser(req: Request, res: Response): Promise<void> {
if (LoginServiceResult.error === "userNotFound") {
console.log("POOL");
res.type("application/json").status(404).json({
res.type("application/json").status(HttpStatusCode.NotFound).json({
error: LoginServiceResult.error,
message: "User not found.",
});
}
if (LoginServiceResult.error === "invalidPassword") {
res.type("application/json").status(401).json({
res.type("application/json").status(HttpStatusCode.NotAcceptable).json({
error: LoginServiceResult.error,
message: "Invalid password.",
});
@ -126,35 +141,35 @@ async function getAllUsers(req: Request, res: Response) {
const bearerToken = authHeader?.split(" ")[1];
if (!bearerToken) {
logger.warn(`Bearer token not provided (${req.ip})`);
return res.type("application/json").status(401).json({
error: "Unauthorized",
return res.type("application/json").status(HttpStatusCode.Forbidden).json({
error: "Invalid token",
});
}
const payload = await JwtService.verify(bearerToken);
if (!payload) {
if (!payload || !payload.sub) {
logger.warn(`Unauthorized access attempt (${req.ip})`);
return res.type("application/json").status(401).json({
error: "Unauthorized",
return res.type("application/json").status(HttpStatusCode.Forbidden).json({
error: "Invalid token",
});
}
const sourceUser = await UserService.getFromId(payload.sub);
if (!sourceUser) {
return res.type("application/json").status(404).json({
return res.type("application/json").status(HttpStatusCode.ImATeapot).json({
error: "You dont exist anymore",
});
}
if (!sourceUser.is_admin) {
return res.type("application/json").status(403).json({
return res.type("application/json").status(HttpStatusCode.Forbidden).json({
error: "Unauthorized",
});
}
const AllUserResponse = await UserService.getAll();
if (!AllUserResponse.users) {
return res.type("application/json").status(500).json({
return res.type("application/json").status(HttpStatusCode.InternalServerError).json({
error: "Internal server error",
});
}
return res.type("application/json").status(200).json(AllUserResponse);
return res.type("application/json").status(HttpStatusCode.Found).json(AllUserResponse);
}
async function getUser(req: Request, res: Response) {
@ -162,25 +177,25 @@ async function getUser(req: Request, res: Response) {
const bearerToken = authHeader?.split(" ")[1];
if (!bearerToken) {
logger.warn(`Bearer token not provided (${req.ip})`);
return res.type("application/json").status(401).json({
return res.type("application/json").status(HttpStatusCode.Unauthorized).json({
error: "Unauthorized",
});
}
const payload = await JwtService.verify(bearerToken);
if (!payload) {
if (!payload || !payload.sub) {
logger.warn(`Unauthorized access attempt (${req.ip})`);
return res.type("application/json").status(401).json({
return res.type("application/json").status(HttpStatusCode.Unauthorized).json({
error: "Unauthorized",
});
}
const sourceUser = await UserService.getFromId(payload.sub);
if (!sourceUser) {
return res.type("application/json").status(404).json({
return res.type("application/json").status(HttpStatusCode.ImATeapot).json({
error: "You dont exist anymore",
});
}
if (!sourceUser.is_admin) {
return res.type("application/json").status(403).json({
if ("username" in sourceUser && !sourceUser.is_admin) {
return res.type("application/json").status(HttpStatusCode.Unauthorized).json({
error: "Unauthorized",
});
}
@ -188,7 +203,7 @@ async function getUser(req: Request, res: Response) {
const dbUser = await UserService.getFromId(userId);
if (!dbUser) {
logger.warn(`User not found (${req.ip})`);
return res.type("application/json").status(404).json({
return res.type("application/json").status(HttpStatusCode.NotFound).json({
error: "User not found",
});
}
@ -196,7 +211,7 @@ async function getUser(req: Request, res: Response) {
delete dbUser.passwordHash;
// @ts-ignore
delete dbUser._id;
return res.type("application/json").status(200).json(dbUser);
return res.type("application/json").status(HttpStatusCode.Found).json(dbUser);
}
//FEAT - Implement re-auth by current password in case of password change
@ -216,7 +231,7 @@ async function editUser(req: Request, res: Response) {
});
}
const payload = await JwtService.verify(bearerToken);
if (!payload) {
if (!payload || !payload.sub) {
logger.warn(`Unauthorized access attempt (${req.ip})`);
return res.type("application/json").status(401).json({
error: "Unauthorized",
@ -373,24 +388,24 @@ async function getSelf(req: Request, res: Response) {
});
}
const payload = await JwtService.verify(bearerToken);
if (!payload) {
if (!payload || !payload.sub) {
logger.warn(`Unauthorized access attempt (${req.ip})`);
return res.type("application/json").status(401).json({
error: "Unauthorized",
});
}
const dbUser = await UserService.getFromId(payload.sub);
if (!dbUser) {
const GetUserResult = await UserService.getFromId(payload.sub);
if (!GetUserResult) {
return res.type("application/json").status(404).json({
error: "User not found",
});
}
return res.type("application/json").status(200).json({
id: dbUser.id,
username: dbUser.username,
firstName: dbUser.firstname,
lastName: dbUser.firstname,
isAdmin: dbUser.firstname,
id: GetUserResult.id,
username: GetUserResult.username,
firstName: GetUserResult.firstname,
lastName: GetUserResult.firstname,
isAdmin: GetUserResult.firstname,
});
}

View File

@ -3,7 +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 { isDebugMode } from "@utils/debugState";
//import {body} from "express-validator";
const logger = new Logger({

View File

@ -1,8 +1,8 @@
import type IDbCategory from "@interfaces/database/IDbCategory";
import CategoryService from "@services/category.service";
import { isDebugMode } from "@utils/debugState";
import type { Request, Response } from "express";
import { Logger } from "tslog";
import {isDebugMode} from "@utils/debugState";
//import {validationResult} from "express-validator";
const logger = new Logger({

View File

@ -1,9 +1,9 @@
import type IDbModel from "@interfaces/database/IDbModel";
import CategoryService from "@services/category.service";
import ModelService from "@services/model.service";
import { isDebugMode } from "@utils/debugState";
import type { Request, Response } from "express";
import { Logger } from "tslog";
import {isDebugMode} from "@utils/debugState";
//import {validationResult} from "express-validator";
const logger = new Logger({

View File

@ -1,57 +1,258 @@
export enum HttpStatusCode {
/**
* Status code for a request indicating that the server received the request headers
* and the client should proceed to send the request body.
* @enum {number}
*/
Continue = 100,
/**
* Status code for a request indicating that the requester has asked the server to switch protocols.
* @enum {number}
*/
SwitchingProtocols = 101,
/**
* Status code for a request indicating that the server has received and is processing the request,
* but no response is available yet.
* @enum {number}
*/
Processing = 102,
/**
* Successful HTTP response
* @enum {number}
*/
Ok = 200,
/**
* Request has been fulfilled; new resource created as a result.
* @enum {number}
*/
Created = 201,
/**
* Request accepted, but not yet processed.
* @enum {number}
*/
Accepted = 202,
/**
* Request successful. Meta-information returned is from original server, not local.
* @enum {number}
*/
NonAuthoritativeInformation = 203,
/**
* Request processed. No content returned.
* @enum {number}
*/
NoContent = 204,
/**
* Server specifies part of document should be reset.
* @enum {number}
*/
ResetContent = 205,
/**
* Partial content returned due to GET.
* @enum {number}
*/
PartialContent = 206,
/**
* Multiple Resource Meta-Data: Could be used for collection instances.
* @enum {number}
*/
MultiStatus = 207,
/**
* Status code used to indicate that a certain request has been already reported.
* @enum {number}
*/
AlreadyReported = 208,
/**
* The server has fulfilled the request for the content, and the content is being conveyed in a manner described by the Content-Encoding, Content-Range, and Content-Type headers.
* @enum {number}
*/
ImUsed = 226,
/**
* @enum {number}
* @description HTTP status code for multiple choices
*/
MultipleChoices = 300,
/**
* @enum {number}
* @description HTTP status code for moved permanently
*/
MovedPermanently = 301,
/**
* @enum {number}
* @description HTTP status code for found
*/
Found = 302,
/**
* @enum {number}
* @description HTTP status code for see other
*/
SeeOther = 303,
/**
* @enum {number}
* @description HTTP status code for not modified
*/
NotModified = 304,
/**
* @enum {number}
* @description HTTP status code for use proxy
*/
UseProxy = 305,
/**
* @enum {number}
* @description HTTP status code for temporary redirect
*/
TemporaryRedirect = 307,
/**
* @enum {number}
* @description HTTP status code for permanent redirect
*/
PermanentRedirect = 308,
/** @description Client error: The server could not understand the request due to invalid syntax. */
BadRequest = 400,
/** @description Client error: The client must authenticate itself to get the requested response. */
Unauthorized = 401,
/** @description Client error: This response code is reserved for future use. */
PaymentRequired = 402,
/** @description Client error: The client does not have access rights to the content; that is, it is unauthorized, so the server is refusing to give the requested resource. */
Forbidden = 403,
/** @description Client error: The server cannot find requested resource. */
NotFound = 404,
/** @description Client error: The request method is not supported by the server and cannot be handled. */
MethodNotAllowed = 405,
/** @description Client error: This response is sent when the web server, after performing server-driven content negotiation, doesn't find any content following the criteria given by the user agent. */
NotAcceptable = 406,
/** @description Client error: This is similar to 401 (Unauthorised), but indicates that the client must authenticate itself to get the requested response. */
ProxyAuthenticationRequired = 407,
/** @description Client error: This response is sent on an idle connection by some servers, even without any previous request by the client. */
RequestTimeout = 408,
/** @description Client error: This response is sent when a request conflicts with the current state of the server. */
Conflict = 409,
/** @description Client error: This response is sent when the requested content has been permanently deleted from server, with no forwarding address. */
Gone = 410,
/** @description Client error: The server refuses to accept the request without a defined Content- Length. */
LengthRequired = 411,
/** @description Client error: The precondition given in the request evaluated to false by the server. */
PreconditionFailed = 412,
/** @description Client error: Request entity is larger than limits defined by server. */
PayloadTooLarge = 413,
/** @description Client error: The URI requested by the client is longer than the server is willing to interpret. */
UriTooLong = 414,
/** @description Client error: The media format of the requested data is not supported by the server, so the server is rejecting the request. */
UnsupportedMediaType = 415,
/** @description Client error: The range specified by the Range header field in the request can't be fulfilled. */
RangeNotSatisfiable = 416,
/** @description Client error: This response code means the expectation indicated by the Expect request header field can't be met by the server. */
ExpectationFailed = 417,
/** @description Client error: The server refuses to brew coffee because it is, permanently, a teapot. */
ImATeapot = 418,
/**
* @enum
* @name MisdirectedRequest
* @description Represents HTTP status code 421: Misdirected Request.
* The client should switch to a different protocol such as TLS/1.0.
*/
MisdirectedRequest = 421,
/**
* @enum
* @name UnprocessableEntity
* @description Represents HTTP status code 422: Unprocessable Entity.
* The request was well-formed but was unable to be followed due to semantic errors.
*/
UnprocessableEntity = 422,
/**
* @enum
* @name Locked
* @description Represents HTTP status code 423: Locked.
* The resource that is being accessed is locked.
*/
Locked = 423,
/**
* @enum
* @name FailedDependency
* @description Represents HTTP status code 424: Failed Dependency.
* The request failed because it depended on another request and that request failed.
*/
FailedDependency = 424,
/**
* @enum
* @name TooEarly
* @description Represents HTTP status code 425: Too Early.
* Indicates that the server is unwilling to risk processing a request that might be replayed.
*/
TooEarly = 425,
/**
* @enum
* @name UpgradeRequired
* @description Represents HTTP status code 426: Upgrade Required.
* The client should switch to a different protocol.
*/
UpgradeRequired = 426,
/**
* @enum
* @name PreconditionRequired
* @description Represents HTTP status code 428: Precondition Required.
* The client must first fulfill certain precondition.
*/
PreconditionRequired = 428,
/**
* @enum
* @name TooManyRequests
* @description Represents HTTP status code 429: Too Many Requests.
* The user has sent too many requests in a certain amount of time.
*/
TooManyRequests = 429,
/**
* @enum
* @name RequestHeaderFieldsTooLarge
* @description Represents HTTP status code 431: Request Header Fields Too Large.
* The server is unwilling to process the request because its header fields are too large.
*/
RequestHeaderFieldsTooLarge = 431,
/**
* @enum
* @name UnavailableForLegalReasons
* @description Represents HTTP status code 451: Unavailable For Legal Reasons.
* The server is denying access for legal reasons.
*/
UnavailableForLegalReasons = 451,
InternalServerError = 500,
@ -64,5 +265,5 @@ export enum HttpStatusCode {
InsufficientStorage = 507,
LoopDetected = 508,
NotExtended = 510,
NetworkAuthenticationRequired = 511
}
NetworkAuthenticationRequired = 511,
}

View File

@ -1,8 +1,8 @@
import type IDbBrand from "@interfaces/database/IDbBrand";
import MysqlService from "@services/mysql.service";
import { isDebugMode } from "@utils/debugState";
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({

View File

@ -1,8 +1,8 @@
import type { IDbCategory } from "@interfaces/database/IDbCategory";
import MysqlService from "@services/mysql.service";
import { isDebugMode } from "@utils/debugState";
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({

View File

@ -1,3 +1,4 @@
import { isDebugMode } from "@utils/debugState";
import {
type JWTHeaderParameters,
type JWTPayload,
@ -5,7 +6,6 @@ import {
jwtVerify,
} from "jose";
import { Logger } from "tslog";
import {isDebugMode} from "@utils/debugState";
const logger = new Logger({
name: "JwtService",

View File

@ -1,8 +1,8 @@
import type IDbModel from "@interfaces/database/IDbModel";
import MysqlService from "@services/mysql.service";
import { isDebugMode } from "@utils/debugState";
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({

View File

@ -10,9 +10,9 @@ import type { IDbStatusResult } from "@interfaces/database/IDbStatusResult";
import type { IDbUser } from "@interfaces/database/IDbUser";
import type { IDbVehicle } from "@interfaces/database/IDbVehicle";
import type { IUserUpdate } from "@interfaces/services/IUserUpdate";
import { isDebugMode } from "@utils/debugState";
import mysql, { type Connection, type ConnectionOptions } from "mysql2";
import { Logger } from "tslog";
import {isDebugMode} from "@utils/debugState";
const access: ConnectionOptions = {
host: `${process.env["MYSQL_HOST"]}`,
@ -38,9 +38,10 @@ class MysqlHandler {
this.Logger.error(`\n\n> Error connecting to MySQL: \n${err}\n`);
process.exit(1);
}
if (isDebugMode()) this.Logger.debug(
`\n\n> Connected to MySQL database (${access.database})\n`,
);
if (isDebugMode())
this.Logger.debug(
`\n\n> Connected to MySQL database (${access.database})\n`,
);
});
}
closeConnection() {
@ -82,12 +83,16 @@ class MysqlHandler {
(key: string) => `${key}`,
);
const values = Object.values(data.values).map((val) => val);
if (isDebugMode()) this.Logger.debug(
`\n\n>-> Factorized ${_sqlQueryKeys.length} keys for a prepare Query.\n>-> Action: ${data.actionName}\n`,
);
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) {
if (isDebugMode()) this.Logger.trace(`\n\n> Id post-pushed in factorized data.\n ${_id}`);
if (isDebugMode())
this.Logger.trace(
`\n\n> Id post-pushed in factorized data.\n ${_id}`,
);
values.push(_id);
}

View File

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