diff --git a/search-app/server.js b/search-app/server.js
index 1ffc8ca..3b0da82 100644
--- a/search-app/server.js
+++ b/search-app/server.js
@@ -70,7 +70,65 @@ function immichRequest(method, apiPath, headers, body) {
});
}
+// Standalone recovery page: unregister service workers, clear all caches,
+// clear storage, and reload. Use when a PWA is stuck on a broken cached
+// chunk or service worker state.
+const RESET_HTML = `
+
Resetting…
+
+
+
+
+Clearing caches…
+
+`;
+
const server = http.createServer((req, res) => {
+ // --- Recovery page: /__reset ---
+ if ((req.url === '/__reset' || req.url.startsWith('/__reset?')) && req.method === 'GET') {
+ res.writeHead(200, {
+ 'Content-Type': 'text/html; charset=utf-8',
+ 'Cache-Control': 'no-store, no-cache, must-revalidate',
+ 'Service-Worker-Allowed': '/'
+ });
+ res.end(RESET_HTML);
+ return;
+ }
+
// --- Custom endpoint: report current injected-script version ---
if (req.url === '/api/custom/inject-version' && req.method === 'GET') {
res.writeHead(200, {
@@ -184,7 +242,16 @@ const server = http.createServer((req, res) => {
});
} else {
// Stream non-HTML responses directly (videos, images, API JSON)
- res.writeHead(proxyRes.statusCode, proxyRes.headers);
+ const streamHeaders = { ...proxyRes.headers };
+ // Don't let browsers or CDNs cache error responses — a 5xx
+ // that sneaks into a long-lived cache entry can brick the PWA.
+ if (proxyRes.statusCode >= 500) {
+ streamHeaders['cache-control'] = 'no-store, no-cache, must-revalidate';
+ delete streamHeaders['expires'];
+ delete streamHeaders['etag'];
+ delete streamHeaders['last-modified'];
+ }
+ res.writeHead(proxyRes.statusCode, streamHeaders);
proxyRes.pipe(res);
}
});