2026-10-07 10:16:12 +07:00
|
|
|
import { NextResponse } from "next/server";
|
2026-10-22 22:33:14 +07:00
|
|
|
import { extractContentControls, extractBookmark, templateContentControls, setCustomDocProps, getDocProps,templateDynamicContents } from "@/lib/lib-tmpl";
|
|
|
|
|
import { verifyDataEd25519, verifyHmacSha256 } from "@/lib/lib-utils";
|
2026-10-07 10:16:12 +07:00
|
|
|
|
|
|
|
|
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
|
|
|
|
|
|
|
|
|
|
export async function POST(request: Request) {
|
|
|
|
|
try {
|
|
|
|
|
const formData = await request.formData();
|
|
|
|
|
const file = formData.get("file");
|
2026-10-22 22:33:14 +07:00
|
|
|
const TEST_SECRET = String(formData.get("secret"))??"";
|
|
|
|
|
const TEST_PUBLIC_KEY_HEX = String(formData.get("public_key"))??"";
|
|
|
|
|
|
2026-10-07 10:16:12 +07:00
|
|
|
|
|
|
|
|
if (!(file instanceof File)) {
|
|
|
|
|
return NextResponse.json(
|
|
|
|
|
{
|
|
|
|
|
success: false,
|
|
|
|
|
error: 'Missing DOCX file in field "file".',
|
|
|
|
|
},
|
|
|
|
|
{ status: 400 },
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (
|
|
|
|
|
file.type !== DOCX_CONTENT_TYPE &&
|
|
|
|
|
!file.name.toLowerCase().endsWith(".docx")
|
|
|
|
|
) {
|
|
|
|
|
return NextResponse.json(
|
|
|
|
|
{
|
|
|
|
|
success: false,
|
|
|
|
|
error: 'Field "file" must be a .docx file.',
|
|
|
|
|
},
|
|
|
|
|
{ status: 400 },
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (file.size > MAX_UPLOAD_BYTES) {
|
|
|
|
|
return NextResponse.json(
|
|
|
|
|
{
|
|
|
|
|
success: false,
|
|
|
|
|
error: 'Field "file" exceeds the 100MB size limit.',
|
|
|
|
|
},
|
|
|
|
|
{ status: 400 },
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// File → Buffer
|
|
|
|
|
const buffer = Buffer.from(await file.arrayBuffer());
|
|
|
|
|
|
|
|
|
|
// ใช้ lib-tmpl.ts เดิม
|
2026-10-22 22:33:14 +07:00
|
|
|
// const data = extractContentControls(buffer);
|
|
|
|
|
|
|
|
|
|
const cc = extractContentControls(buffer);
|
|
|
|
|
const bookmark = extractBookmark(buffer)
|
|
|
|
|
const properties = getDocProps(buffer)
|
|
|
|
|
let verify: Record<string, unknown> ={}
|
|
|
|
|
const ticket_data = String(properties?.custom?.ticket_data)
|
|
|
|
|
const HmacSha256 = String(properties?.custom?.HmacSha256)
|
|
|
|
|
const Ed25519 = String(properties?.custom?.Ed25519)
|
2026-10-07 10:16:12 +07:00
|
|
|
|
2026-10-22 22:33:14 +07:00
|
|
|
if(ticket_data){
|
|
|
|
|
const ticketDataObj= JSON.parse(ticket_data)
|
|
|
|
|
const unix_timestamp = Number(ticketDataObj.exp)*1000
|
|
|
|
|
if(unix_timestamp){
|
|
|
|
|
verify['exp'] = new Date(unix_timestamp).toLocaleString("th-TH", { timeZone: "Asia/Bangkok" })
|
|
|
|
|
}
|
|
|
|
|
if(TEST_SECRET && HmacSha256){
|
|
|
|
|
verify['verifyHmacSha256'] = verifyHmacSha256(ticket_data,TEST_SECRET,HmacSha256)
|
|
|
|
|
}
|
|
|
|
|
if(TEST_PUBLIC_KEY_HEX&& Ed25519){
|
|
|
|
|
verify["Ed25519"]=verifyDataEd25519(ticket_data,Ed25519,TEST_PUBLIC_KEY_HEX)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
let returnData : Record<string, unknown> = {
|
2026-10-07 10:16:12 +07:00
|
|
|
success: true,
|
2026-10-22 22:33:14 +07:00
|
|
|
cc,bookmark,properties,verify
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return NextResponse.json(returnData);
|
2026-10-07 10:16:12 +07:00
|
|
|
} catch (error) {
|
|
|
|
|
console.error(error);
|
|
|
|
|
|
|
|
|
|
return NextResponse.json(
|
|
|
|
|
{
|
|
|
|
|
success: false,
|
|
|
|
|
error:
|
|
|
|
|
error instanceof Error
|
|
|
|
|
? error.message
|
|
|
|
|
: "Internal Server Error",
|
|
|
|
|
},
|
|
|
|
|
{ status: 500 },
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|