feat(#392): publish quarantine + playlist mutation events

Slice 3a — extends the producer set onto the SSE bus.

quarantine events (5 producer sites):
- quarantine.flagged (user-side flag): broadcast so the flagger's other
  clients invalidate their Hidden tab and admins' clients invalidate
  their queue.
- quarantine.unflagged (user-side unflag): scoped to the user.
- quarantine.resolved / .file_deleted / .deleted_via_lidarr (admin
  actions): broadcast because a single admin action can affect every
  user who'd flagged that track.

playlist events (6 producer sites):
- playlist.created / .updated / .deleted: owner-scoped.
- playlist.tracks_changed: emitted for AppendTracks / RemoveTrack /
  Reorder. Owner-scoped. Single kind for all three mutations because
  the client invalidation logic is identical (refetch the detail).

Public-playlist subscribers (other users viewing someone else's public
playlist) intentionally left out — needs a separate broadcast kind, not
exercised yet at single-household scale.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-12 21:28:05 -04:00
parent 3ffa5608d8
commit ee7f0cdb42
4 changed files with 59 additions and 0 deletions
+7
View File
@@ -111,6 +111,8 @@ func (h *handlers) handleResolveQuarantine(w http.ResponseWriter, r *http.Reques
writeAdminJSONErr(w, http.StatusInternalServerError, "server_error")
return
}
// Broadcast: affects every user who'd flagged this track.
h.publishQuarantineEvent("quarantine.resolved", pgtype.UUID{}, id, true)
writeJSON(w, http.StatusOK, actionResultView{
ActionID: uuidToString(action.ID),
AffectedUsers: action.AffectedUsers,
@@ -140,6 +142,9 @@ func (h *handlers) handleDeleteQuarantineFile(w http.ResponseWriter, r *http.Req
}
return
}
// Broadcast: the track row is gone; every client's library/likes/queue
// invalidates so stale references stop showing.
h.publishQuarantineEvent("quarantine.file_deleted", pgtype.UUID{}, id, true)
writeJSON(w, http.StatusOK, actionResultView{
ActionID: uuidToString(action.ID),
AffectedUsers: action.AffectedUsers,
@@ -179,6 +184,8 @@ func (h *handlers) handleDeleteQuarantineViaLidarr(w http.ResponseWriter, r *htt
}
return
}
// Broadcast: same rationale as delete-file — track row gone everywhere.
h.publishQuarantineEvent("quarantine.deleted_via_lidarr", pgtype.UUID{}, id, true)
writeJSON(w, http.StatusOK, actionResultView{
ActionID: uuidToString(action.ID),
AffectedUsers: action.AffectedUsers,