| |
| |
|
|
| interface CacheEntry<T> { |
| value: T; |
| expiresAt: number; |
| } |
|
|
| class Cache { |
| private store: Map<string, CacheEntry<unknown>> = new Map(); |
| private cleanupInterval: NodeJS.Timeout | null = null; |
|
|
| constructor() { |
| |
| this.cleanupInterval = setInterval(() => this.cleanup(), 5 * 60 * 1000); |
| } |
|
|
| set<T>(key: string, value: T, ttlSeconds: number = 300): void { |
| const expiresAt = Date.now() + (ttlSeconds * 1000); |
| this.store.set(key, { value, expiresAt }); |
| } |
|
|
| get<T>(key: string): T | null { |
| const entry = this.store.get(key); |
| |
| if (!entry) return null; |
| |
| |
| if (Date.now() > entry.expiresAt) { |
| this.store.delete(key); |
| return null; |
| } |
| |
| return entry.value as T; |
| } |
|
|
| has(key: string): boolean { |
| const entry = this.store.get(key); |
| if (!entry) return false; |
| |
| if (Date.now() > entry.expiresAt) { |
| this.store.delete(key); |
| return false; |
| } |
| |
| return true; |
| } |
|
|
| delete(key: string): void { |
| this.store.delete(key); |
| } |
|
|
| clear(): void { |
| this.store.clear(); |
| } |
|
|
| private cleanup(): void { |
| const now = Date.now(); |
| for (const [key, entry] of this.store.entries()) { |
| if (now > entry.expiresAt) { |
| this.store.delete(key); |
| } |
| } |
| } |
|
|
| destroy(): void { |
| if (this.cleanupInterval) { |
| clearInterval(this.cleanupInterval); |
| this.cleanupInterval = null; |
| } |
| this.clear(); |
| } |
| } |
|
|
| |
| export const cache = new Cache(); |
|
|
| |
| export async function getCached<T>( |
| key: string, |
| fetcher: () => Promise<T>, |
| ttlSeconds: number = 300 |
| ): Promise<T> { |
| |
| const cached = cache.get<T>(key); |
| if (cached !== null) { |
| return cached; |
| } |
| |
| |
| const data = await fetcher(); |
| |
| |
| cache.set(key, data, ttlSeconds); |
| |
| return data; |
| } |
|
|
| |
| export const cacheKeys = { |
| user: (userId: string) => `user:${userId}`, |
| businesses: (userId: string) => `businesses:${userId}`, |
| templates: (userId: string) => `templates:${userId}`, |
| stats: (userId: string) => `stats:${userId}`, |
| analytics: (userId: string, period: string) => `analytics:${userId}:${period}`, |
| }; |
|
|