Fix build time fail: export applyTemplate not compatible with Next.js(move to lib-tmpl), add var in .env.example
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
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
This commit is contained in:
parent
a3d14fd58d
commit
69d83ea493
4 changed files with 73 additions and 49 deletions
16
.env.example
16
.env.example
|
|
@ -6,6 +6,22 @@ DATABASE_URL=postgresql://postgres:postgres@127.0.0.1:54322/ecourt-note
|
|||
# ไม่ตั้ง/ว่าง = ปฏิเสธทุก request (fail closed) — ตั้งค่ายาวและสุ่ม เช่น 32+ ตัวอักษร
|
||||
# EXTERNAL_API_KEY=
|
||||
|
||||
# สำหรับตัวอย่างระบบนอกเรียกใช้ API ของ template
|
||||
DOCEDIT_BASE_URL=http://192.168.2.51:3000/docedit
|
||||
# สำหรับตัวอย่างระบบนอกเรียกใช้ API ของ template
|
||||
EXTERNAL_API_KEY='secret-api-key-1234'
|
||||
# ที่เก็บไฟล์เทมเพลต(docx)
|
||||
TMPL_PATH='./public/tmpl'
|
||||
|
||||
# Sign/Hash ที่ใช้ฝังในเอกสาร(docx)
|
||||
SECRET="super_secret"
|
||||
PRIVATE_KEY_HEX="8f2077b57b39399b6a7828f35da03a63ade8e3a36a6bf437b091a8bdb3d18ee1"
|
||||
PUBLIC_KEY_HEX="66b09b4a1b66271d5728c592c2409657e1c070a7bf371bf9f6ed457855270546"
|
||||
# Optional เวลาหมดอายุที่ฝังไว้ในข้อมูล (ticket_data)
|
||||
TICKET_SEC=300
|
||||
|
||||
|
||||
|
||||
# MinIO ของ API เอกสาร versioning (docs/PLAN-DOCVER.md §6) — ค่านี้ตรง docker-compose.yml
|
||||
# (MINIO_ENDPOINT/MINIO_ACCESS_KEY/MINIO_SECRET_KEY ไม่ตั้ง = ทุก endpoint ของกลุ่มนี้ตอบ 500)
|
||||
# หมายเหตุ: พอร์ตโฮสต์เป็น 19000 เพราะ 9000/9001 ชนกับโปรเจกต์อื่นในเครื่อง dev
|
||||
|
|
|
|||
|
|
@ -5,12 +5,12 @@
|
|||
import { NextRequest, NextResponse } from 'next/server';
|
||||
import {readFile} from 'fs/promises';
|
||||
import path from 'path';
|
||||
import { applyTemplate } from '../render/route'
|
||||
import { DocumentTemplate, templateContentControls, setCustomDocProps, templateDynamicContents } from "@/lib/lib-tmpl";
|
||||
import {DocumentTemplate,applyTemplate} from "@/lib/lib-tmpl";
|
||||
const EXTERNAL_API_KEY = process.env.EXTERNAL_API_KEY || 'secret-api-key-1234';
|
||||
const TMPL_PATH = process.env.TMPL_PATH || ''
|
||||
const PRIVATE_KEY_HEX = process.env.PRIVATE_KEY_HEX ||''
|
||||
const SECRET = process.env.SECRET || ''
|
||||
const TICKET_SEC = Number(process.env.TICKET_SEC||'300')
|
||||
|
||||
if (EXTERNAL_API_KEY || !TMPL_PATH || !PRIVATE_KEY_HEX || !SECRET)
|
||||
console.log("api/template/generate: require EXTERNAL_API_KEY, TMPL_PATH and PRIVATE_KEY_HEX")
|
||||
|
|
@ -61,7 +61,7 @@ export async function POST(request: NextRequest) {
|
|||
const tmplPath = path.join(TMPL_PATH, `${tid}.docx`);
|
||||
let templateBuffer: Buffer = await readFile(tmplPath)
|
||||
|
||||
const output = await applyTemplate(templateBuffer, templateData, SECRET, PRIVATE_KEY_HEX);
|
||||
const output = await applyTemplate(templateBuffer, templateData, SECRET, PRIVATE_KEY_HEX, TICKET_SEC);
|
||||
|
||||
// สร้าง ReadableStream ของไฟล์ .docx
|
||||
// ในโค้ดจริง ส่วนนี้จะดึง Binary Buffer จาก Library เช่น docx, docxtemplater หรืออ่านจาก File System
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
import { NextResponse } from "next/server";
|
||||
import { DocumentTemplate, templateContentControls, setCustomDocProps, templateDynamicContents } from "@/lib/lib-tmpl";
|
||||
import { generateHmacSha256, signEd25519, sortObjectKeys } from '@/lib/lib-utils';
|
||||
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 นี้เปิดกว้างให้ภายนอกยิงได้
|
||||
|
|
@ -102,7 +102,7 @@ export async function POST(request: Request) {
|
|||
let templateBuffer: Buffer = Buffer.from(
|
||||
await template.arrayBuffer(),
|
||||
);
|
||||
const output = await applyTemplate(templateBuffer,templateData,TEST_SECRET,TEST_PRIVATE_KEY_HEX);
|
||||
const output = await applyTemplate(templateBuffer,templateData,TEST_SECRET,TEST_PRIVATE_KEY_HEX,TICKET_SEC);
|
||||
|
||||
// -----------------------------
|
||||
// ส่ง DOCX กลับ
|
||||
|
|
@ -133,46 +133,3 @@ export async function POST(request: Request) {
|
|||
}
|
||||
}
|
||||
|
||||
export async function applyTemplate(templateBuffer:Buffer,templateData:DocumentTemplate,
|
||||
secret:string,private_key:string):Promise<Buffer<ArrayBufferLike>>
|
||||
{
|
||||
//Calculate hash
|
||||
const originalTicket_data = templateData?.info?.ticket_data //expect valid json string
|
||||
// console.log("templateData",templateData?.info?.ticket_data)
|
||||
if (originalTicket_data && typeof originalTicket_data === 'string') {
|
||||
try {
|
||||
const originalTicketObj = JSON.parse(originalTicket_data)//sort and add expire to ticket data
|
||||
if (originalTicketObj && typeof originalTicketObj === 'object') {
|
||||
console.log("Found valid ticket data: hash it")
|
||||
// คำนวณค่า expire (ปัจจุบัน + จำนวนวินาทีที่ต้องการให้หมดอายุ)
|
||||
// หาร 1000 เพราะต้องการหน่วยวินาที (sec) ไม่ใช่มิลลิวินาที (ms)
|
||||
const expireTimestamp = Math.floor(Date.now() / 1000) + TICKET_SEC;
|
||||
const fullObj = { ...originalTicketObj, exp: expireTimestamp };
|
||||
const sortedObj = sortObjectKeys(fullObj);
|
||||
|
||||
// แปลงกลับเป็น JSON string เพื่อนำไปใช้ส่งและทำ Hash
|
||||
const ticket_data = JSON.stringify(sortedObj);
|
||||
// console.log("check secret:",TEST_SECRET,TEST_PRIVATE_KEY_HEX,ticket_data)
|
||||
const HmacSha256 = generateHmacSha256(ticket_data, secret)
|
||||
|
||||
const Ed25519 = signEd25519(ticket_data, private_key)
|
||||
templateBuffer = setCustomDocProps(templateBuffer, { ticket_data, HmacSha256, Ed25519 })
|
||||
}
|
||||
} catch (error) { //just skip
|
||||
console.error("Set custom property fail",error);
|
||||
}
|
||||
}
|
||||
|
||||
// Apply Dynamic Content
|
||||
if (templateData?.dc && typeof templateData?.dc === "object") {
|
||||
console.log("Apply Dynamic Content")
|
||||
templateBuffer = Buffer.from(await templateDynamicContents(templateBuffer, templateData.dc))
|
||||
}
|
||||
// Apply Content Control
|
||||
if (templateData?.cc && typeof templateData?.cc === "object") {
|
||||
console.log("Apply Content Control")
|
||||
templateBuffer = templateContentControls(templateBuffer, templateData.cc);
|
||||
}
|
||||
return templateBuffer
|
||||
|
||||
}
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
import { generateHmacSha256, signEd25519, sortObjectKeys } from '@/lib/lib-utils';
|
||||
import createReport from 'docx-templates';
|
||||
import PizzaZip from "pizzip";
|
||||
import {
|
||||
|
|
@ -1261,3 +1262,53 @@ export const templateDynamicContents = async (template: Buffer, data: Record<str
|
|||
}
|
||||
});
|
||||
}
|
||||
|
||||
// สร้างเอกสารจาก เทมเพลต+data render และ generate ใช้งานเหมือนกัน
|
||||
// ยังไม่ค่อยแน่ใจว่าควรอยู่ใน library นี้หรือเปล่าเพราะมีการทำ hash/sign ticket_data
|
||||
// - ticket_data ต้องมีข้อมูลสมบูรณ์ {fid,tid,uid,uname}
|
||||
// - สร้าง signature/hash จาก ticket_data
|
||||
// - สร้าง Dynamic Content (docx-templates)
|
||||
// - เอาข้อมูลใส่ Content Control
|
||||
export async function applyTemplate(templateBuffer:Buffer,templateData:DocumentTemplate,
|
||||
secret:string,private_key:string,ttl:number):Promise<Buffer<ArrayBufferLike>>
|
||||
{
|
||||
//Calculate hash
|
||||
const originalTicket_data = templateData?.info?.ticket_data //expect valid json string
|
||||
// console.log("templateData",templateData?.info?.ticket_data)
|
||||
if (originalTicket_data && typeof originalTicket_data === 'string') {
|
||||
try {
|
||||
const originalTicketObj = JSON.parse(originalTicket_data)//sort and add expire to ticket data
|
||||
if (originalTicketObj && typeof originalTicketObj === 'object') {
|
||||
console.log("Found valid ticket data: hash it")
|
||||
// คำนวณค่า expire (ปัจจุบัน + จำนวนวินาทีที่ต้องการให้หมดอายุ)
|
||||
// หาร 1000 เพราะต้องการหน่วยวินาที (sec) ไม่ใช่มิลลิวินาที (ms)
|
||||
const expireTimestamp = Math.floor(Date.now() / 1000) + ttl;
|
||||
const fullObj = { ...originalTicketObj, exp: expireTimestamp };
|
||||
const sortedObj = sortObjectKeys(fullObj);
|
||||
|
||||
// แปลงกลับเป็น JSON string เพื่อนำไปใช้ส่งและทำ Hash
|
||||
const ticket_data = JSON.stringify(sortedObj);
|
||||
// console.log("check secret:",TEST_SECRET,TEST_PRIVATE_KEY_HEX,ticket_data)
|
||||
const HmacSha256 = generateHmacSha256(ticket_data, secret)
|
||||
|
||||
const Ed25519 = signEd25519(ticket_data, private_key)
|
||||
templateBuffer = setCustomDocProps(templateBuffer, { ticket_data, HmacSha256, Ed25519 })
|
||||
}
|
||||
} catch (error) { //just skip
|
||||
console.error("Set custom property fail",error);
|
||||
}
|
||||
}
|
||||
|
||||
// Apply Dynamic Content
|
||||
if (templateData?.dc && typeof templateData?.dc === "object") {
|
||||
console.log("Apply Dynamic Content")
|
||||
templateBuffer = Buffer.from(await templateDynamicContents(templateBuffer, templateData.dc))
|
||||
}
|
||||
// Apply Content Control
|
||||
if (templateData?.cc && typeof templateData?.cc === "object") {
|
||||
console.log("Apply Content Control")
|
||||
templateBuffer = templateContentControls(templateBuffer, templateData.cc);
|
||||
}
|
||||
return templateBuffer
|
||||
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue