fix(Updates): avoid issues with stale cache when checking latest version

This commit is contained in:
Jake Turner 2026-02-11 22:32:13 -08:00 committed by Jake Turner
parent 76ac713406
commit a49322b63b
2 changed files with 65 additions and 10 deletions

View File

@ -324,10 +324,12 @@ export class SystemService {
throw new Error('Invalid response from GitHub API')
}
const latestVersion = response.data.tag_name.replace(/^v/, '') // Remove leading 'v' if present
const latestVersion = response.data.tag_name.replace(/^v/, '').trim() // Remove leading 'v' and whitespace
logger.info(`Current version: ${currentVersion}, Latest version: ${latestVersion}`)
const updateAvailable = process.env.NODE_ENV === 'development' ? false : latestVersion !== currentVersion
const updateAvailable = process.env.NODE_ENV === 'development'
? false
: this.isNewerVersion(latestVersion, currentVersion.trim())
// Cache the results in KVStore for frontend checks
await KVStore.setValue('system.updateAvailable', updateAvailable.toString())
@ -463,4 +465,27 @@ export class SystemService {
}
})
}
/**
* Compare two semantic version strings to determine if the first is newer than the second.
* @param version1 - The version to check (e.g., "1.25.0")
* @param version2 - The current version (e.g., "1.24.0")
* @returns true if version1 is newer than version2
*/
private isNewerVersion(version1: string, version2: string): boolean {
const v1Parts = version1.split('.').map((part) => parseInt(part, 10))
const v2Parts = version2.split('.').map((part) => parseInt(part, 10))
const maxLength = Math.max(v1Parts.length, v2Parts.length)
for (let i = 0; i < maxLength; i++) {
const v1Part = v1Parts[i] || 0
const v2Part = v2Parts[i] || 0
if (v1Part > v2Part) return true
if (v1Part < v2Part) return false
}
return false // Versions are equal
}
}

View File

@ -77,7 +77,7 @@ function ContentUpdatesSection() {
addNotification({ type: 'success', message: `Started ${succeeded} update(s)` })
}
if (failed > 0) {
addNotification({ type: 'warning', message: `${failed} update(s) could not be started` })
addNotification({ type: 'error', message: `${failed} update(s) could not be started` })
}
// Remove successful updates from the list
const successIds = new Set(result.results.filter((r) => r.success).map((r) => r.resource_id))
@ -230,6 +230,7 @@ export default function SystemUpdatePage(props: {
const [showLogs, setShowLogs] = useState(false)
const [logs, setLogs] = useState<string>('')
const [email, setEmail] = useState('')
const [versionInfo, setVersionInfo] = useState(props.system)
useEffect(() => {
if (!isUpdating) return
@ -311,6 +312,34 @@ export default function SystemUpdatePage(props: {
}
}
const checkVersionMutation = useMutation({
mutationKey: ['checkLatestVersion'],
mutationFn: () => api.checkLatestVersion(true),
onSuccess: (data) => {
if (data) {
setVersionInfo({
updateAvailable: data.updateAvailable,
latestVersion: data.latestVersion,
currentVersion: data.currentVersion,
})
if (data.updateAvailable) {
addNotification({
type: 'success',
message: `Update available: ${data.latestVersion}`,
})
} else {
addNotification({ type: 'success', message: 'System is up to date' })
}
setError(null)
}
},
onError: (error: any) => {
const errorMessage = error?.message || 'Failed to check for updates'
setError(errorMessage)
addNotification({ type: 'error', message: errorMessage })
},
})
const getProgressBarColor = () => {
if (updateStatus?.stage === 'error') return 'bg-desert-red'
if (updateStatus?.stage === 'complete') return 'bg-desert-olive'
@ -415,10 +444,10 @@ export default function SystemUpdatePage(props: {
<div className="text-center">
<p className="text-sm text-desert-stone mb-1">Current Version</p>
<p className="text-xl font-bold text-desert-green">
{props.system.currentVersion}
{versionInfo.currentVersion}
</p>
</div>
{props.system.updateAvailable && (
{versionInfo.updateAvailable && (
<>
<div className="flex items-center">
<svg
@ -438,7 +467,7 @@ export default function SystemUpdatePage(props: {
<div className="text-center">
<p className="text-sm text-desert-stone mb-1">Latest Version</p>
<p className="text-xl font-bold text-desert-olive">
{props.system.latestVersion}
{versionInfo.latestVersion}
</p>
</div>
</>
@ -464,15 +493,16 @@ export default function SystemUpdatePage(props: {
size="lg"
icon="IconDownload"
onClick={handleStartUpdate}
disabled={!props.system.updateAvailable}
disabled={!versionInfo.updateAvailable}
>
{props.system.updateAvailable ? 'Start Update' : 'No Update Available'}
{versionInfo.updateAvailable ? 'Start Update' : 'No Update Available'}
</StyledButton>
<StyledButton
variant="ghost"
size="lg"
icon="IconRefresh"
onClick={() => window.location.reload()}
onClick={() => checkVersionMutation.mutate()}
loading={checkVersionMutation.isPending}
>
Check Again
</StyledButton>