Skip to main content
Glama
cache.ts3.51 kB
// ... existing code ... interface CacheEntry<T> { data: T; timestamp: number; accessCount: number; } interface CacheConfig { enabled: boolean; ttl: { search: number; mediaDetails: number; requests: number; }; maxSize: number; } export class CacheManager { private cache: Map<string, CacheEntry<any>>; private config: CacheConfig; private hits: Map<string, number>; private misses: Map<string, number>; constructor() { this.cache = new Map(); this.hits = new Map(['search', 'mediaDetails', 'requests'].map(k => [k, 0])); this.misses = new Map(['search', 'mediaDetails', 'requests'].map(k => [k, 0])); this.config = { enabled: process.env.CACHE_ENABLED !== 'false', ttl: { search: parseInt(process.env.CACHE_SEARCH_TTL || '300000'), // 5 min mediaDetails: parseInt(process.env.CACHE_MEDIA_TTL || '1800000'), // 30 min requests: parseInt(process.env.CACHE_REQUESTS_TTL || '60000'), // 1 min }, maxSize: parseInt(process.env.CACHE_MAX_SIZE || '1000'), }; } private getCacheKey(type: string, params: any): string { return `${type}:${JSON.stringify(params)}`; } private isExpired(entry: CacheEntry<any>, ttl: number): boolean { return Date.now() - entry.timestamp > ttl; } private evictLRU(): void { if (this.cache.size < this.config.maxSize) return; let lruKey: string | null = null; let lruAccessCount = Infinity; for (const [key, entry] of this.cache.entries()) { if (entry.accessCount < lruAccessCount) { lruAccessCount = entry.accessCount; lruKey = key; } } if (lruKey) { this.cache.delete(lruKey); } } get<T>(type: keyof CacheConfig['ttl'], params: any): T | null { if (!this.config.enabled) return null; const key = this.getCacheKey(type, params); const entry = this.cache.get(key); if (!entry) { this.misses.set(type, (this.misses.get(type) || 0) + 1); return null; } const ttl = this.config.ttl[type]; if (this.isExpired(entry, ttl)) { this.cache.delete(key); this.misses.set(type, (this.misses.get(type) || 0) + 1); return null; } entry.accessCount++; this.hits.set(type, (this.hits.get(type) || 0) + 1); return entry.data as T; } set<T>(type: keyof CacheConfig['ttl'], params: any, data: T): void { if (!this.config.enabled) return; this.evictLRU(); const key = this.getCacheKey(type, params); this.cache.set(key, { data, timestamp: Date.now(), accessCount: 0, }); } invalidate(type?: keyof CacheConfig['ttl']): void { if (type) { // Invalidate specific type const prefix = `${type}:`; for (const key of this.cache.keys()) { if (key.startsWith(prefix)) { this.cache.delete(key); } } } else { // Clear all cache this.cache.clear(); } } getStats() { const stats = { enabled: this.config.enabled, size: this.cache.size, maxSize: this.config.maxSize, types: {} as Record<string, any>, }; for (const type of ['search', 'mediaDetails', 'requests']) { const hits = this.hits.get(type) || 0; const misses = this.misses.get(type) || 0; const total = hits + misses; stats.types[type] = { hits, misses, hitRate: total > 0 ? ((hits / total) * 100).toFixed(1) + '%' : '0%', }; } return stats; } }

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/jhomen368/overseerr-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server