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.
This commit is contained in:
@@ -0,0 +1,41 @@
|
||||
-- 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);
|
||||
Reference in New Issue
Block a user