/** * MYP Platform Service Worker * Offline-First Caching Strategy für Mercedes-Benz 3D-Druck-Management */ // Cache-Namen für verschiedene Ressourcen-Typen const CACHE_NAME = 'myp-platform-cache-v1'; const STATIC_CACHE = 'myp-static-v1'; const DYNAMIC_CACHE = 'myp-dynamic-v1'; // Statische Assets die gecacht werden sollen const ASSETS_TO_CACHE = [ '/', '/dashboard', '/static/css/tailwind.min.css', '/static/css/tailwind-dark.min.css', '/static/js/ui-components.js', '/static/js/offline-app.js', '/static/icons/mercedes-logo.svg', '/static/icons/icon-144x144.png', '/static/favicon.ico' ]; // Patterns für statische Dateien const STATIC_PATTERNS = [ /\.css$/, /\.js$/, /\.svg$/, /\.png$/, /\.ico$/, /\.woff2?$/ ]; // API-Request-Patterns die nicht gecacht werden sollen const API_PATTERNS = [ /^\/api\//, /^\/auth\// ]; // API-Endpoints die gecacht werden können const API_CACHE_PATTERNS = [ /^\/api\/dashboard/, /^\/api\/printers/, /^\/api\/jobs/, /^\/api\/stats/ ]; // Install Event - Cache core assets self.addEventListener('install', (event) => { console.log('Service Worker: Installing...'); event.waitUntil( caches.open(STATIC_CACHE) .then((cache) => { console.log('Service Worker: Caching static files'); return cache.addAll(ASSETS_TO_CACHE); }) .then(() => { console.log('Service Worker: Static files cached'); return self.skipWaiting(); }) .catch((error) => { console.error('Service Worker: Error caching static files', error); }) ); }); // Activate Event - Clean up old caches self.addEventListener('activate', (event) => { console.log('Service Worker: Activating...'); event.waitUntil( caches.keys() .then((cacheNames) => { return Promise.all( cacheNames.map((cacheName) => { if (cacheName !== STATIC_CACHE && cacheName !== DYNAMIC_CACHE) { console.log('Service Worker: Deleting old cache', cacheName); return caches.delete(cacheName); } }) ); }) .then(() => { console.log('Service Worker: Activated'); return self.clients.claim(); }) ); }); // Fetch Event - Handle requests mit korrekter Response-Behandlung self.addEventListener('fetch', (event) => { const { request } = event; const url = new URL(request.url); // Nur GET-Requests und HTTP/HTTPS unterstützen if (request.method !== 'GET' || (url.protocol !== 'http:' && url.protocol !== 'https:')) { return; // Lasse andere Requests durch } // Chrome-Extension-URLs ignorieren if (url.protocol === 'chrome-extension:') { return; } // Request-Typ bestimmen und entsprechend behandeln if (isStaticFile(url.pathname)) { event.respondWith(handleStaticFile(request)); } else if (isAPIRequest(url.pathname)) { event.respondWith(handleAPIRequest(request)); } else if (isPageRequest(request)) { event.respondWith(handlePageRequest(request)); } else { // Fallback für andere Requests event.respondWith(handleGenericRequest(request)); } }); // Statische Dateien behandeln - Cache First Strategy async function handleStaticFile(request) { try { // Zuerst im Cache suchen const cachedResponse = await caches.match(request); if (cachedResponse) { return cachedResponse; } // Netzwerk-Request versuchen const networkResponse = await fetch(request); // Erfolgreiche Responses cachen if (networkResponse && networkResponse.ok) { const cache = await caches.open(STATIC_CACHE); cache.put(request, networkResponse.clone()); } return networkResponse; } catch (error) { console.error('Service Worker: Error handling static file', error); // Cache-Fallback versuchen const cachedResponse = await caches.match(request); if (cachedResponse) { return cachedResponse; } // Offline-Fallback Response return new Response('Offline - Datei nicht verfügbar', { status: 503, statusText: 'Service Unavailable', headers: { 'Content-Type': 'text/plain; charset=utf-8' } }); } } // API-Requests behandeln - Network First mit Cache-Fallback async function handleAPIRequest(request) { try { // Netzwerk-Request versuchen const networkResponse = await fetch(request); if (networkResponse && networkResponse.ok) { // GET-Requests für bestimmte Endpoints cachen if (request.method === 'GET' && shouldCacheAPIResponse(request.url)) { const cache = await caches.open(DYNAMIC_CACHE); cache.put(request, networkResponse.clone()); } return networkResponse; } throw new Error(`HTTP ${networkResponse.status}`); } catch (error) { console.log('Service Worker: Network failed for API request, trying cache'); // Cache-Fallback für GET-Requests if (request.method === 'GET') { const cachedResponse = await caches.match(request); if (cachedResponse) { return cachedResponse; } } // Offline-Response für API-Requests return new Response(JSON.stringify({ error: 'Offline - Daten nicht verfügbar', offline: true, timestamp: new Date().toISOString() }), { status: 503, statusText: 'Service Unavailable', headers: { 'Content-Type': 'application/json; charset=utf-8' } }); } } // Seiten-Requests behandeln - Network First mit Offline-Fallback async function handlePageRequest(request) { try { // Netzwerk-Request versuchen const networkResponse = await fetch(request); if (networkResponse && networkResponse.ok) { // Erfolgreiche Seiten-Responses cachen const cache = await caches.open(DYNAMIC_CACHE); cache.put(request, networkResponse.clone()); return networkResponse; } throw new Error(`HTTP ${networkResponse.status}`); } catch (error) { console.log('Service Worker: Network failed for page request, trying cache'); // Cache-Fallback versuchen const cachedResponse = await caches.match(request); if (cachedResponse) { return cachedResponse; } // Offline-Seite als Fallback return new Response(`