bagg-builder 1adf88d195 phase0: wallabag-compat API + PWA frontend
- Wallabag v2 API compatible (OAuth2, entries CRUD, tags)
- Express + SQLite (better-sqlite3), zero extra deps
- Gated web UI with session auth
- PWA: service worker, manifest, offline support
- Mobile-first design, dark mode, FAB + modal
2026-05-02 22:35:27 +00:00

73 lines
2.4 KiB
JavaScript

const CACHE_NAME = 'bagg-v1';
const STATIC_ASSETS = [
'/login.html',
'/style.css',
'/app.js',
'/manifest.json',
];
// ── Install: cache static assets ─────────────────────────────────────────────
self.addEventListener('install', e => {
e.waitUntil(
caches.open(CACHE_NAME).then(cache => cache.addAll(STATIC_ASSETS))
);
self.skipWaiting();
});
// ── Activate: clean old caches ────────────────────────────────────────────────
self.addEventListener('activate', e => {
e.waitUntil(
caches.keys().then(keys =>
Promise.all(keys.filter(k => k !== CACHE_NAME).map(k => caches.delete(k)))
)
);
self.clients.claim();
});
// ── Fetch strategy ────────────────────────────────────────────────────────────
self.addEventListener('fetch', e => {
const { request } = e;
const url = new URL(request.url);
// Skip non-GET and cross-origin requests
if (request.method !== 'GET' || url.origin !== location.origin) return;
// API requests: network-first, no cache
if (url.pathname.startsWith('/web/api/') || url.pathname.startsWith('/api/')) {
e.respondWith(fetch(request).catch(() => new Response('{"error":"offline"}', {
headers: { 'Content-Type': 'application/json' }
})));
return;
}
// Static assets: cache-first
if (STATIC_ASSETS.includes(url.pathname) || url.pathname.startsWith('/icons/')) {
e.respondWith(
caches.match(request).then(cached => cached || fetch(request).then(res => {
if (res.ok) {
const clone = res.clone();
caches.open(CACHE_NAME).then(c => c.put(request, clone));
}
return res;
}))
);
return;
}
// Navigation (HTML pages): network-first, fallback to cache
if (request.mode === 'navigate') {
e.respondWith(
fetch(request)
.then(res => {
if (res.ok) {
const clone = res.clone();
caches.open(CACHE_NAME).then(c => c.put(request, clone));
}
return res;
})
.catch(() => caches.match(request).then(cached => cached || caches.match('/login.html')))
);
return;
}
});