feat: update button (download, verify, stage)

This commit is contained in:
fsylvain committed 2026-10-01 18:13:45 +02:00
1 parent 834493a4f5
commit 70460e4170
41 files changed
+5345 -97

No files matched your search

+81 -12
View File
@@ -12,6 +12,7 @@ import { useSessionStore } from '@/stores/session'
import { useUpdateStore } from '@/stores/update'
const SKELETON_DELAY_MS = 150
const POLL_INTERVAL_MS = 1000
const UNREACHABLE_COPY = "Couldn't reach RenewCMS. Check your connection and try again."
type LoadState = 'loading' | 'ready' | 'error'
@@ -85,19 +86,60 @@ const updatesStatus = computed(() => {
const updatesPhase = computed(() => (update.status ? update.phase : 'failed'))
async function onCheck() {
const result = await update.check()
if (result !== 'forbidden') {
return
}
async function reloadAfterRoleChange() {
await session.refresh()
await update.load()
}
function notifyOnlyOwner() {
toast.add({
color: 'error',
icon: 'i-lucide-circle-x',
title: 'Only the owner can do this',
description: 'Ask the owner of this site to update RenewCMS.',
})
await session.refresh()
await update.load()
}
async function onUpdate() {
const result = await update.startDownload()
if (result === 'limited') {
return
}
if (result === 'forbidden') {
notifyOnlyOwner()
await reloadAfterRoleChange()
return
}
if (result === 'nothing') {
toast.add({
color: 'info',
icon: 'i-lucide-info',
title: 'Nothing to update',
description: 'You already have the latest version.',
})
await update.load()
return
}
if (result === 'conflict') {
await update.load()
if (update.jobRunning) {
toast.add({
color: 'info',
icon: 'i-lucide-info',
title: 'An update is already running',
description: 'Another owner started it. Progress shows here.',
})
}
}
}
async function onCheck() {
const result = await update.check()
if (result !== 'forbidden') {
return
}
notifyOnlyOwner()
await reloadAfterRoleChange()
}
const updateRequested = ref(false)
@@ -115,7 +157,8 @@ watch(
return
}
autoCheckHandled = true
if (update.isOwner && isCheckDue(update.status?.checked_at ?? null)) {
const jobActive = update.jobRunning || update.readyToInstall
if (update.isOwner && !jobActive && isCheckDue(update.status?.checked_at ?? null)) {
onCheck()
}
},
@@ -123,20 +166,44 @@ watch(
)
watch(
() => update.phase,
(phase) => {
if (phase === 'limited') {
() => [update.phase, update.applyPhase] as const,
([phase, applyPhase]) => {
if (phase === 'limited' || applyPhase === 'limited') {
retry.start(update.retryAfter)
}
},
)
let pollTimer: ReturnType<typeof setInterval> | null = null
function stopPolling() {
if (pollTimer !== null) {
clearInterval(pollTimer)
pollTimer = null
}
}
function startPolling() {
if (pollTimer === null) {
pollTimer = setInterval(update.poll, POLL_INTERVAL_MS)
}
}
watch(
() => update.jobRunning,
(running) => (running ? startPolling() : stopPolling()),
{ immediate: true },
)
onMounted(async () => {
loadSettings()
await update.load()
updateRequested.value = true
})
onBeforeUnmount(clearSkeletonTimer)
onBeforeUnmount(() => {
clearSkeletonTimer()
stopPolling()
})
</script>
<template>
@@ -198,9 +265,11 @@ onBeforeUnmount(clearSkeletonTimer)
:status="updatesStatus"
:role="session.session?.role"
:phase="updatesPhase"
:apply-phase="update.applyPhase"
:retry-label="retry.formatted.value"
:retry-active="retry.active.value"
@check="onCheck"
@update="onUpdate"
/>
</div>
</DashboardShell>