import { createCache, type CacheProvider, type CacheStats } from '@stock-bot/cache'; import type { RedisConfig } from './types'; import { getServiceConfig, type ServiceConfig } from './service-registry'; /** * Service-aware cache that uses the service's Redis DB * Automatically prefixes keys with the service's cache namespace */ export class ServiceCache implements CacheProvider { private cache: CacheProvider; private prefix: string; constructor( serviceName: string, redisConfig: RedisConfig, isGlobalCache: boolean = false, logger?: any ) { // Get service configuration const serviceConfig = getServiceConfig(serviceName); if (!serviceConfig && !isGlobalCache) { throw new Error(`Unknown service: ${serviceName}`); } // Determine Redis DB and prefix let db: number; let prefix: string; if (isGlobalCache) { // Global cache uses db:0 db = 0; prefix = 'stock-bot:shared'; } else { // Service cache uses service's DB db = serviceConfig!.db; prefix = serviceConfig!.cachePrefix; } // Create underlying cache with correct DB const cacheConfig = { redisConfig: { ...redisConfig, db, }, keyPrefix: prefix + ':', logger, }; this.cache = createCache(cacheConfig); this.prefix = prefix; } // Implement CacheProvider interface async get(key: string): Promise { return this.cache.get(key); } async set( key: string, value: T, options?: | number | { ttl?: number; preserveTTL?: boolean; onlyIfExists?: boolean; onlyIfNotExists?: boolean; getOldValue?: boolean; } ): Promise { return this.cache.set(key, value, options); } async del(key: string): Promise { return this.cache.del(key); } async exists(key: string): Promise { return this.cache.exists(key); } async clear(): Promise { return this.cache.clear(); } async keys(pattern: string): Promise { return this.cache.keys(pattern); } getStats(): CacheStats { return this.cache.getStats(); } async health(): Promise { return this.cache.health(); } async waitForReady(timeout?: number): Promise { return this.cache.waitForReady(timeout); } isReady(): boolean { return this.cache.isReady(); } // Enhanced cache methods (delegate to underlying cache if available) async update(key: string, value: T): Promise { if (this.cache.update) { return this.cache.update(key, value); } // Fallback implementation return this.cache.set(key, value, { preserveTTL: true }); } async setIfExists(key: string, value: T, ttl?: number): Promise { if (this.cache.setIfExists) { return this.cache.setIfExists(key, value, ttl); } // Fallback implementation const result = await this.cache.set(key, value, { onlyIfExists: true, ttl }); return result !== null; } async setIfNotExists(key: string, value: T, ttl?: number): Promise { if (this.cache.setIfNotExists) { return this.cache.setIfNotExists(key, value, ttl); } // Fallback implementation const result = await this.cache.set(key, value, { onlyIfNotExists: true, ttl }); return result !== null; } async replace(key: string, value: T, ttl?: number): Promise { if (this.cache.replace) { return this.cache.replace(key, value, ttl); } // Fallback implementation return this.cache.set(key, value, ttl); } async updateField(key: string, updater: (current: T | null) => T, ttl?: number): Promise { if (this.cache.updateField) { return this.cache.updateField(key, updater, ttl); } // Fallback implementation const current = await this.cache.get(key); const updated = updater(current); return this.cache.set(key, updated, ttl); } /** * Get the actual Redis key with prefix */ getKey(key: string): string { return `${this.prefix}:${key}`; } } /** * Factory function to create service cache */ export function createServiceCache( serviceName: string, redisConfig: RedisConfig, options: { global?: boolean; logger?: any } = {} ): ServiceCache { return new ServiceCache(serviceName, redisConfig, options.global, options.logger); }