feat(console): library-map M5 — 總庫搜尋頁藏書地圖首頁區塊(R4)
library-map SDD M5(Arcrun#39):console 首頁從空白搜尋框變全館地圖。 落點裁定:console 是 hash-routing 單頁殼,home 隨 profile(full=駕駛艙、 rag=總庫搜尋)——地圖放總庫搜尋頁搜尋框上方:rag profile 該頁即首頁; full profile 它是全館入口,「點庫卡片→進該庫搜尋」動線同頁最短,一份實作 兩 profile 全吃。 - console.ts:藏書地圖區塊(lm-section)——每庫一張卡(庫名+narrative+ top entities 前 5 帶 degree+triplet_count+commit_hash/updated 更新時間感)、 跨庫 bridges 有資料才列一小區;兩段式渲染(全館列表先出卡,逐庫詳圖補 degree/bridges,詳圖失敗維持名字版不擋);點庫卡片=設 library filter (/kbdb/search 既有 library 參數,不新造)進庫搜尋,附清除鈕搜回全館; /map 空陣列或錯誤=誠實空狀態(指引 POST /map/recompute backfill), 不擋搜尋等其他功能。品牌沿 CONSOLE_BRAND 變數(#21),零外部庫。 - kbdb-proxy.ts:GET /kbdb/map+/kbdb/map/:library 純轉發(rule 07,聚合真身 在 kbdb base M2)。owner_id 不強制注入只選擇性透傳(同 M4 MCP kbdb_get_map 語意;現行 backfill map block owner=NULL,強制注入租戶=首頁永遠假空狀態); 仍要求 X-Arcrun-API-Key 同閘;KBDB 不可達誠實 502。 - 測試:kbdb-map-proxy.test.ts 9 測(租戶閘/轉發/owner 透傳/空陣列/404/502)+ console-library-map-page.test.ts 6 測(route 200/區塊在搜尋框上方/資料源接線/ library 參數/誠實空狀態文案/品牌變數)——15/15 過。 - 誠實回報:tsc 16 行 ExecutionContext.tracing 錯誤與 executor.test 1 失敗= main 基線既有(git stash 對照實證),非本分支引入。 merge 後需 gated redeploy cypher-executor(leo 閘)。關聯 #39。 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JUmjwkHLVBHM3ydhT1WSW3
This commit is contained in:
@@ -218,6 +218,44 @@ kbdbProxyRouter.get('/kbdb/graph/neighbors/:name', async (c) => {
|
||||
}
|
||||
});
|
||||
|
||||
// ── map(藏書地圖,library-map SDD M5/R4:console 首頁全館地圖)──────────────────
|
||||
//
|
||||
// 純轉發(rule 07):聚合真身在 KBDB 基本盤(kbdb/src/routes/map.ts,M2 已 merge)。
|
||||
// owner_id **不強制注入、只選擇性透傳**——與 MCP kbdb_get_map(M4)同義,理由兩層:
|
||||
// 1. map block 是庫級聚合摘要(narrative+top entities+規模),非租戶私資料;
|
||||
// 2. 現行 backfill recompute 未帶 owner(map block owner_id=NULL)——若比照 search
|
||||
// 強制注入租戶,`e.owner_id = ?` 會把 NULL 列全濾掉 → 首頁永遠假空狀態(不誠實)。
|
||||
// 仍要求 X-Arcrun-API-Key(與本檔其他端點同閘;console 登入後才有 tenant 字串)。
|
||||
|
||||
// GET /kbdb/map — 全館地圖(每庫一行:library+narrative+top 3 entities+triplet_count+updated_at)。
|
||||
kbdbProxyRouter.get('/kbdb/map', async (c) => {
|
||||
if (!tenant(c)) return c.json(NEED_KEY, 401);
|
||||
const { base, headers } = kbdbBase(c.env);
|
||||
const owner = c.req.query('owner_id');
|
||||
const qs = owner ? `?owner_id=${encodeURIComponent(owner)}` : '';
|
||||
try {
|
||||
const res = await fetch(`${base}/map${qs}`, { headers });
|
||||
return new Response(res.body, { status: res.status, headers: { 'Content-Type': 'application/json' } });
|
||||
} catch (e) {
|
||||
// KBDB 不可達 → 誠實回報(前端顯示「地圖服務不可達」,不擋首頁其他功能)
|
||||
return c.json({ success: false, error: `KBDB 不可達(${base}):${e instanceof Error ? e.message : String(e)}` }, 502);
|
||||
}
|
||||
});
|
||||
|
||||
// GET /kbdb/map/:library — 該庫詳圖(完整 top_entities 帶 degree/relation_profile/bridges/commit_hash)。
|
||||
kbdbProxyRouter.get('/kbdb/map/:library', async (c) => {
|
||||
if (!tenant(c)) return c.json(NEED_KEY, 401);
|
||||
const { base, headers } = kbdbBase(c.env);
|
||||
const owner = c.req.query('owner_id');
|
||||
const qs = owner ? `?owner_id=${encodeURIComponent(owner)}` : '';
|
||||
try {
|
||||
const res = await fetch(`${base}/map/${encodeURIComponent(c.req.param('library'))}${qs}`, { headers });
|
||||
return new Response(res.body, { status: res.status, headers: { 'Content-Type': 'application/json' } });
|
||||
} catch (e) {
|
||||
return c.json({ success: false, error: `KBDB 不可達(${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);
|
||||
|
||||
Reference in New Issue
Block a user