feat(admin): hide the batch-create entry unless snapshot sharing is on
- a batch clones every user from a shared snapshot code, and handleUserBatch already refuses when snapshots.share is off; the entry point now follows, so the admin is not offered a form that can only be rejected - carry snapshot-sharing state into the admin page data (ShareEnabled) - correct the snapshots.share help text, which still claimed "default true" although sharing is an explicit opt-in and defaults to false
This commit is contained in:
1 parent
b7ebdb5374
commit
1be1a94139
4 files changed
+22
-11
No files matched your search
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -223,7 +223,7 @@ a.copy.copied { border-color: #86efac; background: #dcfce7; color: #15803d; }
|
||||
|
||||
<div class="card">
|
||||
<h2>{{if eq .Lang "zh"}}创建用户{{else}}Create user{{end}}</h2>
|
||||
<div style="margin:-4px 0 10px"><button type="button" class="btn" id="batchBtn">{{if eq .Lang "zh"}}批量创建用户{{else}}Batch create{{end}}</button></div>
|
||||
{{if .ShareEnabled}}<div style="margin:-4px 0 10px"><button type="button" class="btn" id="batchBtn">{{if eq .Lang "zh"}}批量创建用户{{else}}Batch create{{end}}</button></div>{{end}}
|
||||
<div class="plans" id="plans"></div>
|
||||
<form action="{{.Prefix}}/user-add" method="post" class="form-grid" id="createForm">
|
||||
<div class="field create-field name-field"><label>{{if eq .Lang "zh"}}用户名{{else}}Username{{end}}</label><input name="name" id="cfName" pattern="[a-z]([a-z0-9-]*[a-z0-9])?" maxlength="31" autocomplete="off" required title="{{if eq .Lang "zh"}}小写字母开头和结尾,仅含小写字母、数字和连字符{{else}}start/end with a lowercase letter; lowercase letters, digits, hyphens only{{end}}"><small id="cfNameHint" style="color:#6b7280;display:block;margin-top:4px;min-height:1em">{{if eq .Lang "zh"}}自动转为小写{{else}}Folded to lowercase{{end}}</small></div>
|
||||
|
||||
@@ -449,7 +449,7 @@ var Fields = []Field{
|
||||
return nil
|
||||
}},
|
||||
{"snapshots.share", KindOperator, ApplyImmediate,
|
||||
"allow users to share a checkpoint behind a code and install a container from it (default true); stored codes are kept when turned off",
|
||||
"allow users to share a checkpoint behind a code and install a container from it (off by default; explicit opt-in); stored codes are kept when turned off",
|
||||
"true or false",
|
||||
getStr(func(c *Config) string { return strconv.FormatBool(c.Snapshots.Share) }),
|
||||
func(c *Config, v string) error {
|
||||
|
||||
Reference in new issue
Block a user