/** * MYP Platform - CSS Caching Service Worker * Intelligentes Caching für optimierte CSS-Performance */ const CACHE_NAME = 'myp-css-cache-v1.0'; const CSS_CACHE_NAME = 'myp-css-resources-v1.0'; // Kritische CSS-Ressourcen für sofortiges Caching const CRITICAL_CSS_RESOURCES = [ '/static/css/caching-optimizations.css', '/static/css/optimization-animations.css', '/static/css/glassmorphism.css', '/static/css/professional-theme.css', '/static/css/tailwind.min.css' ]; // Nicht-kritische CSS-Ressourcen für Prefetching const NON_CRITICAL_CSS_RESOURCES = [ '/static/css/components.css', '/static/css/printers.css', '/static/fontawesome/css/all.min.css' ]; // CSS-spezifische Konfiguration const CSS_CACHE_CONFIG = { maxAge: 24 * 60 * 60 * 1000, // 24 Stunden maxEntries: 50, networkTimeoutSeconds: 5 }; // Service Worker Installation self.addEventListener('install', event => { console.log('[CSS-SW] Service Worker wird installiert...'); event.waitUntil( caches.open(CSS_CACHE_NAME) .then(cache => { console.log('[CSS-SW] Kritische CSS-Ressourcen werden gecacht...'); return cache.addAll(CRITICAL_CSS_RESOURCES); }) .then(() => { console.log('[CSS-SW] Installation abgeschlossen'); return self.skipWaiting(); }) .catch(error => { console.error('[CSS-SW] Fehler bei Installation:', error); }) ); }); // Service Worker Aktivierung self.addEventListener('activate', event => { console.log('[CSS-SW] Service Worker wird aktiviert...'); event.waitUntil( caches.keys() .then(cacheNames => { return Promise.all( cacheNames.map(cacheName => { // Alte Caches löschen if (cacheName !== CACHE_NAME && cacheName !== CSS_CACHE_NAME) { console.log('[CSS-SW] Alter Cache wird gelöscht:', cacheName); return caches.delete(cacheName); } }) ); }) .then(() => { console.log('[CSS-SW] Aktivierung abgeschlossen'); return self.clients.claim(); }) .then(() => { // Nicht-kritische Ressourcen im Hintergrund prefetchen return prefetchNonCriticalResources(); }) ); }); // CSS-Request-Behandlung mit Cache-First-Strategie self.addEventListener('fetch', event => { const { request } = event; // Nur CSS-Requests verarbeiten if (!request.url.includes('.css') && !request.url.includes('/static/css/')) { return; } event.respondWith( handleCSSRequest(request) ); }); // CSS-Request-Handler mit intelligenter Cache-Strategie async function handleCSSRequest(request) { const url = new URL(request.url); try { // 1. Cache-First für CSS-Dateien const cachedResponse = await caches.match(request); if (cachedResponse) { console.log('[CSS-SW] Cache-Hit für:', url.pathname); // Hintergrund-Update für kritische Ressourcen if (CRITICAL_CSS_RESOURCES.some(resource => url.pathname.includes(resource))) { updateCacheInBackground(request); } return cachedResponse; } // 2. Network-Request mit Timeout const networkResponse = await fetchWithTimeout(request, CSS_CACHE_CONFIG.networkTimeoutSeconds * 1000); if (networkResponse && networkResponse.ok) { // Response für Cache klonen const responseToCache = networkResponse.clone(); // Asynchron cachen cacheResponse(request, responseToCache); console.log('[CSS-SW] Network-Response für:', url.pathname); return networkResponse; } // 3. Fallback für kritische CSS return await getFallbackCSS(request); } catch (error) { console.error('[CSS-SW] Fehler bei CSS-Request:', error); return await getFallbackCSS(request); } } // Network-Request mit Timeout function fetchWithTimeout(request, timeout) { return new Promise((resolve, reject) => { const timer = setTimeout(() => { reject(new Error('Network timeout')); }, timeout); fetch(request) .then(response => { clearTimeout(timer); resolve(response); }) .catch(error => { clearTimeout(timer); reject(error); }); }); } // Response asynchron cachen async function cacheResponse(request, response) { try { const cache = await caches.open(CSS_CACHE_NAME); await cache.put(request, response); // Cache-Größe prüfen und ggf. alte Einträge löschen await maintainCacheSize(); } catch (error) { console.error('[CSS-SW] Fehler beim Cachen:', error); } } // Cache-Größe überwachen und bereinigen async function maintainCacheSize() { try { const cache = await caches.open(CSS_CACHE_NAME); const requests = await cache.keys(); if (requests.length > CSS_CACHE_CONFIG.maxEntries) { console.log('[CSS-SW] Cache-Bereinigung wird durchgeführt...'); // Älteste Einträge löschen (LRU-ähnlich) const excessCount = requests.length - CSS_CACHE_CONFIG.maxEntries; for (let i = 0; i < excessCount; i++) { await cache.delete(requests[i]); } } } catch (error) { console.error('[CSS-SW] Fehler bei Cache-Wartung:', error); } } // Hintergrund-Update für kritische Ressourcen async function updateCacheInBackground(request) { try { const response = await fetch(request); if (response && response.ok) { const cache = await caches.open(CSS_CACHE_NAME); await cache.put(request, response.clone()); console.log('[CSS-SW] Hintergrund-Update für:', request.url); } } catch (error) { console.log('[CSS-SW] Hintergrund-Update fehlgeschlagen:', error); } } // Fallback-CSS für kritische Requests async function getFallbackCSS(request) { const url = new URL(request.url); // Minimales Fallback-CSS für kritische Komponenten const fallbackCSS = ` /* Fallback CSS für Offline-Nutzung */ body { font-family: system-ui, sans-serif; margin: 0; padding: 20px; background: #f8fafc; color: #1f2937; } .offline-notice { background: #fef3c7; border: 1px solid #f59e0b; border-radius: 8px; padding: 16px; margin-bottom: 20px; text-align: center; } .btn { background: #0073ce; color: white; border: none; padding: 8px 16px; border-radius: 4px; cursor: pointer; } .card { background: white; border: 1px solid #e5e7eb; border-radius: 8px; padding: 16px; margin-bottom: 16px; } `; return new Response(fallbackCSS, { headers: { 'Content-Type': 'text/css', 'Cache-Control': 'no-cache' } }); } // Nicht-kritische Ressourcen im Hintergrund prefetchen async function prefetchNonCriticalResources() { try { const cache = await caches.open(CSS_CACHE_NAME); for (const resource of NON_CRITICAL_CSS_RESOURCES) { try { const request = new Request(resource); const cachedResponse = await cache.match(request); if (!cachedResponse) { const response = await fetch(request); if (response && response.ok) { await cache.put(request, response); console.log('[CSS-SW] Prefetch erfolgreich für:', resource); } } } catch (error) { console.log('[CSS-SW] Prefetch fehlgeschlagen für:', resource); } } } catch (error) { console.error('[CSS-SW] Fehler beim Prefetching:', error); } } // Message-Handler für Cache-Management self.addEventListener('message', event => { const { type, data } = event.data; switch (type) { case 'SKIP_WAITING': self.skipWaiting(); break; case 'CLEAR_CSS_CACHE': clearCSSCache().then(() => { event.ports[0].postMessage({ success: true }); }); break; case 'PREFETCH_CSS': prefetchSpecificCSS(data.urls).then(() => { event.ports[0].postMessage({ success: true }); }); break; case 'GET_CACHE_STATS': getCacheStats().then(stats => { event.ports[0].postMessage(stats); }); break; } }); // CSS-Cache leeren async function clearCSSCache() { try { await caches.delete(CSS_CACHE_NAME); console.log('[CSS-SW] CSS-Cache wurde geleert'); } catch (error) { console.error('[CSS-SW] Fehler beim Leeren des CSS-Cache:', error); } } // Spezifische CSS-Dateien prefetchen async function prefetchSpecificCSS(urls) { try { const cache = await caches.open(CSS_CACHE_NAME); for (const url of urls) { try { const response = await fetch(url); if (response && response.ok) { await cache.put(url, response); console.log('[CSS-SW] Spezifisches Prefetch für:', url); } } catch (error) { console.log('[CSS-SW] Spezifisches Prefetch fehlgeschlagen für:', url); } } } catch (error) { console.error('[CSS-SW] Fehler beim spezifischen Prefetching:', error); } } // Cache-Statistiken abrufen async function getCacheStats() { try { const cache = await caches.open(CSS_CACHE_NAME); const requests = await cache.keys(); return { cssEntries: requests.length, maxEntries: CSS_CACHE_CONFIG.maxEntries, cacheUtilization: (requests.length / CSS_CACHE_CONFIG.maxEntries) * 100, cachedUrls: requests.map(req => req.url) }; } catch (error) { console.error('[CSS-SW] Fehler beim Abrufen der Cache-Stats:', error); return { error: error.message }; } } // Performance-Monitoring self.addEventListener('install', () => { console.log('[CSS-SW] Installation gestartet um:', new Date().toISOString()); }); self.addEventListener('activate', () => { console.log('[CSS-SW] Aktivierung abgeschlossen um:', new Date().toISOString()); }); // Globaler Error-Handler self.addEventListener('error', event => { console.error('[CSS-SW] Globaler Fehler:', event.error); }); self.addEventListener('unhandledrejection', event => { console.error('[CSS-SW] Unbehandelte Promise-Rejection:', event.reason); }); console.log('[CSS-SW] CSS-Caching Service Worker geladen');