diff --git a/web/templates/pwa/service-worker.js b/web/templates/pwa/service-worker.js new file mode 100644 index 0000000..3ae7e9a --- /dev/null +++ b/web/templates/pwa/service-worker.js @@ -0,0 +1,76 @@ +// Service Worker — Illustrations DB +const VERSION = 'v1.0.0'; +const STATIC_CACHE = `static-${VERSION}`; +const PAGES_CACHE = `pages-${VERSION}`; +const OFFLINE_URL = '/offline/'; + +const STATIC_ASSETS = [ + '/', // optional: remove if you don’t want to cache / + '/static/pwa/icon-192.png', + '/static/pwa/icon-512.png' +]; + +self.addEventListener('install', (event) => { + event.waitUntil( + caches.open(STATIC_CACHE).then((cache) => cache.addAll(STATIC_ASSETS)) + .then(() => self.skipWaiting()) + ); +}); + +self.addEventListener('activate', (event) => { + event.waitUntil( + caches.keys().then((keys) => + Promise.all( + keys.filter(k => ![STATIC_CACHE, PAGES_CACHE].includes(k)) + .map(k => caches.delete(k)) + ) + ).then(() => self.clients.claim()) + ); +}); + +function isNavigation(req) { + return req.mode === 'navigate' || + (req.method === 'GET' && req.headers.get('accept')?.includes('text/html')); +} + +self.addEventListener('fetch', (event) => { + const req = event.request; + if (req.method !== 'GET') return; + + // HTML pages: stale-while-revalidate w/ offline fallback + if (isNavigation(req)) { + event.respondWith((async () => { + try { + const network = await fetch(req); + const cache = await caches.open(PAGES_CACHE); + cache.put(req, network.clone()); + return network; + } catch { + const cache = await caches.open(PAGES_CACHE); + const cached = await cache.match(req); + return cached || caches.match(OFFLINE_URL); + } + })()); + return; + } + + // Static files under /static: cache-first + const url = new URL(req.url); + if (url.pathname.startsWith('/static/')) { + event.respondWith((async () => { + const cache = await caches.open(STATIC_CACHE); + const cached = await cache.match(req); + if (cached) return cached; + try { + const network = await fetch(req); + cache.put(req, network.clone()); + return network; + } catch { + return cached; // best effort + } + })()); + return; + } + + // Default: pass-through +}); \ No newline at end of file