From a499da95c416d37e7cf6193e6af0633925e8fb71 Mon Sep 17 00:00:00 2001 From: shy3130 <415333856@qq.com> Date: Sun, 30 Aug 2026 19:05:44 +0800 Subject: [PATCH] =?UTF-8?q?fix(onboarding):=20=E5=BC=95=E5=AF=BC=E9=A1=B5?= =?UTF-8?q?=E6=94=AF=E6=8C=81=E5=86=85=E8=81=94=E9=85=8D=E7=BD=AE=E7=BC=BA?= =?UTF-8?q?=20Key=20=E5=9E=8B=E6=8F=92=E4=BB=B6(=E5=A6=82=20fuyao)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 此前 fuyao 因未配 Key 被自检判为不可用, 向导数据源步骤按「缺依赖」 一律禁用, 文案误导(需安装依赖)且无处可配。 - 区分两类不可用: 缺依赖(真禁用) vs 缺 Key(api_key_env 声明型, 卡片可点, 显示「需配置 API Key,点击填写」) - 点击展开内联 Key 表单: 官网申请链接 + 先探后存(POST /plugin-key, 无效不落盘), 验证成功自动选用该数据源 - 浏览器实测: fuyao 卡片可点、表单呈现/自动聚焦/空值禁用正确, 无效 Key 返回实探错误且不落盘, 取消正常关闭 --- frontend/src/pages/Onboarding.tsx | 116 ++++++++++++++++++++++++++++-- 1 file changed, 110 insertions(+), 6 deletions(-) diff --git a/frontend/src/pages/Onboarding.tsx b/frontend/src/pages/Onboarding.tsx index dbd5f27..1994e0e 100644 --- a/frontend/src/pages/Onboarding.tsx +++ b/frontend/src/pages/Onboarding.tsx @@ -22,6 +22,7 @@ import { Database, Plus, Puzzle, + KeyRound, } from 'lucide-react' import { api } from '@/lib/api' import { useCapabilities, usePreferences, useSettings } from '@/lib/useSharedQueries' @@ -298,6 +299,10 @@ function DataSourceStep({ onNext, onBack }: { onNext: () => void; onBack: () => const [picked, setPicked] = useState(null) // 是否展开「添加自有数据源」编辑器 const [adding, setAdding] = useState(false) + // 正在内联配置 Key 的插件名 (仅 api_key_env 声明型, 如 fuyao) + const [keyFor, setKeyFor] = useState(null) + const [keyInput, setKeyInput] = useState('') + const [keyError, setKeyError] = useState(null) const builtin = sources.data?.builtin ?? [] const plugins = sources.data?.plugins ?? [] @@ -345,6 +350,28 @@ function DataSourceStep({ onNext, onBack }: { onNext: () => void; onBack: () => switchProvider.mutate(name) } + // 内联 Key 配置 (先探后存): 验证通过才落盘 secrets.json, 成功即自动选用该源 + const keyPlugin = keyFor ? plugins.find(p => p.name === keyFor) : undefined + const saveKey = useMutation({ + mutationFn: (name: string) => api.savePluginKey(name, keyInput.trim()), + onSuccess: (data, name) => { + qc.invalidateQueries({ queryKey: QK.dataSources }) + qc.invalidateQueries({ queryKey: QK.capabilities }) + if (data.ok) { + setKeyFor(null) + setKeyInput('') + setKeyError(null) + if (data.plugin_available) { + setPicked(name) + switchProvider.mutate(name) + } + } else { + setKeyError(data.error || (data.reason === 'invalid' ? 'Key 验证失败,请检查后重试' : '保存失败,请重试')) + } + }, + onError: (e: Error) => setKeyError(`保存失败: ${e.message}`), + }) + return (
@@ -369,23 +396,35 @@ function DataSourceStep({ onNext, onBack }: { onNext: () => void; onBack: () =>
{items.map(item => { const isSelected = selected === item.name - const unavailable = item.kind === 'plugin' && !item.available const plugin = item.kind === 'plugin' ? plugins.find(p => p.name === item.name) : undefined + // 缺 Key 型 (声明了 api_key_env) 不算不可用: 引导页内联填写验证即可启用 + const needsKey = item.kind === 'plugin' && !item.available && !!plugin?.api_key_env + const unavailable = item.kind === 'plugin' && !item.available && !needsKey // 切换中的目标卡片: 圆点位置显示转圈, 其余卡片压暗 const switchingToThis = switchProvider.isPending && switchProvider.variables === item.name return ( + + + {keyError && ( +

+ + {keyError} +

+ )} +
+ )} + {/* 添加自有数据源: 复用设置页编辑器 (命名/鉴权/数据集字段映射/测试/启用) */} {adding && (