120f52a29a
Old cached index.html was likely serving stale JS on mobile devices. v8 purge ensures all clients fetch the latest code on next load.
124 lines
4.5 KiB
JavaScript
124 lines
4.5 KiB
JavaScript
// ═══════════════════════════════════════════════════════════════════════════
|
|
// Canteen Asset Tracker — Service Worker
|
|
// Caches app shell + CDN deps. Network-first for API, cache fallback for static.
|
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
|
|
const CACHE_NAME = 'canteen-v8';
|
|
|
|
// App shell — core resources needed to boot the PWA
|
|
const APP_SHELL = [
|
|
'/',
|
|
'/index.html',
|
|
];
|
|
|
|
// CDN resources we want cached for offline resilience
|
|
const CDN_URLS = [
|
|
'https://cdn.jsdelivr.net/npm/@zxing/library@0.20.0/umd/index.min.js',
|
|
'https://unpkg.com/leaflet@1.9.4/dist/leaflet.css',
|
|
'https://unpkg.com/leaflet-draw@1.0.4/dist/leaflet.draw.css',
|
|
'https://unpkg.com/leaflet@1.9.4/dist/leaflet.js',
|
|
'https://unpkg.com/leaflet-draw@1.0.4/dist/leaflet.draw.js',
|
|
'https://unpkg.com/leaflet.heat@0.2.0/dist/leaflet-heat.js',
|
|
];
|
|
|
|
// ── Install: pre-cache app shell + CDN resources ──────────────────────────
|
|
|
|
self.addEventListener('install', (event) => {
|
|
console.log('[SW] Install — caching app shell + CDN assets');
|
|
event.waitUntil(
|
|
caches.open(CACHE_NAME).then((cache) => {
|
|
return cache.addAll([...APP_SHELL, ...CDN_URLS]).catch((err) => {
|
|
console.warn('[SW] Some resources failed to cache (non-fatal):', err.message);
|
|
});
|
|
})
|
|
);
|
|
// Activate immediately — don't wait for old SW to release
|
|
self.skipWaiting();
|
|
});
|
|
|
|
// ── Activate: purge old caches ────────────────────────────────────────────
|
|
|
|
self.addEventListener('activate', (event) => {
|
|
console.log('[SW] Activate — purging old caches');
|
|
event.waitUntil(
|
|
caches.keys().then((keys) => {
|
|
return Promise.all(
|
|
keys.filter((k) => k !== CACHE_NAME).map((k) => caches.delete(k))
|
|
);
|
|
})
|
|
);
|
|
// Take control of all clients immediately
|
|
self.clients.claim();
|
|
});
|
|
|
|
// ── Fetch: network-first for everything, cache fallback ───────────────────
|
|
|
|
self.addEventListener('fetch', (event) => {
|
|
const url = new URL(event.request.url);
|
|
|
|
// Navigation requests: network-first, fallback to cached index.html
|
|
if (event.request.mode === 'navigate') {
|
|
event.respondWith(
|
|
fetch(event.request)
|
|
.then((response) => {
|
|
// Cache the latest version
|
|
const clone = response.clone();
|
|
caches.open(CACHE_NAME).then((cache) => cache.put(event.request, clone));
|
|
return response;
|
|
})
|
|
.catch(() => {
|
|
return caches.match(event.request).then((cached) => cached || caches.match('/'));
|
|
})
|
|
);
|
|
return;
|
|
}
|
|
|
|
// API GET calls: network-first, cache fallback (offline reads)
|
|
// API POST/PUT/DELETE: pass through — main thread handles offline queuing
|
|
if (url.pathname.startsWith('/api/')) {
|
|
if (event.request.method === 'GET') {
|
|
event.respondWith(
|
|
fetch(event.request)
|
|
.then((response) => {
|
|
if (response.ok) {
|
|
const clone = response.clone();
|
|
caches.open(CACHE_NAME).then((cache) => cache.put(event.request, clone));
|
|
}
|
|
return response;
|
|
})
|
|
.catch(() => caches.match(event.request))
|
|
);
|
|
}
|
|
// Non-GET API calls: pass through — main thread queues offline writes
|
|
return;
|
|
}
|
|
|
|
// Static assets (local + CDN): network-first, cache fallback
|
|
event.respondWith(
|
|
fetch(event.request)
|
|
.then((response) => {
|
|
if (response.ok) {
|
|
const clone = response.clone();
|
|
caches.open(CACHE_NAME).then((cache) => cache.put(event.request, clone));
|
|
}
|
|
return response;
|
|
})
|
|
.catch(() => {
|
|
return caches.match(event.request);
|
|
})
|
|
);
|
|
});
|
|
|
|
// ── Message handler: allow main thread to trigger queue sync ──────────────
|
|
|
|
self.addEventListener('message', (event) => {
|
|
if (event.data && event.data.type === 'SYNC_QUEUE') {
|
|
// Forward sync request to all controlled clients
|
|
self.clients.matchAll().then((clients) => {
|
|
clients.forEach((client) => {
|
|
client.postMessage({ type: 'PROCESS_OFFLINE_QUEUE' });
|
|
});
|
|
});
|
|
}
|
|
});
|