package adminapi import ( "context" "encoding/json" "errors" "net/http" "time" "gitea.mynodes.xyz/ilker/llmgw/internal/control/catalog" ) // PresetsView is the models that come with llmgw, and whether the leader adds // them by itself. type PresetsView struct { // Auto is whether a preset is added when a node fits it and the catalog // has no model that makes what it makes (presets.auto). Auto bool `json:"auto"` Presets []PresetView `json:"presets"` } // PresetView is one preset as it stands in this cluster. type PresetView struct { Name string `json:"name"` Makes string `json:"makes"` Description string `json:"description"` Card string `json:"card"` Licence catalog.License `json:"licence"` // Fits is whether some node has a card that meets the preset's need; // FitsClasses names those classes. Fits bool `json:"fits"` FitsClasses []string `json:"fits_classes,omitempty"` InCatalog bool `json:"in_catalog"` // AddedBy is "auto" when the leader added it, who did otherwise, "" // when it was not added as a preset; AddedAt is when. AddedBy string `json:"added_by,omitempty"` AddedAt time.Time `json:"added_at,omitzero"` // Declined is whether somebody removed it after it was added, so it is // not added again by itself; adding it again clears that. Declined bool `json:"declined"` } // PresetsAutoRequest turns the automatic adding of presets on or off. type PresetsAutoRequest struct { On bool `json:"on"` } // errPresetsUnavailable is a control plane without the store presets are // kept in. var errPresetsUnavailable = errors.New("this control plane has no store, so presets cannot be added here") // Presets is the presets as they stand in this cluster. func (a *API) Presets(ctx context.Context) (PresetsView, error) { return PresetsView{}, errPresetsUnavailable } // AddPreset adds the preset of that name, and clears its decline. func (a *API) AddPreset(ctx context.Context, name, by string) error { return errPresetsUnavailable } // SetAutoPresets turns presets.auto on or off. func (a *API) SetAutoPresets(ctx context.Context, on bool, by string) error { return errPresetsUnavailable } func (a *API) registerPresets(mux Mux) { mux.HandleFunc("GET /api/v1/presets", func(w http.ResponseWriter, r *http.Request) { v, err := a.Presets(r.Context()) if err != nil { writeError(w, presetStatus(err), err.Error()) return } writeJSON(w, http.StatusOK, v) }) mux.HandleFunc("POST /api/v1/presets/{name}", func(w http.ResponseWriter, r *http.Request) { if err := a.AddPreset(r.Context(), r.PathValue("name"), actorOf(r)); err != nil { writeError(w, presetStatus(err), err.Error()) return } writeJSON(w, http.StatusOK, ModelAdded{Added: r.PathValue("name")}) }) mux.HandleFunc("PUT /api/v1/presets/auto", func(w http.ResponseWriter, r *http.Request) { var req PresetsAutoRequest if err := json.NewDecoder(r.Body).Decode(&req); err != nil { writeError(w, http.StatusBadRequest, err.Error()) return } if err := a.SetAutoPresets(r.Context(), req.On, actorOf(r)); err != nil { writeError(w, presetStatus(err), err.Error()) return } v, err := a.Presets(r.Context()) if err != nil { writeError(w, presetStatus(err), err.Error()) return } writeJSON(w, http.StatusOK, v) }) } // errNoSuchPreset is a name no preset has. var errNoSuchPreset = errors.New("no preset of that name") func presetStatus(err error) int { switch { case errors.Is(err, errPresetsUnavailable): return http.StatusNotImplemented case errors.Is(err, errNoSuchPreset): return http.StatusNotFound default: return http.StatusBadRequest } }