eCourt-note/.claude
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
..
agents BASE_PATH: serve ใต้ /docedit/note — next.config.mjs ตั้ง basePath + env NEXT_PUBLIC_BASE_PATH (ประกาศค่าเดียวที่ไฟล์นี้) ให้ utils/base-path.ts (ใหม่) withBasePath() เติม prefix ให้ URL ฝั่ง browser ที่ Next ไม่เติมให้เอง (fetch/EventSource/plain href/workerSrc/img src): PDFViewer (workerSrc, /api/pdf ซ่อม ToUnicode, fallback file.pdfUrl, image branch N3 รวบเป็นตัวแปร fileUrl, export PDF F8), SSE stream ใน useAnnotationRealtime, ปุ่มส่งออก bookmark JSON (N7) + คัดลอกลิงก์ F12 ใน AnnotationSidebar (window.location.origin + basePath กัน paste แล้ว 404), Export CSV (F16) หน้า summary — <Link>/next/navigation/middleware.ts ไม่ต้องแก้ (Next 14.2 auto-prefix ให้ + matcher จับ path ที่ตัด basePath แล้ว + NextURL redirect ใส่กลับเอง ยืนยันจาก source ใน node_modules) + pdf_url ใน DB เก็บ path ไม่รวม basePath เติมตอนใช้เท่านั้น + sync เอกสารตาม governance: CLAUDE.md invariant #6 (workerSrc ผ่าน withBasePath) + #12 ใหม่ (URL ฝั่ง browser ที่สร้างเองต้องผ่าน withBasePath), PLAN.md A3/§4, rules pdf-viewer/realtime, plan-reviewer #8, README URL localhost:3000/docedit/note — verify: tsc strict ผ่าน + curl (root / 404, ไม่มี cookie 307→/docedit/note/login, worker 200, SSE reachable, มี cookie 200) + Playwright login จริงเปิดไฟล์ 44 หน้า render ครบ (SSE /api/pdf /worker 200 ผ่าน basePath) + code-reviewer ผ่าน (vitest 126 tests, ไม่พบ bug, blob URL ไม่โดน prefix ซ้ำ, env inline ครบทั้ง client/server ตาม define-env-plugin) — deploy: basePath เป็น build-time ต้อง build image ใหม่ถึงจะเสิร์ฟที่ /docedit/note + reverse proxy ต้อง forward path เต็มแบบไม่ strip prefix 2026-09-17 16:06:20 +07:00
rules fix(upload): decode mojibake of Thai filenames 2026-10-05 11:30:56 +07:00
skills bookmark เรียงตามลำดับหน้า (N5) + ค้นหา bookmark พร้อมเงื่อนไข คำค้น/ช่วงหน้า/ผู้บันทึก/ช่วงวันที่ พร้อม bookmarks.user_name (N6) + UX audit sidebar 2026-09-16: เมนู ⋯ แทนไอคอนชุดติดกัน, มุมมองรอง ตามหน้า หัวกลุ่มหุบได้, focus ring, ปุ่ม sidebar จิ้มง่าย >=32px, Escape ปิด dialog ประวัติ + แผนใหม่ docs/PLAN-BOOKMARK.md + agent ui-ux-designer แทน ui-designer + skill frontend-design 2026-09-16 13:24:33 +07:00