feat: Add MariaDB service and validators utility, refactor MongoDB service, and update configs
This commit introduces several changes: - A new MariaDB service is implemented including connection handling and query execution. - Field validators (email, username, password) are added to utils. - The MongoDB service is refactored by moving the connect method into the constructor. - The "noDelete" configuration field is updated in biome.json. - A new interface is added for factorizing data into SQL queries. - The app.ts file is updated to include the MariaDB test alongside MongoDB.
This commit is contained in:
parent
02224e0727
commit
39b4bfc022
@ -18,7 +18,7 @@
|
|||||||
"recommended": true,
|
"recommended": true,
|
||||||
"performance": {
|
"performance": {
|
||||||
"recommended": true,
|
"recommended": true,
|
||||||
"noDelete": "off"
|
"noDelete": "off",
|
||||||
},
|
},
|
||||||
"suspicious": {
|
"suspicious": {
|
||||||
"noExplicitAny": "warn"
|
"noExplicitAny": "warn"
|
||||||
|
13
src/app.ts
13
src/app.ts
@ -6,6 +6,7 @@ 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 {MariadbService} from "@services/databases/mariadb.service";
|
||||||
|
|
||||||
console.log("\n\n> Starting...\n\n\n\n");
|
console.log("\n\n> Starting...\n\n\n\n");
|
||||||
|
|
||||||
@ -63,4 +64,14 @@ try {
|
|||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
const mongo = new MongodbService("App");
|
async function test() {
|
||||||
|
|
||||||
|
const mongo = new MongodbService("App");
|
||||||
|
const maria = new MariadbService('App');
|
||||||
|
logger.debug('Testing...')
|
||||||
|
return await mongo.use().collection('testing').countDocuments()
|
||||||
|
}
|
||||||
|
|
||||||
|
test().then((r)=>{
|
||||||
|
logger.info(r)
|
||||||
|
})
|
54
src/interfaces/sqlfactorizer.interface.ts
Normal file
54
src/interfaces/sqlfactorizer.interface.ts
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
/**
|
||||||
|
* Represents the output of the factorization function.
|
||||||
|
*/
|
||||||
|
export interface ISqlFactorizeOutput {
|
||||||
|
/**
|
||||||
|
* Description: The variable `_valuesArray` is an array that can contain values of type `string`, `boolean`, `number`, or `Date`.
|
||||||
|
* (The value associated with the keys of `_keysTemplate`)
|
||||||
|
*
|
||||||
|
* @type {Array<string | boolean | number | Date>}
|
||||||
|
*/
|
||||||
|
_valuesArray: Array<string | boolean | number | Date>;
|
||||||
|
/**
|
||||||
|
* Represents the SQL Query template for the keys.
|
||||||
|
* @type {string}
|
||||||
|
*/
|
||||||
|
_keysTemplate: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The list of ? for the "VALUE" section
|
||||||
|
*/
|
||||||
|
_questionMarksFields: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The total number of fields.
|
||||||
|
*
|
||||||
|
* @type {number}
|
||||||
|
*/
|
||||||
|
totalFields: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface ISqlFactorizeInput represents the input required to factorize a SQL query.
|
||||||
|
*/
|
||||||
|
export interface ISqlFactorizeInput {
|
||||||
|
/**
|
||||||
|
* An object containing values that will be in a SQL Query.
|
||||||
|
*
|
||||||
|
* @type {Array<string | boolean | number | Date>}
|
||||||
|
*/
|
||||||
|
values: object;
|
||||||
|
/**
|
||||||
|
* Represents the name of the action that will result of the prepared SQL Query.
|
||||||
|
*
|
||||||
|
* @type {string}
|
||||||
|
*/
|
||||||
|
actionName: string;
|
||||||
|
/**
|
||||||
|
* Indicates whether an error should be thrown when encountering an error.
|
||||||
|
* If set to true, an error will be thrown. If set to false or not provided, the error will not be thrown.
|
||||||
|
*
|
||||||
|
* @type {boolean}
|
||||||
|
*/
|
||||||
|
throwOnError?: true;
|
||||||
|
}
|
@ -0,0 +1,144 @@
|
|||||||
|
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 {
|
||||||
|
|
||||||
|
private envs: EnvUtils;
|
||||||
|
private logs: LogsUtils;
|
||||||
|
private readonly contextName: string;
|
||||||
|
private Connection: Connection;
|
||||||
|
|
||||||
|
constructor(contextName?: string) {
|
||||||
|
this.envs = new EnvUtils(`MariaDB >> ${contextName}`);
|
||||||
|
this.logs = new LogsUtils(`MariaDB >> ${contextName}`);
|
||||||
|
this.contextName = contextName || "Unknown";
|
||||||
|
this.logs = new LogsUtils(this.contextName);
|
||||||
|
this.Connection = mysql.createConnection({
|
||||||
|
host: `${this.envs.get('MYSQL_HOST')}`,
|
||||||
|
port: Number.parseInt(`${this.envs.get('MYSQL_PORT')}`),
|
||||||
|
user: `${this.envs.get('MYSQL_USERNAME')}`,
|
||||||
|
database: `${this.envs.get('MYSQL_DATABASE')}`,
|
||||||
|
password: `${this.envs.get('MYSQL_PASSWORD')}`,
|
||||||
|
});
|
||||||
|
this.Connection.connect((err) => {
|
||||||
|
if (err) {
|
||||||
|
this.logs.error(`Error connecting to MySQL:`, err);
|
||||||
|
}
|
||||||
|
this.logs.info('Connected to MariaDB', this.envs.get('MYSQL_DATABASE'))
|
||||||
|
});
|
||||||
|
}
|
||||||
|
closeConnection() {
|
||||||
|
this.Connection.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
query(queryString: string) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
this.Connection.query(queryString, (err, results) => {
|
||||||
|
if (err) {
|
||||||
|
this.logs.error(`Error executing query:`, err);
|
||||||
|
reject(err);
|
||||||
|
} else {
|
||||||
|
resolve(results);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Factorize the input data values into a database query.
|
||||||
|
* `id` field will not be injected in result to avoid problems.
|
||||||
|
*
|
||||||
|
* @param {ISqlFactorizeInput} data - The input data containing values to factorize.
|
||||||
|
* @return {Promise<ISqlFactorizeOutput>} - A promise resolving to the factorized output.
|
||||||
|
*/
|
||||||
|
factorize(data: ISqlFactorizeInput): Promise<ISqlFactorizeOutput> {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
try {
|
||||||
|
let _id = "";
|
||||||
|
// @ts-ignore
|
||||||
|
if (data.values.id) {
|
||||||
|
// @ts-ignore
|
||||||
|
_id = data.values.id;
|
||||||
|
// @ts-ignore
|
||||||
|
delete data.values.id;
|
||||||
|
}
|
||||||
|
const _sqlQueryKeys = Object.keys(data.values).map(
|
||||||
|
(key: string) => `${key}`,
|
||||||
|
);
|
||||||
|
const values = Object.values(data.values).map((val) => val);
|
||||||
|
this.logs.debug(
|
||||||
|
`Factorized ${_sqlQueryKeys.length} keys for a prepare Query.`, `Action: ${data.actionName}`,
|
||||||
|
);
|
||||||
|
const sqlQueryKeys = _sqlQueryKeys.join(", ");
|
||||||
|
if (_id && _id.length > 2) {
|
||||||
|
this.logs.trace(`Id post-pushed in factorized data.`, _id);
|
||||||
|
values.push(_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
const questionMarksFields = Array(values.length)
|
||||||
|
.fill("?")
|
||||||
|
.join(", ")
|
||||||
|
.toString();
|
||||||
|
|
||||||
|
const factorizedOutput: ISqlFactorizeOutput = {
|
||||||
|
_keysTemplate: sqlQueryKeys,
|
||||||
|
_questionMarksFields: questionMarksFields,
|
||||||
|
totalFields: _sqlQueryKeys.length,
|
||||||
|
_valuesArray: values,
|
||||||
|
};
|
||||||
|
resolve(factorizedOutput);
|
||||||
|
} catch (err) {
|
||||||
|
if (data.throwOnError) throw new Error(`${err}`);
|
||||||
|
this.logs.softError(err);
|
||||||
|
reject(`${err}`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Executes a query using the provided queryString and values.
|
||||||
|
* @param {string} queryString - The SQL query string to execute.
|
||||||
|
* @param {Array<string | boolean | Date | number>} values - The values to be inserted into the query.
|
||||||
|
* @returns {Promise<unknown>} - A promise that resolves with the query results or rejects with an error.
|
||||||
|
*/
|
||||||
|
execute(
|
||||||
|
queryString: string,
|
||||||
|
values: Array<string | boolean | Date | number>,
|
||||||
|
): Promise<unknown> {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
this.Connection.execute(
|
||||||
|
queryString,
|
||||||
|
values,
|
||||||
|
(err: mysql.QueryError | null, results: mysql.QueryResult) => {
|
||||||
|
if (err) {
|
||||||
|
this.logs.error(`Error executing query:`, err);
|
||||||
|
reject(err);
|
||||||
|
} else {
|
||||||
|
resolve(results);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unprepare a previously prepared SQL query.
|
||||||
|
*
|
||||||
|
* @param {string} queryString
|
||||||
|
* - The SQL query string to unprepare.
|
||||||
|
* @return {Promise}
|
||||||
|
* - A promise that resolves if the unprepare operation is successful,
|
||||||
|
* or rejects with an error if there was an error unpreparing the query.
|
||||||
|
*/
|
||||||
|
unprepare(queryString: string): Promise<unknown> {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
try {
|
||||||
|
resolve(this.Connection.unprepare(queryString));
|
||||||
|
} catch (err) {
|
||||||
|
reject(err);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -13,24 +13,17 @@ export class MongodbService {
|
|||||||
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
|
||||||
|
.connect()
|
||||||
|
.then(() => {
|
||||||
|
this.logs.info("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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
connect() {
|
|
||||||
return this.client
|
|
||||||
.connect()
|
|
||||||
.then(() => {
|
|
||||||
this.logs.info("Connected to MongoDB");
|
|
||||||
})
|
|
||||||
.catch((error) => {
|
|
||||||
this.logs.error("Error connecting to MongoDB:", error);
|
|
||||||
//throw error;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
use() {
|
use() {
|
||||||
try {
|
try {
|
||||||
return this.client.db(`${this.envs.get("MONGO_DATABASE")}`);
|
return this.client.db(`${this.envs.get("MONGO_DATABASE")}`);
|
||||||
|
17
src/utils/valiators.util.ts
Normal file
17
src/utils/valiators.util.ts
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
const Validators = {
|
||||||
|
isEmail: (value: string) => {
|
||||||
|
const emailRegex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
|
||||||
|
return emailRegex.test(value)
|
||||||
|
},
|
||||||
|
isUsername: (value: string) => {
|
||||||
|
const usernameRegex = /^[a-zA-Z0-9._]{3,16}$/;
|
||||||
|
return usernameRegex.test(value);
|
||||||
|
},
|
||||||
|
|
||||||
|
isPassword: (value: string) => {
|
||||||
|
const passwordRegex = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$/;
|
||||||
|
return passwordRegex.test(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Validators
|
Loading…
x
Reference in New Issue
Block a user