// redisSingleton.mjs // Conexión Singleton a Redis para Authentik (AK) import { createClient } from 'redis'; class RedisAuthentik { static instance = null; constructor() { if (RedisAuthentik.instance) { return RedisAuthentik.instance; } const url = process.env.AK_REDIS_URL; if (!url) { throw new Error('Falta AK_REDIS_URL Ej: redis://:pass@host:6379/0'); } if (!/^redis(s)?:\/\//i.test(url)) { throw new Error('AK_REDIS_URL inválida: debe comenzar con "redis://" o "rediss://".'); } this.url = url; this.client = createClient({ url: this.url, socket: { connectTimeout: 5000 }, }); this.client.on('connect', () => console.log(`[REDIS AK] Conectando a ${this.url}`)); this.client.on('ready', () => console.log('[REDIS AK] Conexión lista.')); this.client.on('end', () => console.warn('[REDIS AK] Conexión cerrada.')); this.client.on('reconnecting', () => console.warn('[REDIS AK] Reintentando conexión...')); this.client.on('error', (err) => console.error('[REDIS AK] Error:', err?.message || err)); this._connectingPromise = null; RedisAuthentik.instance = this; } async connect() { if (this.client.isOpen) return this.client; if (this._connectingPromise) return this._connectingPromise; this._connectingPromise = this.client.connect() .then(() => this.client) .catch((err) => { this._connectingPromise = null; console.error('[REDIS AK] Falló la conexión inicial:', err?.message || err); throw err; }); return this._connectingPromise; } getClient() { return this.client; } async release() { try { if (this.client?.isOpen) await this.client.quit(); } catch (e) { console.warn('[REDIS AK] Error al cerrar:', e?.message || e); } finally { this._connectingPromise = null; } } } // Instancia única const redisAuthentik = new RedisAuthentik(); // --------------------- Healthcheck --------------------- async function verificarConexionRedisAuthentik() { try { console.log(`[REDIS AK] Comprobando accesibilidad a Redis en ${redisAuthentik.url} ...`); await redisAuthentik.connect(); const client = redisAuthentik.getClient(); const pong = await client.ping(); const timeArr = await client.sendCommand(['TIME']); const serverDate = new Date(Number(timeArr?.[0] || 0) * 1000); await client.set('hc:authentik', String(Date.now()), { EX: 10 }); console.log(`[REDIS AK] Conexión OK. PING=${pong}. Hora Redis:`, serverDate.toISOString()); } catch (error) { console.error('[REDIS AK] Error al conectar:', error?.message || error); console.error('[REDIS AK] Revisar AK_REDIS_URL, credenciales, red y firewall.'); } } // Export al estilo de poolSingleton.mjs export default { redisAuthentik, verificarConexionRedisAuthentik }; export { redisAuthentik, verificarConexionRedisAuthentik };