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:
@@ -2625,6 +2625,40 @@ function importBackup(e) {
|
|||||||
e.target.value = '';
|
e.target.value = '';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// Build-tag polling — detect server restarts / new deploys (WEB mode only)
|
||||||
|
//
|
||||||
|
// The server stamps every response from GET /api/version with a `buildTag`
|
||||||
|
// that is stable for the lifetime of the process (changes on restart/deploy).
|
||||||
|
// We baseline the tag on first fetch; any subsequent change means new code is
|
||||||
|
// live and we surface the existing update banner so the user can reload.
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
let _knownBuildTag = null;
|
||||||
|
|
||||||
|
async function checkBuildTag() {
|
||||||
|
try {
|
||||||
|
const res = await fetch('/api/version', { cache: 'no-store' });
|
||||||
|
if (!res.ok) return;
|
||||||
|
const data = await res.json();
|
||||||
|
const tag = data.buildTag;
|
||||||
|
if (!tag) return;
|
||||||
|
if (!_knownBuildTag) {
|
||||||
|
_knownBuildTag = tag; // baseline on first successful fetch
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (tag !== _knownBuildTag) {
|
||||||
|
_knownBuildTag = tag; // update so we don't show the banner twice
|
||||||
|
showUpdateBanner();
|
||||||
|
}
|
||||||
|
} catch { /* network error — try again next interval */ }
|
||||||
|
}
|
||||||
|
|
||||||
|
function pollBuildTag() {
|
||||||
|
checkBuildTag();
|
||||||
|
setInterval(checkBuildTag, 5 * 60 * 1000); // recheck every 5 minutes
|
||||||
|
}
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// PWA — service worker registration and update banner (WEB mode only)
|
// PWA — service worker registration and update banner (WEB mode only)
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
@@ -2720,6 +2754,7 @@ async function boot() {
|
|||||||
|
|
||||||
// Register service worker + ping server with fingerprint (WEB mode only)
|
// Register service worker + ping server with fingerprint (WEB mode only)
|
||||||
registerServiceWorker();
|
registerServiceWorker();
|
||||||
|
if (WEB) pollBuildTag();
|
||||||
if (WEB) {
|
if (WEB) {
|
||||||
fetch('/api/user/sync', {
|
fetch('/api/user/sync', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
|
|||||||
@@ -30,6 +30,10 @@ const PORT = parseInt(process.env.PORT || '3000', 10);
|
|||||||
const APP_VERSION = process.env.APP_VERSION || '1.0.0';
|
const APP_VERSION = process.env.APP_VERSION || '1.0.0';
|
||||||
const YTDLP = process.env.YTDLP_PATH || 'yt-dlp';
|
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 SEARCH_LIMIT = 25;
|
||||||
const CHANNEL_LIMIT = 60;
|
const CHANNEL_LIMIT = 60;
|
||||||
|
|
||||||
@@ -119,7 +123,15 @@ app.use('*', logger());
|
|||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|
||||||
// GET /api/version
|
// 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>
|
// GET /api/search?q=<query>
|
||||||
app.get('/api/search', async (c) => {
|
app.get('/api/search', async (c) => {
|
||||||
|
|||||||
Reference in New Issue
Block a user