feat: Agent采集uptime, 后端持久化kernel/uptime/diskIO, 前端磁盘BPS+IOPS展示
- Agent: system.go 添加 readUptime(), SystemStat 携带 Uptime - Backend: Server 模型新增 Kernel 字段, ReceiveReport 持久化 kernel+uptime - Backend: /agent/latest 返回 kernel/uptime/port_info/disk_io_info/disk_total - Frontend: 磁盘监控改为 BPS+IOPS (4张卡片), 系统信息修复内核/运行时长/监听端口/磁盘容量 - 图表单位从 MB/s 改为 KB/s, 添加 formatBPS 工具函数
This commit is contained in:
1 parent
31c2e338a3
commit
5949ec5d0c
48 files changed
+2182
-956
No files matched your search
@@ -37,10 +37,8 @@ import {
|
||||
Cloud,
|
||||
FileSpreadsheet,
|
||||
RefreshCw,
|
||||
Rocket,
|
||||
ChevronLeft,
|
||||
ChevronRight,
|
||||
Cpu,
|
||||
} from 'lucide-react';
|
||||
import { serverApi } from '@/services/api';
|
||||
import { ServerStatsCards } from './ServerStatsCards';
|
||||
@@ -53,7 +51,6 @@ import { ExcelServerImport } from './ExcelServerImport';
|
||||
import { ServerForm, EMPTY_FORM, type ServerFormData } from './ServerForm';
|
||||
import { getTags } from './serverHelpers';
|
||||
import { ServerTableRow } from './ServerTableRow';
|
||||
import { AgentWizard } from '@/components/agent/AgentWizard';
|
||||
|
||||
export function ServerManager() {
|
||||
const confirm = useConfirm();
|
||||
@@ -74,60 +71,15 @@ export function ServerManager() {
|
||||
|
||||
// 终端 Dialog 状态
|
||||
const [terminalOpen, setTerminalOpen] = useState(false);
|
||||
const [terminalServer, setTerminalServer] = useState<{ id: number; name: string; ip: string } | null>(null);
|
||||
const [terminalServer, setTerminalServer] = useState<{ id: number; name: string; ip: string; hasAuth?: boolean } | null>(null);
|
||||
|
||||
// 分页状态
|
||||
const [page, setPage] = useState(1);
|
||||
const [pageSize, setPageSize] = useState(10);
|
||||
const [total, setTotal] = useState(0);
|
||||
|
||||
// Agent 安装向导状态
|
||||
const [agentWizardOpen, setAgentWizardOpen] = useState(false);
|
||||
const [wizardServer, setWizardServer] = useState<any>(null);
|
||||
// Agent 安装状态: { [serverId]: 'installing' | 'success' | 'failed' }
|
||||
const [installingIds, setInstallingIds] = useState<Set<number>>(new Set());
|
||||
const [installResults, setInstallResults] = useState<Map<number, { success: boolean; message: string }>>(new Map());
|
||||
|
||||
const openInstallAgent = (server: any) => {
|
||||
setWizardServer(server);
|
||||
setAgentWizardOpen(true);
|
||||
};
|
||||
|
||||
const handleBatchInstallAgents = async () => {
|
||||
const token = useUserStore.getState().token;
|
||||
if (!token) { toast.error('请先登录'); return; }
|
||||
const ids = selectedServers.map(id => Number(id));
|
||||
|
||||
setInstallingIds(prev => {
|
||||
const next = new Set(prev);
|
||||
ids.forEach(id => next.add(id));
|
||||
return next;
|
||||
});
|
||||
|
||||
try {
|
||||
const res: any = await serverApi.batchInstallAgent(ids, token);
|
||||
const results = res?.results || [];
|
||||
setInstallResults(prev => {
|
||||
const next = new Map(prev);
|
||||
results.forEach((r: any) => {
|
||||
next.set(r.server_id, { success: r.installed, message: r.message });
|
||||
});
|
||||
return next;
|
||||
});
|
||||
toast.success(`批量安装完成: ${res?.success || 0}/${ids.length} 成功`);
|
||||
} catch (e: any) {
|
||||
toast.error(`批量安装失败: ${e?.message || '未知错误'}`);
|
||||
} finally {
|
||||
setInstallingIds(prev => {
|
||||
const next = new Set(prev);
|
||||
ids.forEach(id => next.delete(id));
|
||||
return next;
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const openTerminal = (server: any) => {
|
||||
setTerminalServer({ id: server.id, name: server.hostname, ip: server.ip });
|
||||
setTerminalServer({ id: server.id, name: server.hostname, ip: server.ip, hasAuth: server.has_auth ?? true });
|
||||
setTerminalOpen(true);
|
||||
};
|
||||
|
||||
@@ -444,8 +396,8 @@ export function ServerManager() {
|
||||
onClick={() => setExcelImportOpen(true)}
|
||||
>
|
||||
<FileSpreadsheet className="h-4 w-4 mr-2 text-emerald-400" />
|
||||
Excel导入
|
||||
<span className="ml-2 text-xs text-[var(--dg-text-muted)]">批量导入服务器</span>
|
||||
CSV导入
|
||||
<span className="ml-2 text-xs text-[var(--dg-text-muted)]">hostname + IP 即可</span>
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
@@ -493,15 +445,6 @@ export function ServerManager() {
|
||||
{selectedServers.length > 0 && (
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="text-sm text-[var(--dg-text-secondary)]">已选择 {selectedServers.length} 项</span>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={handleBatchInstallAgents}
|
||||
className="border-[var(--dg-cyan)]/30 text-[var(--dg-cyan)] hover:bg-[var(--dg-cyan)]/10"
|
||||
>
|
||||
<Cpu className="h-4 w-4 mr-2" />
|
||||
批量安装Agent
|
||||
</Button>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
@@ -562,9 +505,6 @@ export function ServerManager() {
|
||||
onOpenTerminal={openTerminal}
|
||||
onOpenEdit={openEditForm}
|
||||
onDelete={handleDelete}
|
||||
onInstallAgent={openInstallAgent}
|
||||
installing={installingIds.has(server.id)}
|
||||
installResult={installResults.get(server.id) || null}
|
||||
/>
|
||||
))
|
||||
)}
|
||||
@@ -645,32 +585,6 @@ export function ServerManager() {
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* Agent 安装向导 */}
|
||||
<AgentWizard
|
||||
open={agentWizardOpen}
|
||||
onOpenChange={(open) => {
|
||||
setAgentWizardOpen(open);
|
||||
if (!open) setWizardServer(null);
|
||||
}}
|
||||
server={wizardServer}
|
||||
onInstalled={(serverId) => {
|
||||
setInstallResults(prev => {
|
||||
const next = new Map(prev);
|
||||
next.set(serverId, { success: true, message: 'Agent 已安装' });
|
||||
return next;
|
||||
});
|
||||
// 刷新列表以更新 agent_status
|
||||
loadData();
|
||||
}}
|
||||
onFailed={(serverId, msg) => {
|
||||
setInstallResults(prev => {
|
||||
const next = new Map(prev);
|
||||
next.set(serverId, { success: false, message: msg || '安装失败' });
|
||||
return next;
|
||||
});
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* 终端 Dialog */}
|
||||
<TerminalDialog
|
||||
open={terminalOpen}
|
||||
@@ -678,6 +592,7 @@ export function ServerManager() {
|
||||
serverId={terminalServer?.id ?? null}
|
||||
serverName={terminalServer?.name ?? ''}
|
||||
serverIp={terminalServer?.ip ?? ''}
|
||||
hasAuth={terminalServer?.hasAuth ?? true}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
Reference in new issue
Block a user