Files
Projektarbeit-MYP/backend/static/js/sw.js
Till Tomczak 956c24d8ca 🔧 Update: Enhanced error handling and logging across various modules
**Änderungen:**
-  app.py: Hinzugefügt, um CSRF-Fehler zu behandeln
-  models.py: Fehlerprotokollierung bei der Suche nach Gastanfragen per OTP
-  api.py: Fehlerprotokollierung beim Markieren von Benachrichtigungen als gelesen
-  calendar.py: Fallback-Daten zurückgeben, wenn keine Kalenderereignisse vorhanden sind
-  guest.py: Status-Check-Seite für Gäste aktualisiert
-  hardware_integration.py: Debugging-Informationen für erweiterte Geräteinformationen hinzugefügt
-  tapo_status_manager.py: Rückgabewert für Statusabfrage hinzugefügt

**Ergebnis:**
- Verbesserte Fehlerbehandlung und Protokollierung für eine robustere Anwendung
- Bessere Nachverfolgbarkeit von Fehlern und Systemverhalten

🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
2025-06-15 22:45:20 +02:00

430 lines
12 KiB
JavaScript

/**
* 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(`
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>MYP Platform - Offline</title>
<style>
body { font-family: Arial, sans-serif; text-align: center; padding: 50px; }
.offline-message { max-width: 500px; margin: 0 auto; }
</style>
</head>
<body>
<div class="offline-message">
<h1>Offline</h1>
<p>Sie sind momentan offline. Bitte überprüfen Sie Ihre Internetverbindung.</p>
<button onclick="window.location.reload()">Erneut versuchen</button>
</div>
</body>
</html>
`, {
status: 200,
statusText: 'OK',
headers: {
'Content-Type': 'text/html; charset=utf-8'
}
});
}
}
// Generische Request-Behandlung
async function handleGenericRequest(request) {
try {
return await fetch(request);
} catch (error) {
console.error('Service Worker: Generic request failed', error);
return new Response('Request failed', {
status: 503,
statusText: 'Service Unavailable',
headers: {
'Content-Type': 'text/plain; charset=utf-8'
}
});
}
}
// Hilfsfunktionen für Request-Typ-Erkennung
function isStaticFile(pathname) {
return STATIC_PATTERNS.some(pattern => pattern.test(pathname));
}
function isAPIRequest(pathname) {
return API_PATTERNS.some(pattern => pattern.test(pathname));
}
function isPageRequest(request) {
return request.mode === 'navigate' ||
request.headers.get('accept')?.includes('text/html');
}
function shouldCacheAPIResponse(url) {
const pathname = new URL(url).pathname;
return API_CACHE_PATTERNS.some(pattern => pattern.test(pathname));
}
// Background Sync für Offline-Aktionen
self.addEventListener('sync', (event) => {
console.log('Service Worker: Background sync triggered', event.tag);
if (event.tag === 'background-sync') {
event.waitUntil(doBackgroundSync());
}
});
async function doBackgroundSync() {
try {
// Pending Requests aus IndexedDB oder localStorage holen
const pendingRequests = await getPendingRequests();
for (const request of pendingRequests) {
try {
await fetch(request.url, request.options);
await removePendingRequest(request.id);
console.log('Service Worker: Synced request', request.url);
} catch (error) {
console.error('Service Worker: Failed to sync request', request.url, error);
}
}
} catch (error) {
console.error('Service Worker: Background sync failed', error);
}
}
// Placeholder-Funktionen für IndexedDB-Integration
async function getPendingRequests() {
return [];
}
async function removePendingRequest(id) {
console.log('Service Worker: Removing pending request', id);
}
// Push Notification Handling
self.addEventListener('push', (event) => {
console.log('Service Worker: Push notification received');
const options = {
body: 'Sie haben neue Benachrichtigungen',
icon: '/static/icons/icon-192x192.png',
badge: '/static/icons/badge-72x72.png',
vibrate: [100, 50, 100],
data: {
dateOfArrival: Date.now(),
primaryKey: 1
},
actions: [
{
action: 'explore',
title: 'Anzeigen',
icon: '/static/icons/checkmark.png'
},
{
action: 'close',
title: 'Schließen',
icon: '/static/icons/xmark.png'
}
]
};
event.waitUntil(
self.registration.showNotification('MYP Platform', options)
);
});
// Notification Click Handling
self.addEventListener('notificationclick', (event) => {
console.log('Service Worker: Notification clicked');
event.notification.close();
if (event.action === 'explore') {
event.waitUntil(
clients.openWindow('/dashboard')
);
}
});
// Message Handling vom Main Thread
self.addEventListener('message', (event) => {
console.log('Service Worker: Message received', event.data);
if (event.data && event.data.type === 'SKIP_WAITING') {
self.skipWaiting();
}
if (event.data && event.data.type === 'CACHE_URLS') {
event.waitUntil(
cacheUrls(event.data.urls)
);
}
});
// Spezifische URLs cachen
async function cacheUrls(urls) {
try {
const cache = await caches.open(DYNAMIC_CACHE);
await cache.addAll(urls);
console.log('Service Worker: URLs cached', urls);
} catch (error) {
console.error('Service Worker: Error caching URLs', error);
}
}
// Periodic Background Sync (falls unterstützt)
self.addEventListener('periodicsync', (event) => {
console.log('Service Worker: Periodic sync triggered', event.tag);
if (event.tag === 'content-sync') {
event.waitUntil(syncContent());
}
});
// Content periodisch synchronisieren
async function syncContent() {
try {
const endpoints = ['/api/dashboard', '/api/jobs'];
for (const endpoint of endpoints) {
try {
const response = await fetch(endpoint);
if (response && response.ok) {
const cache = await caches.open(DYNAMIC_CACHE);
cache.put(endpoint, response.clone());
}
} catch (error) {
console.error('Service Worker: Error syncing', endpoint, error);
}
}
} catch (error) {
console.error('Service Worker: Content sync failed', error);
}
}
console.log('Service Worker: Script loaded successfully');