135 lines
4.8 KiB
JavaScript
135 lines
4.8 KiB
JavaScript
// import mariadb from 'mariadb';
|
|
import * as mariadb from 'mariadb';
|
|
|
|
// Fix para BigInt en la serialización JSON (necesario para IDs grandes en MariaDB)
|
|
BigInt.prototype.toJSON = function () {
|
|
return this.toString();
|
|
};
|
|
|
|
// Configuración del Pool
|
|
export const pool = mariadb.createPool({
|
|
host: process.env.DB_HOST,
|
|
user: process.env.DB_USER,
|
|
password: process.env.DB_PASSWORD,
|
|
database: 'Reale',
|
|
connectionLimit: 20,
|
|
timezone: 'Z',
|
|
insertIdAsNumber: true
|
|
});
|
|
|
|
// Helper para escapar identificadores (tablas/columnas)
|
|
const b = (id) => `\`${id.replace(/`/g, '``')}\``;
|
|
|
|
export class Database {
|
|
constructor(connector) {
|
|
/** @type {mariadb.Pool | mariadb.PoolConnection} */
|
|
this.connector = connector;
|
|
}
|
|
|
|
async insert(table, data) {
|
|
const rows = Array.isArray(data) ? data : [data];
|
|
if (!rows.length) return null;
|
|
const fields = Object.keys(rows[0]);
|
|
const query = `INSERT INTO ${b(table)} (${fields.map(b).join(',')}) VALUES (${fields.map(() => '?').join(',')})`;
|
|
return this.connector.batch(query, rows.map(r => fields.map(f => r[f])));
|
|
}
|
|
|
|
async update(table, data, where) {
|
|
const fields = Object.keys(data);
|
|
const wFields = Object.keys(where);
|
|
const query = `UPDATE ${b(table)} SET ${fields.map(f => `${b(f)}=?`).join(',')} WHERE ${wFields.map(f => `${b(f)}=?`).join(' AND ')}`;
|
|
return this.connector.query(query, [...fields.map(f => data[f]), ...wFields.map(f => where[f])]);
|
|
}
|
|
|
|
async upsert(table, data, ignore = false) {
|
|
const rows = Array.isArray(data) ? data : [data];
|
|
if (!rows.length) return null;
|
|
const fields = Object.keys(rows[0]);
|
|
const set = fields.filter(f => f !== 'id').map(f => `${b(f)}=VALUES(${b(f)})`).join(',');
|
|
const query = `INSERT ${ignore ? 'IGNORE' : ''} INTO ${b(table)} (${fields.map(b).join(',')}) VALUES (${fields.map(() => '?').join(',')}) ON DUPLICATE KEY UPDATE ${set}`;
|
|
return this.connector.batch(query, rows.map(r => fields.map(f => r[f])));
|
|
}
|
|
|
|
async delete(table, where) {
|
|
const keys = Object.keys(where);
|
|
if (!keys.length) throw new Error('Delete requiere condiciones');
|
|
return this.connector.query(`DELETE FROM ${b(table)} WHERE ${keys.map(k => `${b(k)}=?`).join(' AND ')}`, keys.map(k => where[k]));
|
|
}
|
|
|
|
async select(table, where = {}, opts = { limit: 5000, order: null }) {
|
|
const keys = Object.keys(where);
|
|
let sqlStr = `SELECT * FROM ${b(table)}`;
|
|
if (keys.length) sqlStr += ` WHERE ${keys.map(k => `${b(k)}=?`).join(' AND ')}`;
|
|
if (opts.order) sqlStr += ` ORDER BY ${b(opts.order)}`;
|
|
return this.connector.query(sqlStr + ` LIMIT ${opts.limit}`, keys.map(k => where[k]));
|
|
}
|
|
|
|
async one(table, where) {
|
|
const res = await this.select(table, where, { limit: 1 });
|
|
return res[0] || null;
|
|
}
|
|
|
|
async count(table, where = {}) {
|
|
const keys = Object.keys(where);
|
|
let sqlStr = `SELECT COUNT(*) as total FROM ${b(table)}`;
|
|
if (keys.length) sqlStr += ` WHERE ${keys.map(k => `${b(k)}=?`).join(' AND ')}`;
|
|
const res = await this.connector.query(sqlStr, keys.map(k => where[k]));
|
|
return Number(res[0].total);
|
|
}
|
|
|
|
async search(table, fields, term, limit = 1000) {
|
|
if (!fields.length || !term) return [];
|
|
const where = fields.map(f => `${b(f)} LIKE ?`).join(' OR ');
|
|
return this.connector.query(`SELECT * FROM ${b(table)} WHERE ${where} LIMIT ${limit}`, fields.map(() => `%${term}%`));
|
|
}
|
|
|
|
async unique(table, fields, where = {}) {
|
|
const f = Array.isArray(fields) ? fields.map(b).join(',') : b(fields);
|
|
const keys = Object.keys(where);
|
|
let sqlStr = `SELECT DISTINCT ${f} FROM ${b(table)}`;
|
|
if (keys.length) sqlStr += ` WHERE ${keys.map(k => `${b(k)}=?`).join(' AND ')}`;
|
|
const sort = Array.isArray(fields) ? b(fields[0]) : f;
|
|
return this.connector.query(sqlStr + ` ORDER BY ${sort} ASC`, keys.map(k => where[k]));
|
|
}
|
|
|
|
async raw(query, data = {}) {
|
|
const params = [];
|
|
const sqlStr = query.replace(/:(\w+)/g, (_, key) => {
|
|
params.push(data[key]);
|
|
return '?';
|
|
});
|
|
return this.connector.query(sqlStr, params);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Lógica de Transacciones
|
|
*/
|
|
export const trx = async (opts, actions) => {
|
|
const tasks = Array.isArray(opts) ? opts : [opts];
|
|
const conn = await pool.getConnection();
|
|
try {
|
|
await conn.beginTransaction();
|
|
const transactionDb = new Database(conn);
|
|
const results = {};
|
|
|
|
for (const item of tasks) {
|
|
if (typeof actions[item.action] !== 'function') {
|
|
throw new Error(`Acción ${item.action} no existe`);
|
|
}
|
|
results[item.action] = await actions[item.action](transactionDb, item);
|
|
}
|
|
|
|
await conn.commit();
|
|
return results;
|
|
} catch (err) {
|
|
console.error('Error en la transacción:', err);
|
|
await conn.rollback();
|
|
throw err;
|
|
} finally {
|
|
if (conn) conn.release();
|
|
}
|
|
};
|
|
|
|
// Exportamos la instancia principal vinculada al Pool
|
|
export const db = new Database(pool); |