101 lines
3.0 KiB
TypeScript
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();
|
|
}
|
|
);
|
|
}
|