feat(api): DELETE /api/admin/tracks/{id} for M7 #372
Admin-only handler. Calls tracks.RemoveTrack with the unmonitor flag parsed from the query string. ErrNotFound -> 404; everything else unexpected -> 500. Lidarr-side failures during unmonitor flow as lidarr_unmonitor_failed: true in the success envelope (non-fatal — the file + DB delete already completed). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -3,6 +3,7 @@ package server
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"log/slog"
|
||||
"net/http"
|
||||
"strings"
|
||||
@@ -23,9 +24,31 @@ import (
|
||||
"git.fabledsword.com/bvandeusen/minstrel/internal/lidarrrequests"
|
||||
"git.fabledsword.com/bvandeusen/minstrel/internal/playevents"
|
||||
"git.fabledsword.com/bvandeusen/minstrel/internal/subsonic"
|
||||
"git.fabledsword.com/bvandeusen/minstrel/internal/tracks"
|
||||
"git.fabledsword.com/bvandeusen/minstrel/web"
|
||||
)
|
||||
|
||||
// lidarrUnmonitorAdapter wires the per-call lidarrClientFn factory pattern
|
||||
// (used elsewhere in this package so admin Lidarr config edits take effect
|
||||
// without restart) into the tracks.LidarrUnmonitorer interface that
|
||||
// internal/tracks expects. When the factory returns nil (Lidarr disabled)
|
||||
// we surface a sentinel error — tracks.Service treats UnmonitorTrack
|
||||
// failures as non-fatal, so this collapses to a `lidarr_unmonitor_failed`
|
||||
// flag in the response and the destructive part still runs.
|
||||
type lidarrUnmonitorAdapter struct {
|
||||
fn func() *lidarr.Client
|
||||
}
|
||||
|
||||
var errLidarrDisabled = errors.New("lidarr disabled")
|
||||
|
||||
func (a lidarrUnmonitorAdapter) UnmonitorTrack(ctx context.Context, trackMbid, albumMbid string) error {
|
||||
c := a.fn()
|
||||
if c == nil {
|
||||
return errLidarrDisabled
|
||||
}
|
||||
return c.UnmonitorTrack(ctx, trackMbid, albumMbid)
|
||||
}
|
||||
|
||||
// ScanTrigger is the subset of the scanner the HTTP handler needs. Kept as an
|
||||
// interface so tests can stub it without touching the DB.
|
||||
type ScanTrigger interface {
|
||||
@@ -74,7 +97,8 @@ func (s *Server) Router() http.Handler {
|
||||
}
|
||||
lidarrReqs := lidarrrequests.NewService(s.Pool, lidarrCfg, lidarrClientFn, nil)
|
||||
lidarrQuar := lidarrquarantine.NewService(s.Pool, lidarrCfg, lidarrClientFn)
|
||||
api.Mount(r, s.Pool, s.Logger, writer, s.RecommendationCfg, lidarrCfg, lidarrReqs, lidarrQuar)
|
||||
tracksSvc := tracks.NewService(s.Pool, s.Logger, lidarrUnmonitorAdapter{fn: lidarrClientFn})
|
||||
api.Mount(r, s.Pool, s.Logger, writer, s.RecommendationCfg, lidarrCfg, lidarrReqs, lidarrQuar, tracksSvc)
|
||||
// /api/admin/scan is the only admin route owned by the server package
|
||||
// (it needs the Scanner). Register it as a single inline-middleware
|
||||
// route — using r.Route("/api/admin", ...) here would create a second
|
||||
|
||||
Reference in New Issue
Block a user