| Server IP : 202.61.199.114 / Your IP : 216.73.217.139 Web Server : nginx/1.22.1 System : Linux de.arni-solutions.de 6.1.0-49-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.1.174-1 (2026-05-26) x86_64 User : web20 ( 1018) PHP Version : 8.4.23 Disable Function : NONE MySQL : OFF | cURL : ON | WGET : ON | Perl : ON | Python : OFF | Sudo : ON | Pkexec : ON Directory : /var/www/seaside.pacim.de/web/ |
Upload File : |
/*
* sw.js – Service Worker der PaCIM Web-App (Terminal / Anreise-Abreise-Ansicht / Buchung).
* Zweck: Offline-NAVIGATION + LESEN. KEIN Schreib-Queue (Check-in/Zahlung brauchen weiter Verbindung).
*
* Strategie:
* - App-Seiten (terminal.php / day-list.php / checkin.php): stale-while-revalidate (cache-first) –
* gecachte Seite kommt SOFORT, Aktualisierung im Hintergrund. Dadurch öffnet der Wechsel
* Ansicht<->Terminal ohne Warten; bei neuem SW wird der Cache geleert -> erster Aufruf frisch.
* - Statische Assets (/assets/...): stale-while-revalidate.
* - ALLES andere (POST, AJAX-Endpunkte wie action=poll/dashwidget, das CRM) wird NICHT angefasst
* und läuft normal über das Netz.
*/
const CACHE = 'pacim-webapp-v40';
// terminal.php bewusst NICHT hier: es ist das Auth-Gateway und liefert unter derselben URL je nach
// PIN-/Login-Cookie unterschiedliche Seiten -> wird separat NETWORK-FIRST behandelt (siehe fetch).
const APP_PAGES = ['/day-list.php', '/checkin.php'];
// Voll-Seite einer App-Ansicht? (GET, App-Pfad, KEIN action=… -> AJAX-Endpunkte wie poll/dashwidget bleiben außen vor)
// Gilt für echte Navigationen UND fürs Hintergrund-Vorladen (prefetch) der Ansichten.
function isAppDoc(req, url) {
return req.method === 'GET' && APP_PAGES.indexOf(url.pathname) >= 0 && !url.searchParams.has('action');
}
// Offline-Fallback (kein Netz UND nichts im Cache): zentrierte Meldung + „Erneut versuchen"-Button.
const OFFLINE_HTML = `<!doctype html><html lang="de"><head><meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1,viewport-fit=cover">
<style>
html,body{margin:0;height:100%;}
body{display:flex;flex-direction:column;align-items:center;justify-content:center;min-height:100vh;
font-family:-apple-system,Segoe UI,Roboto,Helvetica,Arial,sans-serif;color:#0d2c44;text-align:center;padding:24px;}
.ov-t{font-size:18px;font-weight:700;}
.ov-s{font-size:15px;color:#5b7488;margin-top:6px;}
.ov-btn{margin-top:24px;display:inline-flex;align-items:center;gap:8px;background:#1b6ca8;color:#fff;border:0;
border-radius:12px;padding:14px 24px;font:600 16px -apple-system,Segoe UI,Roboto,sans-serif;cursor:pointer;
-webkit-tap-highlight-color:transparent;}
.ov-btn:active{filter:brightness(.92);}
</style></head><body>
<div class="ov-t">Keine Verbindung</div>
<div class="ov-s">und keine gespeicherte Ansicht vorhanden.</div>
<button type="button" class="ov-btn" onclick="location.reload()">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round"><path d="M23 4v6h-6"/><path d="M20.49 15a9 9 0 1 1-2.12-9.36L23 10"/></svg>
Erneut versuchen
</button>
</body></html>`;
self.addEventListener('install', () => { self.skipWaiting(); });
self.addEventListener('activate', (e) => {
e.waitUntil((async () => {
const keys = await caches.keys();
await Promise.all(keys.filter((k) => k !== CACHE).map((k) => caches.delete(k))); // alte Caches (alte HTML/JS) wegwerfen
await self.clients.claim();
// Offene Fenster auf die frische Version neu laden, damit niemand auf veralteter, gecachter Seite hängenbleibt.
const clients = await self.clients.matchAll({ type: 'window' });
for (const c of clients) { try { c.navigate(c.url); } catch (_) {} }
})());
});
self.addEventListener('fetch', (e) => {
const req = e.request;
if (req.method !== 'GET') return; // Schreibzugriffe nie abfangen
let url;
try { url = new URL(req.url); } catch (_) { return; }
if (url.origin !== self.location.origin) return; // nur eigene Domain
// --- Terminal (Auth-Gateway): NETWORK-FIRST ---
// terminal.php liefert je nach PIN-/Login-Cookie unterschiedliche Seiten (PIN-Screen / Login-Auswahl /
// Übersicht) UNTER DERSELBEN URL. Cache-first würde nach Login/Logout die falsche gecachte Seite zeigen
// (z. B. nach Logout die Übersicht -> „eingeloggt ohne PIN", oder PIN doppelt). Daher immer frisch aus dem
// Netz; der Cache dient nur als Offline-Rückfallebene.
if (req.method === 'GET' && url.pathname === '/terminal.php' && !url.searchParams.has('action')) {
e.respondWith((async () => {
const cache = await caches.open(CACHE);
try {
const resp = await fetch(req);
if (resp && resp.ok) { try { await cache.put(req, resp.clone()); } catch (_) {} }
return resp;
} catch (_) {
const cached = await cache.match(req, { ignoreVary: true });
return cached || new Response( OFFLINE_HTML, { headers: { 'Content-Type': 'text/html; charset=utf-8' }, status: 503 } );
}
})());
return;
}
// --- App-Seiten: stale-while-revalidate (CACHE-FIRST) ---
// Ist die Seite gecacht (per Vorladen oder früherem Besuch), kommt sie SOFORT aus dem Cache -> der Klick
// öffnet ohne Warten. Im Hintergrund wird der Cache frisch nachgeladen. Bei neuem SW ist der Cache nach
// dem activate leer -> erster Aufruf holt frisch (= neuer Code/Stand landet zuverlässig), dann wieder schnell.
if (isAppDoc(req, url)) {
const net = (async () => {
const resp = await fetch(req).catch(() => null);
if (resp && resp.ok) { try { const c = await caches.open(CACHE); await c.put(req, resp.clone()); } catch (_) {} }
return resp;
})();
e.waitUntil(net.catch(() => {})); // Hintergrund-Aktualisierung am Leben halten
e.respondWith((async () => {
const cache = await caches.open(CACHE);
const cached = await cache.match(req, { ignoreVary: true });
if (cached) return cached; // sofort: gespeicherte Ansicht
return (await net) || new Response( OFFLINE_HTML, // kein Cache (erster Besuch / nach Update) -> Netz
{ headers: { 'Content-Type': 'text/html; charset=utf-8' }, status: 503 } );
})());
return;
}
// --- Statische Assets: stale-while-revalidate ---
if (url.pathname.indexOf('/assets/') === 0) {
e.respondWith((async () => {
const cache = await caches.open(CACHE);
const cached = await cache.match(req);
const net = fetch(req).then((resp) => {
if (resp && resp.ok) cache.put(req, resp.clone());
return resp;
}).catch(() => null);
return cached || (await net) || Response.error();
})());
return;
}
// alles andere: nicht anfassen (Netz)
});