fix: keep LXC updates running when the browser session ends

This commit is contained in:
VAIO73
2026-10-26 07:57:20 +00:00
parent af29bc9274
commit 44ab9d506d
12 changed files with 1145 additions and 40 deletions
+42 -26
View File
@@ -22,6 +22,8 @@ import useSWR from "swr"
import { MetricsView } from "./metrics-dialog"
import { LxcTerminalModal } from "./lxc-terminal-modal"
import { ScriptTerminalModal } from "./script-terminal-modal"
import { LxcUpdateJobs } from "./lxc-update-jobs"
import { markUpdateJobViewed, type LxcUpdateJob } from "../lib/lxc-update-jobs"
import { OCI_CANCELLED_EXIT_CODE, shouldRefreshOciAfterCompletion } from "@/lib/script-completion"
import { LxcAppPanel, ThemeAwareLogo, ociImageLabel } from "./lxc-app-panel"
import { GuestBackupList } from "./guest-backup-list"
@@ -1998,6 +2000,7 @@ export function VirtualMachines() {
const [applyRestart, setApplyRestart] = useState(false)
const [applyStartedAt, setApplyStartedAt] = useState<number>(0)
const [applyRunId, setApplyRunId] = useState<string>("")
const [applyResume, setApplyResume] = useState(false)
const [applyCheckedOs, setApplyCheckedOs] = useState(false)
const [applyTargetIds, setApplyTargetIds] = useState<string[]>([])
const [applyTargetLabels, setApplyTargetLabels] = useState<string[]>([])
@@ -2639,6 +2642,7 @@ export function VirtualMachines() {
setApplyVmid(vmid)
setApplyTarget(target)
setApplyRunId(runId)
setApplyResume(false)
setApplyCheckedOs(opts?.checkedOs === true)
setApplyTargetIds(opts?.targetIds?.length ? opts.targetIds : fallbackTargets)
setApplyTargetLabels(opts?.targetLabels?.length ? opts.targetLabels : fallbackLabels)
@@ -2659,27 +2663,37 @@ export function VirtualMachines() {
setApplyStartedAt(Date.now())
setApplyOpen(true)
}
const handleApplyComplete = async (exitCode = 0) => {
const resumeApplyJob = (job: LxcUpdateJob) => {
setApplyVmid(job.vmid)
setApplyTarget(job.target)
setApplyRunId(job.id)
setApplyCheckedOs(job.script_path.endsWith("/install_checked_os_updates.sh"))
setApplyResume(true)
setApplyStartedAt(job.created_at * 1000)
setApplyOpen(true)
}
const handleApplyComplete = async (exitCode = 0, notifyBackend = true) => {
if (applyVmid == null) return
const duration = Math.max(0, Math.round((Date.now() - applyStartedAt) / 1000))
// The modal fires onComplete on any WS close (success or user cancel).
// Report the same run ID so the backend can collapse this compatibility
// callback with its authoritative process-completion hook.
// Legacy callers may report the same run ID to the compatibility route.
// Background Updates only refresh the view after the server's real exit.
try {
await fetchApi(`/api/lxc-updates/${applyVmid}/applied`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
success: exitCode === 0,
run_id: applyRunId,
target: applyTarget,
requested_targets: applyTargetIds,
target_labels: applyTargetLabels,
duration_seconds: duration,
ct_name: selectedVM?.name || `CT-${applyVmid}`,
refresh_docker_inventory: applyDockerRefresh,
}),
})
if (notifyBackend) {
await fetchApi(`/api/lxc-updates/${applyVmid}/applied`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
success: exitCode === 0,
run_id: applyRunId,
target: applyTarget,
requested_targets: applyTargetIds,
target_labels: applyTargetLabels,
duration_seconds: duration,
ct_name: selectedVM?.name || `CT-${applyVmid}`,
refresh_docker_inventory: applyDockerRefresh,
}),
})
}
} catch {
// Non-fatal — the backend process hook owns finalization.
}
@@ -7960,6 +7974,8 @@ export function VirtualMachines() {
/>
)}
<LxcUpdateJobs activeRunId={applyOpen ? applyRunId : undefined} onResume={resumeApplyJob} />
{/* LXC Update Apply Terminal — streams the apply_updates.sh
script over WS/PTY (same wiring as the hardware installers).
The backend finalizes on process exit; the close callback only
@@ -7971,7 +7987,13 @@ export function VirtualMachines() {
setApplyOpen(false)
setApplyVmid(null)
}}
onComplete={handleApplyComplete}
onComplete={(exitCode) => {
markUpdateJobViewed(applyRunId)
// The server-owned job is the sole completion authority.
void handleApplyComplete(exitCode ?? 1, false)
}}
backgroundUpdate={{ runId: applyRunId, vmid: applyVmid, resume: applyResume }}
cancelledExitCode={130}
scriptPath={applyCheckedOs
? "/usr/local/share/proxmenux/scripts/lxc/install_checked_os_updates.sh"
: "/usr/local/share/proxmenux/scripts/lxc/apply_updates.sh"}
@@ -7981,13 +8003,7 @@ export function VirtualMachines() {
completedWithErrorMessage={(exitCode) =>
t("vmLxc.updates.terminalCompletedWithError", { code: exitCode })
}
description={
applyTarget === "os"
? t("vmLxc.updates.terminalDescriptionOs")
: applyTarget === "app"
? t("vmLxc.updates.terminalDescriptionApp")
: t("vmLxc.updates.terminalDescriptionBoth")
}
description={t("vmLxc.updates.backgroundDescription")}
params={{
VMID: String(applyVmid),
RUN_ID: applyRunId,