feat(moderation): report a message to moderators with a site-admin review queue (LC-334) #345
Loading…
Reference in a new issue
No description provided.
Delete branch "feat/lc-334-message-reports"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
What
Adds member-initiated message reporting with a site-admin review queue. A "Report" action in the message hover menu (shown for other members' non-system messages) opens a modal where the reporter picks a preset category (Spam / Harassment / Inappropriate / Other) and an optional note. Site admins triage open reports at a new
/admin/reportsqueue: each row shows the category, reporter, note, room, author, a message excerpt, and a jump-to-message link, with Resolve and Dismiss actions. Enforcement (delete message, ban/mute author) keeps using the existing tools; this is the intake + triage layer.How
chat.dbmigration0063_message_reports:UNIQUE(message_id, reporter_id)makes a re-report by the same user a no-op (insert isON CONFLICT DO NOTHING);room_idis denormalized so the queue renders without a join and survives a soft-deleted message; FK tomessageswithON DELETE CASCADE; index on(status, created_at).db::reports(create / list_open / count_open / set_status) with a server-side category allowlist and a 500-char note cap.set_statusonly transitions reports stillopen, so two admins cannot double-handle one row.GET/POST /messages/{id}/report) lives in a newroutes::reportmodule compiled in BOTH build modes; the table is harmless and forward-compatible to populate in either.Reportsnav link + open-count badge, and the live WS arm/renderer are#[cfg(feature = "standalone")], mirroring the existing admin-surface gating. The newAdminReportChangedevent is broadcast on the existingadmintopic; the WS send task re-queries and swaps#admin-reports-listand#admin-reports-badgeOOB (id-keyed, self-limiting per LC-156), so filing / resolving / dismissing live-updates every admin tab. In saas the event never fires and falls through torender_event(None).admin/reports_items.htmlpartial used by both the full page and the OOB fragment, so a fresh load and a live update render identically. The jump link uses the canonical/m/{id}permalink (resolves room vs DM server-side).hx-trigger="load") so the open count does not have to thread through every admin page struct.en+eswith parity.Testing
just check(both feature sets compile; Askama validates the new templates).just test(standalone) andjust test-saasgreen: 0 failures in both.db_reportsruns in both modes;routes_reportsis file-scoped#![cfg(standalone)]since it exercises/admin/reports.user_idis empty) and were regenerated; no Report button leaks into synthetic-actor output.No operator action: new feature, no env var / config / contract change. The migration auto-applies at startup.
🤖 Generated with Claude Code