import { NextResponse } from "next/server"; import { eq } from "drizzle-orm"; import { db } from "~/server/db"; import { invoices, platformSettings } from "~/server/db/schema"; import { generateInvoicePDFBlob } from "~/lib/pdf-export"; export const runtime = "nodejs"; export async function GET( request: Request, { params }: { params: Promise<{ token: string }> }, ) { const { token } = await params; const invoice = await db.query.invoices.findFirst({ where: eq(invoices.publicToken, token), with: { client: true, // Explicit allowlist: token-based public route — never fetch // secret fields (resendApiKey, resendDomain) for an unauthenticated request. business: { columns: { id: true, name: true, nickname: true, email: true, phone: true, addressLine1: true, addressLine2: true, city: true, state: true, postalCode: true, country: true, website: true, taxId: true, logoStorageKey: true, logoMimeType: true, hideNameWithLogo: true, }, }, items: { orderBy: (i, { asc }) => [ asc(i.date), asc(i.position), asc(i.createdAt), ], }, }, }); if (!invoice) { return NextResponse.json({ error: "Not found" }, { status: 404 }); } if (invoice.publicTokenExpiresAt && new Date(invoice.publicTokenExpiresAt) < new Date()) { return NextResponse.json({ error: "This link has expired" }, { status: 410 }); } const settings = await db.query.platformSettings.findFirst({ where: eq(platformSettings.id, "global"), }); const pdfBlob = await generateInvoicePDFBlob( invoice, { pdfTemplate: settings?.pdfTemplate as "classic" | "minimal" | undefined, pdfAccentColor: settings?.pdfAccentColor, pdfFontFamily: settings?.pdfFontFamily as | "sans" | "serif" | "mono" | undefined, pdfNumericFontFamily: settings?.pdfNumericFontFamily as | "sans" | "serif" | "mono" | undefined, pdfFooterText: settings?.pdfFooterText, pdfShowLogo: settings?.pdfShowLogo, pdfShowPageNumbers: settings?.pdfShowPageNumbers, }, { logoBaseUrl: new URL(request.url).origin }, ); const buffer = await pdfBlob.arrayBuffer(); const filename = `invoice-${invoice.invoiceNumber}.pdf`; return new Response(buffer, { headers: { "Content-Type": "application/pdf", "Content-Disposition": `inline; filename="${filename}"`, "Cache-Control": "private, no-store", }, }); }