import { NextRequest, NextResponse } from "next/server"; import { z } from "zod"; import { admin, required } from "@/lib/supabase"; import { verifySupportWebhook } from "@/lib/support-signature"; import { supportCommand } from "@/shared/bug-reports"; export const runtime = "nodejs"; export async function POST(request: NextRequest) { const secret = process.env.LINEAR_WEBHOOK_SECRET; if (!secret) return NextResponse.json( { error: "Support integration is not configured" }, { status: 503 }, ); const reader = request.body?.getReader(); if (!reader) return new NextResponse(null, { status: 400 }); const chunks: Uint8Array[] = []; let size = 0; while (true) { const { done, value } = await reader.read(); if (done) break; size += value.length; if (size > 256 * 1024) { await reader.cancel(); return new NextResponse(null, { status: 413 }); } chunks.push(value); } const raw = Buffer.concat(chunks).toString("utf8"); if ( !verifySupportWebhook(raw, request.headers.get("linear-signature"), secret) ) return new NextResponse(null, { status: 401 }); let decoded: unknown; try { decoded = JSON.parse(raw); } catch { return new NextResponse(null, { status: 400 }); } const payload = z .object({ action: z.string(), type: z.string(), actor: z .object({ id: z.string(), type: z.string(), name: z.string().optional(), }) .nullable() .optional(), data: z.object({ id: z.string().uuid(), issueId: z.string().uuid().optional(), body: z.string().optional(), userId: z.string().optional(), }), }) .safeParse(decoded); if (!payload.success) return new NextResponse(null, { status: 400 }); const event = payload.data; // Only explicit new staff comments are public. Internal comments and user echoes stay private. if ( event.action !== "create" || event.type !== "Comment" || event.actor?.type.toLowerCase() !== "user" || event.data.userId !== event.actor.id || !event.data.issueId ) return NextResponse.json({ ok: true }); const command = supportCommand(event.data.body ?? ""); if (!command) return NextResponse.json({ ok: true }); try { required( await admin().rpc("receive_bug_reply", { p_report: event.data.issueId, p_external: event.data.id, p_author: event.actor.name ?? "Support", p_body: command.kind === "reply" ? command.text : "", p_glow: command.kind === "glow" ? command.enabled : null, }), ); return NextResponse.json({ ok: true }); } catch { return NextResponse.json( { error: "Support delivery failed" }, { status: 503 }, ); } }