diff --git a/search-app/server.js b/search-app/server.js
index 7ad5c33..1ffc8ca 100644
--- a/search-app/server.js
+++ b/search-app/server.js
@@ -13,15 +13,35 @@ const UPLOAD_TIMEOUT = 10 * 60 * 1000;
// Load the custom JS to inject. Version hash lets PWAs detect stale caches.
const customJS = fs.readFileSync(path.join(__dirname, 'live-search.js'), 'utf8');
-const INJECT_VERSION = crypto
+const CUSTOM_JS_HASH = crypto
.createHash('sha256')
.update(customJS)
.digest('hex')
.slice(0, 12);
-const SCRIPT_TAG =
- `` +
- ``;
-console.log(`Injected script version: ${INJECT_VERSION}`);
+
+// `currentVersion` combines our custom script with a fingerprint of Immich's
+// served HTML so that any upstream update (chunk-hash rotation, etc.) also
+// bumps the version. Stuck PWAs polling /api/custom/inject-version will see
+// the change and self-reload. Initialized with just the custom hash until the
+// first HTML response lands.
+let currentVersion = CUSTOM_JS_HASH;
+console.log(`Injected script hash: ${CUSTOM_JS_HASH} (initial version: ${currentVersion})`);
+
+function makeScriptTag(version) {
+ return (
+ `` +
+ ``
+ );
+}
+
+// Extract just the chunk/manifest references from Immich's HTML (the parts
+// that actually change on upgrades). Hashing the full HTML would include
+// server-rendered state (CSRF tokens, session user id) that change per-user
+// per-request and would cause spurious version churn.
+function extractShellFingerprint(html) {
+ const matches = html.match(/\/_app\/immutable\/[^"'\s]+/g) || [];
+ return matches.sort().join('|');
+}
// Helper: make an internal request to Immich (for small JSON requests only)
function immichRequest(method, apiPath, headers, body) {
@@ -57,7 +77,7 @@ const server = http.createServer((req, res) => {
'Content-Type': 'application/json',
'Cache-Control': 'no-store, no-cache, must-revalidate'
});
- res.end(JSON.stringify({ version: INJECT_VERSION }));
+ res.end(JSON.stringify({ version: currentVersion }));
return;
}
@@ -129,7 +149,20 @@ const server = http.createServer((req, res) => {
proxyRes.on('data', chunk => htmlChunks.push(chunk));
proxyRes.on('end', () => {
let html = Buffer.concat(htmlChunks).toString('utf8');
- html = html.replace('