Files
minstrel/internal/db/migrations/0032_playback_errors.up.sql
T
bvandeusen 15b59a214d
test-go / test (push) Successful in 29s
test-go / integration (push) Successful in 11m25s
feat(server): playback_errors table + admin inbox endpoints
New client-reported playback-error log. Surfaces zero-duration
tracks (and future load_failed / stalled kinds) into an admin
inbox so the operator can hide / delete / re-request the
offending track.

Schema (migration 0032):
- playback_errors table with CHECK constraints on the kind +
  resolution enums (per the standing rule that new enum values
  need a migration to add)
- Partial index on unresolved rows for fast inbox lookup
- ON DELETE CASCADE from tracks + users so cleanup is automatic

Endpoints:
- POST /api/playback-errors: any signed-in user reports. Body
  validates track existence + kind whitelist; client_id required
  so support can correlate reports from the same device.
- GET /api/admin/playback-errors?resolved=false&offset=&limit=:
  admin list with join to track/album/artist for table render
  without per-row round-trips. Pagination capped at 200/page.
- POST /api/admin/playback-errors/{id}/resolve: admin marks
  resolved with a resolution enum string.

Auto-resolve on Hide/Delete/Re-request from the inbox row is
driven from the web client (two sequential calls) — keeps the
existing track-action endpoints unchanged.
2026-06-02 11:25:50 -04:00

42 lines
1.8 KiB
SQL

-- Client-reported playback errors. Populated by clients when a track
-- fails to play (zero duration, decode error, etc.); surfaced in the
-- admin /admin/playback-errors inbox so the operator can hide / delete
-- / re-request the offending track.
--
-- resolved_at + resolved_by + resolution are NULL until an admin acts
-- on the row. Auto-resolved by the client when the admin clicks Hide /
-- Delete / Re-request from the inbox row, with the resolution string
-- recording which path was taken. The partial index keeps the
-- unresolved-queue lookup fast even as resolved history accumulates.
--
-- CHECK constraints on kind/resolution gate the enum values so client
-- typos can't pollute the data (per the standing rule about
-- enum-CHECK whitelists needing migrations).
CREATE TABLE playback_errors (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
track_id uuid NOT NULL REFERENCES tracks(id) ON DELETE CASCADE,
user_id uuid NOT NULL REFERENCES users(id) ON DELETE CASCADE,
client_id text NOT NULL,
kind text NOT NULL,
detail text,
occurred_at timestamptz NOT NULL DEFAULT now(),
resolved_at timestamptz,
resolved_by uuid REFERENCES users(id),
resolution text,
CONSTRAINT playback_errors_kind_check
CHECK (kind IN ('zero_duration', 'load_failed', 'stalled')),
CONSTRAINT playback_errors_resolution_check
CHECK (resolution IS NULL OR resolution IN
('hidden', 'deleted', 'requested', 'fixed', 'ignored')),
CONSTRAINT playback_errors_resolved_consistency
CHECK ((resolved_at IS NULL) = (resolution IS NULL))
);
CREATE INDEX idx_playback_errors_unresolved
ON playback_errors (occurred_at DESC)
WHERE resolved_at IS NULL;
CREATE INDEX idx_playback_errors_track
ON playback_errors (track_id);