Spaces:
Sleeping
Sleeping
| import { NextRequest, NextResponse } from "next/server"; | |
| import React from "react"; | |
| export const runtime = "nodejs"; | |
| export async function POST(req: NextRequest) { | |
| try { | |
| const data = await req.json(); | |
| // Dynamic import to avoid bundling issues | |
| const { Document, Page, Text, View, StyleSheet, pdf } = await import("@react-pdf/renderer"); | |
| const styles = StyleSheet.create({ | |
| page: { padding: 40, fontFamily: "Helvetica", fontSize: 11, color: "#27272a" }, | |
| title: { fontSize: 22, fontWeight: "bold", marginBottom: 4 }, | |
| subtitle: { fontSize: 11, color: "#71717a" }, | |
| divider: { borderBottomWidth: 1, borderBottomColor: "#e4e4e7", marginVertical: 16 }, | |
| scoreRow: { flexDirection: "row", justifyContent: "space-between", alignItems: "center", marginBottom: 16 }, | |
| scoreLabel: { fontSize: 10, color: "#a1a1aa" }, | |
| scoreValue: { fontSize: 28, fontWeight: "bold" }, | |
| clauseCard: { marginBottom: 12, padding: 12, borderWidth: 1, borderColor: "#e4e4e7", borderRadius: 6 }, | |
| clauseText: { fontSize: 10, color: "#3f3f46", lineHeight: 1.5, marginBottom: 6 }, | |
| tag: { fontSize: 9, fontWeight: "bold", padding: "2 8", borderRadius: 3, marginRight: 4 }, | |
| tagHigh: { backgroundColor: "#fef2f2", color: "#b91c1c" }, | |
| tagMedium: { backgroundColor: "#fffbeb", color: "#a16207" }, | |
| tagLow: { backgroundColor: "#eff6ff", color: "#1d4ed8" }, | |
| footer: { position: "absolute", bottom: 30, left: 40, right: 40, fontSize: 8, color: "#a1a1aa", textAlign: "center" }, | |
| }); | |
| const flagged = (data.results || []).filter((r: any) => r.categories?.length > 0); | |
| const doc = React.createElement( | |
| Document, | |
| null, | |
| React.createElement( | |
| Page, | |
| { size: "A4", style: styles.page }, | |
| // Header | |
| React.createElement(View, null, | |
| React.createElement(Text, { style: styles.title }, "ClauseGuard Report"), | |
| React.createElement(Text, { style: styles.subtitle }, | |
| `${data.source_url || "Manual scan"} β ${new Date().toLocaleDateString()}` | |
| ), | |
| ), | |
| // Score | |
| React.createElement(View, { style: styles.scoreRow }, | |
| React.createElement(View, null, | |
| React.createElement(Text, { style: styles.scoreLabel }, "RISK SCORE"), | |
| React.createElement(Text, { style: styles.scoreValue }, `${data.risk_score}/100`), | |
| ), | |
| React.createElement(Text, { style: { fontSize: 16, fontWeight: "bold" } }, `Grade ${data.grade}`), | |
| ), | |
| React.createElement(Text, { style: styles.subtitle }, | |
| `${data.total_clauses} clauses scanned β ${data.flagged_count} flagged` | |
| ), | |
| React.createElement(View, { style: styles.divider }), | |
| // Clauses | |
| ...flagged.map((clause: any, i: number) => | |
| React.createElement(View, { key: i, style: styles.clauseCard }, | |
| React.createElement(Text, { style: styles.clauseText }, | |
| clause.text?.substring(0, 300) || "" | |
| ), | |
| React.createElement(View, { style: { flexDirection: "row", flexWrap: "wrap" } }, | |
| ...(clause.categories || []).map((cat: any, j: number) => { | |
| const tagStyle = cat.severity === "HIGH" ? styles.tagHigh : cat.severity === "MEDIUM" ? styles.tagMedium : styles.tagLow; | |
| return React.createElement(Text, { key: j, style: [styles.tag, tagStyle] }, cat.name); | |
| }) | |
| ), | |
| ) | |
| ), | |
| // Footer | |
| React.createElement(Text, { style: styles.footer }, | |
| "Generated by ClauseGuard β clauseguard.com β Not legal advice" | |
| ), | |
| ) | |
| ); | |
| const instance = pdf(doc); | |
| const buffer = await instance.toBuffer(); | |
| const chunks: Uint8Array[] = []; | |
| for await (const chunk of buffer as any) { | |
| chunks.push(chunk instanceof Uint8Array ? chunk : new Uint8Array(chunk)); | |
| } | |
| return new Response(Buffer.concat(chunks), { | |
| headers: { | |
| "Content-Type": "application/pdf", | |
| "Content-Disposition": `attachment; filename="clauseguard-report.pdf"`, | |
| }, | |
| }); | |
| } catch (error) { | |
| console.error("PDF generation error:", error); | |
| return NextResponse.json({ error: "PDF generation failed" }, { status: 500 }); | |
| } | |
| } | |