import { getSuperJson, setSuperJson } from '@openpanel/json'; import type { RedisOptions } from 'ioredis'; import Redis from 'ioredis'; const options: RedisOptions = { connectTimeout: 10000, }; export { Redis }; export interface ExtendedRedis extends Redis { getJson: (key: string) => Promise; setJson: ( key: string, expireInSec: number, value: T, ) => Promise; } const createRedisClient = ( url: string, overrides: RedisOptions = {}, ): ExtendedRedis => { const client = new Redis(url, { ...options, ...overrides, }) as ExtendedRedis; client.on('error', (error) => { console.error('Redis Client Error:', error); }); client.getJson = async (key: string): Promise => { const value = await client.get(key); if (value) { const res = getSuperJson(value) as T; if (res && Array.isArray(res) && res.length === 0) { return null; } if (res && typeof res === 'object' && Object.keys(res).length === 0) { return null; } if (res) { return res; } } return null; }; client.setJson = async ( key: string, expireInSec: number, value: T, ): Promise => { await client.setex(key, expireInSec, setSuperJson(value)); }; return client; }; let redisCache: ExtendedRedis; export function getRedisCache() { if (!redisCache) { redisCache = createRedisClient(process.env.REDIS_URL!, options); } return redisCache; } let redisSub: ExtendedRedis; export function getRedisSub() { if (!redisSub) { redisSub = createRedisClient(process.env.REDIS_URL!, options); } return redisSub; } let redisPub: ExtendedRedis; export function getRedisPub() { if (!redisPub) { redisPub = createRedisClient(process.env.REDIS_URL!, options); } return redisPub; } let redisQueue: ExtendedRedis; export function getRedisQueue() { if (!redisQueue) { // Use different redis for queues (self-hosting will re-use the same redis instance) redisQueue = createRedisClient( (process.env.QUEUE_REDIS_URL || process.env.REDIS_URL)!, { ...options, enableReadyCheck: false, maxRetriesPerRequest: null, enableOfflineQueue: true, }, ); } return redisQueue; } export async function getLock(key: string, value: string, timeout: number) { const lock = await getRedisCache().set(key, value, 'PX', timeout, 'NX'); return lock === 'OK'; }