Fix stale queue page: switch pages to network-first in service worker
The service worker was using cache-first for ALL non-API routes, including page routes like /queue, /capture, /browser. This meant the browser kept serving old cached HTML with old inline JS even after deploys, which is why the queue file list never updated (old JS was running, silently crashing on API mismatches). Now only /static/ assets use cache-first (they're versioned via SW cache bumps). All pages and API calls use network-first with cache as offline fallback. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
107
app/static/sw.js
107
app/static/sw.js
@@ -1,9 +1,9 @@
|
||||
// NextSnap Service Worker
|
||||
// Provides offline-first caching for the app shell
|
||||
|
||||
const CACHE_VERSION = 'nextsnap-v18';
|
||||
const APP_SHELL_CACHE = 'nextsnap-shell-v14';
|
||||
const RUNTIME_CACHE = 'nextsnap-runtime-v14';
|
||||
const CACHE_VERSION = 'nextsnap-v19';
|
||||
const APP_SHELL_CACHE = 'nextsnap-shell-v15';
|
||||
const RUNTIME_CACHE = 'nextsnap-runtime-v15';
|
||||
|
||||
// Assets to cache on install
|
||||
const APP_SHELL_ASSETS = [
|
||||
@@ -62,91 +62,72 @@ self.addEventListener('activate', (event) => {
|
||||
);
|
||||
});
|
||||
|
||||
// Fetch event - cache-first for static assets, network-first for API
|
||||
// Fetch event - route requests to appropriate caching strategy
|
||||
self.addEventListener('fetch', (event) => {
|
||||
const { request } = event;
|
||||
const url = new URL(request.url);
|
||||
|
||||
// Skip non-GET requests
|
||||
// Skip non-GET requests (uploads, form posts, etc.)
|
||||
if (request.method !== 'GET') {
|
||||
return;
|
||||
}
|
||||
|
||||
// API requests - network-first with offline fallback
|
||||
if (url.pathname.startsWith('/api/')) {
|
||||
event.respondWith(
|
||||
fetch(request)
|
||||
.then((response) => {
|
||||
// Clone response for cache
|
||||
const responseClone = response.clone();
|
||||
|
||||
// Only cache successful responses (not errors or auth failures)
|
||||
if (response.status === 200) {
|
||||
caches.open(RUNTIME_CACHE).then((cache) => {
|
||||
// Don't cache file uploads or large responses
|
||||
if (!url.pathname.includes('/upload') &&
|
||||
!url.pathname.includes('/thumbnail')) {
|
||||
cache.put(request, responseClone);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return response;
|
||||
})
|
||||
.catch(() => {
|
||||
// Network failed - try cache, then offline response
|
||||
return caches.match(request)
|
||||
.then((cachedResponse) => {
|
||||
if (cachedResponse) {
|
||||
return cachedResponse;
|
||||
}
|
||||
|
||||
// Return offline fallback for API
|
||||
return new Response(
|
||||
JSON.stringify({
|
||||
error: 'offline',
|
||||
message: 'You are offline. This feature requires connectivity.'
|
||||
}),
|
||||
{
|
||||
status: 503,
|
||||
statusText: 'Service Unavailable',
|
||||
headers: { 'Content-Type': 'application/json' }
|
||||
}
|
||||
);
|
||||
});
|
||||
})
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// Static assets - cache-first strategy
|
||||
// Static assets (/static/) - cache-first (versioned via SW cache bump)
|
||||
if (url.pathname.startsWith('/static/')) {
|
||||
event.respondWith(
|
||||
caches.match(request)
|
||||
.then((cachedResponse) => {
|
||||
if (cachedResponse) {
|
||||
return cachedResponse;
|
||||
}
|
||||
|
||||
// Not in cache - fetch from network and cache it
|
||||
return fetch(request)
|
||||
.then((response) => {
|
||||
// Don't cache non-successful responses
|
||||
if (!response || response.status !== 200 || response.type === 'error') {
|
||||
if (!response || response.status !== 200) {
|
||||
return response;
|
||||
}
|
||||
|
||||
// Clone response for cache
|
||||
const responseClone = response.clone();
|
||||
|
||||
caches.open(RUNTIME_CACHE).then((cache) => {
|
||||
caches.open(APP_SHELL_CACHE).then((cache) => {
|
||||
cache.put(request, responseClone);
|
||||
});
|
||||
return response;
|
||||
})
|
||||
.catch(() => new Response('Offline', { status: 503 }));
|
||||
})
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// Everything else (pages, API) - network-first with cache fallback
|
||||
event.respondWith(
|
||||
fetch(request)
|
||||
.then((response) => {
|
||||
// Cache successful GET responses for offline fallback
|
||||
if (response.status === 200) {
|
||||
const responseClone = response.clone();
|
||||
caches.open(RUNTIME_CACHE).then((cache) => {
|
||||
// Don't cache file uploads or large binary responses
|
||||
if (!url.pathname.includes('/upload') &&
|
||||
!url.pathname.includes('/thumbnail')) {
|
||||
cache.put(request, responseClone);
|
||||
}
|
||||
});
|
||||
}
|
||||
return response;
|
||||
})
|
||||
.catch(() => {
|
||||
// Network failed and not in cache
|
||||
// For HTML pages, could return offline page here
|
||||
// Network failed - try cache
|
||||
return caches.match(request)
|
||||
.then((cachedResponse) => {
|
||||
if (cachedResponse) {
|
||||
return cachedResponse;
|
||||
}
|
||||
// No cache - return offline response
|
||||
if (url.pathname.startsWith('/api/')) {
|
||||
return new Response(
|
||||
JSON.stringify({ error: 'offline', message: 'You are offline.' }),
|
||||
{ status: 503, headers: { 'Content-Type': 'application/json' } }
|
||||
);
|
||||
}
|
||||
return new Response('Offline', { status: 503 });
|
||||
});
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user