"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") && (