All checks were successful
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>
41 lines
2.3 KiB
TypeScript
41 lines
2.3 KiB
TypeScript
"use server";
|
||
|
||
// mock login e-Court: รับเฉพาะผู้ใช้ใน MOCK_USERS (user1–user5) + Demo1234
|
||
// → ตั้ง cookie oacCase ตาม format จริง (identity ของแอปอ่านผ่าน lib/session.ts
|
||
// — บังคับด้วย middleware.ts)
|
||
import { cookies } from "next/headers";
|
||
import { redirect } from "next/navigation";
|
||
|
||
import { buildOacCaseCookie, MOCK_USERS, OAC_CASE_COOKIE } from "@/utils/user";
|
||
|
||
const MOCK_PASSWORD = "Demo1234";
|
||
|
||
export async function login(
|
||
username: string,
|
||
password: string,
|
||
): Promise<{ ok: true } | { ok: false; error: string }> {
|
||
// mock ตอบเร็วกว่าตา — หน่วงเล็กน้อยให้เห็นสถานะ "กำลังตรวจสอบ…"
|
||
await new Promise((resolve) => setTimeout(resolve, 500));
|
||
const name = username.trim();
|
||
// username มาจาก dropdown เลือกผิดไม่ได้ — เช็ครายชื่อฝั่ง server เป็น defense กัน hand-craft
|
||
if (!MOCK_USERS.includes(name) || password !== MOCK_PASSWORD) {
|
||
return { ok: false, error: "รหัสผ่านไม่ถูกต้อง" };
|
||
}
|
||
// u_userid ต่างกันต่อคน (= u_userlogin) — สิทธิ์ "แก้ไขได้เฉพาะของตัวเอง" (guard ด้วย
|
||
// userId) ต้องแยกกันจริงทั้ง 5 คน ห้ามแชร์ค่าเดียว
|
||
cookies().set(OAC_CASE_COOKIE, buildOacCaseCookie(name, name), {
|
||
httpOnly: true,
|
||
sameSite: "lax",
|
||
path: "/",
|
||
maxAge: 8 * 60 * 60,
|
||
});
|
||
return { ok: true };
|
||
}
|
||
|
||
export async function logout(): Promise<void> {
|
||
// ลบ cookie ด้วย path "/" เท่านั้น (ตรงกับตอน set) ไม่งั้น browser ไม่ลบให้
|
||
cookies().set(OAC_CASE_COOKIE, "", { path: "/", maxAge: 0 });
|
||
// redirect ใน action เอง — ให้ Next จัดการ navigation กลไกเดียว
|
||
// (ถ้าให้ client router.replace เอง จะชนกับ re-render ของหน้ารายการที่เพิ่งเสีย session จน navigation ไม่เกิด)
|
||
redirect("/note/login");
|
||
}
|