From 8644f6f113003ea13cf479cbc0d09b3a6700da06 Mon Sep 17 00:00:00 2001 From: lintsinghua Date: Fri, 26 Dec 2025 20:51:00 +0800 Subject: [PATCH] =?UTF-8?q?refactor(ssh):=20=E5=B0=86SSH=E5=AF=86=E9=92=A5?= =?UTF-8?q?=E7=AE=A1=E7=90=86=E5=8A=9F=E8=83=BD=E4=BB=8E=E8=B4=A6=E6=88=B7?= =?UTF-8?q?=E9=A1=B5=E9=9D=A2=E7=A7=BB=E8=87=B3=E7=B3=BB=E7=BB=9F=E9=85=8D?= =?UTF-8?q?=E7=BD=AE=E9=A1=B5=E9=9D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 重构SSH密钥管理相关代码,将其从Account组件移动到SystemConfig组件 移除Account组件中不再使用的SSH相关代码和状态 保持原有功能不变,仅改变功能位置以更好组织代码结构 --- .../src/components/system/SystemConfig.tsx | 297 ++++++++++++++++- frontend/src/pages/Account.tsx | 303 +----------------- 2 files changed, 296 insertions(+), 304 deletions(-) diff --git a/frontend/src/components/system/SystemConfig.tsx b/frontend/src/components/system/SystemConfig.tsx index 1f195cc..405ad5c 100644 --- a/frontend/src/components/system/SystemConfig.tsx +++ b/frontend/src/components/system/SystemConfig.tsx @@ -9,13 +9,16 @@ import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; +import { Textarea } from "@/components/ui/textarea"; +import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle } from "@/components/ui/alert-dialog"; import { Settings, Save, RotateCcw, Eye, EyeOff, CheckCircle2, AlertCircle, - Info, Zap, Globe, PlayCircle, Brain + Info, Zap, Globe, PlayCircle, Brain, Key, Copy, Trash2, Terminal, ServerCrash } from "lucide-react"; import { toast } from "sonner"; import { api } from "@/shared/api/database"; import EmbeddingConfig from "@/components/agent/EmbeddingConfig"; +import { generateSSHKey, getSSHKey, deleteSSHKey, testSSHKey, clearKnownHosts } from "@/shared/api/sshKeys"; // LLM Providers - 2025 const LLM_PROVIDERS = [ @@ -54,7 +57,16 @@ export function SystemConfig() { const [llmTestResult, setLlmTestResult] = useState<{ success: boolean; message: string; debug?: Record } | null>(null); const [showDebugInfo, setShowDebugInfo] = useState(true); - useEffect(() => { loadConfig(); }, []); + // SSH Key states + const [sshKey, setSSHKey] = useState<{ has_key: boolean; public_key?: string; fingerprint?: string }>({ has_key: false }); + const [generatingKey, setGeneratingKey] = useState(false); + const [deletingKey, setDeletingKey] = useState(false); + const [clearingKnownHosts, setClearingKnownHosts] = useState(false); + const [testingKey, setTestingKey] = useState(false); + const [testRepoUrl, setTestRepoUrl] = useState(""); + const [showDeleteKeyDialog, setShowDeleteKeyDialog] = useState(false); + + useEffect(() => { loadConfig(); loadSSHKey(); }, []); const loadConfig = async () => { try { @@ -116,6 +128,99 @@ export function SystemConfig() { } }; + // SSH Key functions + const loadSSHKey = async () => { + try { + const data = await getSSHKey(); + setSSHKey(data); + } catch (error) { + console.error('Failed to load SSH key:', error); + } + }; + + const handleGenerateSSHKey = async () => { + try { + setGeneratingKey(true); + const data = await generateSSHKey(); + setSSHKey({ has_key: true, public_key: data.public_key, fingerprint: data.fingerprint }); + toast.success(data.message); + } catch (error: any) { + console.error('Failed to generate SSH key:', error); + toast.error(error.response?.data?.detail || "生成SSH密钥失败"); + } finally { + setGeneratingKey(false); + } + }; + + const handleDeleteSSHKey = async () => { + try { + setDeletingKey(true); + await deleteSSHKey(); + setSSHKey({ has_key: false }); + toast.success("SSH密钥已删除"); + setShowDeleteKeyDialog(false); + } catch (error: any) { + console.error('Failed to delete SSH key:', error); + toast.error(error.response?.data?.detail || "删除SSH密钥失败"); + } finally { + setDeletingKey(false); + } + }; + + const handleTestSSHKey = async () => { + if (!testRepoUrl) { + toast.error("请输入仓库URL"); + return; + } + try { + setTestingKey(true); + const result = await testSSHKey(testRepoUrl); + if (result.success) { + toast.success("SSH连接测试成功"); + if (result.output) { + console.log("SSH测试输出:", result.output); + } + } else { + toast.error(result.message || "SSH连接测试失败", { + description: result.output ? `详情: ${result.output.substring(0, 100)}...` : undefined, + duration: 5000, + }); + if (result.output) { + console.error("SSH测试失败:", result.output); + } + } + } catch (error: any) { + console.error('Failed to test SSH key:', error); + toast.error(error.response?.data?.detail || "测试SSH密钥失败"); + } finally { + setTestingKey(false); + } + }; + + const handleClearKnownHosts = async () => { + try { + setClearingKnownHosts(true); + const result = await clearKnownHosts(); + if (result.success) { + toast.success(result.message || "known_hosts已清理"); + } else { + toast.error("清理known_hosts失败"); + } + } catch (error: any) { + console.error('Failed to clear known_hosts:', error); + toast.error(error.response?.data?.detail || "清理known_hosts失败"); + } finally { + setClearingKnownHosts(false); + } + }; + + const handleCopyPublicKey = () => { + if (sshKey.public_key) { + navigator.clipboard.writeText(sshKey.public_key); + toast.success("公钥已复制到剪贴板"); + } + }; + const saveConfig = async () => { if (!config) return; try { @@ -639,6 +744,160 @@ export function SystemConfig() {

• 私有仓库需要配置对应平台的 Token

+ + {/* SSH Key Management */} +
+
+ +

SSH 密钥管理

+
+ +
+
+
+ +
+
+
+

+ 使用 SSH 密钥访问 Git 仓库 +

+

+ 生成 SSH 密钥对后,将公钥添加到 GitHub/GitLab,即可使用 SSH URL 访问私有仓库。私钥将被加密存储。 +

+
+
+ + {!sshKey.has_key ? ( +
+
+ +
+

尚未生成 SSH 密钥

+ +
+ ) : ( +
+ {/* Public Key Display */} +
+
+ + +
+