Migrar backend a PostgreSQL + Node.js/Express con nuevas funcionalidades
Backend (water-api/): - Crear API REST completa con Express + TypeScript - Implementar autenticación JWT con refresh tokens - CRUD completo para: projects, concentrators, meters, gateways, devices, users, roles - Agregar validación con Zod para todas las entidades - Implementar webhooks para The Things Stack (LoRaWAN) - Agregar endpoint de lecturas con filtros y resumen de consumo - Implementar carga masiva de medidores via Excel (.xlsx) Frontend: - Crear cliente HTTP con manejo automático de JWT y refresh - Actualizar todas las APIs para usar nuevo backend - Agregar sistema de autenticación real (login, logout, me) - Agregar selector de tipo (LORA, LoRaWAN, Grandes) en concentradores y medidores - Agregar campo Meter ID en medidores - Crear modal de carga masiva para medidores - Agregar página de consumo con gráficas y filtros - Corregir carga de proyectos independiente de datos existentes Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
113
water-api/src/config/database.ts
Normal file
113
water-api/src/config/database.ts
Normal file
@@ -0,0 +1,113 @@
|
||||
import { Pool, PoolClient, QueryResult, QueryResultRow } from 'pg';
|
||||
import config from './index';
|
||||
import logger from '../utils/logger';
|
||||
|
||||
const pool = new Pool({
|
||||
host: config.database.host,
|
||||
port: config.database.port,
|
||||
user: config.database.user,
|
||||
password: config.database.password,
|
||||
database: config.database.database,
|
||||
ssl: config.database.ssl ? { rejectUnauthorized: false } : false,
|
||||
max: config.database.maxConnections,
|
||||
idleTimeoutMillis: config.database.idleTimeoutMs,
|
||||
connectionTimeoutMillis: config.database.connectionTimeoutMs,
|
||||
});
|
||||
|
||||
pool.on('connect', () => {
|
||||
logger.debug('New client connected to the database pool');
|
||||
});
|
||||
|
||||
pool.on('error', (err: Error) => {
|
||||
logger.error('Unexpected error on idle database client', err);
|
||||
});
|
||||
|
||||
pool.on('remove', () => {
|
||||
logger.debug('Client removed from the database pool');
|
||||
});
|
||||
|
||||
/**
|
||||
* Execute a query on the database pool
|
||||
* @param text - SQL query string
|
||||
* @param params - Query parameters
|
||||
* @returns Query result
|
||||
*/
|
||||
export const query = async <T extends QueryResultRow = QueryResultRow>(
|
||||
text: string,
|
||||
params?: unknown[]
|
||||
): Promise<QueryResult<T>> => {
|
||||
const start = Date.now();
|
||||
|
||||
try {
|
||||
const result = await pool.query<T>(text, params);
|
||||
const duration = Date.now() - start;
|
||||
|
||||
logger.debug(`Query executed in ${duration}ms`, {
|
||||
query: text,
|
||||
rows: result.rowCount,
|
||||
});
|
||||
|
||||
return result;
|
||||
} catch (error) {
|
||||
logger.error('Database query error', {
|
||||
query: text,
|
||||
error: error instanceof Error ? error.message : 'Unknown error',
|
||||
});
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Get a client from the pool for transactions
|
||||
* @returns Pool client
|
||||
*/
|
||||
export const getClient = async (): Promise<PoolClient> => {
|
||||
try {
|
||||
const client = await pool.connect();
|
||||
logger.debug('Database client acquired from pool');
|
||||
return client;
|
||||
} catch (error) {
|
||||
logger.error('Failed to get database client', {
|
||||
error: error instanceof Error ? error.message : 'Unknown error',
|
||||
});
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Test database connectivity
|
||||
* @returns True if connection is successful, false otherwise
|
||||
*/
|
||||
export const testConnection = async (): Promise<boolean> => {
|
||||
try {
|
||||
const result = await pool.query('SELECT NOW()');
|
||||
logger.info('Database connection successful', {
|
||||
serverTime: result.rows[0]?.now,
|
||||
});
|
||||
return true;
|
||||
} catch (error) {
|
||||
logger.error('Database connection failed', {
|
||||
error: error instanceof Error ? error.message : 'Unknown error',
|
||||
});
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Close all pool connections (for graceful shutdown)
|
||||
*/
|
||||
export const closePool = async (): Promise<void> => {
|
||||
try {
|
||||
await pool.end();
|
||||
logger.info('Database pool closed');
|
||||
} catch (error) {
|
||||
logger.error('Error closing database pool', {
|
||||
error: error instanceof Error ? error.message : 'Unknown error',
|
||||
});
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
export { pool };
|
||||
|
||||
export default pool;
|
||||
Reference in New Issue
Block a user