fix: agent_version 为空 & 恢复批量卸载
- 后端上报时若 agent_version 为空则不覆盖已有版本(防止旧Agent清空版本号) - 安装/升级后写入版本号 v0.1.0(而非 installed/latest) - DB 存量 online 主机 agent_version 补填 v0.1.0 - 前端恢复批量卸载 Agent 按钮
This commit is contained in:
1 parent
502674d73a
commit
4dd3e3238d
3 files changed
+43
-11
No files matched your search
@@ -96,19 +96,30 @@ func (h *AgentHandler) ReceiveReport(c *gin.Context) {
|
||||
}
|
||||
|
||||
// 直接 SQL 更新 asset_servers 监控字段
|
||||
// 若 AgentVersion 非空则同步写入,否则保留数据库中已有版本号(防止旧版Agent空字段覆盖已设置的版本)
|
||||
updateSQL := `UPDATE asset_servers SET
|
||||
cpu_usage = ?, memory_usage = ?, disk_usage = ?,
|
||||
cpu = ?, memory = ?, disk = ?,
|
||||
status = ?, agent_status = ?, agent_version = ?,
|
||||
status = ?, agent_status = ?` + func() string {
|
||||
if req.AgentVersion != "" {
|
||||
return `, agent_version = ?`
|
||||
}
|
||||
return ``
|
||||
}() + `,
|
||||
hostname = CASE WHEN hostname != ? AND ip = ? THEN ? ELSE hostname END,
|
||||
updated_at = NOW()`
|
||||
args := []interface{}{
|
||||
req.CPU.UsagePercent, req.Memory.UsagePercent, diskAvg,
|
||||
req.CPU.CoreCount, memMB, diskGB,
|
||||
"running", "online", req.AgentVersion,
|
||||
"running", "online",
|
||||
}
|
||||
if req.AgentVersion != "" {
|
||||
args = append(args, req.AgentVersion)
|
||||
}
|
||||
args = append(args,
|
||||
// CASE WHEN hostname != ? AND ip = ? THEN ? ELSE hostname END
|
||||
req.Hostname, matchIP, req.Hostname,
|
||||
}
|
||||
)
|
||||
if req.System != nil {
|
||||
if req.System.OSVersion != "" {
|
||||
updateSQL += ", os = ?"
|
||||
|
||||
@@ -171,10 +171,10 @@ func (h *AgentInstallHandler) InstallAgent(c *gin.Context) {
|
||||
installed := contains(verifyOut, "active") || contains(verifyOut, "running")
|
||||
|
||||
if installed {
|
||||
// 更新服务器 Agent 状态
|
||||
// 更新服务器 Agent 状态(版本号:安装成功后由 Agent 下一次上报自动更新)
|
||||
h.db.Model(&server).Updates(map[string]interface{}{
|
||||
"agent_status": "online",
|
||||
"agent_version": "installed",
|
||||
"agent_version": "v0.1.0",
|
||||
"last_check_at": time.Now(),
|
||||
})
|
||||
}
|
||||
@@ -224,7 +224,7 @@ func (h *AgentInstallHandler) installOne(server models.Server, serverURL, token
|
||||
if installed {
|
||||
h.db.Model(&server).Updates(map[string]interface{}{
|
||||
"agent_status": "online",
|
||||
"agent_version": "installed",
|
||||
"agent_version": "v0.1.0",
|
||||
"last_check_at": time.Now(),
|
||||
})
|
||||
}
|
||||
@@ -433,7 +433,7 @@ func (h *AgentInstallHandler) BatchUpgradeAgent(c *gin.Context) {
|
||||
|
||||
// 更新版本号
|
||||
h.db.Model(&server).Updates(map[string]interface{}{
|
||||
"agent_version": "latest",
|
||||
"agent_version": "v0.1.0",
|
||||
"last_check_at": time.Now(),
|
||||
})
|
||||
log.Printf("[BatchUpgrade] %s Agent 升级成功", server.Hostname)
|
||||
|
||||
@@ -3,7 +3,7 @@ import React, { useState, useEffect, useMemo } from 'react';
|
||||
import {
|
||||
Monitor, Plus, RefreshCw, Maximize2, Minimize2, BarChart3, Clock,
|
||||
Server, Activity, Database, Wifi, WifiOff, ArrowUpDown, ArrowUp, ArrowDown,
|
||||
ExternalLink, Download, Rocket, PackageOpen, Loader2, Info
|
||||
ExternalLink, Download, Rocket, PackageOpen, Loader2, Info, Trash2
|
||||
} from 'lucide-react';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Input } from '@/components/ui/input';
|
||||
@@ -24,6 +24,7 @@ import { http } from '@/services/http';
|
||||
import { asList } from '@/components/agent/agentUtils';
|
||||
import { AgentWizard } from '@/components/agent/AgentWizard';
|
||||
import { Checkbox } from '@/components/ui/checkbox';
|
||||
import { useConfirm } from '@/components/ui/confirm-dialog';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
|
||||
// ═══════════ Tab 配置 ═══════════
|
||||
@@ -40,6 +41,7 @@ interface AgentHost { server_id: number; hostname: string; ip: string; cpu_usage
|
||||
type SortField = 'hostname' | 'agent_version' | 'agent_status' | 'cpu_usage' | 'mem_usage' | 'disk_usage' | 'region' | 'ip';
|
||||
|
||||
const AgentTab: React.FC = () => {
|
||||
const _confirm = useConfirm();
|
||||
const navigate = useNavigate();
|
||||
const [hosts, setHosts] = useState<AgentHost[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
@@ -57,6 +59,20 @@ const AgentTab: React.FC = () => {
|
||||
const [batchSubmitting, setBatchSubmitting] = useState(false);
|
||||
// 正在执行操作的主机 ID 集合(版本列 loading)
|
||||
const [operating, setOperating] = useState<Set<number>>(new Set());
|
||||
// 批量卸载
|
||||
const [batchUninstalling, setBatchUninstalling] = useState(false);
|
||||
const handleBatchUninstall = async () => {
|
||||
const ok = await _confirm({ title: '批量卸载 Agent', description: `确定卸载选中的 ${selIds.length} 台主机的 AIOps Agent?卸载将在后台异步执行。`, confirmText: '卸载', variant: 'destructive' });
|
||||
if (!ok) return;
|
||||
setBatchUninstalling(true);
|
||||
try {
|
||||
await agentApi.batchUninstall(selIds);
|
||||
toast.success(`已提交:批量卸载 ${selIds.length} 台主机的 Agent,后台执行中,30秒后自动刷新`);
|
||||
setSelected(new Set());
|
||||
setTimeout(() => fetch(), 30000);
|
||||
} catch { toast.error('提交失败'); }
|
||||
finally { setBatchUninstalling(false); }
|
||||
};
|
||||
const totalPages = Math.max(1, Math.ceil(hosts.length / pageSize));
|
||||
const fetch = async () => { try { setHosts(asList(await http.get('/agent/latest'))); } catch {} finally { setLoading(false); } };
|
||||
useEffect(() => { fetch(); const t = setInterval(fetch, 30000); return () => clearInterval(t); }, []);
|
||||
@@ -141,9 +157,14 @@ const AgentTab: React.FC = () => {
|
||||
<Rocket className="w-3 h-3" />安装 Agent
|
||||
</Button>
|
||||
{selAny && (
|
||||
<Button size="sm" variant="outline" className="h-7 text-xs gap-1 border-blue-500/30 text-blue-400 hover:bg-blue-500/10" onClick={openBatchDialog}>
|
||||
<PackageOpen className="w-3 h-3" />批量部署 ({selected.size})
|
||||
</Button>
|
||||
<>
|
||||
<Button size="sm" variant="outline" className="h-7 text-xs gap-1 border-blue-500/30 text-blue-400 hover:bg-blue-500/10" onClick={openBatchDialog}>
|
||||
<PackageOpen className="w-3 h-3" />批量部署 ({selected.size})
|
||||
</Button>
|
||||
<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="ghost" className="h-7 text-xs gap-1" onClick={fetch}><RefreshCw className="w-3 h-3" />刷新</Button>
|
||||
</div>
|
||||
|
||||
Reference in new issue
Block a user