import { Pool } from 'pg'; import { DatabaseConfig } from '../types'; import { mainPool } from '../config/database'; class DatabasePoolManager { private pools: Map = new Map(); async initialize() { // Load databases from DB instead of env await this.loadDatabasesFromDB(); } private async loadDatabasesFromDB() { try { const result = await mainPool.query( 'SELECT * FROM databases WHERE is_active = true' ); for (const row of result.rows) { const dbConfig: DatabaseConfig = { id: row.id, name: row.name, type: row.type, host: row.host, port: row.port, database_name: row.database_name, username: row.username, password: row.password, ssl: row.ssl, is_active: row.is_active, }; this.addPool(dbConfig); } console.log(`✅ Loaded ${result.rows.length} database connection(s) from DB`); } catch (error) { console.error('❌ Failed to load databases from DB:', error); } } addPool(dbConfig: DatabaseConfig) { if (this.pools.has(dbConfig.id)) { console.warn(`Pool with id ${dbConfig.id} already exists. Skipping.`); return; } const pool = new Pool({ host: dbConfig.host, port: dbConfig.port, database: dbConfig.database_name, user: dbConfig.username, password: dbConfig.password, ssl: dbConfig.ssl ? { rejectUnauthorized: false } : false, max: 20, // Увеличено количество соединений idleTimeoutMillis: 60000, // 60 секунд connectionTimeoutMillis: 10000, // 10 секунд (было 2 секунды) keepAlive: true, // Поддерживать соединения активными keepAliveInitialDelayMillis: 10000, // Начать keepAlive через 10 секунд }); pool.on('error', (err) => { console.error(`Database pool error for ${dbConfig.id}:`, err); }); this.pools.set(dbConfig.id, pool); console.log(`✅ Pool created for database: ${dbConfig.name} (${dbConfig.id})`); } removePool(databaseId: string) { const pool = this.pools.get(databaseId); if (pool) { pool.end(); this.pools.delete(databaseId); console.log(`Pool removed for database: ${databaseId}`); } } getPool(databaseId: string): Pool | undefined { return this.pools.get(databaseId); } async getDatabaseConfig(databaseId: string): Promise { try { const result = await mainPool.query( 'SELECT * FROM databases WHERE id = $1', [databaseId] ); if (result.rows.length === 0) { return null; } const row = result.rows[0]; return { id: row.id, name: row.name, type: row.type, host: row.host, port: row.port, database_name: row.database_name, username: row.username, password: row.password, ssl: row.ssl, is_active: row.is_active, aql_base_url: row.aql_base_url, aql_auth_type: row.aql_auth_type, aql_auth_value: row.aql_auth_value, aql_headers: row.aql_headers, }; } catch (error) { console.error('Error fetching database config:', error); return null; } } async getAllDatabaseConfigs(): Promise { try { const result = await mainPool.query( 'SELECT id, name, type, host, port, database_name, is_active FROM databases WHERE is_active = true' ); return result.rows; } catch (error) { console.error('Error fetching databases:', error); return []; } } async testConnection(databaseId: string): Promise { const pool = this.getPool(databaseId); if (!pool) { return false; } try { const client = await pool.connect(); client.release(); return true; } catch (error) { console.error(`Connection test failed for ${databaseId}:`, error); return false; } } async reloadPool(databaseId: string) { // Remove old pool this.removePool(databaseId); // Load new config from DB const result = await mainPool.query( 'SELECT * FROM databases WHERE id = $1 AND is_active = true', [databaseId] ); if (result.rows.length > 0) { const row = result.rows[0]; const dbConfig: DatabaseConfig = { id: row.id, name: row.name, type: row.type, host: row.host, port: row.port, database_name: row.database_name, username: row.username, password: row.password, ssl: row.ssl, is_active: row.is_active, }; this.addPool(dbConfig); } } async closeAll() { const promises = Array.from(this.pools.values()).map((pool) => pool.end()); await Promise.all(promises); this.pools.clear(); console.log('All database pools closed'); } } export const databasePoolManager = new DatabasePoolManager();