Your Name
a288e5643c
Merge branch 'forgejo-dev' into working
...
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 1m12s
Deploy eCourt Note Web to Server / Deploy eCourt Note Web to Server (push) Successful in 4s
Deploy eCourt Note Web to Server / Notify Deployment Status (push) Successful in 1s
Resolve CLAUDE.md: คำอธิบาย lib/document-versioning.ts เอาของ
forgejo-dev (field `fields` ถอดออก 2026-10-30) ส่วน lib/docver-token.ts
เอาของ working (purpose login / verifyDocverLoginToken — โค้ดฝั่ง
forgejo-dev มีครบแล้ว แค่คำอธิบายล้าหลัง)
Verify: npx tsc --noEmit ผ่าน · vitest 23 files / 338 tests ผ่าน
Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-11-02 10:07:08 +07:00
Your Name
2d466efd4f
fix(note): address code review findings
...
- restoreVersion: ผ่าน validateAnnotationData ก่อน insert สาขา
"ต้นทางถูกลบ" ให้สมมาตรกับสาขา update (invariant #8 )
- PDFViewer: track timer ล้าง exportError ใน ref — กัน export พัง
สองรอบติดกันแล้ว timer รอบแรกเคลียร์ error รอบหลังก่อนเวลา
- useHistory: เพิ่ม onError — undo/redo ล้มแสดง toast ผ่าน
showActionError แทนหายเงียบ ๆ
- DebugConsole: ไม่โหลด Eruda จาก CDN ใน production build
(ระบบ intranet + token อยู่ใน origin เดียวกัน)
- NoteHome: ตัด dead state session ที่ render ไม่ได้อ่าน
- note-auth: export SSO_REQUIRED_BANNER ให้หน้า gate ใช้ตรง ๆ
ถอด non-null assertion ใน AnnotateView + bookmarkDepthOf
Verify: npx tsc --noEmit ผ่าน · vitest 317/317 ผ่าน
Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-11-02 10:01:00 +07:00
oom
c9646eb02a
Merge branch 'dev' of https://forgejo.chamomind.com/chamomind/eCourt-note into dev
2026-11-02 10:00:19 +07:00
oom
45ff0001e6
clear environment variable, update download-docx(sample) to check latest version before query database.
2026-11-02 10:00:12 +07:00
Suphonchai Phoonsawat
0b6ee8cc84
feat: add GET /api/documents/latest returning latest version file with stamped ticket
...
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 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 0s
Server-to-server endpoint for the e-Court backend (x-api-key against
DOCVER_API_KEY or EXTERNAL_API_KEY):
- type=pdf returns the latest version's PDF bytes (404 when absent)
- type=docx stamps fresh ticket custom properties (exp + HmacSha256 via
addinTicketSecret(), Ed25519 when PRIVATE_KEY_HEX is set) into an
in-memory copy before returning — MinIO object is never modified
- docId comes from fid inside the ticket_data query param, which must
include fid/uid/uname so the stamped file stays redeemable by the
add-in at /api/documents/token (buildStampedTicketData drops null
values because parseDocverTicket rejects tid:null)
- adds parseTicketDataParam, parseLatestTypeParam, requireDocumentApiKey,
findLatestVersionForDoc, getVersionObjectBytes + unit tests (320 pass)
- syncs PLAN-DOCVER.md, OpenAPI spec and .env.example
Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-10-31 11:09:42 +07:00
Suphonchai Phoonsawat
5bc8d9a817
chore: remove unused MinIO and document versioning environment variables from Dockerfile
2026-10-31 10:22:00 +07:00
Suphonchai Phoonsawat
cdae1ceedd
Refactor document versioning to extract fields from uploaded .docx files on the server side
...
- Removed the `fields` parameter from the version upload form, as it is now extracted from the uploaded .docx file using the `extractContentControls` function.
- Updated related types and interfaces to reflect the new structure of version fields.
- Modified the OpenAPI specification to document the changes in the versioning process.
- Adjusted tests to ensure compliance with the new behavior, including validation for the absence of the `fields` parameter.
- Implemented a new function `extractDocumentSnapshot` to handle the extraction of content controls and document properties from .docx files.
- Updated metadata and versioning services to align with the new extraction logic.
2026-10-31 10:21:17 +07:00
Your Name
d37bc2a3f0
feat(sso): switch note session from cookies to token
...
เปลี่ยน session ของแอป note จาก cookie คู่ oacCase/oacCaseSig มาเป็น
access_token แบบ add-in ตามคำขอผู้ใช้ (2026-10-30):
- POST /api/note/auth/session แลก credential ของลิงก์ e-Court
(JWT ทาง A / ticket ทาง B) เป็น access_token purpose=login อายุ 8 ชม.
พร้อมลงทะเบียนเอกสาร N8 แบบ trusted ฝั่ง server (fileId จาก fid)
- token เก็บ localStorage "note-auth" (lib/note-session.ts) + Bearer
ผ่าน noteAuthFetch · Server Actions ทุกตัวรับ token เป็น param แรก
และแกะ identity เอง (lib/note-token.ts) — ไม่เชื่อ userId จาก client
- หน้า protected เป็น shell + client gate (NoteHome bootstrap ที่ /note,
AnnotateView + GET /api/note/files/[id]/bootstrap)
- ถอด: middleware.ts, lib/session.ts, utils/user.ts, action logout,
GET /api/note/auth/e-court, entry /note/annotate?fileUrl=
- ทาง ticket ปฏิเสธ secret default "super_secret" เฉพาะ production
- GET /api/note/files/open: ลิงก์เปิดไฟล์ตัวอย่างแบบ /tmpl (path ใน
เครื่อง /samples เท่านั้น)
- หน้า /note/dev: ลิงก์ A Link Get(new) ที่ mint credential แล้วยิง
เข้า flow SSO จริง (JWT/ticket สลับได้) + section Flow + เรียบเรียง
ภาษาให้อ่านง่าย · หน้า /note เป็นหน้าข้อมูลระบบ
- แก้ตาม code review: redo closure เรียก action ด้วย signature เก่า,
bootstrap timeout + strip credential ที่ผิดรูป, แยก no-session ออก
จาก network error, ห้ามอักขระ & = + % ใน identity, ตรวจ purpose
ก่อน exp ของ login-token, audit log strip อักขระควบคุม
- เอกสาร: docs/PLAN-SSO.md §0.5 (ฉบับ token), CLAUDE.md, .env.example
ทดสอบ: tsc ผ่าน · vitest ผ่านทั้งชุด · ทดสอบจริงผ่าน Playwright
(SSO → viewer → เปลี่ยนสี annotation → undo → redo ครบวงจร)
Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-10-30 18:05:36 +07:00
oom
bc61f0fd2e
Add install-word-addins.ps1 script
2026-10-29 16:15:54 +07:00
Suphonchai Phoonsawat
b1449ab668
Refactor ticket verification and installation scripts
...
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 1m17s
Deploy eCourt Note Web to Server / Deploy eCourt Note Web to Server (push) Successful in 4s
Deploy eCourt Note Web to Server / Notify Deployment Status (push) Successful in 1s
- Split `verifyAddinTicket` into two functions: `verifyAddinTicketHmac` (ignores expiration) and `verifyAddinTicket` (checks expiration).
- Add `hasVersionByUser` function to check if a document has been previously worked on by a user, allowing expired tickets to be accepted if the user has a history with the document.
- Update OpenAPI specification to reflect changes in ticket expiration handling.
- Introduce new scripts for installing and uninstalling the Word add-in on Windows and macOS, providing a streamlined process for sideloading the add-in.
- Enhance tests for ticket verification to cover new functionality regarding expired tickets and user history.
2026-10-28 14:42:03 +07:00
Suphonchai Phoonsawat
3726cb742f
feat: enhance document versioning and metadata handling in Word add-in
...
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 1m21s
Deploy eCourt Note Web to Server / Deploy eCourt Note Web to Server (push) Successful in 4s
Deploy eCourt Note Web to Server / Notify Deployment Status (push) Successful in 1s
- Updated API documentation for document versioning to clarify usage and requirements.
- Improved metadata service to support all content control types in Word API 1.5+.
- Added tests for reading content control fields to ensure compatibility with various Word versions.
- Refactored template service to utilize shared metadata functions for better maintainability.
2026-10-28 11:22:52 +07:00
oom
677de2fc29
Merge branch 'dev' of https://forgejo.chamomind.com/chamomind/eCourt-note into dev
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 1m23s
Deploy eCourt Note Web to Server / Deploy eCourt Note Web to Server (push) Successful in 4s
Deploy eCourt Note Web to Server / Notify Deployment Status (push) Successful in 1s
2026-10-27 13:26:17 +07:00
oom
ce646ffc20
Fix Link bug: Replace <a> with <Link> and add more demo page(dl.html)
2026-10-27 13:24:15 +07:00
Suphonchai Phoonsawat
b8ff622999
fix(docver): drop fields value limit + treat history 404 as empty in add-in
...
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) Successful in 1m28s
Deploy eCourt Note Web to Server / Deploy eCourt Note Web to Server (push) Successful in 4s
Deploy eCourt Note Web to Server / Notify Deployment Status (push) Successful in 0s
Two fixes from first real-Word testing of the unmocked versioning flow:
- fields value length: remove the limit entirely (was 10,000, briefly
100,000) — real court documents run 100-200 pages with content
controls several hundred thousand chars long; callers are Bearer-only
and the column is Postgres jsonb (up to ~255MB), so no storage
bottleneck. Structural guards stay: ≤1,000 keys (was 100), key ≤200
chars, value must be string.
- getVersions in versioningService now maps the history 404 to an empty
list: files stamped SavedByAddIn back in the mock era enter the
tracking phase before any real upload exists, so the pane shows
"ยังไม่มี version ในระบบ" and Save & Version creates v1 (implicit
document creation) instead of surfacing a 404 error banner. Other
errors still propagate.
Tests: 288/288 (new cases — 1M-char value passes, key cap 1,000/1,001,
history 404 -> []). E2E against MinIO: 50k and 1M-char Thai field values
upload 200 and roundtrip byte-exact through jsonb. Docs synced:
PLAN-DOCVER §1, CLAUDE.md.
Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-10-27 13:14:04 +07:00
Suphonchai Phoonsawat
37b18c58c2
feat(addin): unmock versioning — real upload/download against docver API
...
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) Successful in 1m27s
Deploy eCourt Note Web to Server / Deploy eCourt Note Web to Server (push) Successful in 4s
Deploy eCourt Note Web to Server / Notify Deployment Status (push) Successful in 1s
Replace the in-memory versioningService with real calls to the docver
group (/api/documents/*) via apiClient with the ticket-issued Bearer;
keep the mock only for ?addindemo (authService.isDemoMode).
- uploadVersion: multipart POST /api/documents/versions (file, optional
pdf when non-empty, clientVersionId, fields JSON, trimmed message,
fileName) — identity fields are never sent (userId/userName/templateId
in the form = 400 per the 2026-10-22 contract; docId comes from the
fid claim)
- getVersions: GET → DocumentVersionDTO mapped to VersionRecord (adds
optional createdByUserName; history panel shows it with userId
fallback)
- getDownloadLink: GET presigned URL (+ ?format=pdf)
- apiClient.toUrl: empty NEXT_PUBLIC_ADDIN_API_BASE_URL = same-origin
through withBasePath (/docedit/api/...); the env is now only for a
split deployment (must include basePath, HTTPS)
- documentId/templateId params stay in signatures but are not sent
- tests: versioningService.test.ts covers both modes (real sign-in via
signInWithTicket, FormData contract assertions, DTO mapping, format
query, 401 -> AuthRequiredError, demo mock without network)
- docs sync: WORDADDIN-DEV (new "การรันกับ backend จริง" section),
addin-ui-flow status note, PLAN-DOCVER §8, PLAN-ADDIN-AUTH §12, CLAUDE.md
Verified: tsc clean, 286/286 tests, full E2E against MinIO (ticket ->
token -> upload docx / docx+pdf -> history -> presigned download of both
formats).
Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-10-27 12:52:29 +07:00
Suphonchai Phoonsawat
4200176239
test(addin): convert crash-repro suite to permanent ticket-auth flow tests
...
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) Successful in 1m28s
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
Rename tests/wordaddin/ticket-auth-crash-repro.test.ts to
ticket-auth-flow.test.ts and strip the temporary [DBG-r3p1] markers.
The debug session closed with an environment cause (stale Next server
holding the port / corrupted .next chunks), not a code defect, so the
suite stays as regression coverage for the signInWithTicket service
layer: single token fetch per fid, token reuse on reopen without
re-hitting /token, memory-fallback session teardown, and no uncaught
throw/rejection on every failure path (missing/invalid ticket, expired
ticket, server 401, webview without crypto.subtle).
Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-10-27 11:54:29 +07:00
Suphonchai Phoonsawat
7cd8939789
Merge commit ' 69d83ea493' into dev
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 1m26s
Deploy eCourt Note Web to Server / Deploy eCourt Note Web to Server (push) Successful in 4s
Deploy eCourt Note Web to Server / Notify Deployment Status (push) Successful in 1s
2026-10-27 11:22:02 +07:00
Suphonchai Phoonsawat
cffc571da5
feat(addin): implement e-Court ticket auth (ticket_data + HmacSha256)
...
Replace the token/owner pass-through with the ticket flow from
docs/requirement/addin-workflow.md (full plan: docs/PLAN-ADDIN-AUTH.md):
- utils/docver-ticket.ts: shared pure ticket parse + hex digest helpers,
client/server inject their own crypto (Web Crypto / node:crypto)
- lib/addin-ticket.ts + POST /api/documents/token second entry: body
{ticket_data, HmacSha256} verified server-side (HMAC timing-safe + exp,
secret ADDIN_TICKET_SECRET -> SECRET -> "super_secret"); x-api-key path
unchanged
- wordaddin: ticketService (Web Crypto HMAC, localStorage session
wordaddin-auth keyed by fid, same-origin token request), authService
rewrite (7-step flow, silent renew), apiClient 401 renew-retry once,
metadataService reads ticket_data/HmacSha256, no-access messages per
cause
- scripts/make-ticket-docx.mjs: dev tool to stamp tickets onto any .docx
for testing in real Word
- app/wordaddin/error.tsx: error boundary showing the real error + stack
in the pane (webviews have no devtools)
- tests: docver-ticket + addin-ticket unit suites (21 cases) + temporary
crash repro suite for the ongoing client-crash debug session
- docs sync: PLAN-DOCVER, WORDADDIN-DEV, addin-ui-flow, openapi (two
request bodies for /token), CLAUDE.md, .env.example
Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-10-27 11:21:15 +07:00
oom
69d83ea493
Fix build time fail: export applyTemplate not compatible with Next.js(move to lib-tmpl), add var in .env.example
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
2026-10-27 10:15:35 +07:00
Suphonchai Phoonsawat
8ebe5f8644
Merge commit ' a3d14fd58d' into dev
2026-10-27 09:45:15 +07:00
Suphonchai Phoonsawat
50af6c10fa
Refactor Word Add-in authentication to use e-Court pass-through; remove Keycloak integration
...
- Updated documentation to reflect removal of login and transition to e-Court pass-through authentication.
- Modified `authService` to handle authentication via custom properties in documents without user login.
- Adjusted `apiClient` to immediately throw `AuthRequiredError` for invalid tokens, as refresh tokens are no longer applicable.
- Revised UI flow to accommodate new authentication method, including changes to state management and error handling.
- Added new requirements document outlining the add-in workflow and authentication process.
2026-10-27 09:44:28 +07:00
oom
a3d14fd58d
Add generate API, Prototype for developer, Clean data
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
2026-10-27 01:47:08 +07:00
Suphonchai Phoonsawat
6ce071ee14
fix(token): restore environment variable checks for DOCVER_API_KEY and DOCVER_TOKEN_SECRET
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 1m16s
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 0s
2026-10-26 18:37:02 +07:00
Suphonchai Phoonsawat
ab62a2b3e2
feat(docker): add MinIO configuration and token settings in Dockerfile
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 1m6s
Deploy eCourt Note Web to Server / Deploy eCourt Note Web to Server (push) Successful in 4s
Deploy eCourt Note Web to Server / Notify Deployment Status (push) Successful in 0s
2026-10-26 13:02:25 +07:00
Suphonchai Phoonsawat
c06da83f92
fix(docver): update token secret handling to use hardcoded value for testing
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 1m6s
Deploy eCourt Note Web to Server / Deploy eCourt Note Web to Server (push) Successful in 4s
Deploy eCourt Note Web to Server / Notify Deployment Status (push) Successful in 3s
2026-10-26 12:37:29 +07:00
Suphonchai Phoonsawat
86353cbe57
refactor(docver): improve code readability and structure in token handling functions
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 1m13s
Deploy eCourt Note Web to Server / Deploy eCourt Note Web to Server (push) Successful in 4s
Deploy eCourt Note Web to Server / Notify Deployment Status (push) Successful in 1s
2026-10-26 12:29:08 +07:00
Suphonchai Phoonsawat
0b2c9e6343
feat(docver): derive docId from fid claim and drop [docId] URL segment
...
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 1m9s
Deploy eCourt Note Web to Server / Deploy eCourt Note Web to Server (push) Successful in 4s
Deploy eCourt Note Web to Server / Notify Deployment Status (push) Successful in 1s
- docId of every docver operation now comes from the Bearer token: the fid
claim (e-Court fileId) is used directly as docId; segment [docId] removed
from URLs — POST/GET /api/documents/versions and
GET /api/documents/versions/{versionId}/download-link
- fid is required and must be a string (trim 1-200): token issuance rejects
missing/blank/non-string fid with 400, verify rejects fid-less tokens with 401
- parseDocId removed from lib/document-versioning.ts (dead code, no callers)
- openapi spec: paths renamed, docId path params removed, fid required + string
- PLAN-DOCVER.md / CLAUDE.md / wordaddin versioningService comment updated
- tests: fid required/string cases added, parseDocId describe removed (256 pass)
Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-10-26 10:05:01 +07:00
Suphonchai Phoonsawat
aad6f155fe
Merge branch 'dev' into working
2026-10-26 09:02:13 +07:00
Suphonchai Phoonsawat
98b370c925
feat(docver): add access token endpoint and Bearer auth on all endpoints
...
- POST /api/documents/token issues JWT HS256 (claims userid/username/fid/template) after x-api-key check against new env DOCVER_API_KEY (fail-closed, timing-safe, uniform 401)
- all docver endpoints now require Authorization: Bearer, verified with env DOCVER_TOKEN_SECRET (TTL via DOCVER_TOKEN_TTL_SECONDS, default 3600); secret unset = 401 on verify / 500 on issue
- identity (userId/userName) + templateId extracted from token claims instead of request payload; removed from multipart form (legacy fields now 400)
- lib/docver-token.ts: hand-rolled JWT via node:crypto only — alg pinned to HS256 (anti alg-confusion), timing-safe compare with both sides hashed, exp enforced, identical 401 for all bearer failure modes
- openapi spec: /api/documents/token path + DocverBearer scheme; PLAN-DOCVER.md / CLAUDE.md / .env.example updated
- tests: new tests/lib/docver-token.test.ts + adjusted parseVersionUploadForm tests (258 pass, no DB env needed)
- package-lock.json: sync minio (^8.0.7) that was missing from the earlier docver commit
Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-10-26 06:29:19 +07:00
oom
ac6c0dc71e
Add new template format with hashing algorithm
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
2026-10-22 22:33:14 +07:00
Suphonchai Phoonsawat
280cc2da40
Merge branch 'dev' into feature/versionning
2026-10-20 11:03:12 +07:00
Suphonchai Phoonsawat
949861ba86
Merge branch 'dev' of https://forgejo.chamomind.com/chamomind/eCourt-note into dev
2026-10-20 11:03:02 +07:00
Suphonchai Phoonsawat
ed8493004c
Merge branch 'dev' of https://forgejo.chamomind.com/chamomind/eCourt-note into feature/versionning
...
# Conflicts:
# CLAUDE.md
2026-10-20 11:02:02 +07:00
Suphonchai Phoonsawat
765946f0d5
feat(docver): add document versioning API (docx+pdf in MinIO)
...
Stand-alone API group at /api/documents/{docId}/versions — one document
per docId (unique), each version stores .docx + optional .pdf as MinIO
objects with uploader userId/userName/createdAt (no auth, like
api/template; callers identify themselves per docs/PLAN-DOCVER.md).
- POST versions (multipart): validates fields/magic bytes/100MB cap,
uploads to MinIO first, cleans up objects if the DB insert fails;
returns {hasPdf, versionId, versionNumber} with server-authoritative
numbering (tx: upsert + SELECT FOR UPDATE, safe under parallel calls)
- GET versions: DocumentVersionDTO[] newest-first (404 for unknown docId)
- GET versions/{versionId}/download-link?format=docx|pdf: presigned URL
(docId+versionId matched together; pdf-missing = 404)
- tables documents/document_versions + migration drizzle/0005
- docker-compose.yml: judes-note-db + judes-note-minio (quay.io image,
host ports 19000/19001, POSTGRES_DB ecourt-note, pinned volume names)
- lib/openapi.ts: documents tag + paths for Swagger UI
- middleware: exempt api/documents from login redirect
- unit tests tests/lib/document-versioning.test.ts (23 cases)
wordaddin untouched — swap the mock later via NEXT_PUBLIC_ADDIN_API_BASE_URL.
Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-10-20 10:52:35 +07:00
Your Name
6daee15851
feat(wordaddin): add template tools tab
...
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 1m12s
Deploy eCourt Note Web to Server / Deploy eCourt Note Web to Server (push) Successful in 14s
Deploy eCourt Note Web to Server / Notify Deployment Status (push) Successful in 0s
แท็บที่สองของ taskpane "เครื่องมือ Template" (TemplateToolsPanel +
templateService — Office.js ล้วน ไม่ยิง backend ใช้ได้ทั้ง phase
untracked/tracking) สำหรับคนออกแบบ template:
- สแกน Content Controls ทั้งเอกสาร + เตือน tag ว่าง/ซ้ำ + ปุ่มดูช่อง
(select เลื่อน Word ไปให้เอง)
- กรอกค่าทดสอบเขียนกลับเอกสารจริงเพื่อดูผลจัดหน้า — ช่องว่างไม่ถูกแตะ,
เขียนแยก Word.run ราย tag ให้ fail เฉพาะตัวพร้อมสาเหตุจาก debugInfo
- แทรกย่อหน้า/ตัวแบ่งบรรทัด/ตัวแบ่งหน้าที่ cursor — page break มี
fallback OOXML block-level กัน E_FAIL ในบางบริบท (เช่น cursor ในตาราง)
- มองเห็น CC ทุกชนิดผ่าน feature-detect WordApi 1.5
(Document.getContentControls) — Office 2019 เห็นเฉพาะ rich text พร้อม
warning แยก state ไม่โดน banner อื่นล้าง, ห้ามยก MinVersion ของ manifest
จาก code review 2026-10-20:
- ปุ่ม toggle แท็บปิดระหว่าง flow ที่เขียนเอกสารครบทุกตัว
(saving/starting/saveBusy/templateBusy — panel รายงาน busy ผ่าน
onBusyChange พร้อม cleanup ตอน unmount) กัน flow versioning มา
stamp/capture ซ้อน operation ของแท็บ template ที่ยังรันอยู่
- banner auto-save error ยกจาก SaveControls มาไว้เหนือ toggle
ให้เห็นทั้งสองแท็บ
- สแกน merge หลังเขียนค่าตัด tag ที่หายไปจากเอกสารทิ้ง (ไม่ค้าง
ไปโดน missingTags รอบถัดไป)
- รวม describeWordError เป็น getDetailedErrorMessage ที่ errors.ts
Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-10-20 09:49:17 +07:00
Suphonchai Phoonsawat
5917c0532c
file
2026-10-20 09:35:25 +07:00
Your Name
a172b111a3
docs(wordaddin): sideload dev ชี้ trusted catalog ที่ public/wordaddin
...
เครื่อง dev ที่มี repo ไม่ต้องดาวน์โหลด/คัดลอก manifest แยกแล้ว —
ชี้ Trusted Add-in Catalogs ไปที่ public/wordaddin ตรง ๆ:
manifest.template.xml คือ dev manifest อยู่แล้ว (URL localhost)
และเนื้อไฟล์อัปเดตตาม branch ที่ checkout เสมอ
ห้ามวาง xml อื่นเพิ่มในโฟลเดอร์นี้ — Word สแกน *.xml ทุกไฟล์
GUID เดียวกันสองไฟล์ทำให้ Word สับสน
เครื่องที่ไม่มี repo (production/ผู้ใช้) ยังใช้ flow ดาวน์โหลด
จาก URL ของ environment นั้นแล้ววางโฟลเดอร์แยกตามเดิม
Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-10-16 14:48:04 +07:00
Your Name
1aeaae38f1
fix(wordaddin): lower WordApi requirement for Office 2019
...
add-in โหลดไม่ขึ้นเลยบนเครื่องศาลที่ใช้ Office 2019 — manifest
ตั้ง WordApi 1.5 ซึ่งเป็น Microsoft 365 เท่านั้น (Office 2019
perpetual รองรับสูงสุด 1.3) host ที่ไม่ถึง requirement จะปฏิเสธ
โหลด add-in ตั้งแต่ต้นตามกลไกของ Office
แก้: รองลงเป็น WordApi 1.3 (customXmlParts คือ API สูงสุดที่ใช้
ประจำ — customProperties/save/contentControls เป็น 1.1)
+ bump Version 1.0.0.1 → 1.0.0.2 ให้ Word ทุกเครื่องเห็นว่าเป็น
เวอร์ชันใหม่แล้วดึง manifest ใหม่ (กันแค manifest เดิมของตัว
poc ที่เคย sideload — GUID เดิม)
ผลข้างเคียงบน Office 2019: ปุ่ม "บันทึกและปิดเอกสาร" /
"บันทึกเวอร์ชันและปิดเอกสาร" ถูกซ่อนอัตโนมัติ — document.close()
ต้องการ WordApi 1.4/1.5 โดย canCloseDocument() เช็ค
isSetSupported("WordApi","1.5") ตอน runtime อยู่แล้ว (กลไกเดิม
ที่ทำไว้สำหรับ Word on the web) ฟีเจอร์อื่นใช้ได้ครบ
ยืนยัน: parse XML ผ่าน (@xmldom) — WordApi=1.3,
OpenBrowserWindowApi=1.1 (Office 2019 รองรับ), Version=1.0.0.2 ·
สำรวจ API ที่ใช้จริงทั้ง wordaddin/services แล้วทุกตัว ≤1.3
ยกเว้น close() ที่มี runtime guard
Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-10-16 14:20:24 +07:00
Your Name
4bcd01a95f
feat(wordaddin): serve manifest with automatic origin rewrite
...
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 1m7s
Deploy eCourt Note Web to Server / Deploy eCourt Note Web to Server (push) Successful in 4s
Deploy eCourt Note Web to Server / Notify Deployment Status (push) Successful in 1s
manifest ของ add-in เดิมเป็น static ใน public/ ฝัง URL dev
(localhost:3000) ทั้งไฟล์ — deploy จริงต้อง rewrite มือเองทุกครั้ง
ก่อนนำไป sideload ใน Word
ตอนนี้ serve ผ่าน route แทน: app/wordaddin/manifest.xml/route.ts
อ่าน template (public/wordaddin/manifest.template.xml — ย้ายจาก
manifest.xml ด้วย git mv) แล้วเขียนทับ origin ให้ตรงโฮสต์ที่ขอ
ไฟล์ — dev/UAT/production ดาวน์โหลด manifest.xml ขอ environment
ตัวเองแล้ว sideload ได้ทันที
- ถอด requestOrigin จาก N10 route เป็น utils/request-origin.ts
ใช้ร่วม + เพิ่ม heuristic: โฮสต์ไม่ใช่ localhost + proto http =
บังคับ https (Office รับ https เท่านั้น และ hop ภายในหลัง IIS
เป็น http เสมอ — Next server ก็ฉีด x-forwarded-proto เอง จึง
ชี้ขาดจากโฮสต์ปลายทาง ไม่เดาว่า header มาจาก proxy หรือถูกฉีด)
deep link ของ N10 ได้พฤติกรรมเดียวกันเป็นผลพวง
- ตรวจ nft trace แล้ว template ถูกดึงเข้า standalone ด้วย (คู่
กับ COPY public จาก commit ก่อน = ชัวร์สองชั้น)
ยืนยัน: tsc + vitest 215 tests ผ่าน · ทดสอบบน dev server ด้วย curl
3 โหมด — direct ได้ http://localhost:3000 (Word ยกเว้น localhost),
ผ่าน proxy ส่ง header ครบได้ https://<โฮสต์>, ผ่าน proxy ไม่ส่ง
proto (กรณี IIS) ได้ https://<โฮสต์> จากการ bump · next build
compile route ผ่าน (ขั้น standalone symlink พังเฉพาะเครื่อง local
Windows สิทธิ์ symlink — ไม่เกี่ยวกับโค้ด Docker build ผ่าน)
Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-10-16 13:54:25 +07:00
Your Name
808233191d
fix(docker): copy public/ into production image
...
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 1m0s
Deploy eCourt Note Web to Server / Deploy eCourt Note Web to Server (push) Successful in 4s
Deploy eCourt Note Web to Server / Notify Deployment Status (push) Successful in 0s
taskpane wordaddin พังใน production — "โหลด Office.js ไม่สำเร็จ"
เพราะ browser ยิง /docedit/wordaddin/office-js/office.js ได้ 404:
image ไม่มี public/wordaddin อยู่เลย
สาเหตุ: Next 14.2 standalone ไม่ copy public/ เอง (comment เดิมใน
Dockerfile เข้าใจผิด) — ไฟล์ static ที่เคยใช้ได้หลุดเข้า image แบบ
บังเอิญผ่าน output file tracing ที่ตาม fs.readFile ใน server code
(public/note จาก lib/server-pdf.ts, ฟอนต์ Sarabun จาก export route)
แต่ public/wordaddin/** ไม่มี server code ไหนอ่านผ่าน fs จึงไม่ถูก
ดึงมา
แก้: เพิ่ม COPY --from=builder /app/public ./public ใน runner stage
ตามเอกสาร deploy ของ Next + แก้ comment Dockerfile และ
docs/WORDADDIN-DEV.md ให้เล่ากลไกจริง
ยืนยันด้วยการ build image ใหม่แล้วรัน container จริง:
office.js / manifest.xml / word-win32-16.01.js / th-th strings /
pdf.worker ทุก URL ตอบ 200
Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-10-16 13:28:04 +07:00
Your Name
2b70a50c62
feat(wordaddin): add e-Court pass-through sign-in
...
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 1m8s
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
ไฟล์ที่ e-Court stamp custom properties token/owner มาใช้งานได้เลย
ไม่ต้องกด login — detect() ของ TaskpaneApp อ่านค่าทั้งสองตอนเปิด
task pane แล้วเรียก signInFromDocument(): username จาก owner →
uid ใน JWT payload → NEXT_PUBLIC_ADDIN_MOCK_USERNAME, token จาก
ไฟล์เก็บใน memory ให้ getAccessToken() ใช้ต่อ
decodeJwtField ถอด base64url เป็น bytes แล้วผ่าน TextDecoder ก่อน
JSON.parse — uid ที่ไม่ใช่ ASCII (เช่น ชื่อไทย) ไม่เป็น mojibake
อัปเดตเอกสาร CLAUDE.md + docs/WORDADDIN-DEV.md พร้อมวิธีทดสอบ
เองใน Word ผ่าน Advanced Properties
Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-10-16 13:06:28 +07:00
Your Name
dc851fbb73
chore: add pnpm lockfile
2026-10-16 12:11:12 +07:00
Your Name
33670d0b18
chore(template): add manual test fixture Test.docx
2026-10-16 12:11:03 +07:00
Your Name
c401f4deb7
feat(wordaddin): port Word add-in taskpane from poc
...
พอร์ต taskpane ของ Word add-in "e-Court Documents Versioning" จาก
D:\word-addin-poc มาเป็น sibling app ที่ route /docedit/wordaddin:
- wordaddin/ (client-only zone): services พอร์ตดิบทั้งชุด
(authService/versioningService เป็น mock รอ backend จริง) + models + config
- components/wordaddin/: WordAddinGate ฉีด office.js self-host (singleton
กัน StrictMode double-inject + fallback timer ครอบเคส onReady เงียบ)
และ TaskpaneApp state machine แปลง JSX เป็น Genesis design system
- scripts/fetch-office-js.mjs (postinstall ตัวที่สาม): ดาวน์โหลด Office.js
runtime ล่าสุดจาก CDN Microsoft ไป public/wordaddin/office-js/ (gitignore
สร้างใหม่ได้ — npm @microsoft/office-js ตกรุ่นไม่มี Document.close)
- middleware/basePath/root page: เปิด /wordaddin เป็น public (auth แยกจาก
แอป note), root /docedit redirect ไป taskpane ยกเว้นลิงก์ e-Court N8
(?fileUrl=&fileId=&typeId=) ที่ยังส่งต่อ /note ตามเดิม
- docker/web.Dockerfile รัน fetch-office-js ตอน build + npm scripts
dev:https สำหรับ sideload ตาม docs/WORDADDIN-DEV.md
Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-10-16 12:07:25 +07:00
Your Name
7a146d48be
fix(config): move root redirect to page and skip slash redirect
...
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 50s
Deploy eCourt Note Web to Server / Deploy eCourt Note Web to Server (push) Successful in 14s
Deploy eCourt Note Web to Server / Notify Deployment Status (push) Successful in 0s
- เข้า /docedit เดิมใช้ redirects() (Next เติม basePath ให้ source /)
ถอดออกให้ app/page.tsx ที่ root จับ /docedit และ /docedit/ แทน
พร้อมส่งต่อ query เอง เพราะ redirect() ของ next/navigation ไม่แนบ
searchParams ให้อัตโนมัติ (รักษาสัญญาลิงก์ e-Court
/docedit/?fileUrl=&fileId=&typeId= ของ N8)
- เพิ่ม skipTrailingSlashRedirect: true ปิด 308 trailing slash
อัตโนมัติของ Next กัน ping-pong กับ rewrite rule ของ IIS ตอน
deploy หลัง proxy
- redirects() ใหม่แบบ basePath:false จับเฉพาะ root ของ host (/ แท้)
ส่งตรงไป /docedit/note
Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-10-08 11:11:48 +07:00
Your Name
b41816e2b6
feat(template): add restrict-edit protection script
...
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 53s
Deploy eCourt Note Web to Server / Deploy eCourt Note Web to Server (push) Successful in 4s
Deploy eCourt Note Web to Server / Notify Deployment Status (push) Successful in 1s
สคริปต์ทดลอง make-protected.mjs ตามแนวทาง Token-Based ใน note.md:
- เปิด documentProtection readOnly + enforcement ใน settings.xml
- ครอบ Content Control ทุกช่องด้วย permStart/permEnd (edGrp=everyone)
ให้แก้ได้เฉพาะในช่อง + เติม w:lock=sdtLocked กันลบช่อง
- ฝัง custom properties (token/primary-key/owner) ที่ docProps/custom.xml
พร้อมแก้ [Content_Types].xml และ _rels/.rels ให้ครบ
- verify อ่านไฟล์กลับทุกครั้งหลังเขียน (exit 1 ถ้าไม่ผ่าน)
- บันทึกบทเรียน/ผลทดสอบ Word จริง รอบ 1-2 ใน note.md
พร้อม T3.docx/output-T3.docx อัปเดตตามผลรัน
Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-10-07 16:55:30 +07:00
Your Name
073f72db7d
feat(api): add swagger ui and docx template api
...
เพิ่มกลุ่ม API docx template สำหรับ wordaddin (WIP):
- POST /api/template/extract ดึงค่า Content Control ทั้งหมดจาก .docx
- POST /api/template/render แทนค่า JSON ลง template คืน .docx ใหม่
- GET /api/template/test ตรวจว่า service มีชีวิต
logic แชร์ที่ app/api/template/lib-tmpl.ts (pizzip+xpath) ไม่แตะ DB
เพิ่มหน้า Swagger UI ที่ GET /api/swagger — spec inline จาก
lib/openapi.ts ครอบ API ทุกกลุ่ม (template + N10), asset self-host
ที่ public/note/swagger-ui/ จาก postinstall scripts/copy-swagger-ui.mjs
(intranet ไม่ใช้ CDN — หาไฟล์ไม่เจอหลังอัปเกรด = exit 1 เหมือน
patch-pdf-worker, Docker build รัน script นี้ด้วยผ่าน web.Dockerfile)
middleware เปิดกว้างให้ api/template, api/swagger, note/swagger-ui
จาก code review: จำกัดไฟล์แนบ 100MB ทั้ง extract/render ก่อนอ่าน
เข้าหน่วยความจำกัน DoS, คืน 400 แทน 500 เมื่อ JSON ผิด format,
sync คำอธิบาย BadRequest ใน openapi spec ให้ตรง behavior ใหม่
Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-10-07 10:16:12 +07:00
Your Name
8cc15ea75d
refactor(routes): move routes under /note, APIs under /api/note
...
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 52s
Deploy eCourt Note Web to Server / Deploy eCourt Note Web to Server (push) Successful in 13s
Deploy eCourt Note Web to Server / Notify Deployment Status (push) Successful in 1s
เตรียม basePath /docedit เป็นพื้นที่กลางให้แอปพี่น้อง (wordaddin ใน
อนาคต) — เดิม basePath ครอบ /docedit/note ทั้งหมดจนแชร์ prefix ไม่ได้
(Next มี basePath ได้ทางเดียว) และจัด API แยกต้นไม้ตามที่ผู้ใช้ขอ
(1) โครงสร้างใหม่: basePath = /docedit + route หน้าทั้งหมดย้ายเข้า
app/note/** + API 4 เส้นย้ายเข้า app/api/note/** + statics ย้ายเข้า
public/note/** (samples, uploads, pdf.worker) — หน้า/statics URL ปลายทาง
คงเดิมเป๊ะ (/docedit/note/…) ส่วน URL API เปลี่ยนเป็น /docedit/api/note/…
ตามคำขอผู้ใช้ (N10 endpoint เปลี่ยน — ต้องแจ้ง backend e-Court ก่อนใช้)
(2) กลไก: withBasePath (utils/base-path.ts) เติม base + app path /note
ยกเว้น path ที่ขึ้นต้น /api/ เติมแค่ base (caller เขียน /api/note/… เต็ม);
resolvePublicPdfPath root ที่ public/note (traversal guard เดิม);
middleware matcher ยกเว้นใหม่ (note/login, api/note, note/uploads,
note/samples, note/pdf.worker, _next, fonts, favicon) + redirect
/note/login; next.config เพิ่ม redirects /docedit -> /docedit/note;
navigation ภายใน 19 จุด + revalidatePath 3 จุด ใส่ prefix /note;
pdf_url ใน DB เป็น logical path ไม่แก้แม้แต่ตัวเดียว (seed/upload
เขียนค่าเดิม, browser เติมผ่าน withBasePath, disk เติมผ่าน guard)
(3) ตามแก้: patch-pdf-worker (DEST public/note + mkdirSync ปลายทาง),
seed-samples (SAMPLES_DIR), uploadFile (UPLOAD_DIR), Dockerfile/
.dockerignore/.gitignore/docker-compose.test.yml, tests 2 ไฟล์ที่อ้าง
disk path จริง, sync เอกสารทั้งหมด (CLAUDE.md invariant #6/#12 + tree,
README, docs/API-DOCUMENTS.md, .claude/rules/*, sync-db skill)
(4) !! ตอน deploy ต้องย้าย volume ตาม: mount /app/public/uploads ->
/app/public/note/uploads + ย้ายไฟล์ใน volume ให้ตรงตำแหน่ง ไม่งั้นไฟล์
ที่เคยอัปโหลด (พร้อม annotation ที่ผูกอยู่) 404 ทันทีที่สลับ image
(5) verify: build ผ่าน (route table /api/note/** + /note/** ครบ), tsc,
vitest 215/215, curl ครบทุก endpoint (login/redirect chain, worker,
samples, uploads, SSE, api/pdf, export, N8 register, N10 bundle + deep
link), Playwright browser จริง (canvas + textLayer ไทย render, browser
ยิง URL ใหม่ทั้งหมดไม่มี request ไป URL เก่า), plan-reviewer +
code-reviewer 3 รายงาน 0 blocking
Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-10-06 14:55:07 +07:00
Your Name
b123ecc9b2
fix(pdf-selection): match text to dragged lines
...
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 52s
Deploy eCourt Note Web to Server / Deploy eCourt Note Web to Server (push) Successful in 13s
Deploy eCourt Note Web to Server / Notify Deployment Status (push) Successful in 1s
ชื่อ bookmark/ข้อความ copy จากการคลุมข้อความเพี้ยน 3 รูปแบบบน
เอกสารไทย (คำขอผู้ใช้ 2026-10-05 รายงานบนฟอร์มราชการ
"แบบบันทึกผลการทดลองปฏิบัติหน้าที่ราชการ" ที่บรรทัดคุมชิด ~16px)
(1) สองบรรทัดสอดแทรกตัวต่อตัว ("แบบบันทึกผ(สำลกหารัรบท…") —
บรรทัดไทยคุมชิด rect สูง ~16px เท่าระยะห่างบรรทัด ตัวอักษร
ของบรรทัดถัดไป (สระบนดันกรอบขึ้น) เหลื่อมกับ rect บรรทัดก่อน
หน้าจริง 1-3px: hit-test เดิม (findIndex ตัวแรกที่เจอ + eps)
จับข้ามบรรทัดจนโดนเรียงรวมกันด้วย x — แก้ selectionTextWithin
เป็นประตู 2 ชั้น: จุดกึ่งกลางแนวตั้งของตัวอักษรต้องอยู่ในแถบ
ของ rect (เกณฑ์หลักแยกบรรทัด) แล้วเลือก rect ที่ซ้อนทับมากสุด
(2) ลากบรรทัดเดียวแต่ได้หลายบรรทัด — DOM range ของ pdf.js มักครอบ
span บรรทัดถัดไปด้วย (ลากจบท้ายบรรทัด/เลยขอบขวา แล้ว caret
ปลายทางหลุดเข้า span ถัดไป: range.toString() ได้ 3 บรรทัดทั้งที่
ลากบรรทัดเดียว) — ประตูจุดกึ่งกลางใน (1) ตัดตัวอักษรนอกแถบ
ทิ้งเอง ได้เฉพาะข้อความที่เห็นบนจอจริง (ดีกว่า native)
(3) ✓ checkbox คอลัมน์ขวาติดมากับบล็อกที่เลือก — pdf.js วาง span ✓
ไว้ระหว่างบรรทัดข้อความพอดีจน native selection ครอบมันตาม DOM
(raw toString มี ✓ เหมือนกัน) — rectsInSweptPath: บรรทัดกลาง
เดิมเก็บทั้งบรรทัดแบบไม่ดูแกน x เปลี่ยนเป็นเก็บเฉพาะช่วงคอลัมน์
เดียวกับข้อความที่เก็บได้จริง: ยึดขอบซ้าย/ขวาจริงของ rect ที่เก็บ
ได้จากบรรทัดแรก/สุดท้าย + ขยายริมละ 2×lineTol (ทน justify gap
ระหว่าง rect, ตาม zoom อัตโนมัติ) — span ที่ห่างเกิน (✓ gap
30px+) ถูกตัดทั้งจากข้อความและกรอบมาร์กเหลือง (rect ชุดเดียวกัน)
(4) แยกบรรทัด/เรียงลำดับกลุ่ม rect ใช้เกณฑ์เดียวกัน — เดิม sameLine
(แทรก \n) ใช้ |midY| ≤ max(height) ซึ่งตัดสินผิดกับบรรทัดคุมชิด
(16 ≤ 16 = บรรทัดเดียวกัน) และ comparator เรียง rectOrder ก็ใช้
heuristic เดิมจนย่อหน้าที่บรรทัดแรกเยื้องเข้า (left มากกว่า
บรรทัดถัดไป) โดนเรียงตาม left กลายเป็นข้อความสลับลำดับบรรทัด —
ทั้งคู่เปลี่ยนมาใช้ "rect ไม่ซ้อนทับแนวตั้งกัน = คนละบรรทัด"
(5) แก้ตาม code review 2026-10-05 ข้อ 1: หน้าต่างคอลัมน์ของบรรทัด
กลางห้ามยึดพิกัดจุดลากตรง ๆ (ลากชัน/ลาก margin ซ้ายแล้ว rect
ครึ่งขวาของบรรทัดกลางโดนตัด) — ยึดขอบจริงตาม (3) แทน; doc comment
ของ rectsInSweptPath ที่ยังบอก "ไม่ตัดแกน x" sync ให้ตรงพฤติกรรม
(6) tests: coordinates +3 เคส (✓ คนละคอลัมน์ถูกตัด + rect คอลัมน์
เดียวกันยังเก็บ / ลากชันผ่าน justify gap ยังเก็บเต็มบรรทัด /
ลาก margin ซ้ายบรรทัดกลางไม่หาย) — รวม 215/215 ผ่าน + tsc
(7) docs sync: rules/pdf-viewer.md + rules/coordinates.md
ยืนยันใน browser จริง (Playwright) บนไฟล์ฟอร์มราชการของผู้ใช้:
heading บรรทัดเดียว / 2 บรรทัด / ลากเลยขอบขวา / บล็อก ๑+๒ ปกติ
และลากชัน — ชื่อ bookmark ตรงเป๊ะ ไม่มีข้อความปน/สลับ/✓ ติดมา
Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-10-05 11:31:33 +07:00
Your Name
c08aaab1ed
fix(upload): decode mojibake of Thai filenames
...
อัปโหลดไฟล์ชื่อไทยแล้ว title ที่แสดงในรายการเพี้ยนเป็น latin1
mojibake ("คำร้องทดสอบอัปโหลด.pdf" → "คำรเน‰à¸à¸‡…") — คำขอผู้ใช้ 2026-10-05
(1) สาเหตุ: browser ใส่ชื่อไฟล์ (UTF-8) ลง header Content-Disposition
ของ multipart body ตรง ๆ แต่ undici ของ Node (ตัว parse
request.formData() ที่ Next.js ใช้) ถอดรหัส header เป็น latin1
ทำให้ file.name ใน Server Action เพี้ยนก่อนถึงโค้ด — เนื้อไฟล์
ไม่เกี่ยว (ผ่าน arrayBuffer() ตรง ๆ) พิสูจน์ด้วย Playwright
อัปโหลดจริงก่อนแก้ได้สตริง mojibake ตรงกับที่ผู้ใช้รายงาน
(2) แก้: utils/upload-name.ts ใหม่ — decodeUploadFilename เข้ารหัส
ชื่อกลับเป็น byte แล้วถอดด้วย UTF-8 ให้คืนชื่อเดิม พร้อม guard
2 ชั้นกันแตะชื่อที่ถูกต้องอยู่แล้ว: ไม่มีตัวอักษรช่วง
U+0080–U+00FF เลย = ASCII ล้วน/ชื่อถูกอยู่แล้ว (ไทย ≥ U+0E00)
ไม่แต้อยู่เดิม / ถอดแล้วได้ U+FFFD (ไบต์ไม่ประกอบ UTF-8 สมบูรณ์
= ชื่อเป็น latin1 จริง) เก็บของเดิม — uploadFile เรียกครั้งเดียว
ที่จุดเข้า ก่อนใช้ทั้ง allowedExt และ sanitizeTitle
(3) tests: tests/utils/upload-name.test.ts ใหม่ 5 เคส (ชื่อไทย
mojibake → คืนเดิม / ไทยผสมตัวเลขวงเล็บ / ASCII ไม่เปลี่ยน /
ชื่อถูกอยู่แล้วไม่ถูกแตะ / latin1 จริงเก็บของเดิม)
(4) code review 2026-10-05: ยอมรับความเสี่ยง false positive กรณีชื่อ
latin1 จริงที่ byte คู่กันบังเอิญประกอบเป็น UTF-8 ครบ ("¢.pdf")
— แคบมากเพราะ browser ส่งชื่อเป็น UTF-8 เสมอ และ heuristic นี้
คือ workaround มาตรฐานของปัญหา undici (บีบ guard เพิ่มจะทำให้
ชื่อ latin1-accent ที่โดน mojibake จริง ๆ กลับไม่ถูกซ่อม)
(5) docs sync: CLAUDE.md (tree entry) + rules/data-layer.md (กติกา
uploadFile)
ผ่าน tsc + vitest + ยืนยันใน browser ว่าการ์ดแสดงชื่อไทยตรงเป๊ะ
Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-10-05 11:30:56 +07:00