refactor: reorganize code and enhance readability

Code imports rearranged and updated for better consistency. Missing semicolons were added in multiple files to improve code readability and standards compliance. The usage of whitespace and indentation was also standardized across multiple files to improve overall code clarity.
This commit is contained in:
Mathis H (Avnyr) 2024-05-23 12:31:57 +02:00
parent b640dc7671
commit 8e14bf77b4
Signed by: Mathis
GPG Key ID: DD9E0666A747D126
19 changed files with 603 additions and 254 deletions

View File

@ -18,7 +18,7 @@
"recommended": true, "recommended": true,
"performance": { "performance": {
"recommended": true, "recommended": true,
"noDelete": "off", "noDelete": "off"
}, },
"suspicious": { "suspicious": {
"noExplicitAny": "warn" "noExplicitAny": "warn"

View File

@ -1,11 +1,11 @@
import * as process from "node:process"; import * as process from "node:process";
import AuthRouter from "@routers/auth.router";
import { EnvUtils } from "@utils/env.util"; import { EnvUtils } from "@utils/env.util";
import { LogsUtils } from "@utils/logs.util"; import { LogsUtils } from "@utils/logs.util";
import compression from "compression"; import compression from "compression";
import cors from "cors"; import cors from "cors";
import express, { type Express } from "express"; import express, { type Express } from "express";
import helmet from "helmet"; import helmet from "helmet";
import AuthRouter from "@routers/auth.router";
console.log("\n\n> Starting...\n\n\n\n"); console.log("\n\n> Starting...\n\n\n\n");
@ -61,4 +61,4 @@ try {
} catch (error) { } catch (error) {
logger.error(`Server failed to start: ${error}`); logger.error(`Server failed to start: ${error}`);
process.exit(1); process.exit(1);
} }

View File

@ -1,95 +1,108 @@
import {Request, Response} from "express"; import type { UserInDatabase } from "@interfaces/db/mariadb.interface";
import {LogsUtils} from "@utils/logs.util"; import type { IRegisterInput } from "@interfaces/services/register.types";
import {HttpStatusCode} from "axios";
import {IRegisterInput} from "@interfaces/services/register.types";
import ValidatorsUtils from "@utils/validators.util";
import UsersService from "@services/users.service";
import RegisterService from "@services/authentication/register.service";
import {HeaderUtil} from "@utils/headers.util";
import JwtService from "@services/authentication/jwt.service"; import JwtService from "@services/authentication/jwt.service";
import {JWTPayload} from "jose"; import RegisterService from "@services/authentication/register.service";
import {UserInDatabase} from "@interfaces/db/mariadb.interface"; import UsersService from "@services/users.service";
import ConvertersUtils from "@utils/converters.util"; import ConvertersUtils from "@utils/converters.util";
import { HeaderUtil } from "@utils/headers.util";
import { LogsUtils } from "@utils/logs.util";
import ValidatorsUtils from "@utils/validators.util";
import { HttpStatusCode } from "axios";
import type { Request, Response } from "express";
import type { JWTPayload } from "jose";
const logs = new LogsUtils('AuthController') const logs = new LogsUtils("AuthController");
async function registerController(req: Request, res: Response) { async function registerController(req: Request, res: Response) {
const body = req.body; const body = req.body;
if (!body.username || !body.email || !body.password) { if (!body.username || !body.email || !body.password) {
logs.warn('Missing required fields', req.ip); logs.warn("Missing required fields", req.ip);
return res.status(HttpStatusCode.BadRequest).json({ error: 'Missing required fields' }); return res
.status(HttpStatusCode.BadRequest)
.json({ error: "Missing required fields" });
} }
if (!ValidatorsUtils.isEmail(body.email)) { if (!ValidatorsUtils.isEmail(body.email)) {
logs.warn('Invalid email format', req.ip); logs.warn("Invalid email format", req.ip);
return res.status(HttpStatusCode.BadRequest).json({ error: 'Invalid email format' }); return res
.status(HttpStatusCode.BadRequest)
.json({ error: "Invalid email format" });
} }
if (!ValidatorsUtils.isUsername(body.username)) { if (!ValidatorsUtils.isUsername(body.username)) {
logs.warn('Invalid username format', req.ip); logs.warn("Invalid username format", req.ip);
return res.status(HttpStatusCode.BadRequest).json({ error: 'Invalid username format' }); return res
.status(HttpStatusCode.BadRequest)
.json({ error: "Invalid username format" });
} }
if (!ValidatorsUtils.isPassword(body.password)) { if (!ValidatorsUtils.isPassword(body.password)) {
logs.warn('Invalid password format', req.ip); logs.warn("Invalid password format", req.ip);
return res.status(HttpStatusCode.BadRequest).json({ error: 'Invalid password format' }); return res
.status(HttpStatusCode.BadRequest)
.json({ error: "Invalid password format" });
} }
const UserIfExist = { const UserIfExist = {
byEmail: await UsersService.get.byEmail(body.email), byEmail: await UsersService.get.byEmail(body.email),
byUsername: await UsersService.get.byUsername(body.username) byUsername: await UsersService.get.byUsername(body.username),
} };
if (UserIfExist.byEmail.length > 0 || UserIfExist.byUsername.length > 0) { if (UserIfExist.byEmail.length > 0 || UserIfExist.byUsername.length > 0) {
logs.warn('User already exists', req.ip); logs.warn("User already exists", req.ip);
return res.status(HttpStatusCode.Found).json({ error: 'User already exists' }); return res
.status(HttpStatusCode.Found)
.json({ error: "User already exists" });
} }
const data: IRegisterInput = { const data: IRegisterInput = {
username: body.username, username: body.username,
email: body.email, email: body.email,
password: body.password password: body.password,
} };
if (body.displayName) data.displayName = body.displayName; if (body.displayName) data.displayName = body.displayName;
const registerResult = await RegisterService(data) const registerResult = await RegisterService(data);
if (!registerResult.success) { if (!registerResult.success) {
logs.error(registerResult.message, req.ip); logs.error(registerResult.message, req.ip);
return res.status(HttpStatusCode.InternalServerError).json({ error: registerResult.message }); return res
} .status(HttpStatusCode.InternalServerError)
.json({ error: registerResult.message });
}
//TODO Image handling //TODO Image handling
logs.info("User registered successfully", req.ip);
logs.info('User registered successfully', req.ip);
return res.status(HttpStatusCode.Created).json({ return res.status(HttpStatusCode.Created).json({
message: 'User registered successfully', message: "User registered successfully",
token: registerResult.token, token: registerResult.token,
id: registerResult.id id: registerResult.id,
}); });
} }
async function GetSelfController(req: Request, res: Response) { async function GetSelfController(req: Request, res: Response) {
const originToken = await HeaderUtil.getToken(req) as string; const originToken = (await HeaderUtil.getToken(req)) as string;
const token = await JwtService.verify(originToken) as JWTPayload; const token = (await JwtService.verify(originToken)) as JWTPayload;
const userInDatabases = await UsersService.get.byId(token.sub as string) as Array<UserInDatabase>; const userInDatabases = (await UsersService.get.byId(
if (userInDatabases.length === 0 || !userInDatabases[0]) { token.sub as string,
logs.warn('User not found', req.ip); )) as Array<UserInDatabase>;
return res.status(HttpStatusCode.Gone).json({ error: 'User not found' }); if (userInDatabases.length === 0 || !userInDatabases[0]) {
} logs.warn("User not found", req.ip);
const User = userInDatabases[0] return res.status(HttpStatusCode.Gone).json({ error: "User not found" });
return res.status(HttpStatusCode.Found).json({ }
const User = userInDatabases[0];
return res.status(HttpStatusCode.Found).json({
id: User.id, id: User.id,
username: User.username, username: User.username,
email: User.email, email: User.email,
displayName: User.display_name, displayName: User.display_name,
isAdmin: ConvertersUtils.bufferToBool(User.admin as Buffer), isAdmin: ConvertersUtils.bufferToBool(User.admin as Buffer),
followers: await UsersService.get.followers(User.id), followers: await UsersService.get.followers(User.id),
following: await UsersService.get.following(User.id), following: await UsersService.get.following(User.id),
}); });
} }
const AuthController = { const AuthController = {
register: registerController, register: registerController,
me: GetSelfController me: GetSelfController,
} };
export default AuthController; export default AuthController;

View File

@ -1,21 +1,21 @@
export interface UserInDatabase { export interface UserInDatabase {
id: string, id: string;
username: string, username: string;
display_name: string, display_name: string;
email: string, email: string;
iat?: number, iat?: number;
uat?: number, uat?: number;
deactivated?: ArrayBuffer, deactivated?: ArrayBuffer;
hash: string, hash: string;
email_activation?: number | null, email_activation?: number | null;
admin?: ArrayBuffer, admin?: ArrayBuffer;
avatar_id?: string | null, avatar_id?: string | null;
gdpr?: number gdpr?: number;
} }
export interface FollowInDatabase { export interface FollowInDatabase {
id: string, id: string;
source_id: string, source_id: string;
target_id: string, target_id: string;
iat?: number iat?: number;
} }

View File

@ -0,0 +1,37 @@
import type { WithId } from "mongodb";
export interface IPost extends WithId<any> {
id: string;
authorId: string;
message: string;
visibility: ContentVisibility;
image?: string;
upvote?: IUpVote[];
metadata: IMetadata;
}
export interface IComment {
id: string;
parentId?: string;
authorId: string;
message: string;
metadata: IMetadata;
}
export interface IUpVote {
sourceId: string;
iat: number;
}
export interface IMetadata {
iat: number;
uat?: number;
reported?: boolean;
}
export enum ContentVisibility {
public = 0,
followers = 1,
friend = 2,
hidden = 3,
}

View File

@ -1,13 +1,13 @@
export interface IRegisterInput { export interface IRegisterInput {
username: string, username: string;
displayName?: string, displayName?: string;
email: string, email: string;
password: string password: string;
} }
export interface IRegisterOutput { export interface IRegisterOutput {
success: boolean, success: boolean;
message: string message: string;
id?: string, id?: string;
token?: string token?: string;
} }

View File

@ -1,60 +1,82 @@
import type {NextFunction, Request, Response} from "express"; import type { UserInDatabase } from "@interfaces/db/mariadb.interface";
import JwtService from "@services/authentication/jwt.service"; import JwtService from "@services/authentication/jwt.service";
import {DatabasesService} from "@services/databases/databases.service"; import { DatabasesService } from "@services/databases/databases.service";
import {UserInDatabase} from "@interfaces/db/mariadb.interface"; import { HeaderUtil } from "@utils/headers.util";
import {HttpStatusCode} from "axios"; import { HttpStatusCode } from "axios";
import {HeaderUtil} from "@utils/headers.util"; import type { NextFunction, Request, Response } from "express";
const db = new DatabasesService('OnlyDevs'); const db = new DatabasesService("OnlyDevs");
async function UserMiddleware(req: Request, res: Response, next: NextFunction) { async function UserMiddleware(req: Request, res: Response, next: NextFunction) {
const originToken = await HeaderUtil.getToken(req); const originToken = await HeaderUtil.getToken(req);
if (!originToken) { if (!originToken) {
return res.status(HttpStatusCode.Forbidden).json({ message: "Unauthorized" }); return res
.status(HttpStatusCode.Forbidden)
.json({ message: "Unauthorized" });
} }
const tokenPayload = await JwtService.verify(`${originToken}`); const tokenPayload = await JwtService.verify(`${originToken}`);
if (!tokenPayload || !tokenPayload.sub) { if (!tokenPayload || !tokenPayload.sub) {
return res.status(HttpStatusCode.Forbidden).json({ message: "Unauthorized" }); return res
.status(HttpStatusCode.Forbidden)
.json({ message: "Unauthorized" });
} }
const UserFound = await db.getUserById(tokenPayload.sub) const UserFound = await db.getUserById(tokenPayload.sub);
const User: UserInDatabase | undefined = UserFound[0] as UserInDatabase const User: UserInDatabase | undefined = UserFound[0] as UserInDatabase;
if (!User) { if (!User) {
return res.status(HttpStatusCode.Forbidden).json({ message: "Unauthorized, you dont exist." }); return res
.status(HttpStatusCode.Forbidden)
.json({ message: "Unauthorized, you dont exist." });
} }
if (User.email_activation) { if (User.email_activation) {
return res.status(HttpStatusCode.Forbidden).json({ message: "You should verify your email first."}) return res
.status(HttpStatusCode.Forbidden)
.json({ message: "You should verify your email first." });
} }
return next() return next();
} }
async function AdminMiddleware(req: Request, res: Response, next: NextFunction) { async function AdminMiddleware(
req: Request,
res: Response,
next: NextFunction,
) {
const originToken = await HeaderUtil.getToken(req); const originToken = await HeaderUtil.getToken(req);
if (!originToken) { if (!originToken) {
return res.status(HttpStatusCode.Forbidden).json({ message: "Unauthorized" }); return res
.status(HttpStatusCode.Forbidden)
.json({ message: "Unauthorized" });
} }
const tokenPayload = await JwtService.verify(`${originToken}`); const tokenPayload = await JwtService.verify(`${originToken}`);
if (!tokenPayload || !tokenPayload.sub) { if (!tokenPayload || !tokenPayload.sub) {
return res.status(HttpStatusCode.Forbidden).json({ message: "Unauthorized" }); return res
.status(HttpStatusCode.Forbidden)
.json({ message: "Unauthorized" });
} }
const UserFound = await db.getUserById(tokenPayload.sub) const UserFound = await db.getUserById(tokenPayload.sub);
const User: UserInDatabase | undefined = UserFound[0] as UserInDatabase const User: UserInDatabase | undefined = UserFound[0] as UserInDatabase;
if (!User) { if (!User) {
return res.status(HttpStatusCode.Forbidden).json({ message: "Unauthorized, you dont exist." }); return res
.status(HttpStatusCode.Forbidden)
.json({ message: "Unauthorized, you dont exist." });
} }
if (User.email_activation) { if (User.email_activation) {
return res.status(HttpStatusCode.Forbidden).json({ message: "You should verify your email first."}) return res
.status(HttpStatusCode.Forbidden)
.json({ message: "You should verify your email first." });
} }
const adminState = User.admin const adminState = User.admin;
console.log(adminState); console.log(adminState);
if (!adminState) { if (!adminState) {
return res.status(HttpStatusCode.PreconditionRequired).json({ message: "Unauthorized, you are not an admin." }); return res
} .status(HttpStatusCode.PreconditionRequired)
return next() .json({ message: "Unauthorized, you are not an admin." });
}
return next();
} }
export const AuthMiddleware = { export const AuthMiddleware = {
user: UserMiddleware, user: UserMiddleware,
admin: AdminMiddleware, admin: AdminMiddleware,
} };

View File

@ -1,7 +1,6 @@
import AuthController from "@controllers/auth.controller"; import AuthController from "@controllers/auth.controller";
import express, {type Router} from "express"; import express, { type Router } from "express";
import {AuthMiddleware} from "src/middlewares/authentication.middleware"; import { AuthMiddleware } from "src/middlewares/authentication.middleware";
const AuthRouter: Router = express.Router(); const AuthRouter: Router = express.Router();
@ -9,4 +8,4 @@ const AuthRouter: Router = express.Router();
AuthRouter.route("/register").post(AuthController.register); AuthRouter.route("/register").post(AuthController.register);
AuthRouter.route("/me").get(AuthMiddleware.user, AuthController.me); AuthRouter.route("/me").get(AuthMiddleware.user, AuthController.me);
export default AuthRouter; export default AuthRouter;

View File

@ -1,11 +1,11 @@
const IntCodeService = { const IntCodeService = {
generate: () => { generate: () => {
const a = Math.floor(Math.random() * Date.now()); const a = Math.floor(Math.random() * Date.now());
const b = a.toString().replace(/0/g, ''); const b = a.toString().replace(/0/g, "");
const c = b.split('') const c = b.split("");
const code = c.join('').slice(0, 6).toString() const code = c.join("").slice(0, 6).toString();
return Number.parseInt(code) return Number.parseInt(code);
} },
} };
export default IntCodeService; export default IntCodeService;

View File

@ -1,14 +1,14 @@
import { EnvUtils } from "@utils/env.util";
import { LogsUtils } from "@utils/logs.util";
import { import {
type JWTHeaderParameters, type JWTHeaderParameters,
type JWTPayload, type JWTPayload,
SignJWT, SignJWT,
jwtVerify, jwtVerify,
} from "jose"; } from "jose";
import {LogsUtils} from "@utils/logs.util";
import {EnvUtils} from "@utils/env.util";
const logs = new LogsUtils('JwtService') const logs = new LogsUtils("JwtService");
const envs = new EnvUtils('JwtService') const envs = new EnvUtils("JwtService");
/** /**
* Verify a JWT token. * Verify a JWT token.
@ -24,7 +24,7 @@ async function JwtVerifyService(
try { try {
const result = await jwtVerify( const result = await jwtVerify(
jwt, jwt,
new TextEncoder().encode(`${envs.get('JWT_SECRET')}`), new TextEncoder().encode(`${envs.get("JWT_SECRET")}`),
{}, {},
); );
return result.payload; return result.payload;
@ -61,7 +61,7 @@ async function JwtSignService(
.setIssuer(`OnlyDevs`) .setIssuer(`OnlyDevs`)
.setAudience(audience) .setAudience(audience)
.setExpirationTime(expTime) .setExpirationTime(expTime)
.sign(new TextEncoder().encode(`${envs.get('JWT_SECRET')}`)); .sign(new TextEncoder().encode(`${envs.get("JWT_SECRET")}`));
} }
logs.debug("Service loaded."); logs.debug("Service loaded.");

View File

@ -1,47 +1,55 @@
import {IRegisterInput, IRegisterOutput} from "@interfaces/services/register.types"; import type { UserInDatabase } from "@interfaces/db/mariadb.interface";
import {UserInDatabase} from "@interfaces/db/mariadb.interface"; import type {
IRegisterInput,
IRegisterOutput,
} from "@interfaces/services/register.types";
import CredentialService from "@services/authentication/credentials.service"; import CredentialService from "@services/authentication/credentials.service";
import IntCodeService from "@services/authentication/intcode.service"; import IntCodeService from "@services/authentication/intcode.service";
import {v4} from "uuid";
import {DatabasesService} from "@services/databases/databases.service";
import JwtService from "@services/authentication/jwt.service"; import JwtService from "@services/authentication/jwt.service";
import { DatabasesService } from "@services/databases/databases.service";
import { v4 } from "uuid";
const db = new DatabasesService('OnlyDevs') const db = new DatabasesService("OnlyDevs");
//TODO Logs //TODO Logs
async function registerService(data: IRegisterInput): Promise<IRegisterOutput> { async function registerService(data: IRegisterInput): Promise<IRegisterOutput> {
const uuid = v4().toString() const uuid = v4().toString();
const User: UserInDatabase = { const User: UserInDatabase = {
id: `${uuid}`, id: `${uuid}`,
username: data.username, username: data.username,
display_name: data.displayName || data.username, display_name: data.displayName || data.username,
hash: await CredentialService.hash(data.password), hash: await CredentialService.hash(data.password),
email: data.email, email: data.email,
email_activation: IntCodeService.generate() email_activation: IntCodeService.generate(),
} };
const dbResult = await db.insertUser(User) const dbResult = await db.insertUser(User);
if (dbResult) { if (dbResult) {
//await sendActivationEmail(User.email, User.email_activation); //await sendActivationEmail(User.email, User.email_activation);
const token = await JwtService.sign({ const token = await JwtService.sign(
sub: uuid, {
iat: Date.now(), sub: uuid,
}, { iat: Date.now(),
alg: "HS256" },
}, '7d', 'Registered user') {
return { alg: "HS256",
success: true, },
message: "User registered successfully", "7d",
"Registered user",
);
return {
success: true,
message: "User registered successfully",
id: User.id, id: User.id,
token: token token: token,
}; };
} else { } else {
return { return {
success: false, success: false,
message: "Failed to register user", message: "Failed to register user",
}; };
} }
} }
export default registerService; export default registerService;

View File

@ -1,7 +1,11 @@
import {MariadbService} from "@services/databases/mariadb.service"; import type {
//import {MongodbService} from "@services/databases/mongodb.service"; FollowInDatabase,
import {LogsUtils} from "@utils/logs.util"; UserInDatabase,
import {FollowInDatabase, UserInDatabase} from "@interfaces/db/mariadb.interface"; } from "@interfaces/db/mariadb.interface";
import type { IComment, IPost } from "@interfaces/db/mongodb.types";
import { MariadbService } from "@services/databases/mariadb.service";
import { MongodbService } from "@services/databases/mongodb.service";
import { LogsUtils } from "@utils/logs.util";
interface MariaDbStatusResult { interface MariaDbStatusResult {
fieldCount: number; fieldCount: number;
@ -16,92 +20,356 @@ interface MariaDbStatusResult {
export class DatabasesService { export class DatabasesService {
private readonly _appName; private readonly _appName;
private readonly maria: MariadbService; private readonly maria: MariadbService;
//private readonly mongo: MongodbService; private readonly mongo: MongodbService;
private readonly logs: LogsUtils; private readonly logs: LogsUtils;
constructor(appName?: string) { constructor(appName?: string) {
this._appName = appName || 'App'; this._appName = appName || "App";
this.maria = new MariadbService(this._appName + ' DbInteractions'); this.maria = new MariadbService(`${this._appName} DbInteractions`);
//this.mongo = new MongodbService(this._appName + ' DbInteractions'); this.mongo = new MongodbService(`${this._appName} DbInteractions`);
this.logs = new LogsUtils(this._appName + ' DbInteractions'); this.logs = new LogsUtils(`${this._appName} DbInteractions`);
} }
async getAllUsers() { async getAllUsers() {
const result = await this.maria.query("SELECT * FROM users") as unknown as Array<UserInDatabase>; const result = (await this.maria.query(
this.logs.debug('Fetching users from database...', `${result?.length} user(s) found.`) "SELECT * FROM users",
return result; )) as unknown as Array<UserInDatabase>;
this.logs.debug(
"Fetching users from database...",
`${result?.length} user(s) found.`,
);
return result;
} }
async getUserById(id: string) { async getUserById(id: string) {
const result = await this.maria.execute(`SELECT * FROM users WHERE id = ?`, [id]) as unknown as Array<UserInDatabase>; const result = (await this.maria.execute(
this.logs.debug(`Fetching user with id ${id} from database...`, `${result?.length} user(s) found.`); "SELECT * FROM users WHERE id = ?",
return result; [id],
} )) as unknown as Array<UserInDatabase>;
this.logs.debug(
`Fetching user with id ${id} from database...`,
`${result?.length} user(s) found.`,
);
return result;
}
async getUserByUsername(username: string) { async getUserByUsername(username: string) {
const result = await this.maria.execute(`SELECT * FROM users WHERE username = ?`, [username]) as unknown as Array<UserInDatabase>; const result = (await this.maria.execute(
this.logs.debug(`Fetching user with username "${username}" from database...`, `${result?.length} user(s) found.`); "SELECT * FROM users WHERE username = ?",
return result; [username],
)) as unknown as Array<UserInDatabase>;
this.logs.debug(
`Fetching user with username "${username}" from database...`,
`${result?.length} user(s) found.`,
);
return result;
} }
async getUserByEmail(email: string) { async getUserByEmail(email: string) {
const result = await this.maria.execute(`SELECT * FROM users WHERE email = ?`, [email]) as unknown as Array<UserInDatabase>; const result = (await this.maria.execute(
this.logs.debug(`Fetching user with email "${email}" from database...`, `${result?.length} user(s) found.`); "SELECT * FROM users WHERE email = ?",
return result; [email],
)) as unknown as Array<UserInDatabase>;
this.logs.debug(
`Fetching user with email "${email}" from database...`,
`${result?.length} user(s) found.`,
);
return result;
} }
async getFollowingById(id: string) { async getFollowingById(id: string) {
const result = await this.maria.execute(`SELECT * FROM follows WHERE source_id = ?`, [id]) as unknown as Array<FollowInDatabase>; const result = (await this.maria.execute(
this.logs.debug(`Fetching followed users for user with id ${id} from database...`, `${result?.length} user(s) found.`); "SELECT * FROM follows WHERE source_id = ?",
return result; [id],
)) as unknown as Array<FollowInDatabase>;
this.logs.debug(
`Fetching followed users for user with id ${id} from database...`,
`${result?.length} user(s) found.`,
);
return result;
} }
async getFollowersById(id:string) { async getFollowersById(id: string) {
const result = await this.maria.execute(`SELECT * FROM follows WHERE target_id = ?`, [id]) as unknown as Array<FollowInDatabase>; const result = (await this.maria.execute(
this.logs.debug(`Fetching followers for user with id ${id} from database...`, `${result?.length} user(s) found.`); "SELECT * FROM follows WHERE target_id = ?",
return result; [id],
)) as unknown as Array<FollowInDatabase>;
this.logs.debug(
`Fetching followers for user with id ${id} from database...`,
`${result?.length} user(s) found.`,
);
return result;
} }
async insertUser(user: UserInDatabase): Promise<boolean> { async insertUser(user: UserInDatabase): Promise<boolean> {
const factorized = await this.maria.factorize({ const factorized = await this.maria.factorize({
values: user, values: user,
actionName: 'Inserting a user', actionName: "Inserting a user",
throwOnError: true throwOnError: true,
}); });
//TODO if no id stop //TODO if no id stop
const valuesArray = factorized._valuesArray; const valuesArray = factorized._valuesArray;
const questionMarks = factorized._questionMarksFields const questionMarks = factorized._questionMarksFields;
const keysArray = factorized._keysTemplate; const keysArray = factorized._keysTemplate;
const _sql = `INSERT INTO users (${keysArray}) VALUES (${questionMarks})` const _sql = `INSERT INTO users (${keysArray}) VALUES (${questionMarks})`;
try { try {
const result = await this.maria.execute(_sql, valuesArray) as unknown as MariaDbStatusResult const result = (await this.maria.execute(
this.logs.debug(`Inserted new user with id ${user.id}`, `Rows affected: ${result.affectedRows}`); _sql,
return true valuesArray,
)) as unknown as MariaDbStatusResult;
this.logs.debug(
`Inserted new user with id ${user.id}`,
`Rows affected: ${result.affectedRows}`,
);
return true;
} catch (err) { } catch (err) {
this.logs.softError('An error occurred.', err) this.logs.softError("An error occurred.", err);
return false return false;
} }
} }
//ToTest //ToTest
async editUser(userId: string, data: object): Promise<object> { async editUser(userId: string, data: object): Promise<object> {
const factorized = await this.maria.factorize({ const factorized = await this.maria.factorize({
values: data, values: data,
actionName: 'Editing a user', actionName: "Editing a user",
throwOnError: true throwOnError: true,
}); });
const valuesArray = factorized._valuesArray; const valuesArray = factorized._valuesArray;
const keysArray = factorized._keysTemplate.split(','); const keysArray = factorized._keysTemplate.split(",");
const setFields = keysArray.map((key) => `${key} = ?`).join(', '); const setFields = keysArray.map((key) => `${key} = ?`).join(", ");
const _sql = `UPDATE users SET ${setFields} WHERE id = ?`; const _sql = `UPDATE users SET ${setFields} WHERE id = ?`;
valuesArray.push(userId); valuesArray.push(userId);
try { try {
const result = await this.maria.execute(_sql, valuesArray) as unknown as MariaDbStatusResult; const result = (await this.maria.execute(
this.logs.debug(`Edited user with id ${userId}`, `Rows affected: ${result.affectedRows}`); _sql,
return result; valuesArray,
} catch (err) { )) as unknown as MariaDbStatusResult;
this.logs.softError('An error occurred.', err); this.logs.debug(
return {}; `Edited user with id ${userId}`,
} `Rows affected: ${result.affectedRows}`,
);
return result;
} catch (err) {
this.logs.softError("An error occurred.", err);
return {};
}
}
//ToTest
async createFollow(sourceId: string, targetId: string): Promise<boolean> {
const _sql = "INSERT INTO follows (source_id, target_id) VALUES (?, ?)";
try {
const result = (await this.maria.execute(_sql, [
sourceId,
targetId,
])) as unknown as MariaDbStatusResult;
this.logs.debug(
`Created follow relationship between user with id ${sourceId} and user with id ${targetId}`,
`Rows affected: ${result.affectedRows}`,
);
return true;
} catch (err) {
this.logs.softError("An error occurred.", err);
return false;
}
}
//ToTest
async deleteFollow(sourceId: string, targetId: string): Promise<boolean> {
const _sql = "DELETE FROM follows WHERE source_id = ? AND target_id = ?";
try {
const result = (await this.maria.execute(_sql, [
sourceId,
targetId,
])) as unknown as MariaDbStatusResult;
this.logs.debug(
`Deleted follow relationship between user with id ${sourceId} and user with id ${targetId}`,
`Rows affected: ${result.affectedRows}`,
);
return true;
} catch (err) {
this.logs.softError("An error occurred.", err);
return false;
}
}
//ToTest
async deleteUser(userId: string): Promise<boolean> {
const _sql = "DELETE FROM users WHERE id = ?";
try {
const result = (await this.maria.execute(_sql, [
userId,
])) as unknown as MariaDbStatusResult;
this.logs.debug(
`Deleted user with id ${userId}`,
`Rows affected: ${result.affectedRows}`,
);
return true;
} catch (err) {
this.logs.softError("An error occurred.", err);
return false;
}
}
async createPost(data: IPost): Promise<string | boolean> {
try {
const result = await this.mongo.use().collection("posts").insertOne(data);
if (result.acknowledged) {
return data.id;
}
return false;
} catch (err) {
this.logs.softError("Error when inserting a post.", err);
return false;
}
}
async getPostById(id: string): Promise<IPost | false> {
try {
const result = (await this.mongo
.use()
.collection("posts")
.findOne({ id })) as unknown as IPost;
if (!result) return false;
return result;
} catch (err) {
this.logs.softError("Error when fetching a post.", err);
return false;
}
}
async getPostsByUserId(userId: string): Promise<IPost[]> {
try {
const result = (await this.mongo
.use()
.collection("posts")
.find({ userId })
.toArray()) as unknown as IPost[];
return result;
} catch (err) {
this.logs.softError("Error when fetching posts by user id.", err);
return [];
}
}
async createComment(data: IComment): Promise<string | boolean> {
try {
const result = await this.mongo
.use()
.collection("comments")
.insertOne(data);
if (result.acknowledged) {
return data.id;
}
return false;
} catch (err) {
this.logs.softError("Error when inserting a comment.", err);
return false;
}
}
async getCommentsByPostId(postId: string): Promise<IComment[]> {
try {
const result = (await this.mongo
.use()
.collection("comments")
.find({ postId })
.toArray()) as unknown as IComment[];
return result;
} catch (err) {
this.logs.softError("Error when fetching comments by post id.", err);
return [];
}
}
async getPostsWithCommentsByUserId(
userId: string,
): Promise<Array<{ post: IPost; comments: IComment[] | undefined }>> {
try {
const posts = await this.getPostsByUserId(userId);
const postIds = posts.map((post) => post.id);
const comments = await Promise.all(
postIds.map((postId) => this.getCommentsByPostId(postId)),
);
const result = posts.map((post, index) => ({
post,
comments: comments[index],
}));
//Log the result
return result;
} catch (err) {
this.logs.softError(
"Error when fetching posts with comments by user id.",
err,
);
return [];
}
}
async deleteComment(commentId: string): Promise<boolean> {
try {
const result = await this.mongo
.use()
.collection("comments")
.deleteOne({ id: commentId });
this.logs.debug(
`Deleted comment with id ${commentId}`,
`Rows affected: ${result.deletedCount}`,
);
return true;
} catch (err) {
this.logs.softError("An error occurred.", err);
return false;
}
}
async deletePost(postId: string): Promise<boolean> {
try {
const result = await this.mongo
.use()
.collection("posts")
.deleteOne({ id: postId });
this.logs.debug(
`Deleted post with id ${postId}`,
`Rows affected: ${result.deletedCount}`,
);
return true;
} catch (err) {
this.logs.softError("An error occurred.", err);
return false;
}
}
async getFollowersCountById(id: string) {
const result = (await this.maria.execute(
"SELECT COUNT(*) FROM follows WHERE target_id = ?",
[id],
)) as unknown as number;
this.logs.debug(
`Fetching followers count for user with id ${id} from database...`,
`${result} follower(s) found.`,
);
return result;
}
async getFollowingCountById(id: string) {
const result = (await this.maria.execute(
"SELECT COUNT(*) AS count FROM follows WHERE source_id = ?",
[id],
)) as unknown as Array<{ count: number }>;
this.logs.debug(
`Fetching count of followed users for user with id ${id} from database...`,
`${result?.[0]?.count} user(s) found.`,
);
return result?.[0]?.count || 0;
}
async getMostFollowedUser() {
const _sql = "SELECT target_id, COUNT(*) AS count FROM follows GROUP BY target_id ORDER BY count DESC LIMIT 3";
const result = (await this.maria.query(_sql)) as unknown as Array<{ target_id: string; count: number }>;
this.logs.debug(
"Fetching most followed users from database...",
`${result?.length} user(s) found.`,
);
} }
} }
export const justForTesting = new DatabasesService('OnlyDevs') export const justForTesting = new DatabasesService("OnlyDevs");

View File

@ -1,10 +1,12 @@
import type {
ISqlFactorizeInput,
ISqlFactorizeOutput,
} from "@interfaces/sqlfactorizer.interface";
import { EnvUtils } from "@utils/env.util";
import { LogsUtils } from "@utils/logs.util";
import mysql, { type Connection } from "mysql2"; import mysql, { type Connection } from "mysql2";
import {LogsUtils} from "@utils/logs.util";
import {EnvUtils} from "@utils/env.util";
import {ISqlFactorizeInput, ISqlFactorizeOutput} from "@interfaces/sqlfactorizer.interface";
export class MariadbService { export class MariadbService {
private envs: EnvUtils; private envs: EnvUtils;
private logs: LogsUtils; private logs: LogsUtils;
private readonly contextName: string; private readonly contextName: string;
@ -16,17 +18,17 @@ export class MariadbService {
this.contextName = contextName || "Unknown"; this.contextName = contextName || "Unknown";
this.logs = new LogsUtils(this.contextName); this.logs = new LogsUtils(this.contextName);
this.Connection = mysql.createConnection({ this.Connection = mysql.createConnection({
host: `${this.envs.get('MYSQL_HOST')}`, host: `${this.envs.get("MYSQL_HOST")}`,
port: Number.parseInt(`${this.envs.get('MYSQL_PORT')}`), port: Number.parseInt(`${this.envs.get("MYSQL_PORT")}`),
user: `${this.envs.get('MYSQL_USERNAME')}`, user: `${this.envs.get("MYSQL_USERNAME")}`,
database: `${this.envs.get('MYSQL_DATABASE')}`, database: `${this.envs.get("MYSQL_DATABASE")}`,
password: `${this.envs.get('MYSQL_PASSWORD')}`, password: `${this.envs.get("MYSQL_PASSWORD")}`,
}); });
this.Connection.connect((err) => { this.Connection.connect((err) => {
if (err) { if (err) {
this.logs.error(`Error connecting to MySQL:`, err); this.logs.error(`Error connecting to MySQL:`, err);
} }
this.logs.debug('Connected to MariaDB', this.envs.get('MYSQL_DATABASE')) this.logs.debug("Connected to MariaDB", this.envs.get("MYSQL_DATABASE"));
}); });
} }
closeConnection() { closeConnection() {
@ -70,12 +72,13 @@ export class MariadbService {
); );
const values = Object.values(data.values).map((val) => val.toString()); const values = Object.values(data.values).map((val) => val.toString());
this.logs.debug( this.logs.debug(
`Factorized ${_sqlQueryKeys.length} keys for a prepare Query.`, `Action: ${data.actionName}`, `Factorized ${_sqlQueryKeys.length} keys for a prepare Query.`,
`Action: ${data.actionName}`,
); );
if (_id.length > 0) { if (_id.length > 0) {
this.logs.trace(`Id post-pushed in factorized data.`, _id); this.logs.trace(`Id post-pushed in factorized data.`, _id);
values.push(_id); values.push(_id);
_sqlQueryKeys.push('id') _sqlQueryKeys.push("id");
} }
const sqlQueryKeys = _sqlQueryKeys.join(", "); const sqlQueryKeys = _sqlQueryKeys.join(", ");
@ -141,4 +144,4 @@ export class MariadbService {
} }
}); });
} }
} }

View File

@ -10,14 +10,14 @@ export class MongodbService {
this.envs = new EnvUtils(`MongoDB >> ${contextName}`); this.envs = new EnvUtils(`MongoDB >> ${contextName}`);
this.logs = new LogsUtils(`MongoDB >> ${contextName}`); this.logs = new LogsUtils(`MongoDB >> ${contextName}`);
try { try {
const uri = `mongodb://${this.envs.get("MONGO_USERNAME")}:${this.envs.get("MONGO_PASSWORD")}@localhost:${this.envs.get("MONGO_PORT")}/`; const uri = `mongodb://${this.envs.get("MONGO_USERNAME")}:${this.envs.get(
"MONGO_PASSWORD",
)}@localhost:${this.envs.get("MONGO_PORT")}/`;
this.logs.trace("MongoDB URI:", uri); this.logs.trace("MongoDB URI:", uri);
this.client = new MongoClient(uri); this.client = new MongoClient(uri);
this.client this.client.connect().then(() => {
.connect() this.logs.debug("Connected to MongoDB", "All databases");
.then(() => { });
this.logs.debug("Connected to MongoDB", 'All databases');
})
} catch (error) { } catch (error) {
this.logs.error(`Error connecting to MongoDB:`, error); this.logs.error(`Error connecting to MongoDB:`, error);
throw new Error(); throw new Error();
@ -29,7 +29,7 @@ export class MongodbService {
return this.client.db(`${this.envs.get("MONGO_DATABASE")}`); return this.client.db(`${this.envs.get("MONGO_DATABASE")}`);
} catch (err) { } catch (err) {
this.logs.error("Error using MongoDB:", err); this.logs.error("Error using MongoDB:", err);
throw err; throw err;
} }
} }

View File

@ -1,30 +1,29 @@
import {DatabasesService} from "@services/databases/databases.service"; import { DatabasesService } from "@services/databases/databases.service";
const db = new DatabasesService("OnlyDevs");
const db = new DatabasesService('OnlyDevs')
async function getByEmail(email: string) { async function getByEmail(email: string) {
return await db.getUserByEmail(email) return await db.getUserByEmail(email);
} }
async function getByUsername(username: string) { async function getByUsername(username: string) {
return await db.getUserByUsername(username) return await db.getUserByUsername(username);
} }
async function getAll() { async function getAll() {
return db.getAllUsers() return db.getAllUsers();
} }
async function getById(id: string) { async function getById(id: string) {
return await db.getUserById(id) return await db.getUserById(id);
} }
async function getFollowers(id: string) { async function getFollowers(id: string) {
return await db.getFollowersById(id) return await db.getFollowersById(id);
} }
async function getFollowing(id: string) { async function getFollowing(id: string) {
return await db.getFollowingById(id) return await db.getFollowingById(id);
} }
const get = { const get = {
@ -33,11 +32,11 @@ const get = {
byId: getById, byId: getById,
all: getAll, all: getAll,
followers: getFollowers, followers: getFollowers,
following: getFollowing following: getFollowing,
} };
const UsersService = { const UsersService = {
get get,
} };
export default UsersService; export default UsersService;

View File

@ -6,7 +6,7 @@ const ConvertersUtils = {
}, },
boolToBuffer: (bol: boolean) => { boolToBuffer: (bol: boolean) => {
return new Uint8Array([bol ? 1 : 0]).buffer; return new Uint8Array([bol ? 1 : 0]).buffer;
} },
} };
export default ConvertersUtils; export default ConvertersUtils;

View File

@ -1,14 +1,13 @@
import type {Request} from "express"; import type { Request } from "express";
async function getTokenFromHeader(req: Request) { async function getTokenFromHeader(req: Request) {
const token: string | undefined = req.headers.authorization?.split(" ")[1]; const token: string | undefined = req.headers.authorization?.split(" ")[1];
if (!token ||token.length <= 0) { if (!token || token.length <= 0) {
return false return false;
} }
return token; return token;
} }
export const HeaderUtil = { export const HeaderUtil = {
getToken: getTokenFromHeader, getToken: getTokenFromHeader,
} };

View File

@ -6,7 +6,7 @@ export class LogsUtils {
constructor(contextName: string) { constructor(contextName: string) {
this.Logger = new Logger({ this.Logger = new Logger({
name: contextName, name: contextName,
prettyLogTimeZone: `local` prettyLogTimeZone: `local`,
}); });
} }

View File

@ -1,7 +1,7 @@
const Validators = { const Validators = {
isEmail: (value: string) => { isEmail: (value: string) => {
const emailRegex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,32}$/; const emailRegex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,32}$/;
return emailRegex.test(value) return emailRegex.test(value);
}, },
isUsername: (value: string) => { isUsername: (value: string) => {
const usernameRegex = /^[a-zA-Z0-9._]{3,14}$/; const usernameRegex = /^[a-zA-Z0-9._]{3,14}$/;
@ -10,9 +10,10 @@ const Validators = {
//displayName //displayName
isPassword: (value: string) => { isPassword: (value: string) => {
const passwordRegex = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,32}$/; const passwordRegex =
/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,32}$/;
return passwordRegex.test(value); return passwordRegex.test(value);
} },
} };
export default Validators export default Validators;