eCourt-note/app/api/template/render/route.ts
oom 69d83ea493
All checks were successful
Deploy eCourt Note Web to Server / Validate Database Schema (Drizzle) (push) Successful in 3s
Deploy eCourt Note Web to Server / Build Web Docker Image (push) Successful in 1m23s
Deploy eCourt Note Web to Server / Deploy eCourt Note Web to Server (push) Successful in 5s
Deploy eCourt Note Web to Server / Notify Deployment Status (push) Successful in 1s
Fix build time fail: export applyTemplate not compatible with Next.js(move to lib-tmpl), add var in .env.example
2026-10-27 10:15:35 +07:00

135 lines
4.3 KiB
TypeScript

import { NextResponse } from "next/server";
import { DocumentTemplate,applyTemplate} from "@/lib/lib-tmpl";
export const runtime = "nodejs";
const DOCX_CONTENT_TYPE =
"application/vnd.openxmlformats-officedocument.wordprocessingml.document";
// cap เดียวกับ N4 (app/actions/files.ts) — route นี้เปิดกว้างให้ภายนอกยิงได้
// ต้องเช็คขนาดก่อนอ่านเข้าหน่วยความจำ กัน DoS (bodySizeLimit ครอบเฉพาะ Server Actions)
const MAX_UPLOAD_BYTES = 100 * 1024 * 1024; // 100MB
const TICKET_SEC = Number(process.env.TICKET_SEC||'300')
export async function POST(request: Request) {
try {
const formData = await request.formData();
const template = formData.get("template");
const dataFile = formData.get("data");
const dataJson = formData.get("dataJson");
const TEST_SECRET = String(formData.get("secret")||"")
const TEST_PRIVATE_KEY_HEX = String(formData.get("private_key")||"")
// ตรวจสอบ Template
if (!(template instanceof File)) {
return NextResponse.json(
{
error: 'Missing DOCX file in field "template".',
},
{ status: 400 },
);
}
if (template.type !== DOCX_CONTENT_TYPE &&
!template.name.toLowerCase().endsWith(".docx") &&
template.size > MAX_UPLOAD_BYTES
) {
return NextResponse.json(
{
error: 'Field "template" must be a .docx file and not exceeds the 100MB size limit.',
},
{ status: 400 },
);
}
// อ่าน JSON
let templateData: DocumentTemplate;
// JSON ผิด syntax เป็นความผิดของฝั่งผู้เรียก — คืน 400 ไม่ใช่ 500
// (catch กลางท้ายฟังก์ชันสงวนไว้ให้ error ของระบบเท่านั้น)
try {
if (typeof dataJson === "string" && dataJson.length > 0) {
// กรณีส่ง JSON ผ่าน form field
templateData = JSON.parse(dataJson) as DocumentTemplate;
} else if (dataFile instanceof File) {
if (dataFile.size > MAX_UPLOAD_BYTES) {
return NextResponse.json(
{
error: 'Field "data" exceeds the 100MB size limit.',
},
{ status: 400 },
);
}
// กรณี upload data.json
const text = await dataFile.text();
templateData = JSON.parse(text) as DocumentTemplate;
} else {
return NextResponse.json(
{
error:
'Missing JSON data. Send either "data" file or "dataJson" field.',
},
{ status: 400 },
);
}
} catch (error) {
return NextResponse.json(
{
error: `Invalid JSON data (${error instanceof Error ? error.message : "parse error"
}).`,
},
{ status: 400 },
);
}
// JSON ต้องเป็น object
if (
templateData === null ||
typeof templateData !== "object" ||
Array.isArray(templateData)
) {
return NextResponse.json(
{
error: "JSON data must be an object.",
},
{ status: 400 },
);
}
// -----------------------------
// Template File → Buffer
// -----------------------------
let templateBuffer: Buffer = Buffer.from(
await template.arrayBuffer(),
);
const output = await applyTemplate(templateBuffer,templateData,TEST_SECRET,TEST_PRIVATE_KEY_HEX,TICKET_SEC);
// -----------------------------
// ส่ง DOCX กลับ
// -----------------------------
if(templateData?.info?.ticket_data)
return new NextResponse(output as BodyInit, {
status: 200,
headers: {
"Content-Type": DOCX_CONTENT_TYPE,
"Content-Disposition":
'attachment; filename="generated-document.docx"',
"Content-Length": String(output.length),
},
});
} catch (error) {
console.error(error);
return NextResponse.json(
{
error:
error instanceof Error
? error.message
: "Internal Server Error",
},
{ status: 500 },
);
}
}