66 lines
2.2 KiB
TypeScript
66 lines
2.2 KiB
TypeScript
// Public REST API — post a reply to a help-center ticket.
|
|
//
|
|
// Bearer-authed. POST inserts a reply ({ content }) authored by the current user
|
|
// into website_help_center_ticket_replies. The target ticket must exist and
|
|
// belong to the authed user. Fail-soft: never a 500.
|
|
|
|
import { apiError, apiJson, positiveBigInt } from "@/lib/api";
|
|
import { bearerUserId } from "@/lib/api-auth";
|
|
import { prisma } from "@/lib/prisma";
|
|
import { createOwnedTicketReply } from "@/lib/services/ticket-replies";
|
|
|
|
export const dynamic = "force-dynamic";
|
|
|
|
// POST /api/tickets/:id/reply body: { content }
|
|
export async function POST(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);
|
|
|
|
const body = (await req.json().catch(() => ({}))) as { content?: unknown };
|
|
const content = String(body.content ?? "")
|
|
.trim()
|
|
.slice(0, 5000);
|
|
if (!content) return apiError("Content is required");
|
|
|
|
try {
|
|
// Ownership check — only the ticket owner may reply.
|
|
const reply = await prisma.$transaction((tx) => createOwnedTicketReply(
|
|
{
|
|
findTicket: (ticketId) => tx.websiteHelpCenterTickets.findUnique({
|
|
where: { id: ticketId },
|
|
select: { id: true, userId: true },
|
|
}),
|
|
createReply: (data) => tx.websiteHelpCenterTicketReplies.create({
|
|
data,
|
|
select: { id: true, userId: true, content: true, createdAt: true },
|
|
}),
|
|
touchTicket: (ticketId, updatedAt) => tx.websiteHelpCenterTickets.update({
|
|
where: { id: ticketId },
|
|
data: { updatedAt },
|
|
select: { id: true },
|
|
}),
|
|
},
|
|
{ ticketId, userId: uid, content },
|
|
));
|
|
if (!reply) return apiError("Ticket not found", 404);
|
|
|
|
return apiJson(
|
|
{
|
|
reply: {
|
|
id: reply.id,
|
|
userId: reply.userId,
|
|
content: reply.content,
|
|
createdAt: reply.createdAt,
|
|
},
|
|
},
|
|
{ status: 201 },
|
|
);
|
|
} catch {
|
|
return apiError("Failed to post reply", 503);
|
|
}
|
|
}
|