Files
echoboard/packages/api/src/routes/admin/notes.ts

101 lines
3.0 KiB
TypeScript

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<typeof createNoteBody> }>(
"/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();
}
);
}