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,
|
||||
"performance": {
|
||||
"recommended": true,
|
||||
"noDelete": "off"
|
||||
"noDelete": "off",
|
||||
},
|
||||
"suspicious": {
|
||||
"noExplicitAny": "warn"
|
||||
|
11
src/app.ts
11
src/app.ts
@ -6,6 +6,7 @@ import compression from "compression";
|
||||
import cors from "cors";
|
||||
import express, { type Express } from "express";
|
||||
import helmet from "helmet";
|
||||
import {MariadbService} from "@services/databases/mariadb.service";
|
||||
|
||||
console.log("\n\n> Starting...\n\n\n\n");
|
||||
|
||||
@ -63,4 +64,14 @@ try {
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
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")}/`;
|
||||
this.logs.trace("MongoDB URI:", uri);
|
||||
this.client = new MongoClient(uri);
|
||||
this.client
|
||||
.connect()
|
||||
.then(() => {
|
||||
this.logs.info("Connected to MongoDB", 'All databases');
|
||||
})
|
||||
} catch (error) {
|
||||
this.logs.error(`Error connecting to MongoDB:`, 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() {
|
||||
try {
|
||||
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