Commit Graph
6 Commits
Author SHA1 Message Date
bvandeusenandClaude Opus 5 d7a8e5f300 fix(library): a track delete that cannot remove its file deletes nothing — #3918
test-go / test (push) Failing after 55s
test-web / test (push) Successful in 56s
test-go / integration (push) Failing after 4m50s
android / Build + lint + test (push) Successful in 5m52s
release / Build signed APK (releases and dev) (push) Successful in 6m5s
release / Build + push container image (push) Successful in 1m14s
release / Verify release artifacts (tag releases only) (push) Skipped
Two delete paths had opposite failure policies. tracks.RemoveTrack
logged a failed os.Remove and deleted the row anyway, which CASCADEs
likes, plays, playlist memberships and tags, while the file survived
for the next scan to re-import as a stranger. library.DeleteTrackFile
stopped correctly but reported it as a bare 500 nobody could read.

One path now: library.DeleteTrackFile removes the file first and, on
anything but ErrNotExist, returns *FileRemoveError with nothing
deleted. Only then does it delete the row and tidy an emptied album
and artist in one transaction, log the sync change and clear orphaned
artist art. RemoveTrack calls it, which also fixes RemoveTrack never
logging a sync change. Quarantine Delete file now tidies emptied
albums and artists too.

Both endpoints answer an unwritable library (EROFS, EACCES, EPERM) with
409 library_not_writable. The message names the directory (removal
writes to the parent), the uid:gid the server runs as, and that
nothing was deleted. Other remove errors are 500 file_delete_failed
with the path.

The reachable surface is quarantine Delete file, which failed
silently: no copy for the code on either client, and Android swallowed
the exception so the row just reappeared. Web and Android now have
copy for both codes and append the server message for exactly those
two. Android's quarantine screen shows it in a snackbar.

DELETE /api/admin/tracks/{id} has had no client since f7278f24, which
kept it on purpose for a safer admin surface, so its history loss was
latent. Fixed rather than removed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SQ31KQpYbStyK5y58UmPLH
2026-09-11 14:23:01 -04:00
bvandeusen 4ce7be4296 fix(server): repair go vet failures from A1+A2 cleanup (orphan imports + needle deref) 2026-05-08 10:46:43 -04:00
bvandeusen bf90a3a868 refactor(server/api): migrate admin_smtp/admin_tracks + playlist refresh preludes (A2 6/N) 2026-05-08 10:18:35 -04:00
bvandeusenandClaude Opus 4.7 58766dbebe fix(server/api): preserve 500-class messages via apierror.InternalMsg
The PR1-T2 admin handler migration replaced bespoke 500-class messages
("lookup failed", "refetch failed", "trigger failed", "bump failed",
"remove failed", "update failed", "schedule row missing", "re-read
failed", "read failed") with apierror.Internal(err), which forces the
wire Message to "internal server error". That violates the PR1
contract that errEnvelope{Code, Message} must remain byte-identical
for existing clients.

Add apierror.InternalMsg(message, cause) — a 500-class constructor
that preserves the call site's user-facing message while keeping the
cause attached for logging and errors.Is. Re-migrate the 12 admin
sites whose pre-1cc7eb6 source had a non-empty bespoke Message so
they restore the original wire string. Sites whose original Message
was empty stay on Internal(err) (humanization to "internal server
error" is a tolerable improvement, not a regression).

admin_smtp.go's send_failed site (line 129) was already preserved
via a raw &apierror.Error literal in 1cc7eb6 and needs no fix.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 20:10:18 -04:00
bvandeusenandClaude Opus 4.7 1cc7eb6cad refactor(server/api): writeErr accepts error; migrate admin handlers (1/3)
Rewrite writeErr(w, err) to wrap *apierror.Error via apierror.From,
preserving the existing {"error": {"code", "message"}} wire envelope.
Add writeErrWithLog helper for 500-class errors that need an operator
log line. Migrate all 13 admin_*.go handler files (~76 call sites) to
the new signature; T3 will sweep the remaining api package.

The old 4-arg writeErr is removed, so non-admin call sites in
internal/api will not compile until T3 lands. This is by design — T2
and T3 are paired.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 19:48:25 -04:00
bvandeusenandClaude Opus 4.7 bb931746e3 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>
2026-05-02 22:41:51 -04:00