import { createClient } from 'redis'; let redisClient: ReturnType | null = null; export const getRedisClient = async () => { if (!redisClient) { const redisUrl = process.env.REDIS_URL || 'redis://localhost:6379'; redisClient = createClient({ url: redisUrl, socket: { reconnectStrategy: (retries) => Math.min(retries * 50, 1000) } }); redisClient.on('error', (err) => { console.error('Redis Client Error:', err); }); redisClient.on('connect', () => { console.log('Redis Client Connected'); }); redisClient.on('ready', () => { console.log('Redis Client Ready'); }); redisClient.on('end', () => { console.log('Redis Client Disconnected'); }); await redisClient.connect(); } return redisClient; }; export const closeRedisConnection = async () => { if (redisClient) { await redisClient.quit(); redisClient = null; } }; // Cache utilities export const cache = { async get(key: string) { try { const client = await getRedisClient(); const value = await client.get(key); return value ? JSON.parse(value) : null; } catch (error) { console.error('Redis GET error:', error); return null; } }, async set(key: string, value: unknown, ttlSeconds = 3600) { try { const client = await getRedisClient(); await client.setEx(key, ttlSeconds, JSON.stringify(value)); return true; } catch (error) { console.error('Redis SET error:', error); return false; } }, async del(key: string) { try { const client = await getRedisClient(); await client.del(key); return true; } catch (error) { console.error('Redis DEL error:', error); return false; } }, async exists(key: string) { try { const client = await getRedisClient(); return await client.exists(key); } catch (error) { console.error('Redis EXISTS error:', error); return false; } }, async flush() { try { const client = await getRedisClient(); await client.flushAll(); return true; } catch (error) { console.error('Redis FLUSH error:', error); return false; } } }; // Session management export const session = { async create(userId: string, data: unknown, ttlSeconds = 86400) { const sessionId = `session:${userId}:${Date.now()}`; await cache.set(sessionId, data, ttlSeconds); return sessionId; }, async get(sessionId: string) { return await cache.get(sessionId); }, async update(sessionId: string, data: unknown, ttlSeconds = 86400) { return await cache.set(sessionId, data, ttlSeconds); }, async destroy(sessionId: string) { return await cache.del(sessionId); } }; // Analytics caching export const analyticsCache = { async getProjectStats(projectId: number) { return await cache.get(`analytics:project:${projectId}`); }, async setProjectStats(projectId: number, stats: unknown, ttlSeconds = 300) { return await cache.set(`analytics:project:${projectId}`, stats, ttlSeconds); }, async getOverallStats() { return await cache.get('analytics:overall'); }, async setOverallStats(stats: unknown, ttlSeconds = 600) { return await cache.set('analytics:overall', stats, ttlSeconds); }, async invalidateProject(projectId: number) { await cache.del(`analytics:project:${projectId}`); await cache.del('analytics:overall'); }, async clearAll() { try { const client = await getRedisClient(); // Clear all analytics-related keys const keys = await client.keys('analytics:*'); if (keys.length > 0) { await client.del(keys); } } catch (error) { console.error('Error clearing analytics cache:', error); } } };