/** * Service de cache pour optimiser les performances */ export interface CacheEntry { data: T; timestamp: number; ttl: number; // Time to live en millisecondes } export class CacheService { private static cache = new Map>(); private static readonly DEFAULT_TTL = 5 * 60 * 1000; // 5 minutes par défaut /** * Stocke une valeur dans le cache */ static set(key: string, data: T, ttl: number = this.DEFAULT_TTL): void { const entry: CacheEntry = { data, timestamp: Date.now(), ttl }; this.cache.set(key, entry); } /** * Récupère une valeur du cache */ static get(key: string): T | null { const entry = this.cache.get(key); if (!entry) { return null; } // Vérifier si l'entrée a expiré if (Date.now() - entry.timestamp > entry.ttl) { this.cache.delete(key); return null; } return entry.data as T; } /** * Supprime une entrée du cache */ static delete(key: string): boolean { return this.cache.delete(key); } /** * Vide tout le cache */ static clear(): void { this.cache.clear(); } /** * Nettoie les entrées expirées */ static cleanup(): void { const now = Date.now(); for (const [key, entry] of this.cache.entries()) { if (now - entry.timestamp > entry.ttl) { this.cache.delete(key); } } } /** * Récupère ou exécute une fonction avec mise en cache */ static async getOrSet( key: string, fetchFunction: () => Promise, ttl: number = this.DEFAULT_TTL ): Promise { // Essayer de récupérer depuis le cache const cached = this.get(key); if (cached !== null) { return cached; } // Exécuter la fonction et mettre en cache try { const data = await fetchFunction(); this.set(key, data, ttl); return data; } catch (error) { // Ne pas mettre en cache les erreurs throw error; } } /** * Invalide le cache pour un pattern de clés */ static invalidatePattern(pattern: string): void { const regex = new RegExp(pattern); for (const key of this.cache.keys()) { if (regex.test(key)) { this.cache.delete(key); } } } /** * Obtient les statistiques du cache */ static getStats(): { size: number; keys: string[]; expired: number; } { const now = Date.now(); let expired = 0; for (const [key, entry] of this.cache.entries()) { if (now - entry.timestamp > entry.ttl) { expired++; } } return { size: this.cache.size, keys: Array.from(this.cache.keys()), expired }; } } // Nettoyage automatique toutes les 10 minutes if (typeof window !== 'undefined') { setInterval(() => { CacheService.cleanup(); }, 10 * 60 * 1000); } // Clés de cache prédéfinies export const CacheKeys = { CLIENTS: 'clients', CHANTIERS: 'chantiers', DEVIS: 'devis', FACTURES: 'factures', DASHBOARD_STATS: 'dashboard_stats', USER_PROFILE: 'user_profile', // Fonctions utilitaires pour générer des clés clientById: (id: string) => `client_${id}`, chantierId: (id: string) => `chantier_${id}`, devisById: (id: string) => `devis_${id}`, factureById: (id: string) => `facture_${id}`, devisByClient: (clientId: string) => `devis_client_${clientId}`, facturesByClient: (clientId: string) => `factures_client_${clientId}`, } as const;