.
This commit is contained in:
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"name": "@suitecoffee/redis",
|
||||
"version": "1.0.0",
|
||||
"type": "module",
|
||||
"main": "./redisSingleton.mjs",
|
||||
"types": "./redisSingleton.d.ts",
|
||||
"exports": {
|
||||
".": {
|
||||
"types": "./redisSingleton.d.ts",
|
||||
"import": "./redisSingleton.mjs",
|
||||
"default": "./redisSingleton.mjs"
|
||||
},
|
||||
"./package.json": "./package.json"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"pg": "^8.16.3"
|
||||
},
|
||||
"files": [
|
||||
"redisSingleton.mjs",
|
||||
"redisSingleton.d.ts"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
// 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 };
|
||||
Reference in New Issue
Block a user