import Redis from "ioredis"; let redisInstance: Redis | null = null; export async function getRedis(): Promise { if (redisInstance) { return redisInstance; } const redisUrl = process.env.REDIS_URL || "redis://localhost:6379"; try { redisInstance = new Redis(redisUrl, { lazyConnect: false, maxRetriesPerRequest: null, enableReadyCheck: false, enableOfflineQueue: true, retryStrategy: (times) => { const delay = Math.min(times * 50, 2000); return delay; }, }); redisInstance.on("error", (err) => { console.error("[Redis] Error:", err.message); }); redisInstance.on("connect", () => { console.log("[Redis] Connected"); }); redisInstance.on("ready", () => { console.log("[Redis] Ready"); }); redisInstance.on("reconnecting", () => { console.log("[Redis] Reconnecting..."); }); // Test connection await redisInstance.ping(); console.log("[Redis] Ping successful"); return redisInstance; } catch (error) { console.error("[Redis] Connection failed:", error); throw error; } } export async function closeRedis(): Promise { if (redisInstance) { await redisInstance.quit(); redisInstance = null; } } // Cache helper functions export async function getCached(key: string): Promise { try { const redis = await getRedis(); const data = await redis.get(key); return data ? JSON.parse(data) : null; } catch (error) { console.error(`[Redis] Error getting cache for ${key}:`, error); return null; } } export async function setCached( key: string, value: T, expirationSeconds?: number ): Promise { try { const redis = await getRedis(); const data = JSON.stringify(value); if (expirationSeconds) { await redis.setex(key, expirationSeconds, data); } else { await redis.set(key, data); } return true; } catch (error) { console.error(`[Redis] Error setting cache for ${key}:`, error); return false; } } export async function deleteCached(key: string): Promise { try { const redis = await getRedis(); const result = await redis.del(key); return result > 0; } catch (error) { console.error(`[Redis] Error deleting cache for ${key}:`, error); return false; } } export async function invalidateCachePattern(pattern: string): Promise { try { const redis = await getRedis(); const keys = await redis.keys(pattern); if (keys.length === 0) return 0; return await redis.del(...keys); } catch (error) { console.error(`[Redis] Error invalidating cache pattern ${pattern}:`, error); return 0; } } // Session helpers for BetterAuth export async function getSession(sessionId: string): Promise { return getCached(`session:${sessionId}`); } export async function setSession( sessionId: string, sessionData: any, expirationSeconds: number = 86400 // 24 hours ): Promise { return setCached(`session:${sessionId}`, sessionData, expirationSeconds); } export async function deleteSession(sessionId: string): Promise { return deleteCached(`session:${sessionId}`); } // Cache key generators export const cacheKeys = { user: (userId: string) => `user:${userId}`, userByEmail: (email: string) => `user:email:${email}`, session: (sessionId: string) => `session:${sessionId}`, webinar: (webinarId: string) => `webinar:${webinarId}`, webinars: (page: number = 1) => `webinars:list:${page}`, registrations: (userId: string) => `registrations:${userId}`, contact: (contactId: string) => `contact:${contactId}`, config: "system:config", adminSetup: "admin:setup", };