eCourt-note/.env.example
Your Name a02e983ba8 feat(n11): add base64 document staging from e-Court
เพิ่มทางเข้าเอกสารสำหรับ e-Court ที่ไม่มี URL ให้ดึง — POST /api/note/files/stage
(x-api-key DOCVER_API_KEY) ส่งเนื้อไฟล์ PDF/รูปเป็น base64 ระบบเก็บใน RAM staging
(TTL sliding 8 ชม. + LRU + cap 100MB/ไฟล์ 256MB รวม — ห้ามเขียน disk/DB/MinIO
แม้แต่ไบต์เดียวตามโจทย์) ส่วน annotation/bookmark เก็บถาวรตามระบบเดิม:

- lib/staged-docs.ts: Map บน globalThis + sentinel "staged:<uuid>.<ext>" ใน
  pdf_url (ext จาก magic bytes — ไม่ต้อง migration) + hasStagedDoc แยกจาก getStagedDoc
- lib/ecourt-fid.ts: external_id เก็บ HMAC-SHA256(pepper, fid) แทน fid ดิบ
  (env FID_HASH_PEPPER fail-closed) + แปลง row เก่าด้วย npm run migrate:ecourt-fid
- route stage: อ่าน body แบบ stream นับไบต์ + idle 60 วิ/total 10 นาที = 408 +
  concurrency default 1 = 429 + base64 strict + magic bytes กำหนด ext เอง
- registerStagedFile: ลำดับกัน race 23505 — stage ใต้ id ก่อน insert โดนชนแล้ว
  re-stage ใต้ id ผู้ชนะ + try/finally คืน RAM ผู้แพ้ทุกทางออก
- ทางอ่าน 4 จุด: /api/note/pdf + export route (410 gate ไทย), resolvePublicPdfPath
  guard PdfPathError, PDFViewer โหลดผ่าน route + objectURL (gate ครบทุกชนิดไฟล์)
- session route (D5): ลิงก์ fid-only ตัดสิน staging ตั้งแต่แลก token — มี bytes
  ได้ fileId / หมดอายุ fileError "พ้นช่วงจัดเตรียม" / token ออกทุกกรณี
- Swagger UI: endpoint ใหม่ + คู่มือ retry (408/429/5xx ยิงซ้ำได้ stage ซ้ำปลอดภัย)
- MockStageForm บน /note/dev ทดสอบ 3 ขั้นในหน้าเดียว (ต้องถือ API key จริง)

Verify ครบตาม docs/PLAN-STAGED.md: tsc สะอาด + vitest 59 เคสใหม่ผ่าน + E2E
browser จริง (textLayer ไทยเรียงถูก / export ฝัง annotation / restart ได้
fileError ตั้งแต่ bootstrap / RAM 100MB: peak WS +580MB commit จริง +92MB) —
ผ่าน code review รอบ 7 แก้ครบทุก finding

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-11-05 12:53:56 +07:00

98 lines
8.3 KiB
Text

# N8 (optional): allowlist โฮสต์ลิงก์ไฟล์รีโมต — ไม่ตั้ง = admincourt.go.th
# EXTERNAL_FILE_HOSTS=admincourt.go.th
# N10 (optional): API key ของ /api/external/* (backend e-Court เรียก server-to-server)
# Sign/Hash ที่ใช้ฝังในเอกสาร(docx)
SECRET=""
PRIVATE_KEY_HEX=""
PUBLIC_KEY_HEX=""
## ไม่ตั้ง/ว่าง = ปฏิเสธทุก request (fail closed) — ตั้งค่ายาวและสุ่ม เช่น 32+ ตัวอักษร
EXTERNAL_API_KEY=""
# docver token (2026-10-22): API key ของ POST /api/documents/token (header x-api-key)
# และ POST /api/note/auth/token (เส้น mint login-token ของแอป note — PLAN-SSO.md)
# ไม่ตั้ง/ว่าง = ปฏิเสธทุก request (fail closed) — ตั้งค่ายาวและสุ่ม เช่น 32+ ตัวอักษร
# (ห้าม log ค่า key ดิบ — เทียบแบบ timing-safe ทุกครั้ง)
# GET /api/documents/latest (2026-10-31) รับ DOCVER_API_KEY นี้ หรือ EXTERNAL_API_KEY
# (ด้านบน) ตัวใดตัวหนึ่งตรง = ผ่าน — ทั้งคู่ไม่ตั้ง = 401 fail closed เช่นกัน
DOCVER_API_KEY=
# secret เซ็น/ตรวจ JWT HS256 ของทุก endpoint /api/documents/* (Authorization: Bearer)
# + login-token ของ SSO (แยกสิทธิ์ด้วย claim purpose — PLAN-SSO.md D4)
# ไม่ตั้ง/ว่าง = ออก token ไม่ได้ (500) และทุก endpoint ตอบ 401 รวมถึงเข้า annotate
# ไม่ได้ (ทางเข้า session เดียวคือลิงก์ SSO) — dev ต้องมีค่านี้ใน .env เสมอ
# ⚠️ ก่อน production: rotate ค่าจริง (ค่า hex เดิมเคยถูก commit ไว้ใน comment — ถือว่าหลุด)
DOCVER_TOKEN_SECRET=
# ใช้ตอน migrate และ การใช้งาน
DATABASE_URL=postgresql://postgres:postgres@127.0.0.1:54322/ecourt-note
### สำหรับโค้ดตัวอย่างระบบนอกเรียกเข้ามาไม่ได้ใช้ในระบบนี้ ห้ามเอาไช้
DOCEDIT_BASE_URL=http://127.0.0.1:3000/docedit
# Optional เวลาหมดอายุที่ฝังไว้ในข้อมูล (ticket_data), worker ควรเท่ากับจำนวน CPU core -1
TMPL_PATH='./public/tmpl'
TICKET_SEC=300
DOCX_WORKERS=3
## สำหรับ docker-compose.yaml
POSTGRES_USER=postgres
POSTGRES_PASSWORD=postgres
POSTGRES_DB=note
MINIO_ENDPOINT=127.0.0.1
MINIO_PORT=19000
MINIO_USE_SSL=false
MINIO_ACCESS_KEY=minioadmin
MINIO_SECRET_KEY=minioadmin
# MINIO_BUCKET=docver
# DOCVER_LINK_TTL_SECONDS=900
# DOCVER_TOKEN_TTL_SECONDS=3600
# (ถอดแล้ว 2026-10-30) NOTE_SESSION_SECRET ของรอบ cookie oacCaseSig — แอป note
# เปลี่ยน session เป็น token แบบ add-in แล้ว (access_token purpose=login จาก
# POST /api/note/auth/session เก็บใน localStorage "note-auth") จึงใช้
# DOCVER_TOKEN_SECRET ตัวเดียวทั้งระบบ: เซ็น login-token ของลิงก์ + เซ็น/ตรวจ
# session token ฝั่ง Server Actions และ API ของแอป (lib/note-token.ts)
# dev-only (ทางเลือก): เปิดฟอร์ม "สร้างลิงก์ทดสอบ" บนหน้า /note + route
# POST /api/note/auth/dev-link — mint ลิงก์ SSO จากหน้าเว็บเพื่อทดสอบ flow จริง
# ⚠️ ตัวช่วยทดสอบเท่านั้น ไม่ใช่ทางเข้าจริง: ปิด = route ตอบ 404 + ฟอร์มหายจากหน้า
# และไม่ทำงานใน production build เด็ดขาด (NODE_ENV=production) — dev server ที่แชร์
# บน LAN เปิดไว้ = คนในวงแชร์เครือข่าย mint ลิงก์เข้าระบบได้ จึงควรเปิดเฉพาะตอนทดสอบ
# NOTE_SSO_DEV_MINT=1
# N11 RAM staging ของเอกสาร base64 จาก e-Court (2026-11-04, docs/PLAN-STAGED.md)
# POST /api/note/files/stage ใช้ DOCVER_API_KEY ด้านบน — env ด้านล่างเป็นตัวปรับล้วน
# ไม่ตั้งทั้งหมด = ใช้ default ทำงานได้เลย · ⚠️ staging อยู่ใน RAM ของ process เดียว
# (เหมือน SSE) — ห้ามรันหลาย instance/cluster · deploy หลัง IIS ต้องขยับ
# maxAllowedContentLength ≥ 140MB (ดู PLAN-STAGED.md D7)
# TTL sliding จากการเข้าใช้ล่าสุด (วินาที) — default 28800 = 8 ชม. เท่า session
# STAGED_DOC_TTL_SECONDS=28800
# โควตารวมทุกไฟล์ใน RAM (ไบต์) — เกิน = LRU evict รายการเก่าสุด
# STAGED_MAX_TOTAL_BYTES=268435456
# ขนาดเนื้อไฟล์ต่อไฟล์ (ไบต์) — เกิน = 413
# STAGED_MAX_FILE_BYTES=104857600
# จำนวน stage request พร้อมกันสูงสุด — เกิน = 429 Retry-After: 5
# STAGED_MAX_CONCURRENT=1
# hash fid ของ e-Court ก่อนเก็บลง files.external_id (2026-11-04, lib/ecourt-fid.ts)
# = HMAC-SHA256(pepper, fid) — ไม่มี fid ดิบใน DB
# ⚠️ ค่าคงที่ตลอดชีพ: ห้ามเปลี่ยนหลังมีข้อมูล (เปลี่ยน = แถวเก่าค้นไม่เจอ + เกิดแถว
# คู่เงียบ ๆ) · ตั้งค่ายาวและสุ่ม 32+ ตัวอักษร · ห้าม commit ค่าจริงลง repo
# · row เก่าที่เก็บ plaintext แปลงครั้งเดียวด้วย npm run migrate:ecourt-fid
# (ต้องใช้ pepper ค่าเดียวกัน — ระหว่างรันต้องหยุด process ของแอปก่อน)
FID_HASH_PEPPER=
# add-in ticket auth (2026-10-27, docs/PLAN-ADDIN-AUTH.md): add-in ใน Word ขอ token
# ที่ POST /api/documents/token ด้วย body {ticket_data, HmacSha256} ที่ e-Court stamp
# มากับไฟล์ — secret ต้องตรงกับตัว stamp ของ e-Court ทั้งสองฝั่ง
# ฝั่ง server ตรวจ HMAC ของ ticket: ADDIN_TICKET_SECRET (ไม่ตั้ง = ใช้ env SECRET
# ของกลุ่ม template → ยังไม่ตั้งอีก = "super_secret" ตาม requirement)
# ⚠️ production ต้องตั้งค่าจริง — ค่า default รู้กันหมด = ใครก็ปลอม ticket ได้
# (ใช้ร่วมกับทาง ticket ของ SSO POST /api/note/auth/session ด้วย — PLAN-SSO.md D12)
# 2026-10-30: ทาง ticket ของ SSO (POST /api/note/auth/session) ปฏิเสธค่า default
# นี้เฉพาะใน production — dev ใช้ default ทดสอบโหมด ticket ได้เลย (เครื่องมือ dev
# อย่าง make-login-link/make-ticket-docx ก็ default ค่าเดียวกัน)
# ADDIN_TICKET_SECRET=super_secret
# ฝั่ง add-in (inline ลง bundle ตอน build — ใช้เป็นแค่ UX gate ตัดเคสไฟล์ถูกแก้ก่อนยิง
# network ส่วนความมั่นคงจริงอยู่ที่ฝั่ง server ข้างบน · ตามคำขอผู้ใช้ 2026-10-29
# แก้ add-in เป็นงานแยก — ยังคงไว้ตาม PLAN-ADDIN-AUTH.md D3)
# NEXT_PUBLIC_ADDIN_TICKET_SECRET=super_secret