/** * Service de cache pour les analytics Jira * Cache en mémoire avec invalidation manuelle */ import { JiraAnalytics } from '@/lib/types'; interface CacheEntry { data: JiraAnalytics; timestamp: number; projectKey: string; configHash: string; // Hash de la config Jira pour détecter les changements } class JiraAnalyticsCacheService { private cache = new Map(); private readonly CACHE_KEY_PREFIX = 'jira-analytics:'; /** * Génère une clé de cache basée sur la config Jira */ private getCacheKey(projectKey: string, configHash: string): string { return `${this.CACHE_KEY_PREFIX}${projectKey}:${configHash}`; } /** * Génère un hash de la configuration Jira pour détecter les changements */ private generateConfigHash(config: { baseUrl: string; email: string; apiToken: string; projectKey: string }): string { const configString = `${config.baseUrl}|${config.email}|${config.apiToken}|${config.projectKey}`; // Simple hash (pour production, utiliser crypto.createHash) let hash = 0; for (let i = 0; i < configString.length; i++) { const char = configString.charCodeAt(i); hash = ((hash << 5) - hash) + char; hash = hash & hash; // Convert to 32-bit integer } return hash.toString(); } /** * Récupère les analytics depuis le cache si disponible */ get(config: { baseUrl: string; email: string; apiToken: string; projectKey: string }): JiraAnalytics | null { const configHash = this.generateConfigHash(config); const cacheKey = this.getCacheKey(config.projectKey, configHash); const entry = this.cache.get(cacheKey); if (!entry) { console.log(`📋 Cache MISS pour projet ${config.projectKey}`); return null; } // Vérifier que la config n'a pas changé if (entry.configHash !== configHash) { console.log(`🔄 Config changée pour projet ${config.projectKey}, invalidation du cache`); this.cache.delete(cacheKey); return null; } console.log(`✅ Cache HIT pour projet ${config.projectKey} (${this.getAgeDescription(entry.timestamp)})`); return entry.data; } /** * Stocke les analytics dans le cache */ set(config: { baseUrl: string; email: string; apiToken: string; projectKey: string }, data: JiraAnalytics): void { const configHash = this.generateConfigHash(config); const cacheKey = this.getCacheKey(config.projectKey, configHash); const entry: CacheEntry = { data, timestamp: Date.now(), projectKey: config.projectKey, configHash }; this.cache.set(cacheKey, entry); console.log(`💾 Analytics mises en cache pour projet ${config.projectKey}`); } /** * Invalide le cache pour un projet spécifique */ invalidate(config: { baseUrl: string; email: string; apiToken: string; projectKey: string }): void { const configHash = this.generateConfigHash(config); const cacheKey = this.getCacheKey(config.projectKey, configHash); const deleted = this.cache.delete(cacheKey); if (deleted) { console.log(`🗑️ Cache invalidé pour projet ${config.projectKey}`); } else { console.log(`ℹ️ Aucun cache à invalider pour projet ${config.projectKey}`); } } /** * Invalide tout le cache */ invalidateAll(): void { const size = this.cache.size; this.cache.clear(); console.log(`🗑️ Tout le cache analytics invalidé (${size} entrées supprimées)`); } /** * Retourne les statistiques du cache */ getStats(): { totalEntries: number; projects: Array<{ projectKey: string; age: string; size: number }>; } { const projects = Array.from(this.cache.entries()).map(([, entry]) => ({ projectKey: entry.projectKey, age: this.getAgeDescription(entry.timestamp), size: JSON.stringify(entry.data).length })); return { totalEntries: this.cache.size, projects }; } /** * Formate l'âge d'une entrée de cache */ private getAgeDescription(timestamp: number): string { const ageMs = Date.now() - timestamp; const ageMinutes = Math.floor(ageMs / (1000 * 60)); const ageHours = Math.floor(ageMinutes / 60); if (ageHours > 0) { return `il y a ${ageHours}h${ageMinutes % 60}m`; } else if (ageMinutes > 0) { return `il y a ${ageMinutes}m`; } else { return 'maintenant'; } } /** * Vérifie si une entrée existe pour un projet */ has(config: { baseUrl: string; email: string; apiToken: string; projectKey: string }): boolean { const configHash = this.generateConfigHash(config); const cacheKey = this.getCacheKey(config.projectKey, configHash); return this.cache.has(cacheKey); } } // Instance singleton export const jiraAnalyticsCache = new JiraAnalyticsCacheService();