This commit is contained in:
2025-10-16 19:49:50 +00:00
parent ba6b4fef4f
commit c4097bc737
119 changed files with 3765 additions and 14390 deletions
+22
View File
@@ -0,0 +1,22 @@
{
"name": "@suitecoffee/db",
"version": "1.0.0",
"type": "module",
"main": "./poolSingleton.mjs",
"types": "./poolSingleton.d.ts",
"exports": {
".": {
"types": "./poolSingleton.d.ts",
"import": "./poolSingleton.mjs",
"default": "./poolSingleton.mjs"
},
"./package.json": "./package.json"
},
"peerDependencies": {
"pg": "^8.16.3"
},
"files": [
"poolSingleton.mjs",
"poolSingleton.d.ts"
]
}
+68
View File
@@ -0,0 +1,68 @@
// packages/core/db/poolSingleton.d.ts
// Declaraciones de tipos para @suitecoffee/db
// Refleja el módulo ESM que expone poolCore y poolTenants (ambos Singletons)
import type {
Pool,
PoolClient,
PoolConfig,
QueryResult,
QueryResultRow,
QueryConfig
} from 'pg';
export type { Pool, PoolClient, PoolConfig, QueryResult, QueryResultRow, QueryConfig };
// Clases modeladas según la implementación JS (no se exportan como valores en runtime,
// pero se exponen como tipos para el consumidor que quiera tipar sus variables).
export declare class DatabaseCore {
/** Instancia singleton interna (solo informativa para tipado). */
static instance?: DatabaseCore;
/** Pool real de `pg`. */
connection: Pool;
constructor();
/** Ejecuta una consulta utilizando el pool. */
query<T extends QueryResultRow = any>(
sql: string | QueryConfig<any[]>,
params?: any[]
): Promise<QueryResult<T>>;
/** Alias al `pool.connect()`; devuelve un `PoolClient`. */
connect(): Promise<PoolClient>;
/** Alias al `pool.connect()`; devuelve un `PoolClient`. */
getClient(): Promise<PoolClient>;
/** Cierra el pool subyacente. */
release(): Promise<void>;
}
export declare class DatabaseTenants {
static instance?: DatabaseTenants;
connection: Pool;
constructor();
query<T extends QueryResultRow = any>(
sql: string | QueryConfig<any[]>,
params?: any[]
): Promise<QueryResult<T>>;
connect(): Promise<PoolClient>;
getClient(): Promise<PoolClient>;
release(): Promise<void>;
}
/** Singletons creados por el módulo. */
export declare const poolCore: DatabaseCore;
export declare const poolTenants: DatabaseTenants;
/** Export por defecto del módulo: objeto con ambos pools. */
declare const _default: {
poolCore: DatabaseCore;
poolTenants: DatabaseTenants;
};
export default _default;
+148
View File
@@ -0,0 +1,148 @@
// poolSingleton.mjs
// Conexión Singleton a base de datos (pg/Pool) para CORE y TENANTS.
// Cambios mínimos respecto a tu versión original.
import { Pool } from 'pg';
// Utilidad mínima para booleans
const isTrue = (v) => String(v).toLowerCase() === 'true';
// --------------------- CORE ---------------------
class DatabaseCore {
static instance = null;
constructor() {
if (DatabaseCore.instance) {
return DatabaseCore.instance; // <-- corrección: antes devolvía Database.instance
}
const host = process.env.CORE_DB_HOST;
const user = process.env.CORE_DB_USER;
const password = process.env.CORE_DB_PASS;
const database = process.env.CORE_DB_NAME;
const port = process.env.CORE_DB_PORT;
const ssl =
isTrue(process.env.CORE_PGSSL ?? process.env.PGSSL)
? { rejectUnauthorized: false }
: undefined;
const config = {
host,
user,
password,
database,
port: port ? Number(port) : undefined,
ssl,
};
this.host = host;
this.dbName = database;
this.connection = new Pool(config);
DatabaseCore.instance = this;
}
async query(sql, params) {
return this.connection.query(sql, params);
}
async connect() { // idempotente a nivel de pool; retorna un client
return this.connection.connect();
}
async getClient() { // alias simple, conserva tu API
return this.connection.connect();
}
async release() { // cierra TODO el pool (uso excepcional)
await this.connection.end();
}
}
// --------------------- TENANTS ---------------------
class DatabaseTenants {
static instance = null;
constructor() {
if (DatabaseTenants.instance) {
return DatabaseTenants.instance; // <-- corrección: antes devolvía Database.instance
}
const host = process.env.TENANTS_DB_HOST;
const user = process.env.TENANTS_DB_USER;
const password = process.env.TENANTS_DB_PASS;
const database = process.env.TENANTS_DB_NAME;
const port = process.env.TENANTS_DB_PORT;
const ssl =
isTrue(process.env.TENANTS_PGSSL ?? process.env.PGSSL)
? { rejectUnauthorized: false }
: undefined;
const config = {
host,
user,
password,
database,
port: port ? Number(port) : undefined,
ssl,
};
this.host = host;
this.dbName = database;
this.connection = new Pool(config);
DatabaseTenants.instance = this;
}
async query(sql, params) {
return this.connection.query(sql, params);
}
async connect() { // idempotente a nivel de pool; retorna un client
return this.connection.connect();
}
async getClient() { // alias simple, conserva tu API
return this.connection.connect();
}
async release() { // cierra TODO el pool (uso excepcional)
await this.connection.end();
}
}
// Instancias únicas por el cache de módulos de Node/ESM + guardas estáticas
const poolCore = new DatabaseCore();
const poolTenants = new DatabaseTenants();
// --------------------- Healthchecks aquí dentro ---------------------
async function verificarConexionCore() {
try {
console.log(`[ PG ] Comprobando accesibilidad a la db ${poolCore.dbName} del host ${poolCore.host} ...`);
const client = await poolCore.getClient();
const { rows } = await client.query('SELECT NOW() AS ahora');
console.log(`[ PG ] Conexión con ${poolCore.dbName} OK. Hora DB:`, rows[0].ahora);
client.release();
} catch (error) {
console.error('[ PG ] Error al conectar con la base de datos al iniciar:', error.message);
console.error('[ PG ] Revisar credenciales, accesos de red y firewall.');
}
}
async function verificarConexionTenants() {
try {
console.log(`[ PG ] Comprobando accesibilidad a la db ${poolTenants.dbName} del host ${poolTenants.host} ...`);
const client = await poolTenants.getClient();
const { rows } = await client.query('SELECT NOW() AS ahora');
console.log(`[ PG ] Conexión con ${poolTenants.dbName} OK. Hora DB:`, rows[0].ahora);
client.release();
} catch (error) {
console.error('[ PG ] Error al conectar con la base de datos al iniciar:', error.message);
console.error('[ PG ] Revisar credenciales, accesos de red y firewall.');
}
}
// Exports (mantengo tu patrón)
export default { poolCore, poolTenants, verificarConexionCore, verificarConexionTenants };
export { poolCore, poolTenants, verificarConexionCore, verificarConexionTenants };
// export { DatabaseCore, DatabaseTenants }; // si lo necesitás para tests