fix: store username on moderation_actions for retention safety
Add denormalized column to moderation_actions so the dashboard TARGET field survives message table purges. Backfills all existing 217 rows. Changes: schema + autoDeleteManager + backend repository query + migration 0017.
This commit is contained in:
@@ -124,7 +124,7 @@ export class ModerationRepository {
|
|||||||
a.score,
|
a.score,
|
||||||
a.evidence,
|
a.evidence,
|
||||||
a.policy_version,
|
a.policy_version,
|
||||||
m.username,
|
a.username,
|
||||||
LEFT(m.content, 300) AS content
|
LEFT(m.content, 300) AS content
|
||||||
FROM moderation_actions a
|
FROM moderation_actions a
|
||||||
LEFT JOIN messages m ON m.id = a.message_id
|
LEFT JOIN messages m ON m.id = a.message_id
|
||||||
@@ -314,7 +314,7 @@ export class ModerationRepository {
|
|||||||
SELECT
|
SELECT
|
||||||
a.id, a.message_id, a.user_id, a.guild_id, a.action_type,
|
a.id, a.message_id, a.user_id, a.guild_id, a.action_type,
|
||||||
a.reason, a.status, a.created_at, a.severity, a.confidence, a.score,
|
a.reason, a.status, a.created_at, a.severity, a.confidence, a.score,
|
||||||
m.username, LEFT(m.content, 300) AS content
|
a.username, LEFT(m.content, 300) AS content
|
||||||
FROM moderation_actions a
|
FROM moderation_actions a
|
||||||
LEFT JOIN messages m ON m.id = a.message_id
|
LEFT JOIN messages m ON m.id = a.message_id
|
||||||
WHERE a.created_at >= ${since}
|
WHERE a.created_at >= ${since}
|
||||||
|
|||||||
+9
@@ -0,0 +1,9 @@
|
|||||||
|
ALTER TABLE "moderation_actions" ADD COLUMN "username" text;
|
||||||
|
--> statement-breakpoint
|
||||||
|
-- Backfill username from messages table for existing actions
|
||||||
|
UPDATE "moderation_actions" a
|
||||||
|
SET "username" = m."username"
|
||||||
|
FROM "messages" m
|
||||||
|
WHERE a."message_id" = m."id"
|
||||||
|
AND a."username" IS NULL
|
||||||
|
AND m."username" IS NOT NULL;
|
||||||
@@ -120,6 +120,13 @@
|
|||||||
"when": 1787185000000,
|
"when": 1787185000000,
|
||||||
"tag": "0016_drop_user_reputations",
|
"tag": "0016_drop_user_reputations",
|
||||||
"breakpoints": true
|
"breakpoints": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idx": 17,
|
||||||
|
"version": "7",
|
||||||
|
"when": 1787810000000,
|
||||||
|
"tag": "0017_add_username_to_moderation_actions",
|
||||||
|
"breakpoints": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -197,6 +197,7 @@ async function logAutoDeleteAttempt(
|
|||||||
action_type: "delete_message",
|
action_type: "delete_message",
|
||||||
reason: result.reason,
|
reason: result.reason,
|
||||||
...verdictToActionFields(message),
|
...verdictToActionFields(message),
|
||||||
|
username: message.username,
|
||||||
executed_by: "auto-delete-manager",
|
executed_by: "auto-delete-manager",
|
||||||
status: result.deleted
|
status: result.deleted
|
||||||
? "executed"
|
? "executed"
|
||||||
@@ -262,6 +263,7 @@ export async function attemptAutoDeleteFlaggedMessage(
|
|||||||
reason:
|
reason:
|
||||||
"nickname melanggar aturan server (offensive_username); pesan dibiarkan",
|
"nickname melanggar aturan server (offensive_username); pesan dibiarkan",
|
||||||
...verdictToActionFields(message),
|
...verdictToActionFields(message),
|
||||||
|
username: message.username,
|
||||||
executed_by: "auto-delete-manager",
|
executed_by: "auto-delete-manager",
|
||||||
status: resetOk ? "executed" : "failed",
|
status: resetOk ? "executed" : "failed",
|
||||||
error: resetOk ? null : "nickname_reset_failed",
|
error: resetOk ? null : "nickname_reset_failed",
|
||||||
|
|||||||
@@ -103,6 +103,7 @@ export class ModerationHandler {
|
|||||||
| "kick_user"
|
| "kick_user"
|
||||||
| "ban_user",
|
| "ban_user",
|
||||||
reason: payload.reason ?? null,
|
reason: payload.reason ?? null,
|
||||||
|
username: null,
|
||||||
executed_by: payload.executed_by ?? "command-handler",
|
executed_by: payload.executed_by ?? "command-handler",
|
||||||
status: "executed",
|
status: "executed",
|
||||||
error: null,
|
error: null,
|
||||||
|
|||||||
@@ -642,6 +642,7 @@ export const pgModerationActionsTable = pgTable(
|
|||||||
score: pgReal("score"), // 0..1 raw model score
|
score: pgReal("score"), // 0..1 raw model score
|
||||||
evidence: pgText("evidence"), // JSON array of short quoted snippets
|
evidence: pgText("evidence"), // JSON array of short quoted snippets
|
||||||
policy_version: pgText("policy_version"), // rules.ts policy version string
|
policy_version: pgText("policy_version"), // rules.ts policy version string
|
||||||
|
username: pgText("username"), // denormalized from messages table for retention safety
|
||||||
},
|
},
|
||||||
(table) => ({
|
(table) => ({
|
||||||
messageIdIdx: pgIndex("idx_moderation_actions_message_id").on(
|
messageIdIdx: pgIndex("idx_moderation_actions_message_id").on(
|
||||||
|
|||||||
@@ -59,6 +59,7 @@ export const pgModerationActionsTable = pgTable(
|
|||||||
score: pgReal("score"), // 0..1 raw model score
|
score: pgReal("score"), // 0..1 raw model score
|
||||||
evidence: pgText("evidence"), // JSON array of short quoted snippets
|
evidence: pgText("evidence"), // JSON array of short quoted snippets
|
||||||
policy_version: pgText("policy_version"), // rules.ts policy version string
|
policy_version: pgText("policy_version"), // rules.ts policy version string
|
||||||
|
username: pgText("username"), // denormalized from messages table for retention safety
|
||||||
},
|
},
|
||||||
(table) => ({
|
(table) => ({
|
||||||
messageIdIdx: pgIndex("idx_moderation_actions_message_id").on(
|
messageIdIdx: pgIndex("idx_moderation_actions_message_id").on(
|
||||||
|
|||||||
@@ -208,6 +208,7 @@ export interface ModerationAction {
|
|||||||
guild_id: string;
|
guild_id: string;
|
||||||
action_type: ModerationActionType;
|
action_type: ModerationActionType;
|
||||||
reason: string | null;
|
reason: string | null;
|
||||||
|
username: string | null;
|
||||||
executed_by: string | null;
|
executed_by: string | null;
|
||||||
status: "pending" | "executed" | "failed";
|
status: "pending" | "executed" | "failed";
|
||||||
error: string | null;
|
error: string | null;
|
||||||
|
|||||||
Reference in New Issue
Block a user