import { redisConfig } from "@config/environment"; import { logger } from "@helpers/logger"; import { type RedisClientType, createClient } from "redis"; class RedisJson { private static instance: RedisJson | null = null; private client: RedisClientType | null = null; private constructor() {} public static async initialize(): Promise { if (!RedisJson.instance) { RedisJson.instance = new RedisJson(); RedisJson.instance.client = createClient({ socket: { host: redisConfig.host, port: redisConfig.port, }, username: redisConfig.username || undefined, password: redisConfig.password || undefined, }); RedisJson.instance.client.on("error", (err: Error) => { logger.error(["Error connecting to Redis:", err, redisConfig.host]); process.exit(1); }); RedisJson.instance.client.on("connect", () => { logger.info([ "Connected to Redis on", `${redisConfig.host}:${redisConfig.port}`, ]); }); await RedisJson.instance.client.connect(); } return RedisJson.instance; } public static getInstance(): RedisJson { if (!RedisJson.instance || !RedisJson.instance.client) { throw new Error( "Redis instance not initialized. Call initialize() first.", ); } return RedisJson.instance; } public async disconnect(): Promise { if (!this.client) { logger.error("Redis client is not initialized."); process.exit(1); } try { await this.client.disconnect(); this.client = null; logger.info("Redis disconnected successfully."); } catch (error) { logger.error("Error disconnecting Redis client:"); logger.error(error as Error); throw error; } } public async get( type: "JSON" | "STRING", key: string, path?: string, ): Promise< string | number | boolean | Record | null | unknown > { if (!this.client) { logger.error("Redis client is not initialized."); throw new Error("Redis client is not initialized."); } try { if (type === "JSON") { const value: unknown = await this.client.json.get(key, { path, }); if (value instanceof Date) { return value.toISOString(); } return value; } if (type === "STRING") { const value: string | null = await this.client.get(key); return value; } throw new Error(`Invalid type: ${type}`); } catch (error) { logger.error(`Error getting value from Redis for key: ${key}`); logger.error(error as Error); throw error; } } public async set( type: "JSON" | "STRING", key: string, value: unknown, expiresInSeconds?: number, path?: string, ): Promise { if (!this.client) { logger.error("Redis client is not initialized."); throw new Error("Redis client is not initialized."); } try { if (type === "JSON") { await this.client.json.set(key, path || "$", value as string); if (expiresInSeconds) { await this.client.expire(key, expiresInSeconds); } } else if (type === "STRING") { if (expiresInSeconds) { await this.client.set(key, value as string, { EX: expiresInSeconds, }); } else { await this.client.set(key, value as string); } } else { throw new Error(`Invalid type: ${type}`); } } catch (error) { logger.error(`Error setting value in Redis for key: ${key}`); logger.error(error as Error); throw error; } } public async delete(type: "JSON" | "STRING", key: string): Promise { if (!this.client) { logger.error("Redis client is not initialized."); throw new Error("Redis client is not initialized."); } try { if (type === "JSON") { await this.client.json.del(key); } else if (type === "STRING") { await this.client.del(key); } else { throw new Error(`Invalid type: ${type}`); } } catch (error) { logger.error(`Error deleting value from Redis for key: ${key}`); logger.error(error as Error); throw error; } } public async expire(key: string, seconds: number): Promise { if (!this.client) { logger.error("Redis client is not initialized."); throw new Error("Redis client is not initialized."); } try { await this.client.expire(key, seconds); } catch (error) { logger.error([`Error expiring key in Redis: ${key}`, error as Error]); throw error; } } public async keys(pattern: string): Promise { if (!this.client) { logger.error("Redis client is not initialized."); throw new Error("Redis client is not initialized."); } try { const keys: string[] = await this.client.keys(pattern); return keys; } catch (error) { logger.error([ `Error getting keys from Redis for pattern: ${pattern}`, error as Error, ]); throw error; } } } export const redis: { initialize: () => Promise; getInstance: () => RedisJson; } = { initialize: RedisJson.initialize, getInstance: RedisJson.getInstance, }; export { RedisJson };