Files
llmgw/internal/web/admin_presets.go
T
Ilker ManapandClaude Opus 5.5 f8d18efaef
CI / test (push) Successful in 5m35s
Turkish pages are Turkish to the end
The errors other packages give a page (store, catalog, mirror) are
shown in its language; a percentage is written as the language writes
it (%93 in Turkish); the footer's time in its date form; and a stale
form is refused with a sentence rather than "bad request".

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-21 09:22:42 +02:00

149 lines
5.2 KiB
Go

package web
import (
"context"
"net/http"
"net/url"
"strings"
"gitea.mynodes.xyz/ilker/llmgw/internal/control/adminapi"
"gitea.mynodes.xyz/ilker/llmgw/internal/store"
)
// PresetAdmin is the models that come with llmgw, as the cluster page shows
// and adds them: flux-schnell for pictures, triposr for 3D models. A site
// whose catalog had neither showed no Images or 3D page, and nothing said
// they were one click away.
type PresetAdmin interface {
Presets(ctx context.Context) (adminapi.PresetsView, error)
AddPreset(ctx context.Context, name, by string) error
SetAutoPresets(ctx context.Context, on bool, by string) error
}
// presetsCard is the presets as the cluster page shows them.
type presetsCard struct {
Auto bool
Rows []presetRow
}
// presetRow is one preset, its words already in the reader's language.
type presetRow struct {
Name, Description, Makes, Card, Licence string
// Fits says which of the cluster's classes can run it; empty when none.
Fits string
// State is where it stands: added, and by whom; removed, so not added
// again by itself; or neither.
State string
InCatalog bool
Declined bool
// Blocked is why the leader does not add it by itself, in the control
// plane's words (English); not said when automatic adding is off, which
// the line under the table says once.
Blocked string
// AddsComfyUI is an Add that also has the GPU nodes install ComfyUI and
// the control planes restart on that setting.
AddsComfyUI bool
}
// presetAddsComfyUI is whether adding a preset by hand sets engines.comfyui:
// the cluster installs none yet.
func presetAddsComfyUI(v adminapi.PresetsView, p adminapi.PresetView) bool {
return !p.InCatalog && v.ComfyUI == ""
}
func init() {
registerKeys("dash.presets.makes.pictures", "dash.presets.makes.meshes")
}
// presetsCardOf reads the presets for the page; nil, and the card left off,
// where the control plane cannot say.
func presetsCardOf(ctx context.Context, l *Lang, a PresetAdmin) *presetsCard {
v, err := a.Presets(ctx)
if err != nil {
return nil
}
c := &presetsCard{Auto: v.Auto}
for _, p := range v.Presets {
row := presetRow{
Name: p.Name, Description: p.Description, Card: p.Card,
Makes: l.T("dash.presets.makes." + p.Makes),
Licence: p.Licence.Name,
Fits: strings.Join(p.FitsClasses, ", "),
InCatalog: p.InCatalog, Declined: p.Declined,
AddsComfyUI: presetAddsComfyUI(v, p),
}
if !strings.HasPrefix(p.Blocked, "presets.auto") && !p.Declined {
row.Blocked = p.Blocked
}
if c := p.Licence.Commercial; c != nil && !*c {
row.Licence = l.T("dash.presets.not_commercial", p.Licence.Name)
}
at := ""
if !p.AddedAt.IsZero() {
at = p.AddedAt.UTC().Format("2006-01-02 15:04")
}
switch {
case p.InCatalog && p.AddedBy == "auto":
row.State = l.T("dash.presets.added_auto", at)
case p.InCatalog && p.AddedBy != "":
row.State = l.T("dash.presets.added_by", p.AddedBy, at)
case p.InCatalog:
row.State = l.T("dash.presets.in_catalog")
case p.Declined:
row.State = l.T("dash.presets.declined")
}
c.Rows = append(c.Rows, row)
}
return c
}
// RegisterPresets mounts adding a preset, and turning their automatic adding
// on and off, on the cluster page: for an administrator, with the session's
// CSRF token, answered by going back to the page with what happened.
func (p *AccountPages) RegisterPresets(mux *http.ServeMux, a PresetAdmin) {
back := func(w http.ResponseWriter, r *http.Request, q url.Values) {
http.Redirect(w, r, "/admin?"+q.Encode()+"#presets", http.StatusSeeOther)
}
checked := func(h func(w http.ResponseWriter, r *http.Request, admin store.User)) http.HandlerFunc {
return p.adminOnly(func(w http.ResponseWriter, r *http.Request, admin store.User) {
sess, _, _ := p.session(r)
if !p.accounts.CheckCSRF(sess, r.FormValue("csrf")) {
badForm(w, r)
return
}
h(w, r, admin)
})
}
// Before the one by name, which would otherwise take "auto" for a preset.
mux.HandleFunc("POST /admin/presets/auto", checked(func(w http.ResponseWriter, r *http.Request, admin store.User) {
on := r.FormValue("on") == "true"
q := url.Values{}
if err := a.SetAutoPresets(r.Context(), on, admin.Email); err != nil {
p.log.Warn("presets.auto was not changed", "admin", admin.Email, "err", err)
q.Set("problem", langOf(r).Err(err))
} else {
p.log.Info("presets.auto changed", "admin", admin.Email, "on", on)
key := "dash.presets.notice.auto_off"
if on {
key = "dash.presets.notice.auto_on"
}
q.Set("notice", langOf(r).T(key))
}
back(w, r, q)
}))
mux.HandleFunc("POST /admin/presets/{name}", checked(func(w http.ResponseWriter, r *http.Request, admin store.User) {
name := r.PathValue("name")
q := url.Values{}
if err := a.AddPreset(r.Context(), name, admin.Email); err != nil {
p.log.Warn("a preset was not added", "admin", admin.Email, "preset", name, "err", err)
// The control plane's reason, in the page's language where it
// is one the pages know, else as it gave it.
q.Set("problem", name+": "+langOf(r).Err(err))
} else {
p.log.Info("a preset was added", "admin", admin.Email, "preset", name)
q.Set("notice", langOf(r).T("dash.presets.notice.added", name))
}
back(w, r, q)
}))
}