Some checks failed
Deploy eCourt Note Web to Server / Validate Database Schema (Drizzle) (push) Successful in 5s
Deploy eCourt Note Web to Server / Build Web Docker Image (push) Failing after 1m11s
Deploy eCourt Note Web to Server / Deploy eCourt Note Web to Server (push) Has been skipped
Deploy eCourt Note Web to Server / Notify Deployment Status (push) Failing after 0s
1138 lines
54 KiB
TypeScript
1138 lines
54 KiB
TypeScript
// OpenAPI 3.0 spec ของ API ทดสอบทั้งหมดของ judes-note — เส้นเดียวกับที่ Swagger UI
|
|
// แสดงผล: กลุ่ม template (DOCX Content Control) + N10 external API + docver
|
|
// (จัด version เอกสาร)
|
|
// route handler GET /api/swagger (app/api/swagger/route.ts) เป็นคนเดียวที่ใช้
|
|
// — inline เป็น JSON ลงใน HTML ตอน respond (ไม่มี endpoint spec แยก)
|
|
//
|
|
// URL ใน paths เป็น path เต็มรวม /api/… ตาม invariant #12 (URL จริง
|
|
// /docedit/api/… — servers เติมแค่ basePath ไม่มี /note ซ้อน)
|
|
const BASE_PATH = process.env.NEXT_PUBLIC_BASE_PATH ?? "";
|
|
|
|
const ANNOTATION_TYPE_ENUM = [
|
|
"HIGHLIGHT",
|
|
"DRAW",
|
|
"COMMENT",
|
|
"SHAPE",
|
|
"REDACTION",
|
|
"TEXTBOX",
|
|
"STAMP",
|
|
] as const;
|
|
|
|
export const openApiSpec = {
|
|
openapi: "3.0.3",
|
|
info: {
|
|
title: "Document Template & External API ของระบบ Judge's Note",
|
|
version: "1.0.0",
|
|
description:
|
|
"API ทดสอบของระบบ Judge's Note — กลุ่ม Template (DOCX Content Control), " +
|
|
"API ภายนอกสำหรับ backend e-Court (N10) และ API จัด version เอกสาร (docver)",
|
|
},
|
|
servers: [{ url: BASE_PATH, description: "origin เดียวกับหน้านี้ (basePath /docedit)" }],
|
|
tags: [
|
|
{
|
|
name: "template",
|
|
description:
|
|
"Next Generation of Template : เป็นเทมเพลต MS Word(docx) รองรับรูปแบบข้อมูลที่ซับซ้อนได้ ",
|
|
},
|
|
{
|
|
name: "documents",
|
|
description:
|
|
"จัด version เอกสาร (docver): เอกสารเดียวต่อ docId แต่ละ " +
|
|
"version เก็บ .docx + .pdf ใน MinIO พร้อมผู้อัปโหลด — auth ด้วย access_token " +
|
|
"ของกลุ่มนี้: ขอที่ POST /api/documents/token (ยืนยัน x-api-key กับ env " +
|
|
"DOCVER_API_KEY) แล้วแนบ Authorization: Bearer ทุก request — identity/template " +
|
|
"และ docId (= fid — fileId ของ e-Court, string) แกะจาก claims ของ token ทั้งหมด " +
|
|
"ไม่รับจาก URL/multipart form อีก",
|
|
},
|
|
{
|
|
name: "Judge's Note",
|
|
description:
|
|
"API ภายนอก (N10) — backend e-Court เรียกแบบ server-to-server ของ (Judge's Note)" +
|
|
"ดึง annotations/bookmarks ของเอกสารที่เคยเปิดในระบบนี้",
|
|
},
|
|
],
|
|
paths: {
|
|
"/api/template/test": {
|
|
get: {
|
|
tags: ["template"],
|
|
summary: "ตรวจสอบว่า service มีชีวิต",
|
|
responses: {
|
|
200: {
|
|
description: "service พร้อมใช้งาน",
|
|
content: {
|
|
"application/json": {
|
|
schema: {
|
|
type: "object",
|
|
properties: {
|
|
ok: { type: "boolean" },
|
|
service: { type: "string" },
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
"/api/template/render": {
|
|
post: {
|
|
tags: ["template"],
|
|
summary: "สร้างเอกสารจาก template (.docx) + data (JSON) ใช้สำหรับนักพัฒนาเท่านั้น",
|
|
description:
|
|
"แทนค่า JSON ลงใน Content Control ของ template แล้วคืนไฟล์ .docx ใหม่ " +
|
|
'(filename ตอน download = generated-document.docx) — ส่ง data ได้ 2 ทาง: ' +
|
|
'field "data" เป็นไฟล์ JSON หรือ field "dataJson" เป็น string ตรง ๆ (เลือกอย่างใดอย่างหนึ่ง)<br/>'+
|
|
'<i>สำหรับนักพัฒนา Template เท่านั้น ไม่ต้อง ใช้ API Key ต้องตั้งค่า secret และ private key เองไม่ใช้ของระบบของระบบ</i>',
|
|
requestBody: {
|
|
required: true,
|
|
content: {
|
|
"multipart/form-data": {
|
|
schema: {
|
|
type: "object",
|
|
required: ["template"],
|
|
properties: {
|
|
template: {
|
|
type: "string",
|
|
format: "binary",
|
|
description: "ไฟล์ .docx ที่เป็น template (มี Content Control)",
|
|
},
|
|
data: {
|
|
type: "string",
|
|
format: "binary",
|
|
description: 'ไฟล์ JSON เช่น { "blackno": "คดีหมายเลขดำที่", … }',
|
|
},
|
|
dataJson: {
|
|
type: "string",
|
|
description:
|
|
"JSON เป็น string ตรง ๆ (ทางเลือกแทนการแนบไฟล์ data)<br/>{\"info:\"{\"ticket_data\":\"JSON String\"},\"dc:\"{..},\"dc:\"{\"blackyear\": \"๙๐/๒๕๖๙\",...}}",
|
|
example:
|
|
'',
|
|
},
|
|
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',
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
responses: {
|
|
200: {
|
|
description: "ไฟล์ .docx ที่กรอกค่าแล้ว (binary)",
|
|
content: {
|
|
"application/vnd.openxmlformats-officedocument.wordprocessingml.document": {
|
|
schema: { type: "string", format: "binary" },
|
|
},
|
|
},
|
|
},
|
|
400: { $ref: "#/components/responses/BadRequest" },
|
|
500: { $ref: "#/components/responses/ServerError" },
|
|
},
|
|
},
|
|
},
|
|
"/api/template/extract": {
|
|
post: {
|
|
tags: ["template"],
|
|
summary: "ดึงค่า Content Control ทั้งหมดออกมาจากไฟล์ .docx ใช้สำหรับนักพัฒนาเท่านั้น",
|
|
description:
|
|
"อ่าน Content Control, bookmark , property ของ MS Word(docx) ออกมาแสดง" +
|
|
"ค่าของ Content Control จะเป็น Plain Text ไม่มี markup ซึ่งเหมาะกับการเอาไปใส่ในฐานข้อมูล"+
|
|
'<i>สำหรับนักพัฒนา Template เท่านั้น ไม่ต้อง ใช้ API Key ต้องตั้งค่า secret และ Public key เองไม่ใช้ของระบบของระบบ</i>',
|
|
requestBody: {
|
|
required: true,
|
|
content: {
|
|
"multipart/form-data": {
|
|
schema: {
|
|
type: "object",
|
|
required: ["file"],
|
|
properties: {
|
|
file: {
|
|
type: "string",
|
|
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",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
responses: {
|
|
200: {
|
|
description: "รายการแท็กพร้อมข้อความ",
|
|
content: {
|
|
"application/json": {
|
|
schema: { $ref: "#/components/schemas/ExtractResponse" },
|
|
},
|
|
},
|
|
},
|
|
400: { $ref: "#/components/responses/BadRequest" },
|
|
500: { $ref: "#/components/responses/ServerError" },
|
|
},
|
|
},
|
|
},
|
|
"/api/template/generate": {
|
|
post: {
|
|
tags: ["template"],
|
|
summary: "สร้างเอกสาร DOCX จากข้อมูล JSON และ template ที่กำหนด",
|
|
description:
|
|
"เทมเพลตมีแท็กแบบพิเศษ(รองรับตัวแปร ลูป เงื่อนไข), Content Control เมื่อรวมกับข้อมูล(JSON) "+
|
|
"จะได้เอกสาร รูปแบบฟอร์มที่กรอกค่าแล้วดึงค่ากลับมาใช้งานต่อได้เช่นอัปเดตฐานข้อมูล<br><ul>"+
|
|
"<li>JSON payload ต้องมี info.ticket_data เป็น JSON string รูปแบบถูกต้อวมีค่าครบ(tid, fid และ uid)</li>" +
|
|
"<li>ไฟล์เทมเพลต(tid)ในระบบ + ข้อมูล(payload) เพื่อสร้างเอกสาร </li>" +
|
|
"<li>ticket_data จะทำการ hash/sign แบบอายุสั้นแล้วนำไปฝังไว้ใน custom property เพื่อนำไปใช้ร่วมกับระบบ Versioning</li>"+
|
|
"</ul>",
|
|
security: [{ ApiKeyAuth: [] }],
|
|
requestBody: {
|
|
required: true,
|
|
content: {
|
|
"application/json": {
|
|
schema: {
|
|
type: "object",
|
|
required: ["info"],
|
|
properties: {
|
|
info: {
|
|
type: "object",
|
|
required: ["ticket_data"],
|
|
properties: {
|
|
ticket_data: {
|
|
type: "string",
|
|
description:
|
|
"JSON string ที่ต้อง parse ได้ และต้องมี tid, fid และ uid",
|
|
example:
|
|
'{"tid":"T12","fid":"DOC-2569-0001","uid":"123"}',
|
|
},
|
|
},
|
|
additionalProperties: true,
|
|
},
|
|
},
|
|
additionalProperties: true,
|
|
},
|
|
example: {
|
|
"info": {
|
|
"ticket_data": "{\"fid\":\"T12_b2_IN\",\"tid\":\"T12_X\",\"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จึงมีคำสั่งรับคำฟ้องไว้พิจารณาและสั่งให้ส่งสำเนาคำฟ้องพร้อมทั้งสำเนาพยานหลักฐานไปยังผู้ถูกฟ้องคดีเพื่อให้ผู้ถูกฟ้องคดีทำคำให้การยื่นต่อศาลภายใน ๓๐ วัน นับแต่วันที่ได้รับคำสั่ง"
|
|
}
|
|
},
|
|
},
|
|
},
|
|
},
|
|
responses: {
|
|
200: {
|
|
description: "ไฟล์ DOCX ที่สร้างสำเร็จ",
|
|
headers: {
|
|
"Content-Disposition": {
|
|
description: "ชื่อไฟล์สำหรับดาวน์โหลด",
|
|
schema: { type: "string" },
|
|
},
|
|
"Cache-Control": {
|
|
description: "ป้องกันการ cache response",
|
|
schema: { type: "string", example: "no-store, max-age=0" },
|
|
},
|
|
},
|
|
content: {
|
|
"application/vnd.openxmlformats-officedocument.wordprocessingml.document": {
|
|
schema: { type: "string", format: "binary" },
|
|
},
|
|
},
|
|
},
|
|
400: {
|
|
description:
|
|
"JSON ไม่มี info.ticket_data, ticket_data ไม่ใช่ string, JSON string ไม่ถูกต้อง หรือไม่มี tid/fid/uid",
|
|
content: {
|
|
"application/json": {
|
|
schema: { $ref: "#/components/schemas/Error" },
|
|
examples: {
|
|
missingTicketData: {
|
|
value: {
|
|
error:
|
|
"template/generate json.info.ticket_data not avaliable",
|
|
},
|
|
},
|
|
missingIdentifiers: {
|
|
value: {
|
|
error:
|
|
"template/generate require json.info.ticket_data.(fid|tid|uid)",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
401: {
|
|
description: "ไม่ได้ส่ง x-api-key หรือ API key ไม่ถูกต้อง",
|
|
content: {
|
|
"application/json": {
|
|
schema: { $ref: "#/components/schemas/Error" },
|
|
example: { error: "Unauthorized: Invalid API Key" },
|
|
},
|
|
},
|
|
},
|
|
404: {
|
|
description: "ไม่สามารถดึงข้อมูลขึ้นมาได้",
|
|
content: {
|
|
"application/json": {
|
|
schema: { $ref: "#/components/schemas/Error" },
|
|
example: { error: "NotFound: Invalid template ID or incorrect storage path" },
|
|
},
|
|
},
|
|
},
|
|
500: {
|
|
description:
|
|
"เกิดข้อผิดพลาดระหว่างอ่าน template หรือสร้างเอกสาร",
|
|
content: {
|
|
"application/json": {
|
|
schema: { $ref: "#/components/schemas/Error" },
|
|
example: { error: "Failed to generate document payload" },
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
"/api/template/download-docx": {
|
|
get: {
|
|
tags: ["template"],
|
|
summary: "ดาวน์โหลดเอกสาร DOCX ผ่าน Query Parameters (GET) ใช้เพื่อเป็นตัวอย่างจะถูกเอาออกในภายหลัง",
|
|
description:
|
|
"รับค่า fid, tid, uid, uname จาก Query Parameter อ่านข้อมูล JSON ประจำไฟล์ fid จาก TMPL_PATH แล้วส่งไปประมวลผลต่อที่ /api/template/generate เพื่อสร้างและคืนค่าเป็นไฟล์ DOCX stream ใช้เพื่อเป็นตัวอย่างสำหรับนักพัฒนา ต้องนำออกก่อน production",
|
|
parameters: [
|
|
{
|
|
name: "fid",
|
|
in: "query",
|
|
required: true,
|
|
schema: { type: "string" },
|
|
description: "ID เอกสาร (File ID)",
|
|
example: "T_no_warning",
|
|
},
|
|
{
|
|
name: "tid",
|
|
in: "query",
|
|
required: true,
|
|
schema: { type: "string" },
|
|
description: "ID เทมเพลต (Template ID)",
|
|
example: "T_X",
|
|
},
|
|
{
|
|
name: "uid",
|
|
in: "query",
|
|
required: true,
|
|
schema: { type: "string" },
|
|
description: "รหัสผู้ใช้งาน (User ID)",
|
|
example: "123",
|
|
},
|
|
{
|
|
name: "uname",
|
|
in: "query",
|
|
required: true,
|
|
schema: { type: "string" },
|
|
description: "ชื่อผู้ใช้งาน (User Name)",
|
|
example: "สมชาย ใจดี",
|
|
},
|
|
],
|
|
responses: {
|
|
200: {
|
|
description: "ไฟล์ DOCX stream ที่สร้างสำเร็จ",
|
|
headers: {
|
|
"Content-Disposition": {
|
|
description: "ชื่อไฟล์สำหรับการดาวน์โหลด (attachment)",
|
|
schema: { type: "string", example: 'attachment; filename="T_no_warning_123_xxx.docx"' },
|
|
},
|
|
"Cache-Control": {
|
|
description: "การควบคุม Cache (no-store)",
|
|
schema: { type: "string", example: "no-store, max-age=0" },
|
|
},
|
|
},
|
|
content: {
|
|
"application/vnd.openxmlformats-officedocument.wordprocessingml.document": {
|
|
schema: { type: "string", format: "binary" },
|
|
},
|
|
},
|
|
},
|
|
400: {
|
|
description: "พารามิเตอร์ไม่ครบถ้วน หรือรูปแบบไฟล์ JSON ไม่ถูกต้อง",
|
|
content: {
|
|
"text/plain": {
|
|
schema: { type: "string" },
|
|
examples: {
|
|
missingParams: { value: "Missing fid,tid,uid or uname" },
|
|
invalidData: { value: "Invalid data format" },
|
|
},
|
|
},
|
|
},
|
|
},
|
|
404: {
|
|
description: "ไม่พบไฟล์ข้อมูล JSON ใน TMPL_PATH",
|
|
content: {
|
|
"application/json": {
|
|
schema: { $ref: "#/components/schemas/Error" },
|
|
example: { error: "Data not found: /path/to/TMPL_PATH/T_no_warning.json" },
|
|
},
|
|
},
|
|
},
|
|
502: {
|
|
description: "เกิดข้อผิดพลาดในการเรียกไปยังบริการ Docedit /api/template/generate",
|
|
content: {
|
|
"text/plain": {
|
|
schema: { type: "string" },
|
|
example: "Error from docedit generate: Expected docx stream but received text/html (Status 500)",
|
|
},
|
|
},
|
|
},
|
|
500: {
|
|
description: "เกิดข้อผิดพลาดภายในเซิร์ฟเวอร์",
|
|
content: {
|
|
"text/plain": {
|
|
schema: { type: "string" },
|
|
example: "Internal Server Error",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
post: {
|
|
tags: ["template"],
|
|
summary: "ดาวน์โหลดเอกสาร DOCX ผ่าน HTML Form / Multipart Form Data (POST) เป็นตัวอย่างเท่านั้น จะถูกเอาออกในภายหลัง",
|
|
description:
|
|
"รับค่า fid, tid, uid, uname จาก Multipart Form Data หรือ Form-urlencoded อ่านข้อมูล JSON และประมวลผลสร้างไฟล์ DOCX stream คืนกลับไป "+
|
|
"จะเรียก generate ในเบื้องหลัง ใช้เพื่อเป็นตัวอย่างสำหรับนักพัฒนา ต้องนำออกก่อน production",
|
|
requestBody: {
|
|
required: true,
|
|
content: {
|
|
"multipart/form-data": {
|
|
schema: {
|
|
type: "object",
|
|
required: ["fid", "tid", "uid", "uname"],
|
|
properties: {
|
|
fid: { type: "string", description: "ID เอกสาร (File ID)",example:"T12_b2_IN" },
|
|
tid: { type: "string", description: "ID เทมเพลต (Template ID)",example:"T12_X" },
|
|
uid: { type: "string", description: "รหัสผู้ใช้งาน (User ID)", example:"john" },
|
|
uname: { type: "string", description: "ชื่อผู้ใช้งาน (User Name)", example:"John Doe"},
|
|
},
|
|
},
|
|
},
|
|
"application/x-www-form-urlencoded": {
|
|
schema: {
|
|
type: "object",
|
|
required: ["fid", "tid", "uid", "uname"],
|
|
properties: {
|
|
fid: { type: "string", description: "ID เอกสาร (File ID)" },
|
|
tid: { type: "string", description: "ID เทมเพลต (Template ID)" },
|
|
uid: { type: "string", description: "รหัสผู้ใช้งาน (User ID)" },
|
|
uname: { type: "string", description: "ชื่อผู้ใช้งาน (User Name)" },
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
responses: {
|
|
200: {
|
|
description: "ไฟล์ DOCX stream ที่สร้างสำเร็จ",
|
|
headers: {
|
|
"Content-Disposition": {
|
|
description: "ชื่อไฟล์สำหรับการดาวน์โหลด (attachment)",
|
|
schema: { type: "string", example: 'attachment; filename="T12_b2_IN_john_xxx.docx"' },
|
|
},
|
|
"Cache-Control": {
|
|
description: "การควบคุม Cache (no-store)",
|
|
schema: { type: "string", example: "no-store, max-age=0" },
|
|
},
|
|
},
|
|
content: {
|
|
"application/vnd.openxmlformats-officedocument.wordprocessingml.document": {
|
|
schema: { type: "string", format: "binary" },
|
|
},
|
|
},
|
|
},
|
|
400: {
|
|
description: "พารามิเตอร์ไม่ครบถ้วน หรือรูปแบบไฟล์ JSON ไม่ถูกต้อง",
|
|
content: {
|
|
"text/plain": {
|
|
schema: { type: "string" },
|
|
examples: {
|
|
missingParams: { value: "Missing fid,tid,uid or uname" },
|
|
invalidData: { value: "Invalid data format" },
|
|
},
|
|
},
|
|
},
|
|
},
|
|
404: {
|
|
description: "ไม่พบไฟล์ข้อมูล JSON ใน TMPL_PATH",
|
|
content: {
|
|
"application/json": {
|
|
schema: { $ref: "#/components/schemas/Error" },
|
|
example: { error: "Data not found: /path/to/TMPL_PATH/T12_b2_IN.json" },
|
|
},
|
|
},
|
|
},
|
|
502: {
|
|
description: "เกิดข้อผิดพลาดในการเรียกไปยังบริการ Docedit /api/template/generate",
|
|
content: {
|
|
"text/plain": {
|
|
schema: { type: "string" },
|
|
example: "Error from docedit generate: Expected docx stream but received text/html (Status 500)",
|
|
},
|
|
},
|
|
},
|
|
500: {
|
|
description: "เกิดข้อผิดพลาดภายในเซิร์ฟเวอร์",
|
|
content: {
|
|
"text/plain": {
|
|
schema: { type: "string" },
|
|
example: "Internal Server Error",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
"/api/note/external/documents": {
|
|
post: {
|
|
tags: ["external"],
|
|
summary: "ดึง annotations/bookmarks ของเอกสาร (N10)",
|
|
description:
|
|
"ถามด้วย fileId (เอกสารเดียว — id ของ e-Court ที่ลิงก์มา) และ/หรือ typeId " +
|
|
"(ทุกเอกสารในกลุ่ม) — ส่งทั้งคู่ = AND เหลือเฉพาะเอกสารเดียวที่ตรงทั้งสองเงื่อนไข " +
|
|
"· response รูปเดียวเสมอ { documents: […] } (กลุ่มว่าง = array ว่าง) " +
|
|
"· read-only: snapshot ข้อมูล ณ ตอนเรียก · แต่ละรายการมี url deep link กลับหน้าอ่านของระบบนี้",
|
|
security: [{ ApiKeyAuth: [] }],
|
|
requestBody: {
|
|
required: true,
|
|
content: {
|
|
"application/json": {
|
|
schema: { $ref: "#/components/schemas/ExternalQuery" },
|
|
examples: {
|
|
"ถามด้วย fileId (เอกสารเดียว)": {
|
|
value: { fileId: "DOC-2569-0001" },
|
|
},
|
|
"ทุกเอกสารในกลุ่ม": {
|
|
value: { typeId: "GROUP-01" },
|
|
},
|
|
"กลุ่ม + เฉพาะ bookmark ของคนใดคนหนึ่ง": {
|
|
value: {
|
|
typeId: "GROUP-01",
|
|
filter: { type: "bookmarks", userId: "u0001" },
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
responses: {
|
|
200: {
|
|
description: "bundle ของเอกสารที่ตรงเงื่อนไข",
|
|
content: {
|
|
"application/json": {
|
|
schema: { $ref: "#/components/schemas/DocumentsResponse" },
|
|
},
|
|
},
|
|
},
|
|
400: { $ref: "#/components/responses/BadRequest" },
|
|
401: {
|
|
description: "ไม่ได้ส่ง header x-api-key หรือค่าไม่ถูกต้อง",
|
|
content: {
|
|
"application/json": {
|
|
schema: { $ref: "#/components/schemas/Error" },
|
|
},
|
|
},
|
|
},
|
|
404: {
|
|
description: "ไม่พบเอกสาร (ยังไม่เคยเปิดในระบบ หรือไม่อยู่ในกลุ่ม typeId ที่ระบุ)",
|
|
content: {
|
|
"application/json": {
|
|
schema: { $ref: "#/components/schemas/Error" },
|
|
},
|
|
},
|
|
},
|
|
500: { $ref: "#/components/responses/ServerError" },
|
|
},
|
|
},
|
|
},
|
|
"/api/documents/token": {
|
|
post: {
|
|
tags: ["documents"],
|
|
summary: "ขอ access_token ของกลุ่ม documents (docver)",
|
|
description:
|
|
"ออก access_token ใช้กับ endpoint อื่นของกลุ่ม documents เท่านั้น — token เป็น " +
|
|
"JWT HS256 ฝัง claims {userid, username, fid, template} ตามที่ส่งมา อายุตาม env " +
|
|
"DOCVER_TOKEN_TTL_SECONDS (default 3600 วินาที) · identity (userId/userName), " +
|
|
"templateId และ docId (= fid) ตอนทำงานแกะจาก claims ของ token นี้ — " +
|
|
"fid จึงบังคับ",
|
|
security: [{ ApiKeyAuth: [] }],
|
|
requestBody: {
|
|
required: true,
|
|
content: {
|
|
"application/json": {
|
|
schema: { $ref: "#/components/schemas/TokenRequest" },
|
|
examples: {
|
|
"ครบทุก claim": {
|
|
value: {
|
|
userid: "123",
|
|
username: "Sorawit Bholsithi",
|
|
fid: "3",
|
|
template: "T12",
|
|
},
|
|
},
|
|
"ขั้นต่ำ (template เป็นทางเลือก)": {
|
|
value: { userid: "123", username: "Sorawit Bholsithi", fid: "3" },
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
responses: {
|
|
200: {
|
|
description: "access_token พร้อมใช้ (แนบเป็น Authorization: Bearer ทุก request ถัดไป)",
|
|
content: {
|
|
"application/json": {
|
|
schema: { $ref: "#/components/schemas/TokenResponse" },
|
|
},
|
|
},
|
|
},
|
|
400: { $ref: "#/components/responses/BadRequest" },
|
|
401: {
|
|
description: "ไม่ได้ส่ง header x-api-key หรือค่าไม่ถูกต้อง (env DOCVER_API_KEY)",
|
|
content: {
|
|
"application/json": {
|
|
schema: { $ref: "#/components/schemas/Error" },
|
|
},
|
|
},
|
|
},
|
|
500: { $ref: "#/components/responses/ServerError" },
|
|
},
|
|
},
|
|
},
|
|
"/api/documents/versions": {
|
|
post: {
|
|
tags: ["documents"],
|
|
summary: "อัปโหลด version ใหม่ของเอกสาร (docx + pdf)",
|
|
description:
|
|
"เอกสารถูกระบุด้วย fid ใน Bearer token (docId = fid) — สร้างเอกสาร " +
|
|
"อัตโนมัติเมื่อ docId ยังไม่เคยมี แล้วเก็บไฟล์ .docx (+ .pdf ถ้าแนบมา) ลง MinIO " +
|
|
"พร้อมผู้อัปโหลด — versionNumber ที่ server จ่ายคือค่าจริงเสมอ (client ที่เลขไม่ตรง " +
|
|
"ต้องแก้ XML part ของตัวเอง) · pdf เป็น optional: ไม่แนบ/ไฟล์เปล่า = version นี้ไม่มี " +
|
|
"สำเนา PDF (ดู hasPdf ใน response) · ผู้อัปโหลด (userId/userName) และ templateId " +
|
|
"แกะจาก Bearer token ไม่รับใน form (ส่ง field เหล่านั้นมา = 400)",
|
|
security: [{ DocverBearer: [] }],
|
|
requestBody: {
|
|
required: true,
|
|
content: {
|
|
"multipart/form-data": {
|
|
schema: {
|
|
type: "object",
|
|
required: ["file"],
|
|
properties: {
|
|
file: {
|
|
type: "string",
|
|
format: "binary",
|
|
description: "ไฟล์ .docx ของ version นี้ (บังคับ)",
|
|
},
|
|
pdf: {
|
|
type: "string",
|
|
format: "binary",
|
|
description:
|
|
"ไฟล์ .pdf ของ version นี้ (optional — ไม่แนบหรือไฟล์เปล่า = ไม่มี)",
|
|
},
|
|
clientVersionId: {
|
|
type: "string",
|
|
description: "GUID ที่ client generate ก่อนเรียก (เก็บไว้สืบสวน ไม่ใช่ identity)",
|
|
},
|
|
fields: {
|
|
type: "string",
|
|
description:
|
|
'JSON string ของค่า Content Control เช่น {"blackno":"คดีหมายเลขดำที่"}',
|
|
},
|
|
message: {
|
|
type: "string",
|
|
description: "commit message ของ version (≤ 2048 ตัวอักษร)",
|
|
},
|
|
fileName: {
|
|
type: "string",
|
|
description:
|
|
"ชื่อไฟล์สำหรับแสดงตอนดาวน์โหลด (ไม่ส่ง = ใช้ชื่อไฟล์ที่แนบ)",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
responses: {
|
|
200: {
|
|
description: "version ถูกบันทึกแล้ว",
|
|
content: {
|
|
"application/json": {
|
|
schema: { $ref: "#/components/schemas/UploadVersionResult" },
|
|
},
|
|
},
|
|
},
|
|
400: { $ref: "#/components/responses/BadRequest" },
|
|
401: { $ref: "#/components/responses/DocverUnauthorized" },
|
|
413: {
|
|
description: "ไฟล์แนบใหญ่เกิน 100MB",
|
|
content: {
|
|
"application/json": {
|
|
schema: { $ref: "#/components/schemas/Error" },
|
|
},
|
|
},
|
|
},
|
|
500: { $ref: "#/components/responses/ServerError" },
|
|
},
|
|
},
|
|
get: {
|
|
tags: ["documents"],
|
|
summary: "รายการ version ทั้งหมดของเอกสาร (ใหม่ → เก่า)",
|
|
description:
|
|
"เอกสารถูกระบุด้วย fid ใน Bearer token (docId = fid) — version ทุกตัว " +
|
|
"ของเอกสารนั้น พร้อมผู้อัปโหลด (userId/userName), เวลา, commit message, ค่า fields " +
|
|
"และ hasPdf — ใช้เขียน UI version history ของ add-in",
|
|
security: [{ DocverBearer: [] }],
|
|
responses: {
|
|
200: {
|
|
description: "รายการ version เรียงใหม่ → เก่า",
|
|
content: {
|
|
"application/json": {
|
|
schema: {
|
|
type: "array",
|
|
items: { $ref: "#/components/schemas/VersionRecord" },
|
|
},
|
|
},
|
|
},
|
|
},
|
|
400: { $ref: "#/components/responses/BadRequest" },
|
|
401: { $ref: "#/components/responses/DocverUnauthorized" },
|
|
404: {
|
|
description: "ไม่พบเอกสารนี้ในระบบ (ยังไม่เคยอัปโหลด version)",
|
|
content: {
|
|
"application/json": {
|
|
schema: { $ref: "#/components/schemas/Error" },
|
|
},
|
|
},
|
|
},
|
|
500: { $ref: "#/components/responses/ServerError" },
|
|
},
|
|
},
|
|
},
|
|
"/api/documents/versions/{versionId}/download-link": {
|
|
get: {
|
|
tags: ["documents"],
|
|
summary: "ขอ presigned URL ดาวน์โหลดไฟล์ของ version (docx/pdf)",
|
|
description:
|
|
"เอกสารถูกระบุด้วย fid ใน Bearer token (docId = fid) · คืน URL อายุสั้น " +
|
|
"(env DOCVER_LINK_TTL_SECONDS, default 900 วินาที) ให้เปิดในเบราว์เซอร์/Word ของ " +
|
|
"ผู้ใช้ — URL อ้าง endpoint MinIO ตรง ๆ จึงต้องเป็นโฮสต์ที่ผู้ใช้เข้าถึงได้ · ขอ pdf " +
|
|
"ใน version ที่ไม่มีสำเนา = 404",
|
|
security: [{ DocverBearer: [] }],
|
|
parameters: [
|
|
{
|
|
name: "versionId",
|
|
in: "path",
|
|
required: true,
|
|
schema: { type: "string", format: "uuid" },
|
|
description: "id ของ version (field versionId ใน VersionRecord)",
|
|
},
|
|
{
|
|
name: "format",
|
|
in: "query",
|
|
required: false,
|
|
schema: { type: "string", enum: ["docx", "pdf"], default: "docx" },
|
|
description: "ชนิดไฟล์ที่จะดาวน์โหลด (ไม่ส่ง = docx)",
|
|
},
|
|
],
|
|
responses: {
|
|
200: {
|
|
description: "presigned URL พร้อมเวลาหมดอายุ",
|
|
content: {
|
|
"application/json": {
|
|
schema: { $ref: "#/components/schemas/DownloadLink" },
|
|
},
|
|
},
|
|
},
|
|
400: { $ref: "#/components/responses/BadRequest" },
|
|
401: { $ref: "#/components/responses/DocverUnauthorized" },
|
|
404: {
|
|
description:
|
|
"ไม่พบ version นี้ของเอกสาร หรือขอ format=pdf ใน version ที่ไม่มีสำเนา PDF",
|
|
content: {
|
|
"application/json": {
|
|
schema: { $ref: "#/components/schemas/Error" },
|
|
},
|
|
},
|
|
},
|
|
500: { $ref: "#/components/responses/ServerError" },
|
|
},
|
|
},
|
|
},
|
|
},
|
|
components: {
|
|
securitySchemes: {
|
|
ApiKeyAuth: {
|
|
type: "apiKey",
|
|
in: "header",
|
|
name: "x-api-key",
|
|
description:
|
|
"x-api-key ของ N10 = env EXTERNAL_API_KEY · ของการขอ token กลุ่ม documents = env " +
|
|
"DOCVER_API_KEY (คนละค่ากับของ N10 — กดปุ่ม Authorize ใส่ครั้งเดียวแล้วใช้ได้ทุก " +
|
|
"request ของกลุ่มที่ key ตรง)",
|
|
},
|
|
DocverBearer: {
|
|
type: "http",
|
|
scheme: "bearer",
|
|
bearerFormat: "JWT",
|
|
description:
|
|
"access_token จาก POST /api/documents/token — แนบเป็น Authorization: Bearer " +
|
|
"กับ endpoint อื่นของกลุ่ม documents ทุกตัว",
|
|
},
|
|
},
|
|
responses: {
|
|
BadRequest: {
|
|
description:
|
|
"request ไม่ถูกต้อง (field หาย / ชนิดไฟล์ผิด / ค่า enum ไม่รู้จัก / " +
|
|
"JSON ผิด format / ไฟล์แนบใหญ่เกิน 100MB)",
|
|
content: {
|
|
"application/json": {
|
|
schema: { $ref: "#/components/schemas/Error" },
|
|
},
|
|
},
|
|
},
|
|
DocverUnauthorized: {
|
|
description:
|
|
"ไม่ได้แนบ Authorization: Bearer หรือ token ไม่ถูกต้อง/หมดอายุ (กลุ่ม documents — ข้อความเดียวกันทุกสาเหตุ)",
|
|
content: {
|
|
"application/json": {
|
|
schema: { $ref: "#/components/schemas/Error" },
|
|
},
|
|
},
|
|
},
|
|
ServerError: {
|
|
description: "เกิดข้อผิดพลาดของระบบ (เช่นไฟล์ .docx ไม่ถูกต้อง / DB ล้ม)",
|
|
content: {
|
|
"application/json": {
|
|
schema: { $ref: "#/components/schemas/Error" },
|
|
},
|
|
},
|
|
},
|
|
},
|
|
schemas: {
|
|
Error: {
|
|
type: "object",
|
|
properties: {
|
|
error: { type: "string" },
|
|
},
|
|
},
|
|
TokenRequest: {
|
|
type: "object",
|
|
description: "claims ที่จะฝังใน access_token ของกลุ่ม documents — key อื่น = 400",
|
|
required: ["userid", "username", "fid"],
|
|
properties: {
|
|
userid: { type: "string", description: "รหัสผู้ใช้ (บังคับ, ≤ 100 ตัวอักษร)" },
|
|
username: { type: "string", description: "ชื่อผู้ใช้ (บังคับ, ≤ 200 ตัวอักษร)" },
|
|
fid: {
|
|
type: "string",
|
|
description:
|
|
"fileId ของ e-Court (บังคับ, ≤ 200 ตัวอักษร) — เป็น docId ของทุก operation ตรง ๆ",
|
|
},
|
|
template: {
|
|
type: "string",
|
|
nullable: true,
|
|
description:
|
|
"รหัส template ที่ใช้สร้างเอกสาร — กลายเป็น templateId ตอนอัปโหลด version",
|
|
},
|
|
},
|
|
},
|
|
TokenResponse: {
|
|
type: "object",
|
|
description: "access_token ของกลุ่ม documents (JWT HS256 อายุสั้น)",
|
|
properties: {
|
|
access_token: { type: "string", description: "JWT พร้อมใช้แนบเป็น Bearer" },
|
|
token_type: { type: "string", example: "Bearer" },
|
|
expires_in: {
|
|
type: "integer",
|
|
description: "อายุวินาที (env DOCVER_TOKEN_TTL_SECONDS, default 3600)",
|
|
},
|
|
},
|
|
},
|
|
ExtractResponse: {
|
|
type: "object",
|
|
properties: {
|
|
data: {
|
|
type: "object",
|
|
description: "ชื่อแท็ก Content Control → ข้อความในนั้น (หลายบรรทัดคั่นด้วย \\n)",
|
|
additionalProperties: { type: "string" },
|
|
example: {
|
|
"cc": {
|
|
"blackyear": "๙๐/๒๕๖๙",
|
|
"redyear": "xxx",
|
|
"curdate": "วันที่ เดือน สิงหาคม พุทธศักราช ๒๕๖๙ ",
|
|
"accuser": "นายธีรพล ร่มเริง",
|
|
"detail1": "ตุลาการเจ้าของสำนวนได้พิจารณาคำฟ้อง...",
|
|
},
|
|
"bookmark": {
|
|
"signature": ""
|
|
},
|
|
"properties": {
|
|
"standard": {
|
|
"creator": "mam",
|
|
"created": "2026-10-22T02:58:00.000Z",
|
|
"modified": "2026-10-22T07:08:00.000Z"
|
|
},
|
|
"extended": {
|
|
"template": "TS_P2_IN.dotx",
|
|
"totalTime": 172,
|
|
"pages": 2,
|
|
"words": 124,
|
|
},
|
|
"custom": {
|
|
"ticket_data": "{\"exp\":1793013726,\"fid\":\"T_no_warning\",\"tid\":\"T_X\",\"uid\":\"john\",\"uname\":\"John Doe\"}",
|
|
"HmacSha256": "1dc53fbd48410845c895ddb841a8fe4ad93da413126fae9c688202e57f2baaa9",
|
|
"Ed25519": "95ea0ca6d81df885c22a2334ed89b2fc3c75a1a309fb5843e0deb0a16c9d05a45b345fb28fd606f3b152285e70560ad43a6389534794aa0d6f06d13f1307b005"
|
|
}
|
|
},
|
|
"verify": {
|
|
"exp": "26/10/2569 18:22:06",
|
|
"HmacSha256": true,
|
|
"Ed25519": true
|
|
}
|
|
},
|
|
},
|
|
},
|
|
},
|
|
ExternalQuery: {
|
|
type: "object",
|
|
description:
|
|
"ต้องมี fileId หรือ typeId อย่างน้อยหนึ่งอย่าง (ส่งทั้งคู่ได้ = AND เหลือเอกสารเดียว)",
|
|
properties: {
|
|
fileId: {
|
|
type: "string",
|
|
description: "id เอกสารของ e-Court — ตรงกับที่ส่งมากับลิงก์ ?fileId= ตอนเปิดเอกสาร",
|
|
},
|
|
typeId: {
|
|
type: "string",
|
|
description: "รหัสกลุ่มเอกสาร — คืนทุกไฟล์ในกลุ่มนี้ (เรียงตามวันที่สร้าง)",
|
|
},
|
|
filter: {
|
|
type: "object",
|
|
properties: {
|
|
type: {
|
|
type: "string",
|
|
enum: ["bookmarks", "annotations"],
|
|
description: "เลือกเฉพาะชนิดรายการ (ไม่ส่ง = ทั้งสอง ส่วนที่ไม่เลือกคืนเป็น array ว่าง)",
|
|
},
|
|
userId: {
|
|
description: "กรองตามเจ้าของรายการ (string เดี่ยวหรือ array)",
|
|
oneOf: [
|
|
{ type: "string" },
|
|
{ type: "array", items: { type: "string" } },
|
|
],
|
|
},
|
|
annotationTypes: {
|
|
description: "กรองชนิดของ annotation — มีผลเฉพาะเมื่อรวม annotations",
|
|
oneOf: [
|
|
{ type: "string", enum: [...ANNOTATION_TYPE_ENUM] },
|
|
{
|
|
type: "array",
|
|
items: { type: "string", enum: [...ANNOTATION_TYPE_ENUM] },
|
|
},
|
|
],
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
DocumentsResponse: {
|
|
type: "object",
|
|
properties: {
|
|
documents: {
|
|
type: "array",
|
|
items: { $ref: "#/components/schemas/DocumentBundle" },
|
|
},
|
|
},
|
|
},
|
|
DocumentBundle: {
|
|
type: "object",
|
|
description: "field ของเอกสารอยู่ระดับเดียวกับ annotations/bookmarks (bundle ยุบชั้น)",
|
|
properties: {
|
|
id: { type: "string", format: "uuid" },
|
|
externalFileId: {
|
|
type: "string",
|
|
nullable: true,
|
|
description: "id ของ e-Court (null = ไฟล์ url:-fallback หรืออัปโหลดในกลุ่ม)",
|
|
},
|
|
title: { type: "string" },
|
|
typeId: { type: "string", nullable: true },
|
|
createdAt: { type: "string", format: "date-time" },
|
|
annotations: {
|
|
type: "array",
|
|
items: { $ref: "#/components/schemas/ExternalAnnotation" },
|
|
},
|
|
bookmarks: {
|
|
type: "array",
|
|
items: { $ref: "#/components/schemas/ExternalBookmark" },
|
|
},
|
|
},
|
|
},
|
|
ExternalAnnotation: {
|
|
type: "object",
|
|
properties: {
|
|
id: { type: "string", format: "uuid" },
|
|
fileId: { type: "string", format: "uuid" },
|
|
pageIndex: { type: "integer", description: "0-based" },
|
|
userId: { type: "string" },
|
|
userName: { type: "string" },
|
|
type: { type: "string", enum: [...ANNOTATION_TYPE_ENUM] },
|
|
data: {
|
|
type: "object",
|
|
description:
|
|
"ข้อมูลเฉพาะชนิด (พิกัดเป็น % ของขนาดหน้า) เช่น HIGHLIGHT { text, color, rects }, COMMENT { message, position }",
|
|
},
|
|
createdAt: { type: "string", format: "date-time" },
|
|
updatedAt: { type: "string", format: "date-time" },
|
|
resolved: { type: "boolean" },
|
|
resolvedAt: { type: "string", format: "date-time", nullable: true },
|
|
url: {
|
|
type: "string",
|
|
description: "deep link กลับหน้าอ่าน พร้อม ?ann= ของรายการนี้",
|
|
},
|
|
replies: {
|
|
type: "array",
|
|
items: { $ref: "#/components/schemas/ExternalReply" },
|
|
},
|
|
},
|
|
},
|
|
ExternalBookmark: {
|
|
type: "object",
|
|
properties: {
|
|
id: { type: "string", format: "uuid" },
|
|
fileId: { type: "string", format: "uuid" },
|
|
pageIndex: { type: "integer", description: "0-based" },
|
|
userId: { type: "string" },
|
|
userName: { type: "string" },
|
|
title: { type: "string" },
|
|
parentId: {
|
|
type: "string",
|
|
nullable: true,
|
|
description: "null = ระดับบนสุด (มีค่า = sub ของ bookmark นั้น)",
|
|
},
|
|
note: { type: "string", nullable: true },
|
|
yOffset: { type: "number", description: "ตำแหน่งบนหน้า (% ของความสูง)" },
|
|
createdAt: { type: "string", format: "date-time" },
|
|
url: {
|
|
type: "string",
|
|
description: "deep link กลับหน้าอ่าน พร้อม ?page= 1-based ของหน้านี้",
|
|
},
|
|
},
|
|
},
|
|
ExternalReply: {
|
|
type: "object",
|
|
properties: {
|
|
id: { type: "string", format: "uuid" },
|
|
annotationId: { type: "string", format: "uuid" },
|
|
userId: { type: "string" },
|
|
userName: { type: "string" },
|
|
message: { type: "string" },
|
|
createdAt: { type: "string", format: "date-time" },
|
|
},
|
|
},
|
|
UploadVersionResult: {
|
|
type: "object",
|
|
description: "ตอบกลับหลังอัปโหลด version สำเร็จ",
|
|
properties: {
|
|
hasPdf: { type: "boolean", description: "true = version นี้มีสำเนา PDF" },
|
|
versionId: { type: "string", format: "uuid" },
|
|
versionNumber: {
|
|
type: "integer",
|
|
description: "เลข version ที่ server จ่าย (ค่าจริง — ไม่ใช่ที่ client คาด)",
|
|
},
|
|
},
|
|
},
|
|
VersionRecord: {
|
|
type: "object",
|
|
description: "รายการ version หนึ่งตัว (GET /versions คืนเป็น array ใหม่ → เก่า)",
|
|
properties: {
|
|
versionId: { type: "string", format: "uuid" },
|
|
versionNumber: { type: "integer" },
|
|
createdAt: { type: "string", format: "date-time" },
|
|
createdByUserId: { type: "string" },
|
|
createdByUserName: { type: "string" },
|
|
contentHash: {
|
|
type: "string",
|
|
description: "SHA-256 hex ของไฟล์ .docx (คำนวณฝั่ง server เสมอ)",
|
|
},
|
|
fields: {
|
|
type: "object",
|
|
nullable: true,
|
|
description: "ค่า Content Control ตอนอัปโหลด (ไม่ส่ง = null)",
|
|
additionalProperties: { type: "string" },
|
|
},
|
|
message: { type: "string", nullable: true, description: "commit message" },
|
|
hasPdf: { type: "boolean" },
|
|
},
|
|
},
|
|
DownloadLink: {
|
|
type: "object",
|
|
properties: {
|
|
url: { type: "string", description: "presigned URL ของ MinIO (GET โดยตรง)" },
|
|
expiresAt: { type: "string", format: "date-time" },
|
|
},
|
|
},
|
|
},
|
|
},
|
|
} as const;
|