import Link from "next/link"; import { notFound } from "next/navigation"; import AppNav from "@/components/AppNav"; import DevLoginLinkForm from "@/components/DevLoginLinkForm"; import MockEcourtForm from "@/components/MockEcourtForm"; import { devMintEnabled } from "@/lib/note-auth"; export const dynamic = "force-dynamic"; /** * /note/dev — เครื่องมือทดสอบ (ย้ายมาจากหน้าแรก 2026-10-30 ตอน /note เปลี่ยนเป็น * landing เรียบ): เปิดเอกสารตัวอย่าง (D14) + คู่มือทางเข้า SSO 2 แบบ + ตัวช่วย mint * ลิงก์ทดสอบ · **เปิดด้วย env NOTE_SSO_DEV_MINT=1 อย่างเดียว — 0/ไม่ตั้ง = 404 * ทั้งหน้า** (คำขอผู้ใช้ 2026-11-02, ใช้ได้ทุก environment รวม production ที่ตั้ง env * ไว้) · ตัวอย่างลิงก์เป็น "รูปแบบ + ขั้นตอน" เท่านั้น ไม่มี secret ในหน้า * · ไม่ query DB · แบนเนอร์ ?sso= และ bootstrap ของ N8 ?fileUrl= อยู่ที่หน้าแรก /note */ /** รายการเอกสาร fixed ในโค้ด (D14) — จาก PDF ตัวอย่างที่มีอยู่จริงของแอป * (public/note/samples/ — ยังไม่ seed กดเปิดแล้ว N8 จะตอบ error เองที่หน้าแรก) */ const SAMPLE_DOCS: Record = { "คำสั่งศาล — คดี 01012-672427": [ "01012-672427-1f-690609-0000840223.pdf", "01012-672427-4f-690609-0000840227.pdf", ], "คำสั่งศาล — ตัวอย่าง": [ "court_command_11_123 1.pdf", "court_command_1_2547 1.pdf", ], }; /** code inline สไตล์เดียวกันทั้งหน้า */ function Code({ children }: { children: React.ReactNode }) { return ( {children} ); } /** บล็อกคำสั่ง/URL ตัวอย่าง — อ่านง่ายบนจอแคบ (scroll แนวนอนได้) */ function CodeBlock({ children }: { children: string }) { return (
      {children}
    
); } export default async function DevToolsPage({ searchParams, }: { searchParams: Record; }) { // ทางเข้าหน้านี้คุมด้วย env ตัวเดียวกับ dev-link (2026-11-02) — ปิด = 404 // เหมือนไม่มีหน้านี้ ไม่เหลือแค่ข้อความแนะนำ if (!devMintEnabled()) notFound(); // โหมด credential ของลิงก์ตัวอย่าง — jwt (default) | ticket สลับผ่าน ?sampleMode= const sampleMode = searchParams.sampleMode === "ticket" ? "ticket" : "jwt"; const mintOn = devMintEnabled(); return ( <>

เครื่องมือทดสอบ

หน้านี้สำหรับผู้พัฒนาและผู้ทดสอบ — ลองเปิดเอกสารตัวอย่าง และดูวิธีเข้าระบบผ่านลิงก์ของ e-Court

{/* ── เปิดเอกสารตัวอย่าง (D14) — A Link Get(new) แบบ /tmpl ─────────── */}

เปิดเอกสารตัวอย่าง

กดชื่อไฟล์แล้วระบบจะสร้างลิงก์แบบเดียวกับที่ e-Court ส่งมาให้ (แบบ{sampleMode === "jwt" ? " JWT" : " ticket"}) แล้วพาเข้าไปอ่านเอกสารนั้น ทันที — ดูแถบที่อยู่ของ browser จะเห็นการทำงานทีละขั้นจริง ๆ ตั้งแต่เข้าระบบจนถึงหน้าอ่านเอกสาร

{mintOn ? (
รูปแบบลิงก์: {(["jwt", "ticket"] as const).map((m) => ( {m === "jwt" ? "JWT (ทาง A)" : "ticket (ทาง B)"} ))}
) : (

ลิงก์ด้านล่างเปิดเอกสารด้วย session ที่มีอยู่เท่านั้น — ยังไม่มี session ใช้ฟอร์ม "จำลอง e-Court" ด้านล่างแทน · dev server ที่ต้องการ mint ให้อัตโนมัติ (ไม่ต้องใส่ key) เพิ่ม NOTE_SSO_DEV_MINT=1{" "} แล้วรีสตาร์ท

)}

A Link Get(new)

{Object.entries(SAMPLE_DOCS).map(([group, files]) => (

{group}

    {files.map((file) => (
  • {file}
  • ))}
))}
{/* จำลอง e-Court ด้วย API key — แสดงทุก environment (คำขอผู้ใช้ 2026-11-02: ใช้ทางเข้าจริงเหมือน e-Court ต้องถือ DOCVER_API_KEY จึงไม่เป็นประตูหลัง) */}

จำลอง e-Court — ขอ token ด้วย API key

ใส่ API key (DOCVER_API_KEY) เพื่อขอ token ผ่าน{" "} POST /api/note/auth/token แบบเดียวกับที่ e-Court ทำจริง แล้วระบบจะพาเข้าอ่านเอกสารที่เลือกเลย — ได้ session 8 ชม. (ใช้ได้ทั้ง dev และ production)

{/* ── Flow ─────────────────────────────────────────────────────────── */}

Flow

e-Court สร้างลิงก์เข้าใช้ (มีตัวยืนยันตัวตน + ที่อยู่ไฟล์)
↓
ผู้ใช้กดลิงก์ → ระบบตรวจแล้วออก session ให้ (ใช้ได้ 8 ชม.)
↓
เข้าหน้าอ่านเอกสาร — มาร์กข้อความ วาดเส้น คอมเมนต์ คั่นหน้า
↓
บันทึกทันทีทุกครั้งที่แก้ คนอื่นที่เปิดไฟล์เดียวกันเห็นสด ๆ
↓
ดูรวมทั้งหมดได้ที่หน้า "สรุปหมายเหตุ" และ "เอกสารในกลุ่ม"
↓
ระบบอื่น (เช่น e-Court) ดึงข้อมูลไปใช้ต่อได้ผ่าน API
{/* ── ทางเข้าระบบผ่านลิงก์ e-Court (คู่มือ 2 ทาง ตามแผน SSO) ────────── */}

ทางเข้าระบบผ่านลิงก์ e-Court (SSO)

แอปนี้ไม่มีหน้าล็อกอิน — ผู้ใช้เข้าได้ทางลิงก์ที่ e-Court สร้างให้อย่างเดียว ลิงก์มี 2 แบบ (ใช้แบบใดแบบหนึ่ง — ห้ามใส่ครบทั้งสองแบบในลิงก์เดียว)

{/* ทาง A — JWT */}

ทาง A — ขอ token (JWT)

แนะนำ

เหมาะเมื่อสร้างลิงก์ให้ผู้ใช้ได้ตอนนั้นเลย (ตอนเปิดหน้า) — คีย์ที่ใช้เซ็น อยู่ฝั่ง server เท่านั้น ปลอมตัวเป็นผู้อื่นไม่ได้

  1. ฝั่ง e-Court เรียก POST /api/note/auth/token พร้อมรหัส{" "} x-api-key (ค่าจาก env DOCVER_API_KEY) และข้อมูล:
{`POST {BASE}/api/note/auth/token x-api-key: {"userid":"","username":"", "fid":"","ttl_seconds":300}`}
  1. ได้ access_token กลับมา (อายุ 1–10 นาที) — นำไปใส่ในลิงก์ เป็นพารามิเตอร์ t ให้ผู้ใช้กด:
{`{BASE}/note ?fileUrl=&typeId=<กลุ่ม ทางเลือก>&t=`}
  1. ผู้ใช้กดลิงก์ → ระบบตรวจแล้วออก session ให้ (เก็บในเครื่องของผู้ใช้ อายุ 8 ชม.) → พาเข้าหน้าอ่านเอกสาร และตัดค่า token ออกจากลิงก์ทันที

ข้อควรระวัง: สร้าง token ตอนแสดงหน้าให้ผู้ใช้คนนั้น ๆ เท่านั้น และ{" "} ห้ามเก็บ token ไว้ในที่อื่น {" "} (อายุสั้นมาก) · token นี้ใช้เข้าแอปนี้อย่างเดียว เอาไปเรียก API กลุ่ม documents ไม่ได้

{/* ทาง B — ticket */}

ทาง B — ticket

สร้างล่วงหน้า

เหมาะเมื่อสร้างลิงก์เตรียมไว้ล่วงหน้าได้ ไม่ต้องเรียก API ตอนแสดงหน้า — ใช้คู่ ticket_data + HmacSha256 ชุดเดียวกับที่ฝัง ไว้ในไฟล์ .docx ของ add-in

  1. e-Court เตรียม ticket_data (JSON มี{" "} exp/fid/uid/uname) แล้วเซ็น HmacSha256 ด้วยคีย์{" "} ADDIN_TICKET_SECRET
  2. นำมาใส่ในลิงก์ให้ผู้ใช้กด:
{`{BASE}/note ?fileUrl=&typeId=<ทางเลือก> &ticket_data=&HmacSha256=`}
  1. ผู้ใช้กดลิงก์ → ระบบตรวจลายเซ็นและวันหมดอายุ → ออก session 8 ชม. → พาเข้าหน้าอ่านเอกสาร

ข้อควรระวัง: ตั้ง exp สั้น ๆ (ไม่เกิน ~10 นาที เพราะค่าเหล่านี้ อยู่ในลิงก์) · ค่าผู้ใช้ (userid/username ทาง A,{" "} uid/uname ทาง B) ต้องเป็นคนเดียวกับ{" "} u_userid/u_userlogin ของ e-Court — ใส่ผิดคน = ข้อมูล ผูกกับคนผิด

{/* dev tool — ฟอร์ม mint ลิงก์ (เปิดด้วย env NOTE_SSO_DEV_MINT=1 เท่านั้น: ตัวช่วยทดสอบ ไม่ใช่ทางเข้าจริง และตายเสมอใน production build) */} {devMintEnabled() && (

สร้างลิงก์ทดสอบเอง (dev)

กรอกชื่อผู้ใช้แล้วกดสร้าง — ระบบจะสร้างลิงก์ให้จากคีย์ของตัวเอง (ค่าลับไม่โผล่ที่หน้าเว็บ) ได้ลิงก์มาแล้วกด "เปิดลิงก์" เพื่อทดสอบเข้าจริงทั้งสองแบบ

)} {/* dev tool — สคริปต์มือ (ใช้ได้เสมอ ไม่ขึ้นกับ env) */}

ทดสอบบนเครื่อง (dev)

สร้างลิงก์เองบนเครื่องโดยไม่ต้องรอ e-Court — สคริปต์อ่านค่าจากไฟล์{" "} .env แล้วพิมพ์ลิงก์เต็มให้ก๊อปไปเปิดใน browser:

{`# ทาง A (default) node scripts/make-login-link.mjs --uid oom --uname "สรวิชญ์" --fid F123 \\ --fileUrl /samples/01012-672427-1f-690609-0000840223.pdf \\ --base http://localhost:3000/docedit # ทาง B node scripts/make-login-link.mjs --mode ticket --uid oom --uname "สรวิชญ์" --fid F123 \\ --fileUrl /samples/01012-672427-1f-690609-0000840223.pdf \\ --base http://localhost:3000/docedit`}

ถ้าลิงก์หมดอายุหรือไม่ถูกต้อง ระบบจะพากลับหน้าแรกพร้อมข้อความบอกสาเหตุ — สร้างลิงก์ใหม่แล้วกดอีกครั้งได้เลย

); }