import { Redis, RedisOptions } from 'ioredis'; export interface RedisServiceConfig { url?: string; options?: RedisOptions; } export class RedisService { private static instance: RedisService; private client: Redis; private constructor(config?: RedisServiceConfig) { const url = config?.url || process.env.REDIS_URL || 'redis://localhost:6379'; this.client = new Redis(url, config?.options || {}); } static getInstance(config?: RedisServiceConfig): RedisService { if (!RedisService.instance) { RedisService.instance = new RedisService(config); } return RedisService.instance; } getClient(): Redis { return this.client; } async isConnected(): Promise { return this.client.status === 'ready'; } async increment(key: string, expirySeconds?: number): Promise { const current = await this.client.incr(key); if (current === 1 && expirySeconds) { await this.client.expire(key, expirySeconds); } return current; } async getCounter(key: string): Promise { const value = await this.client.get(key); return value ? parseInt(value, 10) : 0; } async setWithExpiry(key: string, value: string, seconds: number): Promise<'OK' | 1> { return this.client.setex(key, seconds, value); } async setIfNotExists(key: string, value: string, seconds: number): Promise { const result = await this.client.set(key, value, 'EX', seconds, 'NX'); return result === 'OK'; } async get(key: string): Promise { return this.client.get(key); } async ttl(key: string): Promise { return this.client.ttl(key); } async delete(key: string): Promise { return this.client.del(key); } async getTTL(key: string): Promise { return this.client.ttl(key); } async close(): Promise { await this.client.quit(); } }