feat(dashboard): add moderation log page + message edit history
- Backend moderation module: GET /api/moderation/stats (per-status + failed rate) + GET /api/moderation/actions (filter by status/actionType, cursor paging), joins messages for target username + content - Message GET /api/messages/detail/:id now returns edit_count + edit_history (old_content snapshots from message_edits, newest first) - FE: new /moderation page — summary cards (total/executed/failed/pending + failed-rate), status+type filter chips, timeline rows with action icon, target user, reason, status badge, timestamps, error text - FE: message detail shows 'Riwayat edit' panel with previous versions
This commit is contained in:
@@ -9,6 +9,7 @@ import {
|
||||
notInArray,
|
||||
or,
|
||||
type SQL,
|
||||
sql,
|
||||
} from "drizzle-orm";
|
||||
import { config } from "../../shared/config/index.js";
|
||||
import { getDatabase } from "../../shared/database/index.js";
|
||||
@@ -114,6 +115,27 @@ export class MessagesRepository {
|
||||
return mapMessageRow(row as Record<string, unknown>);
|
||||
}
|
||||
|
||||
/**
|
||||
* Edit history for a message: previous content snapshots (newest first).
|
||||
* Stored in message_edits by the gateway's message-capture module.
|
||||
*/
|
||||
async getEditHistory(
|
||||
messageId: string,
|
||||
): Promise<Array<{ old_content: string; edited_at: number }>> {
|
||||
const db = getDatabase();
|
||||
const result = await db.execute(sql`
|
||||
SELECT old_content, edited_at
|
||||
FROM message_edits
|
||||
WHERE message_id = ${messageId}
|
||||
ORDER BY edited_at DESC
|
||||
LIMIT 50
|
||||
`);
|
||||
return ((result.rows as Record<string, unknown>[]) || []).map((r) => ({
|
||||
old_content: String(r.old_content ?? ""),
|
||||
edited_at: Number(r.edited_at ?? 0),
|
||||
}));
|
||||
}
|
||||
|
||||
async findByChannel(
|
||||
channelId: string,
|
||||
query: MessageQuery,
|
||||
|
||||
@@ -34,7 +34,12 @@ export class MessagesService {
|
||||
throw new NotFoundError(`Message with ID ${id} not found`);
|
||||
}
|
||||
|
||||
return message;
|
||||
const editHistory = await messagesRepository.getEditHistory(id);
|
||||
return {
|
||||
...message,
|
||||
edit_count: editHistory.length,
|
||||
edit_history: editHistory,
|
||||
};
|
||||
}
|
||||
|
||||
async getAttachmentsByChannel(channelId: string, query: MessageQuery) {
|
||||
|
||||
Reference in New Issue
Block a user