diff --git a/src/internal/admin/admin_test.go b/src/internal/admin/admin_test.go index 777dc68..346e5ad 100644 --- a/src/internal/admin/admin_test.go +++ b/src/internal/admin/admin_test.go @@ -1278,12 +1278,13 @@ func TestUserBatchStatus(t *testing.T) { } // TestOverviewBatchUI checks the batch entry point and form render, including -// the admin-key checkboxes and the empty-store hint. +// the admin-key checkboxes and the empty-store hint, and that the entry point +// is hidden while snapshot sharing is off (a batch clones from a share code). func TestOverviewBatchUI(t *testing.T) { srv, _ := newTestServer(t) withKeys := srv.renderToString(t, "admin_overview.html", pageData{ - Prefix: "/" + testAdminSecret, Lang: langEn, + Prefix: "/" + testAdminSecret, Lang: langEn, ShareEnabled: true, AdminKeys: []sshKeyRow{{ID: 5, Name: "ops-host", Key: "ssh-ed25519 AAAABODY ops-host", Active: true}}, }) for _, want := range []string{ @@ -1294,6 +1295,11 @@ func TestOverviewBatchUI(t *testing.T) { t.Errorf("batch UI missing %q", want) } } + // Snapshot sharing off (the default): no invitation to start a batch. + hidden := srv.renderToString(t, "admin_overview.html", pageData{Prefix: "/" + testAdminSecret, Lang: langEn}) + if strings.Contains(hidden, `id="batchBtn"`) { + t.Error("batch entry point must be hidden while snapshot sharing is off") + } // "No keys yet" also appears (hidden) in the pre-existing SSH-keys modal, // so assert on this form's own wording. const emptyHint = "No admin keys" diff --git a/src/internal/admin/handlers.go b/src/internal/admin/handlers.go index b1d5dd0..1b09c15 100644 --- a/src/internal/admin/handlers.go +++ b/src/internal/admin/handlers.go @@ -64,6 +64,10 @@ type pageData struct { // BatchID, when set, is a running/recent batch create the page should show // the progress modal for (carried through the redirect after submitting). BatchID string + // ShareEnabled gates the batch-create entry point: a batch clones every user + // from a shared snapshot code, so with snapshot sharing off there is nothing + // to create from (the handler refuses the same way). + ShareEnabled bool // Global dynamic CPU limit rule, edited by the card below (the DB is the // single source of truth — there is no config.yaml / CLI equivalent). The // Active list is the containers currently capped by it. @@ -150,13 +154,14 @@ type userView struct { func (s *Server) buildPageData(msg, errMsg string) pageData { v4Caps := s.mgr.LiveV4Capabilities() d := pageData{ - Title: "VPS Manager Admin", - Prefix: s.prefix(), - Msg: msg, - Err: errMsg, - V4Policy: s.mgr.LiveV4Policy(), - DirectV4: v4Caps.DirectForwarding, - Colors: append([]string{}, userColorPalette...), + Title: "VPS Manager Admin", + Prefix: s.prefix(), + Msg: msg, + Err: errMsg, + V4Policy: s.mgr.LiveV4Policy(), + DirectV4: v4Caps.DirectForwarding, + ShareEnabled: s.mgr.SnapshotShareEnabled(), + Colors: append([]string{}, userColorPalette...), } hs := s.mgr.HostStats() d.Reboot = hs.RebootNeeded diff --git a/src/internal/admin/templates/admin_overview.html b/src/internal/admin/templates/admin_overview.html index b7be01b..25544a7 100644 --- a/src/internal/admin/templates/admin_overview.html +++ b/src/internal/admin/templates/admin_overview.html @@ -223,7 +223,7 @@ a.copy.copied { border-color: #86efac; background: #dcfce7; color: #15803d; }