import { Redis } from 'ioredis'; import type { Cache, CacheSetOptions } from '../../fundamentals/cache/def'; export class RedisCache implements Cache { constructor(private readonly redis: Redis) {} // standard operation async get(key: string): Promise { return this.redis .get(key) .then(v => { if (v) { return JSON.parse(v); } return undefined; }) .catch(() => undefined); } async set( key: string, value: T, opts: CacheSetOptions = {} ): Promise { if (opts.ttl) { return this.redis .set(key, JSON.stringify(value), 'PX', opts.ttl) .then(() => true) .catch(() => false); } return this.redis .set(key, JSON.stringify(value)) .then(() => true) .catch(() => false); } async increase(key: string, count: number = 1): Promise { return this.redis.incrby(key, count).catch(() => 0); } async decrease(key: string, count: number = 1): Promise { return this.redis.decrby(key, count).catch(() => 0); } async setnx( key: string, value: T, opts: CacheSetOptions = {} ): Promise { if (opts.ttl) { return this.redis .set(key, JSON.stringify(value), 'PX', opts.ttl, 'NX') .then(v => !!v) .catch(() => false); } return this.redis .set(key, JSON.stringify(value), 'NX') .then(v => !!v) .catch(() => false); } async delete(key: string): Promise { return this.redis .del(key) .then(v => v > 0) .catch(() => false); } async has(key: string): Promise { return this.redis .exists(key) .then(v => v > 0) .catch(() => false); } async ttl(key: string): Promise { return this.redis.ttl(key).catch(() => 0); } async expire(key: string, ttl: number): Promise { return this.redis .pexpire(key, ttl) .then(v => v > 0) .catch(() => false); } // list operations async pushBack(key: string, ...values: T[]): Promise { return this.redis .rpush(key, ...values.map(v => JSON.stringify(v))) .catch(() => 0); } async pushFront(key: string, ...values: T[]): Promise { return this.redis .lpush(key, ...values.map(v => JSON.stringify(v))) .catch(() => 0); } async len(key: string): Promise { return this.redis.llen(key).catch(() => 0); } async list( key: string, start: number, end: number ): Promise { return this.redis .lrange(key, start, end) .then(data => data.map(v => JSON.parse(v))) .catch(() => []); } async popFront(key: string, count: number = 1): Promise { return this.redis .lpop(key, count) .then(data => (data ?? []).map(v => JSON.parse(v))) .catch(() => []); } async popBack(key: string, count: number = 1): Promise { return this.redis .rpop(key, count) .then(data => (data ?? []).map(v => JSON.parse(v))) .catch(() => []); } // map operations async mapSet( map: string, key: string, value: T ): Promise { return this.redis .hset(map, key, JSON.stringify(value)) .then(v => v > 0) .catch(() => false); } async mapIncrease( map: string, key: string, count: number = 1 ): Promise { return this.redis.hincrby(map, key, count); } async mapDecrease( map: string, key: string, count: number = 1 ): Promise { return this.redis.hincrby(map, key, -count); } async mapGet(map: string, key: string): Promise { return this.redis .hget(map, key) .then(v => (v ? JSON.parse(v) : undefined)) .catch(() => undefined); } async mapDelete(map: string, key: string): Promise { return this.redis .hdel(map, key) .then(v => v > 0) .catch(() => false); } async mapKeys(map: string): Promise { return this.redis.hkeys(map).catch(() => []); } async mapRandomKey(map: string): Promise { return this.redis .hrandfield(map, 1) .then(v => typeof v === 'string' ? v : Array.isArray(v) ? (v[0] as string) : undefined ) .catch(() => undefined); } async mapLen(map: string): Promise { return this.redis.hlen(map).catch(() => 0); } }