/** * @copyright NHCarrigan * @license Naomi's Public License * @author Naomi Carrigan */ import type { FastifyPluginAsync } from "fastify"; import type { CreateCommentReportDto, CommentReportWithDetails, ReportStatus, UpdateCommentReportDto, } from "@library/shared-types"; import { ReportReason, AchievementCategory } from "@library/shared-types"; import { CommentReportService } from "../../services/comment-report.service.js"; import { AchievementService } from "../../services/achievement.service"; import { adminGuard } from "../../middleware/admin-guard.js"; const commentReportsRoutes: FastifyPluginAsync = async (fastify) => { const commentReportService = new CommentReportService(); // Create a new comment report (authenticated users) fastify.post<{ Body: CreateCommentReportDto; Reply: CommentReportWithDetails | { error: string }; }>( "/", { preValidation: [fastify.authenticate], schema: { body: { type: "object", required: ["reportedCommentId", "reason", "details"], properties: { reportedCommentId: { type: "string" }, reason: { type: "string", enum: Object.values(ReportReason), }, details: { type: "string", minLength: 10, maxLength: 1000 }, }, }, }, }, async (request, reply) => { try { const report = await commentReportService.createReport( request.user.id, request.body, ); return reply.status(201).send(report); } catch (error) { if ( error instanceof Error && (error.message.includes("already have a pending report") || error.message.includes("maximum number of pending reports")) ) { return reply.status(409).send({ error: error.message }); } throw error; } }, ); // Get all comment reports (admin only) fastify.get<{ Querystring: { status?: ReportStatus }; Reply: CommentReportWithDetails[]; }>( "/", { preValidation: [fastify.authenticate, adminGuard], schema: { querystring: { type: "object", properties: { status: { type: "string" }, }, }, }, }, async (request, reply) => { const reports = await commentReportService.getAllReports( request.query.status, ); return reply.send(reports); }, ); // Get a single comment report by ID (admin only) fastify.get<{ Params: { id: string }; Reply: CommentReportWithDetails | { error: string }; }>( "/:id", { preValidation: [fastify.authenticate, adminGuard], }, async (request, reply) => { const report = await commentReportService.getReportById(request.params.id); if (!report) { return reply.status(404).send({ error: "Report not found" }); } return reply.send(report); }, ); // Update a comment report (admin only) fastify.put<{ Params: { id: string }; Body: UpdateCommentReportDto; Reply: CommentReportWithDetails; }>( "/:id", { preValidation: [fastify.authenticate, adminGuard], schema: { body: { type: "object", required: ["status"], properties: { status: { type: "string" }, reviewNotes: { type: "string", maxLength: 1000 }, }, }, }, }, async (request, reply) => { const report = await commentReportService.updateReport( request.params.id, request.user.id, request.body, ); // Check for report achievements for the original reporter if (report.status === "ACTION_TAKEN" || report.status === "DISMISSED") { const achievementService = new AchievementService(); await achievementService.checkAchievements( report.reporterId, AchievementCategory.Report, request ); } return reply.send(report); }, ); }; export default commentReportsRoutes;