Files
top-tickets/src/app/api/portal/tickets/[ticketId]/messages/route.ts
T
ogrechkoandClaude Sonnet 5 cf0b6cf58e MVP-1: helpdesk core with dashboard, Telegram channel, customer portal
Next.js 16 + Drizzle/SQLite (matching ~/project-claude conventions), SSE-based
realtime updates, admin dashboard with status columns, ticket thread view,
Telegram long-polling integration (inbound + outbound), token-based customer
portal, Docker Compose packaging.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QcXH24ky6zjk2UyK5oZUPH
2026-07-26 12:40:56 +00:00

40 lines
1.1 KiB
TypeScript

export const runtime = "nodejs";
import { NextResponse } from "next/server";
import { z } from "zod";
import { getCustomerByPortalToken, appendCustomerReply } from "@/lib/tickets/service";
const schema = z.object({
token: z.string().min(1),
body: z.string().min(1).max(10_000),
});
export async function POST(
request: Request,
{ params }: { params: Promise<{ ticketId: string }> },
) {
const { ticketId } = await params;
const body = await request.json().catch(() => null);
const parsed = schema.safeParse(body);
if (!parsed.success) {
return NextResponse.json({ error: "Invalid input" }, { status: 400 });
}
const customer = await getCustomerByPortalToken(parsed.data.token);
if (!customer) {
return NextResponse.json({ error: "Invalid token" }, { status: 404 });
}
try {
const result = await appendCustomerReply({
ticketId,
customerId: customer.id,
authorName: customer.displayName,
body: parsed.data.body,
});
return NextResponse.json(result, { status: 201 });
} catch {
return NextResponse.json({ error: "Ticket not found" }, { status: 404 });
}
}