Skip to main content
Glama

News Aggregator API

cache.ts5.27 kB
/** * Cache utility for storing and retrieving API responses */ import NodeCache from 'node-cache'; import { logger } from './logger'; // Default cache TTL settings (in seconds) // Extended TTL values for a personal project with limited API quota (100 calls/day) const DEFAULT_TTL = 12 * 60 * 60; // 12 hours const CACHE_SETTINGS = { // Top news - refresh once per day at most top: 24 * 60 * 60, // 24 hours // Sources almost never change sources: 7 * 24 * 60 * 60, // 7 days // Similar articles and article by UUID are permanent until cache clear similar: 30 * 24 * 60 * 60, // 30 days uuid: 30 * 24 * 60 * 60, // 30 days // General search results all: 24 * 60 * 60 // 24 hours }; /** * Cache service for API responses */ export class CacheService { private static instance: CacheService; private cache: NodeCache; private statsTimer: NodeJS.Timeout | null = null; private constructor() { this.cache = new NodeCache({ stdTTL: DEFAULT_TTL, checkperiod: 120, // Check for expired keys every 2 minutes useClones: false // For better performance, don't clone objects }); // Only start the stats timer if not in test environment if (process.env.NODE_ENV !== 'test') { this.startStatsTimer(); } } /** * Start the statistics logging timer */ private startStatsTimer(): void { // Clear any existing timer first this.stopStatsTimer(); // Log cache statistics periodically this.statsTimer = setInterval(() => { const stats = this.cache.getStats(); logger.debug('Cache stats:', stats); }, 15 * 60 * 1000); // Log every 15 minutes } /** * Stop the statistics logging timer */ private stopStatsTimer(): void { if (this.statsTimer) { clearInterval(this.statsTimer); this.statsTimer = null; } } /** * Get singleton instance of cache service * @returns Cache service instance */ public static getInstance(): CacheService { if (!CacheService.instance) { CacheService.instance = new CacheService(); } return CacheService.instance; } /** * Get value from cache * @param key Cache key * @returns Cached value or undefined if not found */ public get<T>(key: string): T | undefined { const value = this.cache.get<T>(key); if (value) { logger.debug(`Cache hit for key: ${key}`); return value; } logger.debug(`Cache miss for key: ${key}`); return undefined; } /** * Set value in cache * @param key Cache key * @param value Value to cache * @param ttl Time to live in seconds (optional) * @returns Success status */ public set<T>(key: string, value: T, ttl?: number): boolean { const ttlValue = ttl !== undefined ? ttl : DEFAULT_TTL; logger.debug(`Setting cache for key: ${key}, TTL: ${ttlValue}s`); return this.cache.set(key, value, ttlValue); } /** * Check if key exists in cache * @param key Cache key * @returns True if key exists */ public has(key: string): boolean { return this.cache.has(key); } /** * Delete key from cache * @param key Cache key * @returns True if key was deleted */ public delete(key: string): boolean { return this.cache.del(key) > 0; } /** * Delete keys with a specific prefix * @param prefix Key prefix to match * @returns Number of keys deleted */ public deleteByPrefix(prefix: string): number { const keys = this.cache.keys().filter(key => key.startsWith(prefix)); return this.cache.del(keys); } /** * Clear the entire cache * @returns Success status */ public clear(): boolean { return this.cache.flushAll(); } /** * Shutdown the cache service, cleaning up resources * Useful for tests to ensure clean teardown */ public shutdown(): void { this.stopStatsTimer(); this.clear(); // Close the cache if needed if (typeof this.cache.close === 'function') { this.cache.close(); } } /** * Get the appropriate TTL based on endpoint type * @param type Type of endpoint ('top', 'all', 'similar', 'uuid', 'sources') * @returns TTL in seconds */ public getTTL(type: keyof typeof CACHE_SETTINGS): number { return CACHE_SETTINGS[type] || DEFAULT_TTL; } /** * Get cache statistics * @returns Object with cache stats */ public getStats(): object { const stats = this.cache.getStats(); const keys = this.cache.keys(); // Count keys by type const keysByType = { top: keys.filter(k => k.startsWith('top_')).length, all: keys.filter(k => k.startsWith('all_')).length, similar: keys.filter(k => k.startsWith('similar_')).length, uuid: keys.filter(k => k.startsWith('news_article_')).length, sources: keys.filter(k => k.startsWith('news_sources_')).length, other: keys.filter(k => !( k.startsWith('top_') || k.startsWith('all_') || k.startsWith('similar_') || k.startsWith('news_article_') || k.startsWith('news_sources_') )).length }; return { ...stats, keys: keys.length, keysByType }; } } // Export singleton instance export const cacheService = CacheService.getInstance();

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/Malachi-devel/the-news-api-mcp-server'

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