/home/techb158/cosmic-risk.abdallabala.com/src/lib
Edit: /home/techb158/cosmic-risk.abdallabala.com/src/lib/redis.js (3517B)
const Redis = require('ioredis');
const REDIS_URL = process.env.REDIS_URL || 'redis://localhost:6379';
const ENABLED = process.env.COSMIC_REDIS_ENABLED !== 'false';
let redis = null;
const fallbackStore = new Map();
const fallbackTimers = new Map();
function createClient() {
if (!ENABLED) return null;
try {
const client = new Redis(REDIS_URL, {
maxRetriesPerRequest: 1,
retryStrategy(times) {
if (times > 3) return null;
return Math.min(times * 200, 2000);
},
lazyConnect: true,
enableOfflineQueue: false,
});
client.on('error', () => {});
client.on('close', () => {});
return client;
} catch {
try { redis.disconnect(); } catch {}
redis = false;
return null;
}
}
async function ensureConnected() {
if (redis === null) redis = createClient();
if (!redis || redis.status === 'ready') return redis;
try {
await redis.connect();
return redis;
} catch {
try { redis.disconnect(); } catch {}
redis = false;
return null;
}
}
function fallbackGet(key) {
const entry = fallbackStore.get(key);
if (!entry) return null;
if (entry.expiry && Date.now() > entry.expiry) {
fallbackStore.delete(key);
return null;
}
return entry.value;
}
function fallbackSet(key, value, ttlMs) {
fallbackStore.set(key, { value, expiry: ttlMs ? Date.now() + ttlMs : null });
if (fallbackTimers.has(key)) clearTimeout(fallbackTimers.get(key));
if (ttlMs) {
const timer = setTimeout(() => { fallbackStore.delete(key); fallbackTimers.delete(key); }, ttlMs);
timer.unref();
fallbackTimers.set(key, timer);
}
}
function fallbackDel(key) {
fallbackStore.delete(key);
if (fallbackTimers.has(key)) { clearTimeout(fallbackTimers.get(key)); fallbackTimers.delete(key); }
}
async function redisGet(key) {
const client = await ensureConnected();
if (client) {
try { return await client.get(key); } catch { return null; }
}
return fallbackGet(key);
}
async function redisSet(key, value, ttlMs) {
const client = await ensureConnected();
if (client) {
try {
if (ttlMs) await client.set(key, value, 'PX', ttlMs);
else await client.set(key, value);
return;
} catch {}
}
fallbackSet(key, value, ttlMs);
}
async function redisDel(key) {
const client = await ensureConnected();
if (client) {
try { await client.del(key); return; } catch {}
}
fallbackDel(key);
}
async function redisIncr(key, ttlMs) {
const client = await ensureConnected();
if (client) {
try {
const val = await client.incr(key);
if (ttlMs && val === 1) await client.pexpire(key, ttlMs);
return val;
} catch {}
}
const val = (parseInt(fallbackGet(key), 10) || 0) + 1;
fallbackSet(key, String(val), ttlMs);
return val;
}
async function redisTtl(key) {
const client = await ensureConnected();
if (client) {
try { return await client.ttl(key); } catch { return -2; }
}
const entry = fallbackStore.get(key);
if (!entry || !entry.expiry) return -1;
const remaining = Math.max(0, Math.floor((entry.expiry - Date.now()) / 1000));
return remaining > 0 ? remaining : -2;
}
async function redisFlush() {
const client = await ensureConnected();
if (client) {
try { await client.flushdb(); return; } catch {}
}
fallbackStore.clear();
for (const timer of fallbackTimers.values()) clearTimeout(timer);
fallbackTimers.clear();
}
module.exports = { redisGet, redisSet, redisDel, redisIncr, redisTtl, redisFlush };