Files
GMW/services/backend/src/modules/messages/messages.service.ts
T
asepharyana f999be4fa0 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
2026-08-05 11:11:10 +07:00

76 lines
2.3 KiB
TypeScript

import { NotFoundError, ValidationError } from "@/shared/errors/index";
import { createChildLogger } from "@/shared/logger/index";
import { messagesRepository } from "./messages.repository.js";
import type { MessageQuery } from "./messages.schema.js";
const logger = createChildLogger("messages.service");
export class MessagesService {
async listMessages(query: MessageQuery) {
if (!query.channelId && !query.guildId) {
throw new ValidationError("Either channelId or guildId is required");
}
logger.debug({ query }, "Listing messages");
return messagesRepository.findMany(query);
}
async getMessagesByChannel(channelId: string, query: MessageQuery) {
if (!channelId) {
throw new ValidationError("channelId is required");
}
logger.debug({ channelId, query }, "Getting messages by channel");
return messagesRepository.findByChannel(channelId, query);
}
async getMessageById(id: string) {
if (!id) {
throw new ValidationError("message ID is required");
}
const message = await messagesRepository.findById(id);
if (!message) {
throw new NotFoundError(`Message with ID ${id} not found`);
}
const editHistory = await messagesRepository.getEditHistory(id);
return {
...message,
edit_count: editHistory.length,
edit_history: editHistory,
};
}
async getAttachmentsByChannel(channelId: string, query: MessageQuery) {
if (!channelId) {
throw new ValidationError("channelId is required");
}
logger.debug({ channelId, query }, "Getting attachments by channel");
return messagesRepository.getAttachmentsByChannel(channelId, query);
}
async getImageMessages(
guildId: string,
limit?: number,
): Promise<ReturnType<typeof messagesRepository.getImageMessages>> {
if (!guildId) {
throw new ValidationError("guildId is required");
}
logger.debug({ guildId, limit }, "Getting image messages");
return messagesRepository.getImageMessages(guildId, limit);
}
async getReviewMessages(
channelId?: string,
limit?: number,
): Promise<Record<string, unknown>[]> {
logger.debug({ channelId, limit }, "Getting review messages");
return messagesRepository.getReviewMessages(channelId, limit);
}
}
export const messagesService = new MessagesService();