diff --git a/frontend/src/components/server/CloudServerImport.tsx b/frontend/src/components/server/CloudServerImport.tsx index c4fbe69..aa1e14c 100644 --- a/frontend/src/components/server/CloudServerImport.tsx +++ b/frontend/src/components/server/CloudServerImport.tsx @@ -57,6 +57,9 @@ export function CloudServerImport({ open, onClose }: CloudServerImportProps) { const [discoverErrors, setDiscoverErrors] = useState([]); const [isImporting, setIsImporting] = useState(false); const [importResult, setImportResult] = useState<{ imported: number; skipped: number } | null>(null); + const [defaultSshPort, setDefaultSshPort] = useState('22'); + const [credentials, setCredentials] = useState([]); + const [selectedCredentialId, setSelectedCredentialId] = useState(''); const loadAccounts = async () => { try { @@ -68,7 +71,13 @@ export function CloudServerImport({ open, onClose }: CloudServerImportProps) { }; useEffect(() => { - if (open) loadAccounts(); + if (open) { + loadAccounts(); + import('@/services/api').then(({ credentialApi }) => { + credentialApi.list().then((res: any) => setCredentials(res?.list || [])) + .catch(() => setCredentials([])); + }); + } }, [open]); const filteredAccounts = cloudAccounts.filter( @@ -140,7 +149,10 @@ export function CloudServerImport({ open, onClose }: CloudServerImportProps) { setIsImporting(true); try { const instancesToImport = cloudServers.filter(s => selectedServers.includes(s.instance_id)); - const res: any = await cloudApi.importInstances(selectedAccount.id, instancesToImport); + const res: any = await cloudApi.importInstances(selectedAccount.id, instancesToImport, { + default_port: Number(defaultSshPort) || 22, + credential_id: selectedCredentialId ? Number(selectedCredentialId) : undefined, + }); setImportResult(res || { imported: 0, skipped: 0 }); } catch (e: any) { console.error('导入失败:', e); @@ -199,6 +211,8 @@ export function CloudServerImport({ open, onClose }: CloudServerImportProps) { setRegionSearch(''); setDiscoverErrors([]); setImportResult(null); + setDefaultSshPort('22'); + setSelectedCredentialId(''); }; const filteredRegions = regions.filter(r => @@ -545,6 +559,43 @@ export function CloudServerImport({ open, onClose }: CloudServerImportProps) { )} + + {/* SSH 端口 + 凭证关联 */} + {cloudServers.length > 0 && selectedServers.length > 0 && ( +
+
+ + setDefaultSshPort(e.target.value)} + className="w-32 bg-[var(--dg-bg-tertiary)] border-[var(--dg-border)] text-[var(--dg-text-primary)]" + /> +
+
+ + +
+
+ )} )} diff --git a/frontend/src/components/server/ExcelServerImport.tsx b/frontend/src/components/server/ExcelServerImport.tsx index 0974006..f554a35 100644 --- a/frontend/src/components/server/ExcelServerImport.tsx +++ b/frontend/src/components/server/ExcelServerImport.tsx @@ -36,11 +36,6 @@ interface ExcelServerImportProps { onClose: () => void; } -const connectionMethods = [ - { id: 'password', name: '密码认证', description: '使用用户名密码连接' }, - { id: 'key', name: '密钥认证', description: '使用SSH密钥连接' }, -]; - type PreviewRow = { row: number; hostname: string; ip: string; port: number; username: string; os: string; cpu: number; memory: number; disk: number; tags: string; @@ -94,13 +89,13 @@ function isValidIP(ip: string): boolean { return /^(\d{1,3}\.){3}\d{1,3}$/.test(ip) && ip.split('.').every((o) => Number(o) >= 0 && Number(o) <= 255); } -// 下载 CSV 导入模板 — 只需 hostname + ip,端口默认22 账号默认root,Agent 安装后自动补全 // Logan.Li +// 下载 CSV 导入模板 — 主机名+IP+端口(默认22),账号root,Agent 安装后自动补全 // Logan.Li function downloadTemplate() { const csv = [ - 'hostname,ip', - 'web-01,192.168.1.10', - 'db-01,192.168.1.20', - 'cache-01,192.168.1.30', + 'hostname,ip,port', + 'web-01,192.168.1.10,22', + 'db-01,192.168.1.20,22', + 'cache-01,192.168.1.30,2222', ].join('\n'); const blob = new Blob(['\ufeff' + csv], { type: 'text/csv;charset=utf-8;' }); const url = URL.createObjectURL(blob); @@ -123,6 +118,7 @@ export function ExcelServerImport({ open, onClose }: ExcelServerImportProps) { // 凭据选择 // Logan.Li const [credentials, setCredentials] = useState([]); const [selectedCredentialId, setSelectedCredentialId] = useState(''); + const [defaultSshPort, setDefaultSshPort] = useState('22'); // 对话框打开时拉取区域列表 + 凭据列表 // Logan.Li useEffect(() => { @@ -221,7 +217,8 @@ export function ExcelServerImport({ open, onClose }: ExcelServerImportProps) { const resetState = () => { setStep(1); setSelectedRegion(''); - setConnectionMethod('password'); + setDefaultSshPort('22'); + setSelectedCredentialId(''); setSelectedFile(null); setPreviewData([]); }; @@ -245,8 +242,8 @@ export function ExcelServerImport({ open, onClose }: ExcelServerImportProps) { const resp: any = await serverApi.batchCreate({ servers, region_id: selectedRegion || undefined, - auth_type: connectionMethod, credential_id: selectedCredentialId ? Number(selectedCredentialId) : undefined, + default_port: Number(defaultSshPort) || 22, }); const r = resp ?? {}; const success = r.success ?? 0; @@ -321,7 +318,7 @@ export function ExcelServerImport({ open, onClose }: ExcelServerImportProps) {

主机导入模板

-

下载 CSV 模板,只填主机名和 IP,端口默认22、账号默认root

+

下载 CSV 模板,只需填主机名、IP和端口(默认22),Agent安装后自动补全