fix(offline): fall back to cached shell on upstream 5xx
Service worker previously only fell back to cache when fetch threw (network unreachable). A 502/503/504 from the origin returned successfully with !response.ok, so the bad page was passed through to the user. Now upstream 5xx is treated like a network failure: try cached page, then offline-shell redirect for recipe routes, then the styled offline page. 4xx still passes through unchanged.
This commit is contained in:
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "homepage",
|
||||
"version": "1.67.3",
|
||||
"version": "1.67.4",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
|
||||
+37
-25
@@ -117,30 +117,34 @@ sw.addEventListener('fetch', (event) => {
|
||||
// SvelteKit adds ?x-sveltekit-invalidated=... which we need to ignore
|
||||
const cacheKey = url.pathname;
|
||||
|
||||
let response: Response | undefined;
|
||||
try {
|
||||
// Try network first
|
||||
const response = await fetch(event.request);
|
||||
response = await fetch(event.request);
|
||||
} catch {
|
||||
// Network unreachable — fall through to cache fallback below.
|
||||
}
|
||||
|
||||
// Cache successful responses for offline use (using pathname as key)
|
||||
if (response.ok) {
|
||||
if (response?.ok) {
|
||||
cache.put(cacheKey, response.clone());
|
||||
}
|
||||
|
||||
return response;
|
||||
} catch {
|
||||
// Network failed - try to serve from cache (ignoring query params)
|
||||
const cached = await cache.match(cacheKey);
|
||||
if (cached) {
|
||||
return cached;
|
||||
}
|
||||
|
||||
// No cached data available - return error response
|
||||
// The page will need to handle this gracefully
|
||||
// Network unreachable OR upstream 5xx (502/503/504 etc.) — serve
|
||||
// stale cached data so the PWA stays usable when the origin is down.
|
||||
if (!response || response.status >= 500) {
|
||||
const cached = await cache.match(cacheKey);
|
||||
if (cached) return cached;
|
||||
}
|
||||
|
||||
// Pass through non-5xx errors (404, 401, ...) untouched.
|
||||
if (response) return response;
|
||||
|
||||
// No response and no cache — synthetic offline error.
|
||||
return new Response(JSON.stringify({ error: 'offline' }), {
|
||||
status: 503,
|
||||
headers: { 'Content-Type': 'application/json' }
|
||||
});
|
||||
}
|
||||
})()
|
||||
);
|
||||
return;
|
||||
@@ -222,27 +226,32 @@ sw.addEventListener('fetch', (event) => {
|
||||
// Use pathname only for cache key (ignore query params)
|
||||
const cacheKey = url.pathname;
|
||||
|
||||
let response: Response | undefined;
|
||||
try {
|
||||
// Try network first
|
||||
const response = await fetch(event.request);
|
||||
response = await fetch(event.request);
|
||||
} catch {
|
||||
// Network unreachable — fall through to fallback below.
|
||||
}
|
||||
|
||||
// Cache successful HTML responses for cacheable pages (using pathname as key)
|
||||
const isCacheablePage = response.ok && (
|
||||
if (response?.ok) {
|
||||
const isCacheablePage =
|
||||
url.pathname.match(/^\/(rezepte|recipes|glaube|faith|fitness)(\/|$)/) ||
|
||||
url.pathname === '/'
|
||||
);
|
||||
url.pathname === '/';
|
||||
if (isCacheablePage) {
|
||||
cache.put(cacheKey, response.clone());
|
||||
}
|
||||
|
||||
return response;
|
||||
} catch {
|
||||
// Network failed - try to serve from cache (ignoring query params)
|
||||
const cached = await cache.match(cacheKey);
|
||||
if (cached) {
|
||||
return cached;
|
||||
}
|
||||
|
||||
// Network unreachable OR upstream 5xx (502 Bad Gateway, 503, 504, ...) —
|
||||
// serve stale shell so the PWA stays usable when the origin is down.
|
||||
const upstreamDown = !response || response.status >= 500;
|
||||
|
||||
if (upstreamDown) {
|
||||
const cached = await cache.match(cacheKey);
|
||||
if (cached) return cached;
|
||||
|
||||
// For recipe routes, redirect to the offline shell with the target URL
|
||||
// The offline shell will then do client-side navigation to load from IndexedDB
|
||||
// Skip if this is already the offline-shell or an offline navigation to prevent loops
|
||||
@@ -262,6 +271,10 @@ sw.addEventListener('fetch', (event) => {
|
||||
return Response.redirect(redirectUrl, 302);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Pass through non-5xx errors (404, 401, ...) untouched.
|
||||
if (response && !upstreamDown) return response;
|
||||
|
||||
// Last resort - return a styled offline response
|
||||
return new Response(
|
||||
@@ -299,7 +312,6 @@ p{color:#aaa}
|
||||
</body></html>`,
|
||||
{ headers: { 'Content-Type': 'text/html' } }
|
||||
);
|
||||
}
|
||||
})()
|
||||
);
|
||||
return;
|
||||
|
||||
Reference in New Issue
Block a user