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>
97 lines
3.2 KiB
Go
97 lines
3.2 KiB
Go
package api
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"errors"
|
|
"net/http"
|
|
|
|
"github.com/jackc/pgx/v5"
|
|
|
|
"git.fabledsword.com/bvandeusen/minstrel/internal/apierror"
|
|
"git.fabledsword.com/bvandeusen/minstrel/internal/db/dbq"
|
|
"git.fabledsword.com/bvandeusen/minstrel/internal/library"
|
|
)
|
|
|
|
// scanStatusResp mirrors the scan_runs row on the wire. Stage tallies are
|
|
// surfaced as json.RawMessage so the frontend gets actual JSON objects
|
|
// rather than base64 byte arrays — the DB column is bytea-shaped jsonb in
|
|
// pgx but the data is already valid JSON.
|
|
type scanStatusResp struct {
|
|
ID string `json:"id"`
|
|
StartedAt string `json:"started_at"`
|
|
FinishedAt *string `json:"finished_at"`
|
|
Library json.RawMessage `json:"library,omitempty"`
|
|
MbidBackfill json.RawMessage `json:"mbid_backfill,omitempty"`
|
|
CoverEnrich json.RawMessage `json:"cover_enrich,omitempty"`
|
|
ArtistArtEnrich json.RawMessage `json:"artist_art_enrich,omitempty"`
|
|
ErrorMessage *string `json:"error_message,omitempty"`
|
|
InFlight bool `json:"in_flight"`
|
|
}
|
|
|
|
// handleGetScanStatus implements GET /api/admin/scan/status.
|
|
// Returns 200 with an empty payload (no fields except in_flight=false) when
|
|
// no scan has ever run.
|
|
func (h *handlers) handleGetScanStatus(w http.ResponseWriter, r *http.Request) {
|
|
q := dbq.New(h.pool)
|
|
row, err := q.GetLatestScanRun(r.Context())
|
|
if err != nil {
|
|
if errors.Is(err, pgx.ErrNoRows) {
|
|
writeJSON(w, http.StatusOK, scanStatusResp{InFlight: false})
|
|
return
|
|
}
|
|
writeErrWithLog(w, h.logger, "admin: get scan status", apierror.InternalMsg("lookup failed", err))
|
|
return
|
|
}
|
|
|
|
resp := scanStatusResp{
|
|
ID: uuidToString(row.ID),
|
|
StartedAt: row.StartedAt.Time.Format("2006-01-02T15:04:05Z07:00"),
|
|
ErrorMessage: row.ErrorMessage,
|
|
InFlight: !row.FinishedAt.Valid,
|
|
}
|
|
if row.FinishedAt.Valid {
|
|
s := row.FinishedAt.Time.Format("2006-01-02T15:04:05Z07:00")
|
|
resp.FinishedAt = &s
|
|
}
|
|
if len(row.Library) > 0 {
|
|
resp.Library = row.Library
|
|
}
|
|
if len(row.MbidBackfill) > 0 {
|
|
resp.MbidBackfill = row.MbidBackfill
|
|
}
|
|
if len(row.CoverEnrich) > 0 {
|
|
resp.CoverEnrich = row.CoverEnrich
|
|
}
|
|
if len(row.ArtistArtEnrich) > 0 {
|
|
resp.ArtistArtEnrich = row.ArtistArtEnrich
|
|
}
|
|
writeJSON(w, http.StatusOK, resp)
|
|
}
|
|
|
|
type scanTriggerResp struct {
|
|
ID string `json:"id,omitempty"`
|
|
}
|
|
|
|
// handleTriggerScan implements POST /api/admin/scan/run.
|
|
// Returns 202 on success, 409 if a scan is already in flight (and not stale),
|
|
// 500 on DB errors. The lazy reaper inside library.TryStartScan handles
|
|
// stale (>1h) in-flight rows automatically, so a row left stuck by a
|
|
// server crash needs no manual clearing.
|
|
func (h *handlers) handleTriggerScan(w http.ResponseWriter, _ *http.Request) {
|
|
bgCtx := context.Background()
|
|
started, existing, err := library.TryStartScan(
|
|
bgCtx, h.pool, h.scanner, h.coverart,
|
|
h.logger.With("source", "manual"), h.scanCfg,
|
|
)
|
|
if err != nil {
|
|
writeErrWithLog(w, h.logger, "admin: trigger scan failed", apierror.InternalMsg("trigger failed", err))
|
|
return
|
|
}
|
|
if !started {
|
|
writeJSON(w, http.StatusConflict, scanTriggerResp{ID: uuidToString(existing.ID)})
|
|
return
|
|
}
|
|
writeJSON(w, http.StatusAccepted, scanTriggerResp{})
|
|
}
|