Add new template format with hashing algorithm
All checks were successful
Deploy eCourt Note Web to Server / Validate Database Schema (Drizzle) (push) Successful in 4s
Deploy eCourt Note Web to Server / Build Web Docker Image (push) Successful in 1m18s
Deploy eCourt Note Web to Server / Deploy eCourt Note Web to Server (push) Successful in 6s
Deploy eCourt Note Web to Server / Notify Deployment Status (push) Successful in 0s
All checks were successful
Deploy eCourt Note Web to Server / Validate Database Schema (Drizzle) (push) Successful in 4s
Deploy eCourt Note Web to Server / Build Web Docker Image (push) Successful in 1m18s
Deploy eCourt Note Web to Server / Deploy eCourt Note Web to Server (push) Successful in 6s
Deploy eCourt Note Web to Server / Notify Deployment Status (push) Successful in 0s
This commit is contained in:
parent
280cc2da40
commit
ac6c0dc71e
17 changed files with 2157 additions and 609 deletions
|
|
@ -1,5 +1,6 @@
|
|||
import { NextResponse } from "next/server";
|
||||
import { extractContentControls } from "../lib-tmpl";
|
||||
import { extractContentControls, extractBookmark, templateContentControls, setCustomDocProps, getDocProps,templateDynamicContents } from "@/lib/lib-tmpl";
|
||||
import { verifyDataEd25519, verifyHmacSha256 } from "@/lib/lib-utils";
|
||||
|
||||
export const runtime = "nodejs";
|
||||
|
||||
|
|
@ -14,6 +15,9 @@ export async function POST(request: Request) {
|
|||
try {
|
||||
const formData = await request.formData();
|
||||
const file = formData.get("file");
|
||||
const TEST_SECRET = String(formData.get("secret"))??"";
|
||||
const TEST_PUBLIC_KEY_HEX = String(formData.get("public_key"))??"";
|
||||
|
||||
|
||||
if (!(file instanceof File)) {
|
||||
return NextResponse.json(
|
||||
|
|
@ -52,12 +56,35 @@ export async function POST(request: Request) {
|
|||
const buffer = Buffer.from(await file.arrayBuffer());
|
||||
|
||||
// ใช้ lib-tmpl.ts เดิม
|
||||
const data = extractContentControls(buffer);
|
||||
// const data = extractContentControls(buffer);
|
||||
|
||||
return NextResponse.json({
|
||||
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)
|
||||
|
||||
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> = {
|
||||
success: true,
|
||||
data,
|
||||
});
|
||||
cc,bookmark,properties,verify
|
||||
}
|
||||
|
||||
return NextResponse.json(returnData);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
import { NextResponse } from "next/server";
|
||||
import {
|
||||
templateContentControls,
|
||||
type TemplateData,
|
||||
} from "../lib-tmpl";
|
||||
import { DocumentTemplate, templateContentControls, setCustomDocProps, getDocProps, templateDynamicContents } from "@/lib/lib-tmpl";
|
||||
import { generateHmacSha256, verifyHmacSha256, signDataEd25519, verifyDataEd25519, sortObjectKeys } from '@/lib/lib-utils';
|
||||
|
||||
export const runtime = "nodejs";
|
||||
|
||||
|
|
@ -20,7 +18,13 @@ export async function POST(request: Request) {
|
|||
const template = formData.get("template");
|
||||
const dataFile = formData.get("data");
|
||||
const dataJson = formData.get("dataJson");
|
||||
const TEST_SECRET = (typeof formData.get("secret") === "string") ?
|
||||
String(formData.get("secret")) : "super_secret";
|
||||
const TEST_PRIVATE_KEY_HEX = (typeof formData.get("private_key") === "string") ?
|
||||
String(formData.get("private_key")) : "8f2077b57b39399b6a7828f35da03a63ade8e3a36a6bf437b091a8bdb3d18ee1";
|
||||
|
||||
console.log(`secret:${TEST_SECRET}`)
|
||||
console.log(`private:${TEST_PRIVATE_KEY_HEX}`)
|
||||
// -----------------------------
|
||||
// ตรวจสอบ Template
|
||||
// -----------------------------
|
||||
|
|
@ -62,14 +66,14 @@ export async function POST(request: Request) {
|
|||
// อ่าน JSON
|
||||
// -----------------------------
|
||||
|
||||
let data: TemplateData;
|
||||
let templateData: DocumentTemplate;
|
||||
|
||||
// JSON ผิด syntax เป็นความผิดของฝั่งผู้เรียก — คืน 400 ไม่ใช่ 500
|
||||
// (catch กลางท้ายฟังก์ชันสงวนไว้ให้ error ของระบบเท่านั้น)
|
||||
try {
|
||||
if (typeof dataJson === "string" && dataJson.length > 0) {
|
||||
// กรณีส่ง JSON ผ่าน form field
|
||||
data = JSON.parse(dataJson) as TemplateData;
|
||||
templateData = JSON.parse(dataJson) as DocumentTemplate;
|
||||
} else if (dataFile instanceof File) {
|
||||
if (dataFile.size > MAX_UPLOAD_BYTES) {
|
||||
return NextResponse.json(
|
||||
|
|
@ -84,7 +88,7 @@ export async function POST(request: Request) {
|
|||
// กรณี upload data.json
|
||||
const text = await dataFile.text();
|
||||
|
||||
data = JSON.parse(text) as TemplateData;
|
||||
templateData = JSON.parse(text) as DocumentTemplate;
|
||||
} else {
|
||||
return NextResponse.json(
|
||||
{
|
||||
|
|
@ -99,9 +103,8 @@ export async function POST(request: Request) {
|
|||
return NextResponse.json(
|
||||
{
|
||||
success: false,
|
||||
error: `Invalid JSON data (${
|
||||
error instanceof Error ? error.message : "parse error"
|
||||
}).`,
|
||||
error: `Invalid JSON data (${error instanceof Error ? error.message : "parse error"
|
||||
}).`,
|
||||
},
|
||||
{ status: 400 },
|
||||
);
|
||||
|
|
@ -109,9 +112,9 @@ export async function POST(request: Request) {
|
|||
|
||||
// JSON ต้องเป็น object
|
||||
if (
|
||||
data === null ||
|
||||
typeof data !== "object" ||
|
||||
Array.isArray(data)
|
||||
templateData === null ||
|
||||
typeof templateData !== "object" ||
|
||||
Array.isArray(templateData)
|
||||
) {
|
||||
return NextResponse.json(
|
||||
{
|
||||
|
|
@ -126,18 +129,52 @@ export async function POST(request: Request) {
|
|||
// Template File → Buffer
|
||||
// -----------------------------
|
||||
|
||||
const templateBuffer = Buffer.from(
|
||||
let templateBuffer: Buffer = Buffer.from(
|
||||
await template.arrayBuffer(),
|
||||
);
|
||||
|
||||
// -----------------------------
|
||||
// ใช้ lib-tmpl.ts เดิม
|
||||
// -----------------------------
|
||||
//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 output = templateContentControls(
|
||||
templateBuffer,
|
||||
data,
|
||||
);
|
||||
const ttlSeconds: number = 300
|
||||
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) + ttlSeconds;
|
||||
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, TEST_SECRET)
|
||||
|
||||
const Ed25519 = signDataEd25519(ticket_data, TEST_PRIVATE_KEY_HEX)
|
||||
templateBuffer = setCustomDocProps(templateBuffer, { ticket_data, HmacSha256, Ed25519 })
|
||||
}
|
||||
} catch (error) {
|
||||
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);
|
||||
}
|
||||
|
||||
const output = templateBuffer;
|
||||
|
||||
// -----------------------------
|
||||
// ส่ง DOCX กลับ
|
||||
|
|
|
|||
1263
lib/lib-tmpl.ts
Executable file
1263
lib/lib-tmpl.ts
Executable file
File diff suppressed because it is too large
Load diff
112
lib/lib-utils.ts
Normal file
112
lib/lib-utils.ts
Normal file
|
|
@ -0,0 +1,112 @@
|
|||
|
||||
import { createHmac, createPrivateKey, createPublicKey, timingSafeEqual, verify, sign } from 'node:crypto';
|
||||
/**
|
||||
* คำนวณ HMAC-SHA256 จาก string หรือ JSON Object
|
||||
*/
|
||||
export function generateHmacSha256(data: string | object, secretKey: string): string {
|
||||
const payload = typeof data === 'string' ? data : JSON.stringify(data);
|
||||
return createHmac('sha256', secretKey)
|
||||
.update(payload, 'utf8')
|
||||
.digest('hex');
|
||||
}
|
||||
|
||||
/**
|
||||
* ตรวจสอบความถูกต้องของ HMAC (ใช้ timingSafeEqual เพื่อป้องกัน Timing Attacks)
|
||||
*/
|
||||
export function verifyHmacSha256(data: string | object, secretKey: string, expectedHmac: string): boolean {
|
||||
const calculatedHmac = generateHmacSha256(data, secretKey);
|
||||
const a = Buffer.from(calculatedHmac, 'hex');
|
||||
const b = Buffer.from(expectedHmac, 'hex');
|
||||
|
||||
if (a.length !== b.length) {
|
||||
return false;
|
||||
}
|
||||
return timingSafeEqual(a, b);
|
||||
}
|
||||
// ==========================================
|
||||
// ตัวอย่างการใช้งาน
|
||||
// ==========================================
|
||||
/*
|
||||
const secret = 'my-super-secret-key';
|
||||
const jsonData = {
|
||||
userId: 12345,
|
||||
action: 'transfer',
|
||||
amount: 500
|
||||
};
|
||||
|
||||
// 1. สร้าง HMAC
|
||||
const hmacSignature = generateHmacSha256(jsonData, secret);
|
||||
console.log('HMAC Signature (Hex):', hmacSignature);
|
||||
|
||||
// 2. ตรวจสอบ HMAC
|
||||
const isValid = verifyHmacSha256(jsonData, secret, hmacSignature);
|
||||
console.log('Is Signature Valid?:', isValid); // true
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* ฟังก์ชันสำหรับลงนามดิจิทัล (Sign) ข้อมูลด้วย Ed25519 Private Key (Hex)
|
||||
* @param message ข้อความดิบที่ต้องการเซ็น (string หรือ Buffer)
|
||||
* @param privateKeyHex คีย์ลับในรูปแบบ Hex String (ความยาว 64 ตัวอักษร)
|
||||
* @returns ลายเซ็น (Signature) ในรูปแบบ Hex String (ความยาว 128 ตัวอักษร)
|
||||
*/
|
||||
export function signDataEd25519(message: string | Buffer, privateKeyHex: string): string {
|
||||
const rawKeyBuffer = Buffer.from(privateKeyHex, 'hex');
|
||||
// ตัดเอาเฉพาะ 32 bytes (Seed) กรณีที่ Hex ยาว 64 bytes (ที่ต่อกับ Public Key มา)
|
||||
const seed = rawKeyBuffer.length === 64 ? rawKeyBuffer.subarray(0, 32) : rawKeyBuffer;
|
||||
// PKCS#8 Header สำหรับ Ed25519 Private Key
|
||||
const pkcs8Header = Buffer.from('302e020100300506032b657004220420', 'hex');
|
||||
const pkcs8Buffer = Buffer.concat([pkcs8Header, seed]);
|
||||
|
||||
const privateKey = createPrivateKey({
|
||||
key: pkcs8Buffer,
|
||||
format: 'der',
|
||||
type: 'pkcs8',
|
||||
});
|
||||
|
||||
const dataBuffer = typeof message === 'string' ? Buffer.from(message, 'utf-8') : message;
|
||||
const signatureBuffer = sign(null, dataBuffer, privateKey);
|
||||
return signatureBuffer.toString('hex');
|
||||
}
|
||||
|
||||
/**
|
||||
* ฟังก์ชันสำหรับตรวจสอบลายเซ็น (Verify) ด้วย Ed25519 Public Key (Hex)
|
||||
* @param message ข้อความดิบที่ใช้ในการเซ็น (string หรือ Buffer)
|
||||
* @param signatureHex ลายเซ็นในรูปแบบ Hex String
|
||||
* @param publicKeyHex คีย์สาธารณะในรูปแบบ Hex String (ความยาว 64 ตัวอักษร)
|
||||
* @returns ผลลัพธ์ความถูกต้อง (true = ผ่าน, false = ไม่ผ่าน)
|
||||
*/
|
||||
export function verifyDataEd25519(message: string | Buffer, signatureHex: string, publicKeyHex: string): boolean {
|
||||
try {
|
||||
const rawPublicKey = Buffer.from(publicKeyHex, 'hex');
|
||||
|
||||
// SPKI Header สำหรับ Ed25519 Public Key (32 bytes)
|
||||
const spkiHeader = Buffer.from('302a300506032b6570032100', 'hex');
|
||||
const spkiBuffer = Buffer.concat([spkiHeader, rawPublicKey]);
|
||||
|
||||
const publicKey = createPublicKey({
|
||||
key: spkiBuffer,
|
||||
format: 'der',
|
||||
type: 'spki',
|
||||
});
|
||||
|
||||
const dataBuffer = typeof message === 'string' ? Buffer.from(message, 'utf-8') : message;
|
||||
const signatureBuffer = Buffer.from(signatureHex, 'hex');
|
||||
|
||||
return verify(null, dataBuffer, publicKey, signatureBuffer);
|
||||
} catch (error) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ฟังก์ชันสำหรับเรียงคีย์ของ Object ตามตัวอักษรเพื่อไม่ให้โครงสร้าง JSON เพี้ยน
|
||||
*/
|
||||
export function sortObjectKeys(obj: Record<string, unknown>): Record<string, unknown> {
|
||||
return Object.keys(obj)
|
||||
.sort()
|
||||
.reduce((result: Record<string, unknown>, key: string) => {
|
||||
result[key] = obj[key];
|
||||
return result;
|
||||
}, {});
|
||||
}
|
||||
|
|
@ -92,6 +92,20 @@ export const openApiSpec = {
|
|||
format: "binary",
|
||||
description: "ไฟล์ .docx ที่มี Content Control",
|
||||
},
|
||||
secret: {
|
||||
type: "string",
|
||||
description:
|
||||
"ค่า secret สำหรับ validate HMAC-SHA256 กับข้อมูลใน custom property" ,
|
||||
example:
|
||||
"super_secret",
|
||||
},
|
||||
public_key: {
|
||||
type: "string",
|
||||
description:
|
||||
"Public key ในรูปแบบ HEX <br/> openssl genpkey -algorithm ED25519 -out private.pem <br/> openssl pkey -in private.pem -outform DER | tail -c 32 | xxd -p -c 32 > private.hex <br/>openssl pkey -in private.pem -pubout -outform DER | tail -c 32 | xxd -p -c 32 > public.hex",
|
||||
example:
|
||||
"66b09b4a1b66271d5728c592c2409657e1c070a7bf371bf9f6ed457855270546",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
@ -140,9 +154,23 @@ export const openApiSpec = {
|
|||
dataJson: {
|
||||
type: "string",
|
||||
description:
|
||||
"JSON เป็น string ตรง ๆ (ทางเลือกแทนการแนบไฟล์ data)",
|
||||
"JSON เป็น string ตรง ๆ (ทางเลือกแทนการแนบไฟล์ data)<br/>{\"info:\"{\"ticket_data\":\"JSON String\"},\"dc:\"{..},\"dc:\"{\"blackyear\": \"๙๐/๒๕๖๙\",...}}",
|
||||
example:
|
||||
'{"notname":"คำสั่งเรียก ให้ทำคำให้การ","blackno":"คดีหมายเลขดำที่","blackyear":"๑๐/๒๕๖๙"}',
|
||||
'',
|
||||
},
|
||||
secret: {
|
||||
type: "string",
|
||||
description:
|
||||
"ค่า secret สำหรับทดสอบการทำ HMAC-SHA256",
|
||||
example:
|
||||
'super_secret',
|
||||
},
|
||||
private_key: {
|
||||
type: "string",
|
||||
description:
|
||||
"Private key ในรูปแบบ HEX <br/> openssl genpkey -algorithm ED25519 -out private.pem <br/> openssl pkey -in private.pem -outform DER | tail -c 32 | xxd -p -c 32 > private.hex <br/>openssl pkey -in private.pem -pubout -outform DER | tail -c 32 | xxd -p -c 32 > public.hex",
|
||||
example:
|
||||
'8f2077b57b39399b6a7828f35da03a63ade8e3a36a6bf437b091a8bdb3d18ee1',
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
|
|||
|
|
@ -37,6 +37,6 @@ export function middleware(req: NextRequest) {
|
|||
// ยังถูกคุมอยู่ดีเพราะ root page ส่งต่อให้ /note ซึ่ง middleware จับตามปกติ
|
||||
export const config = {
|
||||
matcher: [
|
||||
"/((?!note/login|api/note|api/template|api/documents|api/swagger|wordaddin|note/uploads|note/samples|note/swagger-ui|note/pdf\\.worker\\.min\\.mjs|_next|fonts|favicon\\.ico).+)",
|
||||
"/((?!note/login|api/note|api/template|api/documents|api/swagger|tmpl|wordaddin|note/uploads|note/samples|note/swagger-ui|note/pdf\\.worker\\.min\\.mjs|_next|fonts|favicon\\.ico).+)",
|
||||
],
|
||||
};
|
||||
|
|
|
|||
929
package-lock.json
generated
929
package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
|
@ -15,6 +15,7 @@
|
|||
"dependencies": {
|
||||
"@pdf-lib/fontkit": "^1.1.1",
|
||||
"@xmldom/xmldom": "^0.9.12",
|
||||
"docx-templates": "^4.15.0",
|
||||
"drizzle-orm": "^0.45.2",
|
||||
"lucide-react": "^1.42.0",
|
||||
"minio": "^8.0.7",
|
||||
|
|
@ -31,7 +32,7 @@
|
|||
"xpath": "^0.0.34"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^26.5.0",
|
||||
"@types/node": "^26.6.5",
|
||||
"@types/office-js": "^1.0.612",
|
||||
"@types/pngjs": "^6.0.5",
|
||||
"@types/react": "^18.3.31",
|
||||
|
|
|
|||
44
public/tmpl/T12_FD155_3s-long.json
Normal file
44
public/tmpl/T12_FD155_3s-long.json
Normal file
File diff suppressed because one or more lines are too long
BIN
public/tmpl/T12_X.docx
Executable file
BIN
public/tmpl/T12_X.docx
Executable file
Binary file not shown.
41
public/tmpl/T12_b2_IN.json
Normal file
41
public/tmpl/T12_b2_IN.json
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
{
|
||||
"info": {
|
||||
"ticket_data": "{\"fid\":\"T12_b2_IN_XXX\",\"tid\":\"T12\",\"uid\":\"oom\",\"uname\":\"สรวิชญ์\"}"
|
||||
},
|
||||
"dc": {
|
||||
"Interpleader": true,
|
||||
"docname": "",
|
||||
"docabbr": "(ต. ๑๒)",
|
||||
"blackno": "คดีหมายเลขดำที่",
|
||||
"redno": "คดีหมายเลขแดงที่",
|
||||
"court": "ศาลปกครองนครราชสีมา",
|
||||
"panels": [
|
||||
{
|
||||
"pname": "นายวิรุฬห์ จินตนะกุล",
|
||||
"pmname": "ตุลาการศาลปกครองกลาง",
|
||||
"pm": "ตุลาการเจ้าของสำนวน"
|
||||
},
|
||||
{
|
||||
"pname": "นายสัมฤทธิ์ อ่อนคำ",
|
||||
"pmname": "ตุลาการศาลปกครองสูงสุด"
|
||||
}
|
||||
],
|
||||
"pnameT":"",
|
||||
"pmnameT":"",
|
||||
"pmT":"",
|
||||
"acc_name":""
|
||||
|
||||
},
|
||||
"cc": {
|
||||
"blackyear": "๙๐/๒๕๖๙",
|
||||
"redyear": "",
|
||||
"curdate": "วันที่ เดือน สิงหาคม พุทธศักราช ๒๕๖๙",
|
||||
"accuser": "นายธีรพล ร่มเริง",
|
||||
"acc1": "ผู้ฟ้องคดี",
|
||||
"accused": "รร",
|
||||
"acc2": "ผู้ถูกฟ้องคดี",
|
||||
"interpleader": "สมชาย ใจดี",
|
||||
"acc3": "ผู้ร้องสอด",
|
||||
"detail1": "ตุลาการเจ้าของสำนวนได้พิจารณาคำฟ้องและเอกสารประกอบคำฟ้องแล้วเห็นว่าคดีนี้เป็นคดีพิพาทซึ่งไม่อยู่ในอำนาจศาลปกครองสูงสุด ผู้ฟ้องเป็นผู้มีสิทธิฟ้องคดี คดีนี้มีกฎหมายกำหนดให้ต้องดำเนินการแก้ไขความเดือดร้อนหรือเสียหายก่อนฟ้องคดี และผู้ฟ้องคดีได้ดำเนินการครบถ้วน โดยเป็นการยื่นฟ้องคดีภายในระยะเวลาการฟ้องคดีตาม พรบ. จัดตั้งศาลปกครองและวิธีพิจารณาคดีปกครองฯ คดีนี้มีค่าธรรมเนียมที่ต้องชำระ\nโดยผู้ฟ้องคดีได้ชำระค่าธรรมเนียมครบถ้วนแล้ว รวมทั้งคดีนี้ไม่เป็นการฟ้องซ้อน การดำเนินกระบวนพิจารณาซ้ำ และการฟ้องซ้ำ\n\nจึงมีคำสั่งรับคำฟ้องไว้พิจารณาและสั่งให้ส่งสำเนาคำฟ้องพร้อมทั้งสำเนาพยานหลักฐานไปยังผู้ถูกฟ้องคดีเพื่อให้ผู้ถูกฟ้องคดีทำคำให้การยื่นต่อศาลภายใน ๓๐ วัน นับแต่วันที่ได้รับคำสั่ง"
|
||||
}
|
||||
}
|
||||
51
public/tmpl/T12_b2_IN.out.json
Normal file
51
public/tmpl/T12_b2_IN.out.json
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
{
|
||||
"cc": {
|
||||
"blackyear": "๙๐/๒๕๖๙",
|
||||
"redyear": "",
|
||||
"curdate": "วันที่ เดือน สิงหาคม พุทธศักราช ๒๕๖๙",
|
||||
"accuser": "นายธีรพล ร่มเริง",
|
||||
"acc1": "ผู้ฟ้องคดี",
|
||||
"interpleader": "สมชาย ใจดี",
|
||||
"acc3": "ผู้ร้องสอด",
|
||||
"accused": "รร",
|
||||
"acc2": "ผู้ถูกฟ้องคดี",
|
||||
"detail1": "ตุลาการเจ้าของสำนวนได้พิจารณาคำฟ้องและเอกสารประกอบคำฟ้องแล้วเห็นว่าคดีนี้เป็นคดีพิพาทซึ่งไม่อยู่ในอำนาจศาลปกครองสูงสุด ผู้ฟ้องเป็นผู้มีสิทธิฟ้องคดี คดีนี้มีกฎหมายกำหนดให้ต้องดำเนินการแก้ไขความเดือดร้อนหรือเสียหายก่อนฟ้องคดี และผู้ฟ้องคดีได้ดำเนินการครบถ้วน โดยเป็นการยื่นฟ้องคดีภายในระยะเวลาการฟ้องคดีตาม พรบ. จัดตั้งศาลปกครองและวิธีพิจารณาคดีปกครองฯ คดีนี้มีค่าธรรมเนียมที่ต้องชำระ\nโดยผู้ฟ้องคดีได้ชำระค่าธรรมเนียมครบถ้วนแล้ว รวมทั้งคดีนี้ไม่เป็นการฟ้องซ้อน การดำเนินกระบวนพิจารณาซ้ำ และการฟ้องซ้ำ\n\nจึงมีคำสั่งรับคำฟ้องไว้พิจารณาและสั่งให้ส่งสำเนาคำฟ้องพร้อมทั้งสำเนาพยานหลักฐานไปยังผู้ถูกฟ้องคดีเพื่อให้ผู้ถูกฟ้องคดีทำคำให้การยื่นต่อศาลภายใน ๓๐ วัน นับแต่วันที่ได้รับคำสั่ง",
|
||||
"pmname1": "ตุลาการศาลปกครองกลาง",
|
||||
"pmname2": "ตุลาการศาลปกครองสูงสุด"
|
||||
},
|
||||
"bookmark": {
|
||||
"signature1": "",
|
||||
"signature2": ""
|
||||
},
|
||||
"properties": {
|
||||
"standard": {
|
||||
"title": " ",
|
||||
"subject": "",
|
||||
"creator": "oom",
|
||||
"keywords": "",
|
||||
"lastModifiedBy": "Sorawit Bholsithi",
|
||||
"revision": "61",
|
||||
"created": "2026-10-15T05:18:00.000Z",
|
||||
"modified": "2026-10-21T04:59:00.000Z"
|
||||
},
|
||||
"extended": {
|
||||
"template": "Normal.dotm",
|
||||
"totalTime": 345,
|
||||
"pages": 1,
|
||||
"words": 42,
|
||||
"characters": 493,
|
||||
"charactersWithSpaces": 534,
|
||||
"lines": 4,
|
||||
"paragraphs": 1,
|
||||
"application": "Microsoft Office Word",
|
||||
"appVersion": "16.0000",
|
||||
"company": "",
|
||||
"docSecurity": 0
|
||||
},
|
||||
"custom": {
|
||||
"ticket_data": "{\"exp\":1792667826,\"fid\":\"T12_b2_IN_XXX\",\"tid\":\"T12\",\"uid\":\"oom\",\"uname\":\"สรวิชญ์\"}",
|
||||
"HmacSha256": "791987c9274493db02561f4ec16b83d05c89766b54661da615e8e02db293d1d4",
|
||||
"Ed25519": "7ef8b0f2a03a52c49f8c348e3c392f448b71c8462ee860342b0e06a91b555f860cad5649c648dc466982017683f3718caff139c85e17e952ee1570ecfc021e08"
|
||||
}
|
||||
}
|
||||
}
|
||||
50
public/tmpl/TF_no_warning_P2.json
Executable file
50
public/tmpl/TF_no_warning_P2.json
Executable file
|
|
@ -0,0 +1,50 @@
|
|||
{
|
||||
"info": {
|
||||
"ticket_data": "{\"fid\":\"TF_no_warning_P2\",\"tid\":\"T_X\",\"uid\":\"oom\",\"uname\":\"สรวิชญ์\"}"
|
||||
},
|
||||
"dc": {
|
||||
"Interpleader": false,
|
||||
"CommandTo": true,
|
||||
"SendTo": false,
|
||||
"AdditionCommand" : true,
|
||||
"Warning":false,
|
||||
|
||||
"docname":"คำสั่งศาล",
|
||||
"doctype":"(ต. ๔๗.๑)",
|
||||
"blackno": "คดีหมายเลขดำที่",
|
||||
"redno": "คดีหมายเลขแดงที่",
|
||||
|
||||
"court": "ศาลปกครองนครราชสีมา",
|
||||
"doctitle":"คำสั่งศาลถึง",
|
||||
"pname":"สมหญิง",
|
||||
|
||||
"courtname":"ศาลปกครองสุพรรณบุรี",
|
||||
"courtno":"๓๓๓ หมู่ ๖ ตำบลสนามชัย อำเภอเมืองสุพรรณบุรี",
|
||||
"courttam":"จังหวัดสุพรรณบุรี ๗๒๐๐๐",
|
||||
"courttel":"กลุ่มออกหมาย โทรศัพท์ ๐๓๕ ๔๓๒ ๓๓๓ ต่อ ๑๐๐๑,๒๒๐๓",
|
||||
"courttel2":"สอบถามติดตามคดี ขอคำปรึกษาแนะนำ โทรสายด่วน ๑๓๕๕",
|
||||
"pname2":"ชื่อของ pname2"
|
||||
},
|
||||
"cc": {
|
||||
"blackyear": "๙๐/๒๕๖๙",
|
||||
"redyear": "xxx",
|
||||
"curdate": "วันที่ เดือน สิงหาคม พุทธศักราช ๒๕๖๙ ",
|
||||
"commto" : "นายสมชาย จรดปลายเท้า",
|
||||
"addrno" : "3/77 xxx yyy ทุ่งสองห้อง,หลักสี่, จังหวัดกรุงเทพฯ ",
|
||||
"groupname":"ผู้อำนวยการโรงเรียน",
|
||||
|
||||
"accuser": "นายธีรพล ร่มเริง",
|
||||
"acc1": "ผู้ฟ้องคดี",
|
||||
"accused": "รร",
|
||||
"acc2": "ผู้ถูกฟ้องคดี",
|
||||
"interpleader": "สมชาย ใจดี",
|
||||
"acc3": "ผู้ร้องสอด",
|
||||
"detail1": "ตุลาการเจ้าของสำนวนได้พิจารณาคำฟ้อง และเอกสารประกอบคำฟ้องแล้ว เห็นว่าคดีนี้เป็นคดีพิพาทซึ่งไม่อยู่ในอำนาจศาลปกครองสูงสุด\n\nผู้ฟ้องเป็นผู้มีสิทธิฟ้องคดี คดีนี้มีกฎหมายกำหนดให้ต้องดำเนินการแก้ไข ความเดือดร้อนหรือเสียหายก่อนฟ้องคดี ",
|
||||
"sendto" : "นายสมศักดิ์ ฟักมี",
|
||||
|
||||
"warning": "หากผู้ถูกฟ้องคดีไม่ปฏิบัติตามคำสั่งศาลภายในระยะเวลาที่กำหนด ศาลจะถือว่า ผู้ถูกฟ้องคดียอมรับข้อเท็จจริงตามข้อหาของผู้ฟ้องคดี",
|
||||
"detail2":"คำสั่งเพิ่มเติมคือ bla bla",
|
||||
"remarkhead":"หัวของ Remark",
|
||||
"remark":"This is remark Text bla bla "
|
||||
}
|
||||
}
|
||||
52
public/tmpl/TS_P2_IN.json
Executable file
52
public/tmpl/TS_P2_IN.json
Executable file
|
|
@ -0,0 +1,52 @@
|
|||
{
|
||||
"info": {
|
||||
"ticket_data": "{\"fid\":\"TS_P2_IN_YYY\",\"tid\":\"T_X\",\"uid\":\"oom\",\"uname\":\"สรวิชญ์\"}"
|
||||
},
|
||||
"dc": {
|
||||
"Interpleader": true,
|
||||
"CommandTo": true,
|
||||
"SendTo": false,
|
||||
"AdditionCommand" : true,
|
||||
"Warning":true,
|
||||
|
||||
"docname":"คำสั่งเรียกพยานบุคคล",
|
||||
"doctype":"(ต. ๖)",
|
||||
"blackno": "คดีหมายเลขดำที่",
|
||||
"redno": "คดีหมายเลขแดงที่",
|
||||
|
||||
"court": "ศาลปกครองนครราชสีมา",
|
||||
"doctitle":"คำสั่งถึง",
|
||||
"pname":"สมหญิง",
|
||||
|
||||
"courtname":"ศาลปกครองสุพรรณบุรี",
|
||||
"courtno":"๓๓๓ หมู่ ๖ ตำบลสนามชัย อำเภอเมืองสุพรรณบุรี",
|
||||
"courttam":"จังหวัดสุพรรณบุรี ๗๒๐๐๐",
|
||||
"courttel":"กลุ่มออกหมาย โทรศัพท์ ๐๓๕ ๔๓๒ ๓๓๓ ต่อ ๑๐๐๑,๒๒๐๓",
|
||||
"courttel2":"สอบถามติดตามคดี ขอคำปรึกษาแนะนำ โทรสายด่วน ๑๓๕๕",
|
||||
|
||||
"pname2":"ชื่อของ pname2",
|
||||
"remarkhead":"หัวของ Remark"
|
||||
},
|
||||
"cc": {
|
||||
"blackyear": "๙๐/๒๕๖๙",
|
||||
"redyear": "xxx",
|
||||
"curdate": "วันที่ เดือน สิงหาคม พุทธศักราช ๒๕๖๙ ",
|
||||
"commto" : "นายสมชาย จรดปลายเท้า",
|
||||
"addrno" : "3/77 xxx yyy ทุ่งสองห้อง,หลักสี่, จังหวัดกรุงเทพฯ ",
|
||||
"groupname":"ผู้อำนวยการโรงเรียน",
|
||||
|
||||
"accuser": "นายธีรพล ร่มเริง",
|
||||
"acc1": "ผู้ฟ้องคดี",
|
||||
"accused": "รร",
|
||||
"acc2": "ผู้ถูกฟ้องคดี",
|
||||
"interpleader": "สมชาย ใจดี",
|
||||
"acc3": "ผู้ร้องสอด",
|
||||
"detail1": "ตุลาการเจ้าของสำนวนได้พิจารณาคำฟ้อง และเอกสารประกอบคำฟ้องแล้ว เห็นว่าคดีนี้เป็นคดีพิพาทซึ่งไม่อยู่ในอำนาจศาลปกครองสูงสุด\n\nผู้ฟ้องเป็นผู้มีสิทธิฟ้องคดี คดีนี้มีกฎหมายกำหนดให้ต้องดำเนินการแก้ไข ความเดือดร้อนหรือเสียหายก่อนฟ้องคดี ",
|
||||
"sendto" : "นายสมศักดิ์ ฟักมี",
|
||||
|
||||
"warning": "หากผู้ถูกฟ้องคดีไม่ปฏิบัติตามคำสั่งศาลภายในระยะเวลาที่กำหนด ศาลจะถือว่า ผู้ถูกฟ้องคดียอมรับข้อเท็จจริงตามข้อหาของผู้ฟ้องคดี",
|
||||
"detail2":"คำสั่งเพิ่มเติมคือ bla bla",
|
||||
|
||||
"remark":"This is remark Text bla bla "
|
||||
}
|
||||
}
|
||||
BIN
public/tmpl/T_X.docx
Executable file
BIN
public/tmpl/T_X.docx
Executable file
Binary file not shown.
51
public/tmpl/T_no_warning.json
Executable file
51
public/tmpl/T_no_warning.json
Executable file
|
|
@ -0,0 +1,51 @@
|
|||
{
|
||||
"info": {
|
||||
"ticket_data": "{\"fid\":\"T3_P2_IN_XXX\",\"tid\":\"T_X\",\"uid\":\"oom\",\"uname\":\"สรวิชญ์\"}"
|
||||
},
|
||||
"dc": {
|
||||
"Interpleader": false,
|
||||
"CommandTo": true,
|
||||
"SendTo": true,
|
||||
"AdditionCommand" : false,
|
||||
"Warning":false,
|
||||
|
||||
"docname":"คำสั่งเรียก\nให้ทำคำให้การเพิ่มเติม",
|
||||
"doctype":"(ต. ๓)",
|
||||
"blackno": "คดีหมายเลขดำที่",
|
||||
"redno": "คดีหมายเลขแดงที่",
|
||||
|
||||
"court": "ศาลปกครองนครราชสีมา",
|
||||
"doctitle":"คำสั่งถึง",
|
||||
"pname":"สมหญิง",
|
||||
|
||||
"courtname":"ศาลปกครองสุพรรณบุรี",
|
||||
"courtno":"๓๓๓ หมู่ ๖ ตำบลสนามชัย อำเภอเมืองสุพรรณบุรี",
|
||||
"courttam":"จังหวัดสุพรรณบุรี ๗๒๐๐๐",
|
||||
"courttel":"กลุ่มออกหมาย โทรศัพท์ ๐๓๕ ๔๓๒ ๓๓๓ ต่อ ๑๐๐๑,๒๒๐๓",
|
||||
"courttel2":"สอบถามติดตามคดี ขอคำปรึกษาแนะนำ โทรสายด่วน ๑๓๕๕",
|
||||
"pname2":"ชื่อของ pname2"
|
||||
},
|
||||
"cc": {
|
||||
"docname":"คำสั่งเรียกพยานบุคคล",
|
||||
"blackyear": "๙๐/๒๕๖๙",
|
||||
"redyear": "xxx",
|
||||
"curdate": "วันที่ เดือน สิงหาคม พุทธศักราช ๒๕๖๙ ",
|
||||
"commto" : "นายสมชาย จรดปลายเท้า",
|
||||
"addrno" : "3/77 xxx yyy ทุ่งสองห้อง,หลักสี่, จังหวัดกรุงเทพฯ ",
|
||||
"groupname":"ผู้อำนวยการโรงเรียน",
|
||||
|
||||
"accuser": "นายธีรพล ร่มเริง",
|
||||
"acc1": "ผู้ฟ้องคดี",
|
||||
"accused": "รร",
|
||||
"acc2": "ผู้ถูกฟ้องคดี",
|
||||
"interpleader": "สมชาย ใจดี",
|
||||
"acc3": "ผู้ร้องสอด",
|
||||
"detail1": "ตุลาการเจ้าของสำนวนได้พิจารณาคำฟ้อง และเอกสารประกอบคำฟ้องแล้ว เห็นว่าคดีนี้เป็นคดีพิพาทซึ่งไม่อยู่ในอำนาจศาลปกครองสูงสุด\n\nผู้ฟ้องเป็นผู้มีสิทธิฟ้องคดี คดีนี้มีกฎหมายกำหนดให้ต้องดำเนินการแก้ไข ความเดือดร้อนหรือเสียหายก่อนฟ้องคดี ",
|
||||
"sendto" : "นายสมศักดิ์ ฟักมี",
|
||||
|
||||
"warning": "หากผู้ถูกฟ้องคดีไม่ปฏิบัติตามคำสั่งศาลภายในระยะเวลาที่กำหนด ศาลจะถือว่า ผู้ถูกฟ้องคดียอมรับข้อเท็จจริงตามข้อหาของผู้ฟ้องคดี",
|
||||
"detail2":"คำสั่งเพิ่มเติมคือ bla bla",
|
||||
"remarkhead":"หัวของ Remark",
|
||||
"remark":"This is remark Text bla bla "
|
||||
}
|
||||
}
|
||||
20
public/tmpl/index.html
Normal file
20
public/tmpl/index.html
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Document</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Template & Data</h1>
|
||||
|
||||
<ol>
|
||||
<li><a href="T12_X.docx">T12_X</a>
|
||||
(<a href="T12_b2_IN.json">T12_b2_IN</a>, <a href="T12_FD155_3s-long.json">T12_FD155_3s-long</a>)
|
||||
</li>
|
||||
<li><a href="T_X.docx">T_X</a>
|
||||
(<a href="TF_no_warning_P2.json">TF_no_warning_P2</a>, <a href="T_no_warning">T_no_warning</a>, <a href="T_no_warning">TS_P2_IN.json</a>)
|
||||
</li>
|
||||
</ol>
|
||||
</body>
|
||||
</html>
|
||||
Loading…
Add table
Add a link
Reference in a new issue