import { Injectable, Inject, OnModuleInit, OnModuleDestroy, Logger } from '@nestjs/common'; import Redis from 'ioredis'; interface RedisOptions { host: string; port: number; password?: string; db?: number; } @Injectable() export class RedisService implements OnModuleInit, OnModuleDestroy { private readonly logger = new Logger(RedisService.name); private client: Redis; constructor(@Inject('REDIS_OPTIONS') private readonly options: RedisOptions) {} async onModuleInit() { this.client = new Redis({ host: this.options.host, port: this.options.port, password: this.options.password, db: this.options.db ?? 16, retryStrategy: (times) => Math.min(times * 50, 2000), }); this.client.on('error', (err) => this.logger.error('Redis error', err)); this.client.on('connect', () => this.logger.log('Connected to Redis')); } async onModuleDestroy() { await this.client.quit(); } getClient(): Redis { return this.client; } async get(key: string): Promise { return this.client.get(key); } async set(key: string, value: string, ttlSeconds?: number): Promise { if (ttlSeconds) { await this.client.setex(key, ttlSeconds, value); } else { await this.client.set(key, value); } } async getJson(key: string): Promise { const value = await this.get(key); if (!value) return null; try { return JSON.parse(value) as T; } catch { return null; } } async setJson(key: string, value: T, ttlSeconds?: number): Promise { await this.set(key, JSON.stringify(value), ttlSeconds); } async acquireLock(lockKey: string, ttlSeconds: number = 30): Promise { const lockValue = `${Date.now()}-${Math.random().toString(36).substring(7)}`; const ttlMs = Math.round(ttlSeconds * 1000); const result = await this.client.set(lockKey, lockValue, 'PX', ttlMs, 'NX'); return result === 'OK' ? lockValue : null; } async releaseLock(lockKey: string, lockValue: string): Promise { const script = ` if redis.call("get", KEYS[1]) == ARGV[1] then return redis.call("del", KEYS[1]) else return 0 end `; const result = await this.client.eval(script, 1, lockKey, lockValue); return result === 1; } async incr(key: string): Promise { return this.client.incr(key); } async incrByFloat(key: string, increment: number): Promise { return this.client.incrbyfloat(key, increment); } async keys(pattern: string): Promise { return this.client.keys(pattern); } async del(key: string): Promise { return this.client.del(key); } }