import Redis from 'ioredis'; import { logger } from '../utils/logger.js'; export class RedisClient { constructor() { this.client = null; } static getInstance() { if (!RedisClient.instance) { RedisClient.instance = new RedisClient(); } return RedisClient.instance; } async connect() { const config = { host: process.env.REDIS_HOST || 'localhost', port: process.env.REDIS_PORT || 6379, password: process.env.REDIS_PASSWORD || undefined, db: parseInt(process.env.REDIS_DB) || 0, retryStrategy: (times) => { const delay = Math.min(times * 50, 2000); return delay; } }; try { this.client = new Redis(config); this.client.on('connect', () => { logger.info('Redis connection established'); }); this.client.on('error', (err) => { logger.error('Redis error:', err); }); // Wait for connection await this.client.ping(); return this.client; } catch (error) { logger.error('Failed to connect to Redis:', error); throw error; } } async checkHealth() { try { const result = await this.client.ping(); return result === 'PONG'; } catch (error) { logger.error('Redis health check failed:', error); return false; } } async disconnect() { if (this.client) { await this.client.quit(); logger.info('Redis connection closed'); } } // Utility methods async setWithExpiry(key, value, ttl) { return await this.client.setex(key, ttl, JSON.stringify(value)); } async get(key) { const value = await this.client.get(key); return value ? JSON.parse(value) : null; } async del(key) { return await this.client.del(key); } async hset(key, field, value) { return await this.client.hset(key, field, JSON.stringify(value)); } async hget(key, field) { const value = await this.client.hget(key, field); return value ? JSON.parse(value) : null; } async hgetall(key) { const data = await this.client.hgetall(key); const result = {}; for (const [field, value] of Object.entries(data)) { result[field] = JSON.parse(value); } return result; } async hincrby(key, field, increment) { return await this.client.hincrby(key, field, increment); } }