import pg from 'pg' const { Pool } = pg let pool: pg.Pool | null = null /** * Obtiene o crea el pool de conexiones a PostgreSQL. * Usa variables de entorno para la configuración. */ export function getPool(): pg.Pool { if (!pool) { const config = { user: process.env.POSTGRES_USER || 'seguidor', password: process.env.POSTGRES_PASSWORD || 'seguidor_password', database: process.env.POSTGRES_DB || 'seguidor_lotes', host: process.env.POSTGRES_HOST || 'postgres', port: parseInt(process.env.POSTGRES_PORT || '5432'), max: 20, // máximo de conexiones en el pool idleTimeoutMillis: 30000, connectionTimeoutMillis: 10000, // Aumentado a 10s para la primera conexión } pool = new Pool(config) // Manejo de errores del pool pool.on('error', (err) => { console.error('Error inesperado en el pool de PostgreSQL:', err) }) // Solo log en desarrollo para reducir ruido if (process.env.NODE_ENV !== 'production') { pool.on('connect', () => { console.log('Nueva conexión establecida con PostgreSQL') }) } // Verificar conexión inicial con retry let retries = 5 const tryConnect = async () => { try { const client = await pool!.connect() console.log('✅ Conexión inicial a PostgreSQL exitosa') client.release() } catch (err: any) { retries-- if (retries > 0) { console.log(`⚠️ Conexión a PostgreSQL falló, reintentando... (${retries} intentos restantes)`) setTimeout(tryConnect, 2000) // Reintentar en 2 segundos } else { console.error('❌ No se pudo conectar a PostgreSQL después de varios intentos:', err.message) } } } // Ejecutar verificación inicial en background tryConnect() } return pool } /** * Ejecuta una query SQL con parámetros. * Wrapper seguro para evitar inyección SQL. * * @param text - Query SQL con placeholders $1, $2, etc. * @param params - Parámetros para la query * @returns Resultado de la query */ export async function query( text: string, params?: any[] ): Promise> { const pool = getPool() const start = Date.now() try { const result = await pool.query(text, params) const duration = Date.now() - start // Log solo en desarrollo if (process.env.NODE_ENV !== 'production') { console.log('Query ejecutada:', { text, duration: `${duration}ms`, rows: result.rowCount }) } return result } catch (error) { console.error('Error ejecutando query:', { text, params, error }) throw error } } /** * Obtiene un cliente del pool para ejecutar transacciones. * IMPORTANTE: Debes llamar a client.release() al terminar. * * @returns Cliente de PostgreSQL */ export async function getClient(): Promise { const pool = getPool() return await pool.connect() } /** * Cierra el pool de conexiones. * Útil para tests o shutdown graceful. */ export async function closePool(): Promise { if (pool) { await pool.end() pool = null console.log('Pool de PostgreSQL cerrado') } } /** * Verifica que la conexión a la base de datos esté funcionando. * Útil para health checks. * * @returns true si la conexión está OK, false en caso contrario */ export async function checkConnection(): Promise { try { const result = await query('SELECT NOW() as now') return result.rows.length > 0 } catch (error) { console.error('Error verificando conexión a PostgreSQL:', error) return false } }