เปลี่ยน 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>
50 lines
2.9 KiB
TypeScript
50 lines
2.9 KiB
TypeScript
"use client";
|
|
|
|
// Genesis (docs/DESIGN.md) Navigation: sticky แถบบน 56px + backdrop-blur + เส้นแบ่ง
|
|
// ล่าง 1px — โลโก้ซ้าย / ผู้ใช้ + ออกจากระบบ ขวา ใช้ที่หน้าแรก /note และหน้า
|
|
// เครื่องมือทดสอบ /note/dev — หน้ากลุ่ม /note/groups/[typeId] เลิกใช้ตาม
|
|
// redesign 2026-09-24 (ไม่มี navbar เหลือ back link)
|
|
//
|
|
// ฉบับ token 2026-10-30: เป็น client component อ่าน session จาก localStorage
|
|
// ("note-auth") เอง — มี = ชื่อผู้ใช้ + ปุ่มออกจากระบบ (ล้าง token), ไม่มี =
|
|
// hint "เข้าผ่านลิงก์ e-Court" (ไม่มีปุ่ม logout — ไม่มี session ให้ล้าง) ·
|
|
// อ่านหลัง mount กัน hydration mismatch (server ไม่รู้ localStorage)
|
|
import Link from "next/link";
|
|
import { useEffect, useState } from "react";
|
|
|
|
import LogoutButton from "./LogoutButton";
|
|
import { readNoteSession, type NoteSession } from "@/lib/note-session";
|
|
|
|
export default function AppNav() {
|
|
const [session, setSession] = useState<NoteSession | null>(null);
|
|
useEffect(() => {
|
|
setSession(readNoteSession());
|
|
}, []);
|
|
|
|
return (
|
|
// appnav: hook ของกฎ coarse 44px ให้ปุ่มในแถบนี้ (globals.css — อย่าถอด)
|
|
<header className="appnav sticky top-0 z-40 h-14 border-b border-line bg-white/80 backdrop-blur">
|
|
<div className="mx-auto flex h-full max-w-7xl items-center justify-between gap-3 px-4 sm:px-6">
|
|
{/* ชื่อแบรนด์เป็น ink ตามกฎ "น้ำเงินเฉพาะของ interactive" — tile JN น้ำเงินคือ
|
|
แบรนด์มาร์ก (สืบต่อจากหน้าสรุป/login ดั้งเดิม) ทั้งแถบเป็นลิงก์กลับหน้าหลัก */}
|
|
<Link
|
|
href="/note"
|
|
className="focus-ring flex min-w-0 items-center gap-2.5 rounded-md"
|
|
>
|
|
<span className="flex h-8 w-8 shrink-0 items-center justify-center rounded-lg bg-primary text-xs font-bold text-white">
|
|
JN
|
|
</span>
|
|
<span className="font-display truncate text-[15px] font-bold tracking-tight text-ink">
|
|
Judge's Note
|
|
</span>
|
|
</Link>
|
|
<div className="flex min-w-0 items-center gap-3">
|
|
<p className="hidden min-w-0 truncate text-sm text-ink-soft sm:block">
|
|
{session?.username ?? "เข้าผ่านลิงก์ e-Court"}
|
|
</p>
|
|
{session !== null && <LogoutButton onCleared={() => setSession(null)} />}
|
|
</div>
|
|
</div>
|
|
</header>
|
|
);
|
|
}
|