diff --git a/services/backend/src/modules/moderation/moderation.repository.ts b/services/backend/src/modules/moderation/moderation.repository.ts index e813511e..da182b89 100644 --- a/services/backend/src/modules/moderation/moderation.repository.ts +++ b/services/backend/src/modules/moderation/moderation.repository.ts @@ -124,7 +124,7 @@ export class ModerationRepository { a.score, a.evidence, a.policy_version, - m.username, + a.username, LEFT(m.content, 300) AS content FROM moderation_actions a LEFT JOIN messages m ON m.id = a.message_id @@ -314,7 +314,7 @@ export class ModerationRepository { SELECT 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, - m.username, LEFT(m.content, 300) AS content + a.username, LEFT(m.content, 300) AS content FROM moderation_actions a LEFT JOIN messages m ON m.id = a.message_id WHERE a.created_at >= ${since} diff --git a/services/discord-gateway/drizzle/migrations/0017_add_username_to_moderation_actions.sql b/services/discord-gateway/drizzle/migrations/0017_add_username_to_moderation_actions.sql new file mode 100644 index 00000000..4b9b04e1 --- /dev/null +++ b/services/discord-gateway/drizzle/migrations/0017_add_username_to_moderation_actions.sql @@ -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; diff --git a/services/discord-gateway/drizzle/migrations/meta/_journal.json b/services/discord-gateway/drizzle/migrations/meta/_journal.json index e56282fd..2af0157f 100644 --- a/services/discord-gateway/drizzle/migrations/meta/_journal.json +++ b/services/discord-gateway/drizzle/migrations/meta/_journal.json @@ -120,6 +120,13 @@ "when": 1787185000000, "tag": "0016_drop_user_reputations", "breakpoints": true + }, + { + "idx": 17, + "version": "7", + "when": 1787810000000, + "tag": "0017_add_username_to_moderation_actions", + "breakpoints": true } ] } \ No newline at end of file diff --git a/services/discord-gateway/src/modules/ai-moderation/autoDeleteManager.ts b/services/discord-gateway/src/modules/ai-moderation/autoDeleteManager.ts index 29d9bee7..9a7c0fa3 100644 --- a/services/discord-gateway/src/modules/ai-moderation/autoDeleteManager.ts +++ b/services/discord-gateway/src/modules/ai-moderation/autoDeleteManager.ts @@ -197,6 +197,7 @@ async function logAutoDeleteAttempt( action_type: "delete_message", reason: result.reason, ...verdictToActionFields(message), + username: message.username, executed_by: "auto-delete-manager", status: result.deleted ? "executed" @@ -262,6 +263,7 @@ export async function attemptAutoDeleteFlaggedMessage( reason: "nickname melanggar aturan server (offensive_username); pesan dibiarkan", ...verdictToActionFields(message), + username: message.username, executed_by: "auto-delete-manager", status: resetOk ? "executed" : "failed", error: resetOk ? null : "nickname_reset_failed", diff --git a/services/discord-gateway/src/modules/command-handler/moderation.handler.ts b/services/discord-gateway/src/modules/command-handler/moderation.handler.ts index 42f9f7bb..1a2b23f6 100644 --- a/services/discord-gateway/src/modules/command-handler/moderation.handler.ts +++ b/services/discord-gateway/src/modules/command-handler/moderation.handler.ts @@ -103,6 +103,7 @@ export class ModerationHandler { | "kick_user" | "ban_user", reason: payload.reason ?? null, + username: null, executed_by: payload.executed_by ?? "command-handler", status: "executed", error: null, diff --git a/services/discord-gateway/src/shared/database/schema.ts b/services/discord-gateway/src/shared/database/schema.ts index a544ecef..7d93e756 100644 --- a/services/discord-gateway/src/shared/database/schema.ts +++ b/services/discord-gateway/src/shared/database/schema.ts @@ -642,6 +642,7 @@ export const pgModerationActionsTable = pgTable( score: pgReal("score"), // 0..1 raw model score evidence: pgText("evidence"), // JSON array of short quoted snippets policy_version: pgText("policy_version"), // rules.ts policy version string + username: pgText("username"), // denormalized from messages table for retention safety }, (table) => ({ messageIdIdx: pgIndex("idx_moderation_actions_message_id").on( diff --git a/services/discord-gateway/src/shared/database/schema/messages.ts b/services/discord-gateway/src/shared/database/schema/messages.ts index 39f22f10..6fe03744 100644 --- a/services/discord-gateway/src/shared/database/schema/messages.ts +++ b/services/discord-gateway/src/shared/database/schema/messages.ts @@ -59,6 +59,7 @@ export const pgModerationActionsTable = pgTable( score: pgReal("score"), // 0..1 raw model score evidence: pgText("evidence"), // JSON array of short quoted snippets policy_version: pgText("policy_version"), // rules.ts policy version string + username: pgText("username"), // denormalized from messages table for retention safety }, (table) => ({ messageIdIdx: pgIndex("idx_moderation_actions_message_id").on( diff --git a/services/discord-gateway/src/shared/moderation-types.ts b/services/discord-gateway/src/shared/moderation-types.ts index 113603d5..849c52a5 100644 --- a/services/discord-gateway/src/shared/moderation-types.ts +++ b/services/discord-gateway/src/shared/moderation-types.ts @@ -208,6 +208,7 @@ export interface ModerationAction { guild_id: string; action_type: ModerationActionType; reason: string | null; + username: string | null; executed_by: string | null; status: "pending" | "executed" | "failed"; error: string | null;