Bug: r.Route("/api/admin", ...) in server.go for the /scan endpoint
created a second chi subtree at the same prefix as the nested admin
Route inside api.Mount. chi.Walk shows both subtrees registered, but
runtime routing dispatch only matched one — so every /api/admin/*
endpoint registered by api.Mount (Lidarr config, quarantine admin,
requests admin) silently returned 404 to authenticated callers.
Has shipped this way since 06a1fe1; M5a/b/c admin tests didn't catch
it because they call api.Mount on a fresh chi.NewRouter() rather than
going through Server.Router(), so only one /api/admin registration
existed in those tests.
Fix: register /api/admin/scan as a single inline-middleware route
(r.With(...).Post(...)) instead of a Route subtree, eliminating the
duplicate /api/admin mount.
Adds a regression test that uses a real seeded admin session to make
an authenticated GET /api/admin/lidarr/config and asserts != 404.
Verified the test fails without the fix and passes with it.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Validates seed_track + optional limit (default cfg.Recommendation.RadioSize,
clamped to RadioSizeMax). Calls recommendation.LoadCandidates +
recommendation.Shuffle. Prepends seed to result. Server seeds a
math/rand source at startup; handlers package threads that as a
func() float64 so tests inject deterministic RNGs.
Mount + server.New gain a RecommendationConfig parameter.
Discriminated-union JSON over three event types: play_started,
play_ended, play_skipped. Auth via existing RequireUser. play_started
returns play_event_id + session_id; the other two return { ok: true }.
Validates ownership: play_ended/play_skipped on another user's row
returns 403. Mount signature now takes EventsConfig and constructs
the playevents.Writer; server.New propagates it through.
- NotFound wrapper routes unknown /api/* and /rest/* to a JSON 404
- Everything else falls through to the embedded web handler, which
resolves static assets or returns index.html for SPA deep links