Skip to main content
Glama
cache.ts6.25 kB
import * as fs from 'fs/promises'; import * as path from 'path'; import { homedir } from 'os'; import { GitLabActivity, CalendarEvent } from '../types/index.js'; const CACHE_FILE = path.join(homedir(), '.timesheet-assistant-mcp-cache.json'); const DEFAULT_CACHE_TTL = 3600000; // 1 hour in milliseconds interface CacheEntry<T> { data: T; timestamp: number; source: string; } interface CacheData { gitlab: { [dateKey: string]: CacheEntry<GitLabActivity>; }; googleCalendar: { [dateKey: string]: CacheEntry<CalendarEvent[]>; }; outlookCalendar: { [dateKey: string]: CacheEntry<CalendarEvent[]>; }; } export class ActivityCache { private cache: CacheData = { gitlab: {}, googleCalendar: {}, outlookCalendar: {}, }; private cacheTTL: number = DEFAULT_CACHE_TTL; private cacheStats = { gitlabHits: 0, gitlabMisses: 0, googleHits: 0, googleMisses: 0, outlookHits: 0, outlookMisses: 0, }; async load(): Promise<void> { try { const data = await fs.readFile(CACHE_FILE, 'utf-8'); this.cache = JSON.parse(data); } catch (error) { // Cache file doesn't exist yet, that's okay this.cache = { gitlab: {}, googleCalendar: {}, outlookCalendar: {}, }; } } async save(): Promise<void> { await fs.writeFile(CACHE_FILE, JSON.stringify(this.cache, null, 2), { mode: 0o600, }); } private getDateKey(date: Date): string { return date.toISOString().split('T')[0]; // YYYY-MM-DD } private isExpired(timestamp: number): boolean { return Date.now() - timestamp > this.cacheTTL; } // GitLab cache methods getGitLabActivity(date: Date): GitLabActivity | null { const key = this.getDateKey(date); const entry = this.cache.gitlab[key]; if (!entry) { this.cacheStats.gitlabMisses++; return null; } if (this.isExpired(entry.timestamp)) { delete this.cache.gitlab[key]; this.cacheStats.gitlabMisses++; return null; } this.cacheStats.gitlabHits++; return entry.data; } async setGitLabActivity(date: Date, activity: GitLabActivity): Promise<void> { const key = this.getDateKey(date); this.cache.gitlab[key] = { data: activity, timestamp: Date.now(), source: 'gitlab', }; await this.save(); } // Google Calendar cache methods getGoogleCalendarEvents(date: Date): CalendarEvent[] | null { const key = this.getDateKey(date); const entry = this.cache.googleCalendar[key]; if (!entry) { this.cacheStats.googleMisses++; return null; } if (this.isExpired(entry.timestamp)) { delete this.cache.googleCalendar[key]; this.cacheStats.googleMisses++; return null; } this.cacheStats.googleHits++; return entry.data; } async setGoogleCalendarEvents(date: Date, events: CalendarEvent[]): Promise<void> { const key = this.getDateKey(date); this.cache.googleCalendar[key] = { data: events, timestamp: Date.now(), source: 'google_calendar', }; await this.save(); } // Outlook Calendar cache methods getOutlookCalendarEvents(date: Date): CalendarEvent[] | null { const key = this.getDateKey(date); const entry = this.cache.outlookCalendar[key]; if (!entry) { this.cacheStats.outlookMisses++; return null; } if (this.isExpired(entry.timestamp)) { delete this.cache.outlookCalendar[key]; this.cacheStats.outlookMisses++; return null; } this.cacheStats.outlookHits++; return entry.data; } async setOutlookCalendarEvents(date: Date, events: CalendarEvent[]): Promise<void> { const key = this.getDateKey(date); this.cache.outlookCalendar[key] = { data: events, timestamp: Date.now(), source: 'outlook_calendar', }; await this.save(); } // Cache management async clearAll(): Promise<void> { this.cache = { gitlab: {}, googleCalendar: {}, outlookCalendar: {}, }; await this.save(); this.resetStats(); } async clearGitLab(): Promise<void> { this.cache.gitlab = {}; await this.save(); } async clearCalendars(): Promise<void> { this.cache.googleCalendar = {}; this.cache.outlookCalendar = {}; await this.save(); } async clearExpired(): Promise<void> { const now = Date.now(); // Clear expired GitLab entries for (const [key, entry] of Object.entries(this.cache.gitlab)) { if (now - entry.timestamp > this.cacheTTL) { delete this.cache.gitlab[key]; } } // Clear expired Google Calendar entries for (const [key, entry] of Object.entries(this.cache.googleCalendar)) { if (now - entry.timestamp > this.cacheTTL) { delete this.cache.googleCalendar[key]; } } // Clear expired Outlook Calendar entries for (const [key, entry] of Object.entries(this.cache.outlookCalendar)) { if (now - entry.timestamp > this.cacheTTL) { delete this.cache.outlookCalendar[key]; } } await this.save(); } getCacheStats() { const total = this.cacheStats.gitlabHits + this.cacheStats.gitlabMisses + this.cacheStats.googleHits + this.cacheStats.googleMisses + this.cacheStats.outlookHits + this.cacheStats.outlookMisses; const hits = this.cacheStats.gitlabHits + this.cacheStats.googleHits + this.cacheStats.outlookHits; const hitRate = total > 0 ? ((hits / total) * 100).toFixed(1) : '0'; return { ...this.cacheStats, totalHits: hits, totalRequests: total, hitRate: `${hitRate}%`, }; } resetStats() { this.cacheStats = { gitlabHits: 0, gitlabMisses: 0, googleHits: 0, googleMisses: 0, outlookHits: 0, outlookMisses: 0, }; } getCacheInfo() { return { gitlabEntries: Object.keys(this.cache.gitlab).length, googleCalendarEntries: Object.keys(this.cache.googleCalendar).length, outlookCalendarEntries: Object.keys(this.cache.outlookCalendar).length, cacheTTL: this.cacheTTL, cacheFile: CACHE_FILE, }; } setCacheTTL(ttl: number) { this.cacheTTL = ttl; } }

Latest Blog Posts

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/sharadmathuratthepsi/timesheet-mcp'

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