/** * RAG Portal 前端殼 — P3:GET /portal 單檔 HTML(portal-auth design §1 D-1/§6,Gitea #24/#25) * * 形態(D-1):cypher-executor 的新路由、**獨立 HTML 殼**——不在 console 上加 if(「Admin * Console 現狀不動」鐵律)。樣式重用 console 的設計語言(紙感/明體標題/琥珀強調/深淺主題), * 取捨=**複製樣式子集而非抽共用模組**:抽共用要動 console.ts(違「console 不動」),且兩頁 * 受眾不同(owner vs 同仁)預期各自演化;代價=樣式雙份、改版要兩邊同步(PR 說明誠實記)。 * * 頁面(design §6,leo 頁面級拍板): * - 登入頁(brand=CONSOLE_BRAND;未登入只見這個殼) * - 搜尋頁(落地頁):keyword/semantic/graph 三模式+結果含 source 溯源+卡片詳頁 * - 設定頁:改自己密碼、看自己角色與可查庫、主題切換 * - 工作流頁:D-8 定案 admin 可見(PORTAL_SHOW_WORKFLOWS 可調 all/off);唯讀、不開 trigger * - graph 模式按 D-4 粗閘顯示(/portal/session 的 graph_allowed) * - 管理頁(P4,admin-only nav):帳號管理(列表/新增/停用/重設密碼+每帳號勾選可查庫 * 含 ["*"] 全庫)+庫目錄管理(登記/停用/graph_source 標記)。一次性密碼只在畫面顯示 * 一次(server 不留明碼);一般用戶 nav 不顯示、直打 /portal/admin/* 也是 403(role 閘)。 * - Mira 專屬頁(駕駛艙/分流台/憑證/專案)一律不進 * * 安全(design §3.3 關鍵差異 vs console): * - 本頁 JS **沒有任何租戶字串、沒有 X-Arcrun-API-Key**——只持 portal session token * (localStorage `arcrun_portal_session`),一切資料走 /portal/data/*(server-side enforce)。 * - 前端的「藏」(graph 模式不顯示、工作流頁不顯示)只是 UX;真閘在 /portal/data/* 路由層 * (403/404),curl 直打也繞不過(#48 guard 精神)。 * - 所有動態內容經 esc() 跳脫(防 XSS,同 console 慣例);密碼欄位值不進 log、不進 URL。 * * 薄殼(rule 07):零業務邏輯,只 fetch /portal/* 端點渲染。 */ import { Hono } from 'hono'; import type { Bindings } from '../types'; import { TAIPEI_CLIENT_JS } from '../lib/taipei-time'; export const portalUiRouter = new Hono<{ Bindings: Bindings }>(); function renderPortalHtml(brand: string, sourceWebBase = ''): string { return `