feat(console): Mira Console 完整版——8 頁紙感定稿視覺 SPA (Arcrun#3 console 系)

依 claude design 定稿(紙感暖黑 2a)+ brief 8 頁資訊架構重寫 /console:
- 單檔 HTML+原生 JS hash routing,零外部資源;紙紋底/明體標題/琥珀強調/
  呼吸球嵌「安/趕/滯」;手機優先 390px + ≥1024px 側欄 + 底部五 tab
- 頁1 登入/首次設定(沿 console-auth v1)|頁2 駕駛艙(dashboard-data,60s 刷新;
  /console/dashboard 免登入獨立頁同步換視覺)|頁3 總庫搜尋(entry_type chips=
  KBDB 真能篩的欄位、共 N 筆中命中 M(誠實 total)、語意未啟用誠實降級 banner)|
  頁4 卡片詳頁+關聯(Markdown 渲染 + kbdb-graph-plugin 鄰居放射圖,經新 proxy
  /kbdb/graph/neighbors/:name;無 triplet →「尚無關聯資料」)|頁5 工作流
  (/webhooks/named list+手動觸發+最近執行;釘選 localStorage;v0 零件/recipes
  查詢收進本頁折疊區不砍功能)|頁6 憑證(新 GET /credentials/catalog D1 目錄
  唯讀,絕不回密文;新增/替換走既有端點;刪除未接 D1(T9)標「即將開通」)|
  頁7 收件匣(新 GET /console/inbox-data,session 鎖——訊息原文屬機敏)|
  頁8 設定(vectorize 狀態探測讀 search 降級訊號、開關標「即將開通」、
  /console/setup/reset 換帳密、系統資訊)

驗證:tsc exit 0;vitest 26/27(1 失敗 stash 複驗 pre-existing);wrangler
deploy --dry-run 打包過;inline JS 求值後 node --check + esc/mdRender/parseAtMs
純函式行為測試全過(含 XSS escape)。未部署,留總管接手。
This commit is contained in:
uncle6me-web
2026-07-04 17:49:49 +08:00
parent 5174ed6821
commit f9e44abc59
6 changed files with 1364 additions and 376 deletions
+27
View File
@@ -189,6 +189,33 @@ kbdbProxyRouter.get('/kbdb/entries/:id', async (c) => {
return new Response(res.body, { status: res.status, headers: { 'Content-Type': 'application/json' } });
});
// ── graphkbdb-graph-plugin proxyMira Console 卡片詳頁「關聯視圖」)───────────────
//
// 純轉發(rule 07):圖能力真身在 kbdb-graph-plugin workerGET /graph/neighbors/:name
// 回 { node, edges[], neighbors[], edgeCount, neighborCount })。瀏覽器不能持 KBDB_INTERNAL_TOKEN
// 故經 cypher 代轉(token 只在 server 側)。plugin base 現算慣例同 registry/KBDB_BASE_URL。
function graphBase(env: Bindings): string {
if (env.KBDB_GRAPH_URL) return env.KBDB_GRAPH_URL.replace(/\/$/, '');
return `https://kbdb-graph-plugin.${env.WORKER_SUBDOMAIN}.workers.dev`;
}
// GET /kbdb/graph/neighbors/:name — 查某節點(entity/卡片名)的鄰居 + 邊。
// 查無 triplet 資料時 plugin 回空陣列——前端據此顯示「尚無關聯資料」(誠實,不編造關聯)。
kbdbProxyRouter.get('/kbdb/graph/neighbors/:name', async (c) => {
if (!tenant(c)) return c.json(NEED_KEY, 401);
const base = graphBase(c.env);
const headers: Record<string, string> = {};
if (c.env.KBDB_INTERNAL_TOKEN) headers['Authorization'] = `Bearer ${c.env.KBDB_INTERNAL_TOKEN}`;
try {
const res = await fetch(`${base}/graph/neighbors/${encodeURIComponent(c.req.param('name'))}`, { headers });
return new Response(res.body, { status: res.status, headers: { 'Content-Type': 'application/json' } });
} catch (e) {
// plugin worker 沒部署 / 不可達 → 誠實回報(前端顯示「關聯服務未部署」而非假裝無關聯)
return c.json({ error: `kbdb-graph-plugin 不可達(${base}):${e instanceof Error ? e.message : String(e)}` }, 502);
}
});
// PATCH /kbdb/entries/:id — 更新單筆 entry。owner_id 不可被改(剝除 caller 自帶的 owner_id)。
kbdbProxyRouter.patch('/kbdb/entries/:id', async (c) => {
if (!tenant(c)) return c.json(NEED_KEY, 401);