import { FastifyInstance } from "fastify"; import { z } from "zod"; import { prisma } from "../../lib/prisma.js"; function redactEmail(email: string): string { const [local, domain] = email.split("@"); if (!domain) return "***"; return local[0] + "***@" + domain; } const createNoteBody = z.object({ body: z.string().min(1).max(2000).trim(), }); export default async function adminNoteRoutes(app: FastifyInstance) { // Get notes for a post app.get<{ Params: { id: string } }>( "/admin/posts/:id/notes", { preHandler: [app.requireAdmin], config: { rateLimit: { max: 60, timeWindow: "1 minute" } } }, async (req, reply) => { const post = await prisma.post.findUnique({ where: { id: req.params.id } }); if (!post) { reply.status(404).send({ error: "Post not found" }); return; } const notes = await prisma.adminNote.findMany({ where: { postId: post.id }, orderBy: { createdAt: "desc" }, take: 100, include: { admin: { select: { email: true } }, }, }); reply.send({ notes: notes.map((n) => ({ id: n.id, body: n.body, adminEmail: n.admin.email ? redactEmail(n.admin.email) : "Team member", createdAt: n.createdAt, })), }); } ); // Add a note to a post app.post<{ Params: { id: string }; Body: z.infer }>( "/admin/posts/:id/notes", { preHandler: [app.requireAdmin], config: { rateLimit: { max: 20, timeWindow: "1 minute" } } }, async (req, reply) => { const post = await prisma.post.findUnique({ where: { id: req.params.id } }); if (!post) { reply.status(404).send({ error: "Post not found" }); return; } const { body } = createNoteBody.parse(req.body); const admin = await prisma.adminUser.findUnique({ where: { id: req.adminId! }, select: { email: true } }); const note = await prisma.adminNote.create({ data: { body, postId: post.id, adminId: req.adminId!, }, }); reply.status(201).send({ id: note.id, body: note.body, postId: note.postId, adminEmail: admin?.email ? redactEmail(admin.email) : "Team member", createdAt: note.createdAt, }); } ); // Delete a note app.delete<{ Params: { noteId: string } }>( "/admin/notes/:noteId", { preHandler: [app.requireAdmin], config: { rateLimit: { max: 20, timeWindow: "1 minute" } } }, async (req, reply) => { const note = await prisma.adminNote.findUnique({ where: { id: req.params.noteId } }); if (!note) { reply.status(404).send({ error: "Note not found" }); return; } if (note.adminId !== req.adminId) { reply.status(403).send({ error: "You can only delete your own notes" }); return; } await prisma.adminNote.delete({ where: { id: note.id } }); reply.status(204).send(); } ); }