GL-7 Part 2: install-command generator on the customer page

Replaces the static Option-1/2 Setup Command blocks with an interactive,
client-side builder: mode (required radio), cores/memory (required for byo,
optional for appliance), vmid, node, acl-storages (quote-wrapped),
operator-pubkey-file, preserve-state-from, and --dry-run/--preflight-only/
--skip-provision/--allow-new-leaf checkboxes. genFlags()/genUpdate() assemble
a live-updating download-then-run command (never curl|bash) + a local-run
variant, enforcing the script's own rules client-side (mode required; byo
requires caps → shows a warning + no runnable command; appliance hides the
caps requirement; allow-new-leaf shows its leaf-regen warning). Emits ONLY
real host-install v1.12.0 flags; the dangerous/operator-only set
(--force/--rotate-recovery/--enable-oob/--remove-golden/--uninstall/
--adopt-pool/--rescope-acl) is never offered. Graceful static fallback:
the server-rendered Option-1/2 commands keep --customer-id + a --mode
placeholder when JS is off. No framework/CDN/network; ScriptVersion (const,
in sync with SCRIPT_VERSION) drives the header. Render/structure test covers
the control ids, version, fallback, and the excluded-flag absence.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PSK5g6qYLknKj8u3QAFEr6
This commit is contained in:
2026-07-09 08:29:15 +02:00
parent b93dd03f0a
commit 844fbfa749
4 changed files with 258 additions and 10 deletions
+11
View File
@@ -18,6 +18,12 @@ import (
var validCustomerID = regexp.MustCompile(`^[a-zA-Z0-9.\-]+$`)
// hostInstallVersion is the felhom-host-install.sh version the customer page's install-command
// generator targets. Kept in sync with scripts/felhom-host-install.sh SCRIPT_VERSION — the generator
// only ever emits flags this version parses. Display-only (the Option-1 command downloads the served
// script, which is always current); bump when the generator's flag surface follows a new script.
const hostInstallVersion = "1.12.0"
// validSemver matches a bare X.Y.Z controller version (the floor format). Empty is also accepted by
// the floor handlers (clears the override).
var validSemver = regexp.MustCompile(`^\d+\.\d+\.\d+$`)
@@ -307,6 +313,9 @@ func (s *Server) handleCustomerUnified(w http.ResponseWriter, r *http.Request, c
ActiveNav string
CSRFField template.HTML
CSRFToken string
// ScriptVersion drives the install-command generator's header (GL-7). Display-only.
ScriptVersion string
}
// DR recipe presence — show the secret-free reconstruction recipe panel + download link when
@@ -364,6 +373,8 @@ func (s *Server) handleCustomerUnified(w http.ResponseWriter, r *http.Request, c
ActiveNav: "configs",
CSRFField: s.csrfField(r),
CSRFToken: s.csrfToken(r),
ScriptVersion: hostInstallVersion,
}
w.Header().Set("Content-Type", "text/html; charset=utf-8")