From 22b546480c043661dbeec711c102cc73ceda815b Mon Sep 17 00:00:00 2001 From: Doug Richar Date: Sat, 11 Jan 2025 22:25:54 -0500 Subject: [PATCH 1/2] fix(ui): enhance version check reliability and debugging - Add cache-busting and no-cache headers to `version.json` fetch - Add detailed logging for version mismatches - Improve error handling in version check --- ui/src/hooks/useCheckForUpdates.ts | 25 ++++++++++++++++++++++--- ui/vite.config.mjs | 17 ++++++++++++----- 2 files changed, 34 insertions(+), 8 deletions(-) diff --git a/ui/src/hooks/useCheckForUpdates.ts b/ui/src/hooks/useCheckForUpdates.ts index a1b5e6bf..4522e936 100644 --- a/ui/src/hooks/useCheckForUpdates.ts +++ b/ui/src/hooks/useCheckForUpdates.ts @@ -9,11 +9,30 @@ export function useCheckForUpdates() { const checkForUpdates = async () => { try { - const response = await fetch('/version.json') + const response = await fetch(`/version.json?_=${Date.now()}`, { + cache: 'no-store', + headers: { + 'Cache-Control': 'no-cache, no-store, must-revalidate', + Pragma: 'no-cache', + Expires: '0', + }, + }) + + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`) + } + const data = await response.json() const deployedVersion = data.version if (deployedVersion !== __APP_VERSION__) { + // eslint-disable-next-line no-console + console.log('Version mismatch detected:', { + current: __APP_VERSION__, + deployed: deployedVersion, + timestamp: new Date().toISOString(), + }) + toast(`A new version is available! v${deployedVersion}`, { description: 'Click the Reload button to update the app.', action: { @@ -29,15 +48,15 @@ export function useCheckForUpdates() { } } - const delay = Number(import.meta.env.VITE_UPDATE_CHECK_INTERVAL || 1000 * 60) + checkForUpdates() + const delay = Number(import.meta.env.VITE_UPDATE_CHECK_INTERVAL || 1000 * 60) if (Number.isNaN(delay)) { console.error('Invalid update check interval:', import.meta.env.VITE_UPDATE_CHECK_INTERVAL) return } const interval = setInterval(checkForUpdates, delay) - return () => clearInterval(interval) }, []) } diff --git a/ui/vite.config.mjs b/ui/vite.config.mjs index e4d7f2a8..929abde1 100644 --- a/ui/vite.config.mjs +++ b/ui/vite.config.mjs @@ -20,11 +20,18 @@ const replaceVersionPlugin = () => { outDir = config.build.outDir }, generateBundle() { - const filePath = path.resolve(__dirname, 'public/version.json') - const content = fs.readFileSync(filePath, 'utf-8') - const updatedContent = content.replace('__APP_VERSION__', version) - const newFilePath = path.resolve(outDir, 'version.json') - fs.writeFileSync(newFilePath, updatedContent, 'utf-8') + try { + const filePath = path.resolve(__dirname, 'public/version.json') + if (!fs.existsSync(filePath)) { + throw new Error(`version.json not found at ${filePath}`) + } + const content = fs.readFileSync(filePath, 'utf-8') + const updatedContent = content.replace('__APP_VERSION__', version) + const newFilePath = path.resolve(outDir, 'version.json') + fs.writeFileSync(newFilePath, updatedContent, 'utf-8') + } catch (error) { + console.error('Failed to replace version in version.json file:', error) + } }, } } From b75b6eb823ff860493f3170d1c4f132baddc9f2e Mon Sep 17 00:00:00 2001 From: Doug Richar Date: Sat, 11 Jan 2025 22:38:13 -0500 Subject: [PATCH 2/2] fix(ui): improve version comparison logic - Add `isNewerVersion` helper to properly compare semver versions - Only show update toast when deployed version is newer - Update logging message to be more accurate - Handle versions with or without 'v' prefix --- ui/src/hooks/useCheckForUpdates.ts | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/ui/src/hooks/useCheckForUpdates.ts b/ui/src/hooks/useCheckForUpdates.ts index 4522e936..5c788b1c 100644 --- a/ui/src/hooks/useCheckForUpdates.ts +++ b/ui/src/hooks/useCheckForUpdates.ts @@ -1,6 +1,24 @@ import * as React from 'react' import { toast } from 'sonner' +// Helper function to compare versions +function isNewerVersion(current: string, deployed: string): boolean { + // Remove 'v' prefix if present + const cleanCurrent = current.replace(/^v/, '') + const cleanDeployed = deployed.replace(/^v/, '') + + const currentParts = cleanCurrent.split('.').map(Number) + const deployedParts = cleanDeployed.split('.').map(Number) + + // Compare major.minor.patch + for (let i = 0; i < 3; i++) { + if (deployedParts[i] > currentParts[i]) return true + if (deployedParts[i] < currentParts[i]) return false + } + + return false +} + export function useCheckForUpdates() { React.useEffect(() => { if (import.meta.env.MODE !== 'production') { @@ -25,9 +43,10 @@ export function useCheckForUpdates() { const data = await response.json() const deployedVersion = data.version - if (deployedVersion !== __APP_VERSION__) { + // Only show toast if deployed version is newer + if (isNewerVersion(__APP_VERSION__, deployedVersion)) { // eslint-disable-next-line no-console - console.log('Version mismatch detected:', { + console.log('New version detected:', { current: __APP_VERSION__, deployed: deployedVersion, timestamp: new Date().toISOString(),