feat: Agent批量升级 + 版本号注入 + 服务器凭证下拉关联

- Dockerfile: ARG VERSION/BUILD_TIME 通过 ldflags 注入 agent+backend 二进制
- run.sh: 自动从 git tag 获取版本号
- docker-compose: build args 传递 VERSION/BUILD_TIME
- backend: BatchUpgradeAgent SSH下载新二进制+重启, /agent/bin 静态文件服务
- frontend: 监控大屏添加批量升级按钮 (闪电图标)
- frontend: 服务器编辑/添加表单改为凭证下拉选择器
- frontend: ServerForm 加载 /credentials 列表, 选择后自动填充用户名/认证方式
This commit is contained in:
attacker committed 2026-06-19 01:24:47 +00:00
1 parent 228b6b52f9
commit ed08ea255e
9 files changed
+253 -52

No files matched your search

@@ -3,7 +3,7 @@ import React, { useState, useEffect } from 'react';
import {
Monitor, Plus, RefreshCw, Maximize2, Minimize2, BarChart3, Clock,
Server, Activity, Database, Wifi, WifiOff, ArrowUpDown, ArrowUp, ArrowDown,
ExternalLink, Download, Trash2, Rocket
ExternalLink, Download, Trash2, Rocket, Zap
} from 'lucide-react';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
@@ -104,6 +104,23 @@ const AgentTab: React.FC = () => {
finally { setBatchUninstalling(false); }
};
// 批量升级 Agent 到最新版本
const [batchUpgrading, setBatchUpgrading] = useState(false);
const handleBatchUpgrade = async () => {
const online = hosts.filter((h: any) => selIds.has(h.server_id) && h.agent_status === 'online');
if (online.length === 0) { toast.error('所选主机中没有在线 Agent'); return; }
const ids = online.map((h: any) => h.server_id);
const ok = await _confirm({ title: '批量升级 Agent', description: `确定升级选中的 ${ids.length} 台在线主机的 Agent 到最新版本?升级将在后台异步执行。`, confirmText: '升级', variant: 'default' });
if (!ok) return;
setBatchUpgrading(true);
try {
const res = await agentApi.batchUpgrade(ids);
toast.success(res?.message || `已提交:批量升级 ${ids.length} 台主机的 Agent,后台执行中,30秒后自动刷新`);
setTimeout(() => fetch(), 30000);
} catch { toast.error('提交失败'); }
finally { setBatchUpgrading(false); }
};
if (loading) return <div className="text-center py-20 text-[var(--dg-text-muted)]"><Activity className="h-6 w-6 mx-auto mb-2 animate-spin" /><p className="text-xs">加载中…</p></div>;
if (!hosts.length) return <div className="text-center py-20 text-[var(--dg-text-muted)]"><Server className="h-8 w-8 mx-auto mb-2" /><p className="text-sm">暂无服务器资产</p><p className="text-xs mt-1">请先在「服务器管理」中添加主机,然后在此安装 Agent</p></div>;
return (
@@ -127,6 +144,9 @@ const AgentTab: React.FC = () => {
<Button size="sm" variant="outline" className="h-7 text-xs gap-1 border-red-500/30 text-red-400 hover:bg-red-500/10" onClick={handleBatchUninstall} disabled={batchUninstalling}>
<Trash2 className="w-3 h-3" />批量卸载 ({selected.size})
</Button>
<Button size="sm" variant="outline" className="h-7 text-xs gap-1 border-amber-500/30 text-amber-400 hover:bg-amber-500/10" onClick={handleBatchUpgrade} disabled={batchUpgrading}>
<Zap className="w-3 h-3" />批量升级 ({selected.size})
</Button>
</>
)}
<Button size="sm" variant="ghost" className="h-7 text-xs gap-1" onClick={fetch}><RefreshCw className="w-3 h-3" />刷新</Button>