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:
@@ -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 問答(t53,leo 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 () => {
|
||||
|
||||
Reference in New Issue
Block a user