Merge branch 'forgejo-dev' into working
All checks were successful
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>
This commit is contained in:
Your Name 2026-11-02 10:07:08 +07:00
commit a288e5643c
31 changed files with 1445 additions and 278 deletions

View file

@ -4,6 +4,7 @@ import { afterEach, describe, expect, it } from "vitest";
import {
issueDocverToken,
parseTokenRequestBody,
requireDocumentApiKey,
requireDocverApiKey,
requireDocverBearer,
verifyDocverLoginToken,
@ -31,12 +32,16 @@ function ok<T>(result: { ok: boolean } & T): asserts result is { ok: true } & T
// --- env save/restore — require* อ่าน env ตอนเรียก ไม่ใช่ตอน module load
const HAD_API_KEY = Object.prototype.hasOwnProperty.call(process.env, "DOCVER_API_KEY");
const ORIG_API_KEY = process.env.DOCVER_API_KEY;
const HAD_EXTERNAL_KEY = Object.prototype.hasOwnProperty.call(process.env, "EXTERNAL_API_KEY");
const ORIG_EXTERNAL_KEY = process.env.EXTERNAL_API_KEY;
const HAD_SECRET = Object.prototype.hasOwnProperty.call(process.env, "DOCVER_TOKEN_SECRET");
const ORIG_SECRET = process.env.DOCVER_TOKEN_SECRET;
afterEach(() => {
if (HAD_API_KEY) process.env.DOCVER_API_KEY = ORIG_API_KEY;
else delete process.env.DOCVER_API_KEY;
if (HAD_EXTERNAL_KEY) process.env.EXTERNAL_API_KEY = ORIG_EXTERNAL_KEY;
else delete process.env.EXTERNAL_API_KEY;
if (HAD_SECRET) process.env.DOCVER_TOKEN_SECRET = ORIG_SECRET;
else delete process.env.DOCVER_TOKEN_SECRET;
});
@ -176,6 +181,59 @@ describe("requireDocverApiKey", () => {
});
});
// requireDocumentApiKey — auth ของ GET /api/documents/latest: x-api-key เทียบ
// DOCVER_API_KEY หรือ EXTERNAL_API_KEY ตัวใดตัวหนึ่ง (ต่างจาก /token)
describe("requireDocumentApiKey", () => {
function setKeys(docver: string | undefined, external: string | undefined): void {
if (docver === undefined) delete process.env.DOCVER_API_KEY;
else process.env.DOCVER_API_KEY = docver;
if (external === undefined) delete process.env.EXTERNAL_API_KEY;
else process.env.EXTERNAL_API_KEY = external;
}
it("ทั้งคู่ไม่ตั้ง / ว่าง / ช่องว่างล้วน = 401 fail closed ทุกกรณี", async () => {
for (const [docver, external] of [
[undefined, undefined],
["", ""],
[" ", " "],
] as const) {
setKeys(docver, external);
const res = requireDocumentApiKey(reqWithKey("test-key"));
expect(res?.status, `${docver}/${external}`).toBe(401);
expect(await bodyOf(res!)).toEqual({ error: "ระบบยังไม่ได้ตั้งค่า API key" });
}
});
it("ตั้งอันเดียวตัวไหนก็ผ่านเมื่อ key ตรงตัวนั้น · ตั้งทั้งคู่ตรงอันที่สองก็ผ่าน", () => {
setKeys("docver-key", undefined);
expect(requireDocumentApiKey(reqWithKey("docver-key"))).toBeNull();
expect(requireDocumentApiKey(reqWithKey("external-key"))?.status).toBe(401);
setKeys(undefined, "external-key");
expect(requireDocumentApiKey(reqWithKey("external-key"))).toBeNull();
expect(requireDocumentApiKey(reqWithKey("docver-key"))?.status).toBe(401);
setKeys("docver-key", "external-key");
expect(requireDocumentApiKey(reqWithKey("external-key"))).toBeNull();
});
it("ไม่ใส่ header กับ key ผิด ต้องได้ 401 ข้อความเดียวกันเป๊ะ (กัน enumerate)", async () => {
setKeys("docver-key", "external-key");
const missing = requireDocumentApiKey(reqWithKey());
const wrong = requireDocumentApiKey(reqWithKey("not-any-key"));
expect(missing?.status).toBe(401);
expect(wrong?.status).toBe(401);
expect(await bodyOf(missing!)).toEqual(await bodyOf(wrong!));
});
it("env มี whitespace รอบ ๆ ก็ trim ให้เทียบได้ทั้งสองตัว", () => {
setKeys(" docver-key ", undefined);
expect(requireDocumentApiKey(reqWithKey("docver-key"))).toBeNull();
setKeys(undefined, " external-key ");
expect(requireDocumentApiKey(reqWithKey("external-key"))).toBeNull();
});
});
describe("requireDocverBearer", () => {
it("ทุกกรณีพ้นสภาพ (ไม่มี header / ไม่ใช่ Bearer / token เน่า / หมดอายุ / secret ไม่ตั้ง) = 401 ข้อความเดียวกัน", async () => {
const deniedBodies = new Set<string>();