Two parallel agent-batches consolidated: USERS-MGMT (rebreak-backend agent): - Schema: Profile gets banned, bannedAt, bannedReason, deletedAt + indexes - Migration: 20260509_profile_admin_management (additive, idempotent) - DB-layer backend/server/db/adminUsers.ts: listAdminUsers (cursor-pagination, search, plan-filter) updateAdminUser (plan-validation, ban-stamping) softDeleteAdminUser (DSGVO PII-scrub: nickname=null, email=deleted-{shortid}@deleted.local) - 3 endpoints under /api/admin/users: GET (list with ?cursor&limit&q&plan&includeDeleted) PATCH /:id (plan/banned/bannedReason/lyraVoiceId) DELETE /:id (soft-delete idempotent) - 12 tests passing MODERATION (rebreak-backend agent): - Schema: CommunityPost+CommunityReply get isModerated, isDeleted, deletedAt, reportedAt + index (is_moderated, reported_at) - New ModerationAction model → audit-log table - Migration: 20260509_moderation_queue (additive, idempotent) - DB-layer backend/server/db/moderation.ts: listModerationQueue (merge posts+comments, sort by reportedAt, cursor) dismissModerationItem deleteModerationItem (content scrub + audit snapshot) banUserFromModerationItem (reuses banned/bannedAt/bannedReason fields) - 4 endpoints under /api/admin/moderation: GET /queue, POST /:id/dismiss, POST /:id/delete, POST /:id/ban-user - 11 tests passing Backend total: 78 tests passing | 4 skipped (pre-existing requireAdmin tests) Auth: x-admin-secret header (consistent with existing /admin/* endpoints). DSGVO: - Soft-delete scrubt PII statt hard-delete - Email NICHT in admin user-list (lebt nur in auth.users) - Audit-log für moderation-actions (90-day cleanup-cron pending hans-mueller-DSB-review) ⚠️ MIGRATIONS — auto-deploy via pipeline (commit b38bf17 detection): - 20260509_profile_admin_management - 20260509_moderation_queue Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
57 lines
2.9 KiB
SQL
57 lines
2.9 KiB
SQL
-- Admin Moderation Queue — Phase E
|
|
-- Erweitert CommunityPost / CommunityReply um Moderation-Felder und legt
|
|
-- moderation_actions-Audit-Tabelle an.
|
|
--
|
|
-- Reported-Marker:
|
|
-- isModerated=true → in /api/admin/moderation/queue gelistet
|
|
-- Dismiss → isModerated=false (Flag clear)
|
|
-- Delete → content="", isDeleted=true, isModerated bleibt true
|
|
-- (für audit / spätere Re-Review)
|
|
--
|
|
-- Audit-Trail (DSGVO):
|
|
-- Jede Aktion (dismiss / delete / ban_user) schreibt einen ModerationAction-
|
|
-- Eintrag inkl. content-snapshot. Reporter-Info wird nicht persistiert
|
|
-- (anonymous report-flow per Convention).
|
|
--
|
|
-- Drift-Hinweis: Diese Migration wird via `pnpm prisma migrate deploy` auf
|
|
-- staging-/prod-DB gefahren. Lokal NICHT ausführen. Falls Drift erkannt wird:
|
|
-- pnpm prisma migrate resolve --applied 20260509_moderation_queue
|
|
|
|
-- ─── community_posts: Moderation-Felder ──────────────────────────────────────
|
|
ALTER TABLE "rebreak"."community_posts"
|
|
ADD COLUMN IF NOT EXISTS "is_deleted" BOOLEAN NOT NULL DEFAULT false,
|
|
ADD COLUMN IF NOT EXISTS "deleted_at" TIMESTAMP(3),
|
|
ADD COLUMN IF NOT EXISTS "reported_at" TIMESTAMP(3);
|
|
|
|
CREATE INDEX IF NOT EXISTS "community_posts_is_moderated_reported_at_idx"
|
|
ON "rebreak"."community_posts" ("is_moderated", "reported_at");
|
|
|
|
-- ─── community_replies: Moderation-Felder ────────────────────────────────────
|
|
ALTER TABLE "rebreak"."community_replies"
|
|
ADD COLUMN IF NOT EXISTS "is_moderated" BOOLEAN NOT NULL DEFAULT false,
|
|
ADD COLUMN IF NOT EXISTS "is_deleted" BOOLEAN NOT NULL DEFAULT false,
|
|
ADD COLUMN IF NOT EXISTS "deleted_at" TIMESTAMP(3),
|
|
ADD COLUMN IF NOT EXISTS "reported_at" TIMESTAMP(3);
|
|
|
|
CREATE INDEX IF NOT EXISTS "community_replies_is_moderated_reported_at_idx"
|
|
ON "rebreak"."community_replies" ("is_moderated", "reported_at");
|
|
|
|
-- ─── moderation_actions: Audit-Log ───────────────────────────────────────────
|
|
CREATE TABLE IF NOT EXISTS "rebreak"."moderation_actions" (
|
|
"id" UUID NOT NULL DEFAULT gen_random_uuid(),
|
|
"target_type" TEXT NOT NULL,
|
|
"target_id" UUID NOT NULL,
|
|
"action" TEXT NOT NULL,
|
|
"admin_user_id" UUID,
|
|
"content_snapshot" TEXT,
|
|
"reason" TEXT,
|
|
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
|
|
CONSTRAINT "moderation_actions_pkey" PRIMARY KEY ("id")
|
|
);
|
|
|
|
CREATE INDEX IF NOT EXISTS "moderation_actions_target_idx"
|
|
ON "rebreak"."moderation_actions" ("target_type", "target_id");
|
|
CREATE INDEX IF NOT EXISTS "moderation_actions_created_at_idx"
|
|
ON "rebreak"."moderation_actions" ("created_at");
|