From 6644b62f6300272456dfc5201e566a954a91377a Mon Sep 17 00:00:00 2001 From: MacRimi Date: Sat, 5 Sep 2026 16:10:31 +0200 Subject: [PATCH] Improve LXC updater selection, error handling and dashboard consistency --- AppImage/components/app-updater-editor.tsx | 148 +++++ AppImage/components/audit-report.tsx | 519 ++++++++++++++++ AppImage/components/lxc-app-panel.tsx | 12 +- AppImage/components/metrics-cache-notice.tsx | 16 + AppImage/components/network-traffic-chart.tsx | 235 ++++---- AppImage/components/node-metrics-charts.tsx | 195 +++--- AppImage/components/proxmox-dashboard.tsx | 8 + AppImage/components/virtual-machines.tsx | 115 ++-- AppImage/lib/lxc-apps-cache.ts | 87 ++- AppImage/messages/de/common.json | 173 +++++- AppImage/messages/en/common.json | 163 ++++- AppImage/messages/es/common.json | 173 +++++- AppImage/messages/fr/common.json | 173 +++++- AppImage/messages/it/common.json | 173 +++++- AppImage/messages/pt/common.json | 173 +++++- AppImage/messages/sk/common.json | 163 ++++- AppImage/messages/sv/common.json | 173 +++++- AppImage/scripts/audit_checks.py | 342 +++++++++++ AppImage/scripts/audit_checks_pve.py | 430 +++++++++++++ AppImage/scripts/audit_store.py | 426 +++++++++++++ AppImage/scripts/build_appimage.sh | 4 + AppImage/scripts/flask_audit_routes.py | 281 +++++++++ AppImage/scripts/flask_server.py | 547 ++++++----------- AppImage/scripts/lxc_apps.py | 157 ++++- scripts/lxc/apply_updates.sh | 51 +- tests/lxc_updates/test_apply_updates.sh | 164 +++++ .../lxc_updates/test_download_update_guard.py | 163 +++++ tests/lxc_updates/test_update_contract.py | 565 ++++++++++++++++++ .../lxc_updates/test_update_method_choice.py | 181 ++++++ tests/lxc_updates/test_updater_editor.cjs | 280 +++++++++ .../monitor/dashboard/vms-lxcs-updates.json | 10 +- .../monitor/dashboard/vms-lxcs-updates.json | 10 +- .../monitor/dashboard/vms-lxcs-updates.json | 10 +- 33 files changed, 5660 insertions(+), 660 deletions(-) create mode 100644 AppImage/components/app-updater-editor.tsx create mode 100644 AppImage/components/audit-report.tsx create mode 100644 AppImage/components/metrics-cache-notice.tsx create mode 100644 AppImage/scripts/audit_checks.py create mode 100644 AppImage/scripts/audit_checks_pve.py create mode 100644 AppImage/scripts/audit_store.py create mode 100644 AppImage/scripts/flask_audit_routes.py create mode 100644 tests/lxc_updates/test_apply_updates.sh create mode 100644 tests/lxc_updates/test_download_update_guard.py create mode 100644 tests/lxc_updates/test_update_contract.py create mode 100644 tests/lxc_updates/test_update_method_choice.py create mode 100644 tests/lxc_updates/test_updater_editor.cjs diff --git a/AppImage/components/app-updater-editor.tsx b/AppImage/components/app-updater-editor.tsx new file mode 100644 index 00000000..9637d361 --- /dev/null +++ b/AppImage/components/app-updater-editor.tsx @@ -0,0 +1,148 @@ +"use client" + +import { useId, useState } from "react" +import { Info, ExternalLink, Check, Loader2, Trash2 } from "lucide-react" +import { Button } from "./ui/button" +import { Textarea } from "./ui/textarea" +import { Label } from "./ui/label" +import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription } from "./ui/dialog" +import { useT } from "@/lib/i18n/provider" + +export type AppUpdateMethod = "none" | "helper" | "custom" + +export function AppUpdaterEditor({ method, command, helperAvailable, helperSlug, configured, saving, changed, + onMethodChange, onCommandChange, onSave, onCancel, onRemove }: { + method: AppUpdateMethod + command: string + helperAvailable: boolean + helperSlug?: string + configured: boolean + saving: boolean + changed: boolean + onMethodChange: (method: AppUpdateMethod) => void + onCommandChange: (command: string) => void + onSave: () => void + onCancel: () => void + onRemove: () => void +}) { + const t = useT() + const commandId = useId() + const [help, setHelp] = useState<"helper" | "custom" | null>(null) + const scriptUrl = helperSlug && /^[a-z0-9][a-z0-9._-]*$/.test(helperSlug) + ? `https://github.com/community-scripts/ProxmoxVE/blob/main/ct/${helperSlug}.sh` : null + // Keep the editable command readable. Download guards belong to the runner, + // which also protects this literal launcher when saved as a custom command. + const helperCommand = scriptUrl + ? `PHS_SILENT=1 bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/${helperSlug}.sh)"` + : null + const valid = method === "helper" ? helperAvailable && !!helperCommand : method === "custom" && !!command.trim() + const displayedCommand = method === "helper" ? (helperCommand || "") : command + const editCommand = (value: string) => { + // Edited launchers belong to the existing custom-command execution path. + // Never leave the method as "helper": saving it would discard the edits. + if (method === "helper") { + if (value.trim() === (helperCommand || "").trim()) return + onMethodChange("custom") + } + onCommandChange(value) + } + const customExamples = [ + { title: "customScriptTitle", description: "customScriptDescription", command: "/opt/my-app/update.sh" }, + { title: "customPackageTitle", description: "customPackageDescription", command: "apt-get update &&\napt-get install -y --only-upgrade my-package" }, + { title: "customBinaryTitle", description: "customBinaryDescription", command: "install -b -m 0755 /tmp/my-app.new /opt/my-app/my-app &&\nsystemctl restart my-app" }, + ] + const onlineExampleCommand = `script=$(mktemp) || exit 1 +trap 'rm -f "$script"' EXIT +curl -fsSL 'https://example.com/my-app/update.sh' -o "$script" && +bash "$script"` + + return ( +
+

{t("vmLxc.updates.updaterChoiceHint")}

+
+ {([...(helperAvailable ? ["helper" as const] : []), "custom" as const]).map((choice) => ( +
+ + +
+ ))} +
+ {helperAvailable &&

{t("vmLxc.updates.helperDetectedChoice")}

} + {method === "helper" && (!helperAvailable || !helperCommand) && ( +

{t("vmLxc.updates.helperUnavailableChoice")}

+ )} + {(method === "helper" || method === "custom") && ( +
+ +