// Deploy/restart warning banner, ported from mnw.js:578-652. Polls // /api/restart-status; shows a countdown banner when a restart is scheduled. /** Start polling for a scheduled restart and render the countdown banner. */ export function initRestartBanner(): void { let banner: HTMLElement | null = null; let countdownInterval: ReturnType | null = null; let restartAt: number | null = null; function createBanner(): void { if (banner) return; banner = document.createElement('div'); banner.id = 'restart-banner'; banner.className = 'banner banner--warning'; banner.setAttribute('role', 'alert'); document.body.prepend(banner); } function removeBanner(): void { if (banner) { banner.remove(); banner = null; } if (countdownInterval) { clearInterval(countdownInterval); countdownInterval = null; } restartAt = null; } function updateCountdown(): void { if (!banner || restartAt === null) return; const remaining = Math.max(0, Math.round(restartAt - Date.now() / 1000)); if (remaining > 0) { banner.textContent = 'Update deploying — restarting in ' + remaining + 's'; } else { banner.textContent = 'Restarting now...'; if (countdownInterval) { clearInterval(countdownInterval); countdownInterval = null; } } } function startCountdown(ts: number): void { restartAt = ts; createBanner(); updateCountdown(); if (countdownInterval) clearInterval(countdownInterval); countdownInterval = setInterval(updateCountdown, 1000); } function poll(): void { fetch('/api/restart-status') .then((r) => r.json()) .then((data: { restart_at?: number }) => { if (data.restart_at) { if (restartAt === null || restartAt !== data.restart_at) startCountdown(data.restart_at); } else { removeBanner(); } }) .catch(() => { // Already counting down and the poll failed: assume it's happening now. if (restartAt !== null) { if (banner) banner.textContent = 'Restarting now...'; if (countdownInterval) { clearInterval(countdownInterval); countdownInterval = null; } } }); } setTimeout(poll, 2000); setInterval(poll, 10000); }