Files
minstrel/internal/api/admin_reacquisition.go
T
bvandeusenandClaude Opus 5.5 08a2c06b85 docs: api comments say what the endpoints do, not how they came to be (#6277)
Rewrites the internal/api comment blocks to rule 32: task, milestone and
rule ids gone, the stories behind them restated as the constraint they
left, stale references corrected (the stream secret is wired from config,
the timezone handler already refreshes the scheduler, the Android app is
the native client, the design docs cited are not in the repo). Also
rewraps comment lines over 80 columns left by the earlier batches.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 12:23:58 -04:00

79 lines
2.8 KiB
Go

package api
import (
"encoding/json"
"errors"
"net/http"
"git.fabledsword.com/bvandeusen/minstrel/internal/apierror"
"git.fabledsword.com/bvandeusen/minstrel/internal/db/dbq"
"git.fabledsword.com/bvandeusen/minstrel/internal/reacquisition"
)
// reacquisitionSettingsResp is the admin card's payload.
//
// Carries more than the stored settings: [UnnameableAlbums] is the count of
// albums with missing files that can never be wanted automatically because
// neither they nor their artist has an MBID. The card states that number
// rather than leaving an admin to wonder why some rows never get wanted —
// a feature that silently does nothing for part of its input reads as broken.
type reacquisitionSettingsResp struct {
Enabled bool `json:"enabled"`
GraceHours int32 `json:"grace_hours"`
MaxPerPass int32 `json:"max_per_pass"`
UnnameableAlbums int64 `json:"unnameable_albums"`
}
// handleGetReacquisitionSettings implements GET
// /api/admin/library/reacquisition.
func (h *handlers) handleGetReacquisitionSettings(w http.ResponseWriter, r *http.Request) {
writeJSON(w, http.StatusOK, h.reacquisitionPayload(r))
}
// handleUpdateReacquisitionSettings implements PUT
// /api/admin/library/reacquisition.
func (h *handlers) handleUpdateReacquisitionSettings(w http.ResponseWriter, r *http.Request) {
var req reacquisitionSettingsResp
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
writeErr(w, apierror.BadRequest("invalid_body", "malformed JSON"))
return
}
_, err := h.reacqSettings.Set(r.Context(), reacquisition.Settings{
Enabled: req.Enabled,
GraceHours: req.GraceHours,
MaxPerPass: req.MaxPerPass,
})
if err != nil {
// The Go-side validation mirrors migration 0056's CHECKs so the
// operator gets a readable message naming the field, rather than a
// constraint-violation string leaking through as a 500.
if errors.Is(err, reacquisition.ErrOutOfRange) {
writeErr(w, apierror.BadRequest("invalid_setting", err.Error()))
return
}
writeErrWithLog(w, h.logger, "admin reacquisition: update failed", apierror.Internal(err))
return
}
// Echo the payload recomputed under the new values so the card reflects
// what it just did without a reload.
writeJSON(w, http.StatusOK, h.reacquisitionPayload(r))
}
func (h *handlers) reacquisitionPayload(r *http.Request) reacquisitionSettingsResp {
cur := h.reacqSettings.Get()
out := reacquisitionSettingsResp{
Enabled: cur.Enabled,
GraceHours: cur.GraceHours,
MaxPerPass: cur.MaxPerPass,
}
// Best-effort: the settings are the point of this endpoint, and failing
// the whole card because a count query hiccuped would be the wrong trade.
if n, err := dbq.New(h.pool).CountAlbumsMissingWithoutMbid(r.Context()); err == nil {
out.UnnameableAlbums = n
} else {
h.logger.Warn("admin reacquisition: unnameable count failed", "err", err)
}
return out
}