/** * @copyright NHCarrigan * @license Naomi's Public License * @author Naomi Carrigan */ import type { FastifyPluginAsync } from "fastify"; import type { CreateReportDto, ProfileReportWithUsers, ReportStatus, UpdateReportDto, } from "@library/shared-types"; import { ReportReason, AchievementCategory } from "@library/shared-types"; import { ReportService } from "../../services/report.service.js"; import { AchievementService } from "../../services/achievement.service"; import { adminGuard } from "../../middleware/admin-guard.js"; const reportsRoutes: FastifyPluginAsync = async (fastify) => { const reportService = new ReportService(); // Create a new report (authenticated users) fastify.post<{ Body: CreateReportDto; Reply: ProfileReportWithUsers | { error: string }; }>( "/", { preValidation: [fastify.authenticate], schema: { body: { type: "object", required: ["reportedUserId", "reason", "details"], properties: { reportedUserId: { type: "string" }, reason: { type: "string", enum: Object.values(ReportReason), }, details: { type: "string", minLength: 10, maxLength: 1000 }, }, }, }, }, async (request, reply) => { try { const report = await reportService.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") ) { return reply.status(409).send({ error: error.message }); } throw error; } }, ); // Get all reports (admin only) fastify.get<{ Querystring: { status?: ReportStatus }; Reply: ProfileReportWithUsers[]; }>( "/", { preValidation: [fastify.authenticate, adminGuard], schema: { querystring: { type: "object", properties: { status: { type: "string" }, }, }, }, }, async (request, reply) => { const reports = await reportService.getAllReports( request.query.status, ); return reply.send(reports); }, ); // Get a single report by ID (admin only) fastify.get<{ Params: { id: string }; Reply: ProfileReportWithUsers | { error: string }; }>( "/:id", { preValidation: [fastify.authenticate, adminGuard], }, async (request, reply) => { const report = await reportService.getReportById(request.params.id); if (!report) { return reply.status(404).send({ error: "Report not found" }); } return reply.send(report); }, ); // Update a report (admin only) fastify.put<{ Params: { id: string }; Body: UpdateReportDto; Reply: ProfileReportWithUsers; }>( "/: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 reportService.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 reportsRoutes;