export const runtime = "nodejs"; import { NextResponse } from "next/server"; import { requireSession } from "@/lib/auth/require"; import { deliverAgentMessage } from "@/lib/tickets/delivery"; import { getTicketWithMessages } from "@/lib/tickets/service"; import { isTicketVisibleTo } from "@/lib/tickets/visibility"; import { saveAttachment, AttachmentTooLargeError, MAX_ATTACHMENT_BYTES } from "@/lib/attachments/storage"; export async function POST(request: Request, { params }: { params: Promise<{ id: string }> }) { const { session, response } = await requireSession(); if (!session) return response; const currentUser = { id: session.user.id, role: session.user.role }; const { id } = await params; const existing = await getTicketWithMessages(id); if (!existing || !isTicketVisibleTo(existing.ticket, currentUser)) { return NextResponse.json({ error: "Ticket not found" }, { status: 404 }); } const formData = await request.formData().catch(() => null); const file = formData?.get("file"); if (!(file instanceof File)) { return NextResponse.json({ error: "Missing file" }, { status: 400 }); } if (file.size > MAX_ATTACHMENT_BYTES) { return NextResponse.json({ error: "File too large" }, { status: 413 }); } const captionRaw = formData?.get("caption"); const caption = typeof captionRaw === "string" ? captionRaw.trim() : ""; // Internal notes are admin-only — see messages/route.ts for the same rule. const visibility = currentUser.role === "admin" && formData?.get("visibility") === "internal" ? "internal" : "public"; const buffer = Buffer.from(await file.arrayBuffer()); let saved; try { saved = await saveAttachment(buffer); } catch (err) { if (err instanceof AttachmentTooLargeError) { return NextResponse.json({ error: "File too large" }, { status: 413 }); } throw err; } try { const result = await deliverAgentMessage({ ticketId: id, agentId: session.user.id, agentName: session.user.name, body: caption || file.name || "Вложение", visibility, attachment: { filename: file.name || "file", mimeType: file.type || "application/octet-stream", sizeBytes: saved.sizeBytes, storageKey: saved.storageKey, buffer, }, }); return NextResponse.json(result, { status: 201 }); } catch { return NextResponse.json({ error: "Ticket not found" }, { status: 404 }); } }