feat: poll /api/version buildTag to detect new deploys and prompt reload

Server stamps a BUILD_TAG (stable per process, changes on restart/deploy)
into GET /api/version alongside the existing version string. Cache-Control
is set to no-store so the response is never cached by the SW or browser.

Client (WEB mode only) baselines the tag on first fetch after boot, then
rechecks every 5 minutes. On mismatch it calls the existing showUpdateBanner()
so the user sees the 'Update ready — Reload now' toast and can reload at will.

This mirrors the build-id polling pattern used across the BukidBountyApp
derivatives to avoid stale UI after deploys.
This commit is contained in:
Jonathan Sykes
2026-07-01 00:53:22 +08:00
parent d9371fdea4
commit 6738644d4b
2 changed files with 48 additions and 1 deletions

View File

@@ -30,6 +30,10 @@ const PORT = parseInt(process.env.PORT || '3000', 10);
const APP_VERSION = process.env.APP_VERSION || '1.0.0';
const YTDLP = process.env.YTDLP_PATH || 'yt-dlp';
// Stable tag for this server process — changes on every deploy/restart.
// The frontend polls /api/version and reloads when the tag drifts.
const BUILD_TAG = process.env.BUILD_TAG || Date.now().toString(36);
const SEARCH_LIMIT = 25;
const CHANNEL_LIMIT = 60;
@@ -119,7 +123,15 @@ app.use('*', logger());
// ============================================================================
// GET /api/version
app.get('/api/version', (c) => c.json({ version: APP_VERSION }));
// Returns version string + a build tag that changes on every server restart/deploy.
// Clients poll this to detect when a new build is live and prompt a reload.
app.get('/api/version', (c) =>
c.json(
{ version: APP_VERSION, buildTag: BUILD_TAG },
200,
{ 'Cache-Control': 'no-store, no-cache, must-revalidate' }
)
);
// GET /api/search?q=<query>
app.get('/api/search', async (c) => {