// Public REST API — a single help-center ticket (with replies). // // Bearer-authed. GET returns one ticket that MUST belong to the authed user, // together with its replies; reply author usernames are resolved in a single // users lookup. Fail-soft: never a 500. import { apiError, apiJson, positiveBigInt } from "@/lib/api"; import { bearerUserId } from "@/lib/api-auth"; import { prisma } from "@/lib/prisma"; export const dynamic = "force-dynamic"; // GET /api/tickets/:id export async function GET( req: Request, { params }: { params: Promise<{ id: string }> }, ) { const uid = await bearerUserId(req); if (!uid) return apiError("Unauthorized", 401); const { id } = await params; const ticketId = positiveBigInt(id); if (!ticketId) return apiError("Invalid ticket id", 422); try { const ticket = await prisma.websiteHelpCenterTickets.findUnique({ where: { id: ticketId }, select: { id: true, userId: true, categoryId: true, title: true, content: true, open: true, createdAt: true, }, }); // Ownership check — return 404 (not 403) so a foreign id is indistinguishable // from a missing one. if (!ticket || ticket.userId !== uid) return apiError("Ticket not found", 404); const replies = await prisma.websiteHelpCenterTicketReplies.findMany({ where: { ticketId }, select: { id: true, userId: true, content: true, createdAt: true }, orderBy: { id: "asc" }, }); // Resolve author usernames in one query. const authorIds = [...new Set(replies.map((r) => r.userId))]; const authors = authorIds.length ? await prisma.user.findMany({ where: { id: { in: authorIds } }, select: { id: true, username: true }, }) : []; const nameById = new Map(authors.map((a) => [a.id, a.username])); return apiJson({ ticket: { id: ticket.id, categoryId: ticket.categoryId, title: ticket.title, content: ticket.content, open: ticket.open, createdAt: ticket.createdAt, replies: replies.map((r) => ({ id: r.id, userId: r.userId, username: nameById.get(r.userId) ?? null, content: r.content, createdAt: r.createdAt, })), }, }); } catch { return apiError("Failed to load ticket", 503); } }