package genius import ( "context" "encoding/json" "errors" "github.com/jackc/pgx/v5" "pipu/api/internal/api/gen" "pipu/api/internal/auth" "pipu/api/internal/platform/apperr" ) type Handler struct{ svc *Service } func NewHandler(s *Service) *Handler { return &Handler{svc: s} } func requireAdmin(ctx context.Context) error { p, ok := auth.PrincipalFrom(ctx) if !ok { return apperr.Unauthorized("auth.unauthorized", "authentication required") } if p.Role != auth.RoleAdmin { return apperr.Forbidden("auth.forbidden", "administrator required") } return nil } func (h *Handler) GetGeniusStats(ctx context.Context, _ gen.GetGeniusStatsRequestObject) (gen.GetGeniusStatsResponseObject, error) { if err := requireAdmin(ctx); err != nil { return nil, err } stats, err := h.svc.Stats(ctx) return gen.GetGeniusStats200JSONResponse(stats), err } func (h *Handler) ControlGenius(ctx context.Context, req gen.ControlGeniusRequestObject) (gen.ControlGeniusResponseObject, error) { if err := requireAdmin(ctx); err != nil { return nil, err } if !h.svc.configured { return nil, apperr.Conflict("genius.disabled", "configure GENIUS_ACCESS_TOKEN in deploy/.env") } if req.Body == nil { return nil, apperr.Validation("action", "validation.required", "action required", nil) } tx, err := h.svc.pool.Begin(ctx) if err != nil { return nil, err } defer tx.Rollback(ctx) switch string(req.Body.Action) { case "pause": _, err = tx.Exec(ctx, `UPDATE genius_state SET paused=true`) case "start": _, err = tx.Exec(ctx, `UPDATE genius_state SET paused=false,last_error=''`) case "retry": _, err = tx.Exec(ctx, `UPDATE genius_tracks SET status='pending',next_attempt=now(),last_error='' WHERE status IN ('error','partial','unmatched') AND locked_until<=now()`) if err == nil { _, err = tx.Exec(ctx, `UPDATE genius_state SET paused=false,last_error=''`) } default: return nil, apperr.Validation("action", "validation.invalid", "unknown action", nil) } if err != nil { return nil, err } if err = tx.Commit(ctx); err != nil { return nil, err } h.svc.Wake() stats, err := h.svc.Stats(ctx) return gen.ControlGenius200JSONResponse(stats), err } func (h *Handler) GetTrackGenius(ctx context.Context, req gen.GetTrackGeniusRequestObject) (gen.GetTrackGeniusResponseObject, error) { if _, ok := auth.PrincipalFrom(ctx); !ok { return nil, apperr.Unauthorized("auth.unauthorized", "authentication required") } content := emptyContent() var body []byte err := h.svc.pool.QueryRow(ctx, `SELECT COALESCE(g.content,'{}'),COALESCE(g.status,'pending') FROM tracks t LEFT JOIN genius_tracks g ON g.track_id=t.id WHERE t.id=$1`, req.Id).Scan(&body, &content.Status) if errors.Is(err, pgx.ErrNoRows) { return nil, apperr.NotFound("catalog.not_found", "track not found") } if err != nil { return nil, err } status := content.Status if err = json.Unmarshal(body, &content); err != nil { return nil, err } content.Status = status if !h.svc.configured && status == "pending" { content.Status = "disabled" } return gen.GetTrackGenius200JSONResponse(content), nil }