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
|
// NextSnap Service Worker
|
||||||
// Provides offline-first caching for the app shell
|
// Provides offline-first caching for the app shell
|
||||||
|
|
||||||
const CACHE_VERSION = 'nextsnap-v18';
|
const CACHE_VERSION = 'nextsnap-v19';
|
||||||
const APP_SHELL_CACHE = 'nextsnap-shell-v14';
|
const APP_SHELL_CACHE = 'nextsnap-shell-v15';
|
||||||
const RUNTIME_CACHE = 'nextsnap-runtime-v14';
|
const RUNTIME_CACHE = 'nextsnap-runtime-v15';
|
||||||
|
|
||||||
// Assets to cache on install
|
// Assets to cache on install
|
||||||
const APP_SHELL_ASSETS = [
|
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) => {
|
self.addEventListener('fetch', (event) => {
|
||||||
const { request } = event;
|
const { request } = event;
|
||||||
const url = new URL(request.url);
|
const url = new URL(request.url);
|
||||||
|
|
||||||
// Skip non-GET requests
|
// Skip non-GET requests (uploads, form posts, etc.)
|
||||||
if (request.method !== 'GET') {
|
if (request.method !== 'GET') {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// API requests - network-first with offline fallback
|
// Static assets (/static/) - cache-first (versioned via SW cache bump)
|
||||||
if (url.pathname.startsWith('/api/')) {
|
if (url.pathname.startsWith('/static/')) {
|
||||||
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
|
|
||||||
event.respondWith(
|
event.respondWith(
|
||||||
caches.match(request)
|
caches.match(request)
|
||||||
.then((cachedResponse) => {
|
.then((cachedResponse) => {
|
||||||
if (cachedResponse) {
|
if (cachedResponse) {
|
||||||
return cachedResponse;
|
return cachedResponse;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Not in cache - fetch from network and cache it
|
|
||||||
return fetch(request)
|
return fetch(request)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
// Don't cache non-successful responses
|
if (!response || response.status !== 200) {
|
||||||
if (!response || response.status !== 200 || response.type === 'error') {
|
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clone response for cache
|
|
||||||
const responseClone = response.clone();
|
const responseClone = response.clone();
|
||||||
|
caches.open(APP_SHELL_CACHE).then((cache) => {
|
||||||
caches.open(RUNTIME_CACHE).then((cache) => {
|
|
||||||
cache.put(request, responseClone);
|
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;
|
return response;
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
// Network failed and not in cache
|
// Network failed - try cache
|
||||||
// For HTML pages, could return offline page here
|
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 });
|
return new Response('Offline', { status: 503 });
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user