feat(portal/t53): 進站完成安裝清單(leo 07-25:金鑰與 daemon 要在站內做,不然安裝沒完成)——常駐三項清單(下載小幫手/下載 config.json 站內生成/貼 Gemini 金鑰即時啟用)+新端點 POST /portal/admin/chat-key(改寫 tenant rag_chat 的 x-goog-api-key,admin 閘、金鑰不落 log)+session 回 email 供 config 生成

This commit is contained in:
uncle6me-web
2026-07-25 23:26:44 +08:00
parent 11ffd42899
commit a8d246ebe9
2 changed files with 129 additions and 21 deletions
+43
View File
@@ -487,6 +487,7 @@ portalRouter.get('/portal/session', (c) =>
return c.json({
valid: true,
display_name: v.display_name ?? '',
email: v.email ?? '', // t53:完成安裝清單在站內生 daemon config.json 要用(身分顯示欄)
role,
libraries,
graph_allowed: await hasGraphAccess(c.env, libraries),
@@ -698,6 +699,48 @@ function toPublicLibrary(rec: PortalRecord) {
};
}
// POST /portal/admin/chat-key — body {key}。站內啟用 AI 問答(t53leo 07-25
// 「他進到網站要去給 gemini API Key」——不再回安裝器)。手法=G10 直嵌同款:
// 改寫 tenant 的 rag_chat workflow 記錄,把 x-goog-api-key 換成實值(KV 覆寫=重推)。
// 金鑰只過境不落 log;admin 閘(與庫登記同級)。
portalRouter.post('/portal/admin/chat-key', (c) =>
run(c, async () => {
const auth = await requirePortalAdmin(c);
if (!auth.ok) return auth.res;
const body = (await c.req.json().catch(() => null)) as { key?: string } | null;
const key = String(body?.key ?? '').trim();
if (!key) return c.json({ error: '請貼上你的 Google AI 金鑰' }, 400);
const tenant = portalTenant(c.env);
const kvKey = `${tenant}:wf:rag_chat`;
const raw = await c.env.WEBHOOKS.get(kvKey, 'text');
if (!raw) return c.json({ error: '這個實例沒有安裝 AI 問答工作流' }, 404);
let record: Record<string, unknown>;
try {
record = JSON.parse(raw) as Record<string, unknown>;
} catch {
return c.json({ error: 'AI 問答工作流記錄損壞,請重新安裝' }, 500);
}
// 結構不動、只換金鑰值:走遍 graph/config,凡 x-goog-api-key 欄一律設為新值
//(現值可能是 {{credential.gemini_api_key}} 佔位、空字串或舊 key,都直接覆蓋)。
let replaced = 0;
const visit = (o: unknown): void => {
if (Array.isArray(o)) { o.forEach(visit); return; }
if (o && typeof o === 'object') {
const rec = o as Record<string, unknown>;
for (const k of Object.keys(rec)) {
if (k.toLowerCase() === 'x-goog-api-key') { rec[k] = key; replaced += 1; }
else visit(rec[k]);
}
}
};
visit(record['graph']);
visit(record['config']);
if (replaced === 0) return c.json({ error: '工作流裡找不到金鑰欄位,請重新安裝後再試' }, 500);
await c.env.WEBHOOKS.put(kvKey, JSON.stringify(record));
return c.json({ success: true, replaced });
}),
);
// GET /portal/admin/libraries — 庫目錄列表。
portalRouter.get('/portal/admin/libraries', (c) =>
run(c, async () => {