在管理页面增加设备 IP 信息
This commit is contained in:
1 parent
80de85d51d
commit
ab1a13d5b2
8 files changed
+68
-3
No files matched your search
@@ -243,6 +243,7 @@ func setupApi(app *orz.App, components *AppComponents) {
|
||||
adminApi.GET("/agents/statistics", components.AgentHandler.GetStatistics)
|
||||
adminApi.GET("/agents/tags", components.AgentHandler.GetTags)
|
||||
adminApi.GET("/agents/:id", components.AgentHandler.GetForAdmin)
|
||||
adminApi.GET("/agents/:id/metrics/latest", components.AgentHandler.GetAdminLatestMetrics)
|
||||
adminApi.PUT("/agents/:id", components.AgentHandler.UpdateInfo)
|
||||
adminApi.POST("/agents/batch/tags", components.AgentHandler.BatchUpdateTags)
|
||||
adminApi.DELETE("/agents/:id", components.AgentHandler.Delete)
|
||||
|
||||
@@ -51,6 +51,17 @@ func (h *AgentHandler) GetForAdmin(c echo.Context) error {
|
||||
return orz.Ok(c, agent)
|
||||
}
|
||||
|
||||
// GetAdminLatestMetrics 获取探针最新指标(管理员接口,显示完整信息)
|
||||
func (h *AgentHandler) GetAdminLatestMetrics(c echo.Context) error {
|
||||
id := c.Param("id")
|
||||
|
||||
metrics, ok := h.metricService.GetLatestMetrics(id)
|
||||
if !ok {
|
||||
return orz.NewError(404, "探针最新指标不存在")
|
||||
}
|
||||
return orz.Ok(c, metrics)
|
||||
}
|
||||
|
||||
// SendCommand 向探针发送指令
|
||||
func (h *AgentHandler) SendCommand(c echo.Context) error {
|
||||
agentID := c.Param("id")
|
||||
|
||||
@@ -142,6 +142,8 @@ func (h *AgentHandler) GetLatestMetrics(c echo.Context) error {
|
||||
if !ok {
|
||||
return orz.NewError(404, "探针最新指标不存在")
|
||||
}
|
||||
// 清空网络接口指标,避免返回敏感信息
|
||||
metrics.NetworkInterfaces = nil
|
||||
|
||||
return orz.Ok(c, metrics)
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@ type LatestMetrics struct {
|
||||
Memory *protocol.MemoryData `json:"memory,omitempty"`
|
||||
Disk *DiskSummary `json:"disk,omitempty"`
|
||||
Network *NetworkSummary `json:"network,omitempty"`
|
||||
NetworkInterfaces []protocol.NetworkData `json:"networkInterfaces,omitempty"`
|
||||
NetworkConnection *protocol.NetworkConnectionData `json:"networkConnection,omitempty"`
|
||||
Host *protocol.HostInfoData `json:"host,omitempty"`
|
||||
GPU []protocol.GPUData `json:"gpu,omitempty"`
|
||||
|
||||
@@ -127,6 +127,7 @@ func (s *MetricService) HandleMetricData(ctx context.Context, agentID string, me
|
||||
TotalBytesRecvTotal: totalRecvTotal,
|
||||
TotalInterfaces: len(networkDataList),
|
||||
}
|
||||
latestMetrics.NetworkInterfaces = networkDataList
|
||||
// 更新流量统计
|
||||
if err := s.trafficService.UpdateAgentTraffic(ctx, agentID, totalRecvTotal, totalSentTotal); err != nil {
|
||||
s.logger.Error("更新探针流量统计失败",
|
||||
|
||||
@@ -6,8 +6,14 @@ import {Activity, ArrowLeft, Clock, FileWarning, Lock, RefreshCw, Shield, Termin
|
||||
import TamperProtection from './TamperProtection.tsx';
|
||||
import SSHLoginMonitor from './SSHLoginMonitor.tsx';
|
||||
import TrafficStats from './TrafficStats.tsx';
|
||||
import {getAgentForAdmin, getAuditResult, sendAuditCommand, type VPSAuditResult} from '@/api/agent.ts';
|
||||
import type {Agent} from '@/types';
|
||||
import {
|
||||
getAgentForAdmin,
|
||||
getAgentLatestMetricsForAdmin,
|
||||
getAuditResult,
|
||||
sendAuditCommand,
|
||||
type VPSAuditResult
|
||||
} from '@/api/agent.ts';
|
||||
import type {Agent, LatestMetrics} from '@/types';
|
||||
import dayjs from 'dayjs';
|
||||
import {getErrorMessage} from '@/lib/utils';
|
||||
import AuditResultView from './AuditResultView';
|
||||
@@ -19,6 +25,7 @@ const AgentDetail = () => {
|
||||
const {message: messageApi} = App.useApp();
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [agent, setAgent] = useState<Agent | null>(null);
|
||||
const [latestMetrics, setLatestMetrics] = useState<LatestMetrics | null>(null);
|
||||
const [auditResult, setAuditResult] = useState<VPSAuditResult | null>(null);
|
||||
const [auditing, setAuditing] = useState(false);
|
||||
const [activeTab, setActiveTab] = useState<string>(searchParams.get('tab') || 'info');
|
||||
@@ -28,13 +35,15 @@ const AgentDetail = () => {
|
||||
|
||||
setLoading(true);
|
||||
try {
|
||||
const [agentRes, auditRes] = await Promise.all([
|
||||
const [agentRes, auditRes, latestMetricsRes] = await Promise.all([
|
||||
getAgentForAdmin(id),
|
||||
getAuditResult(id).catch(() => ({data: null})),
|
||||
getAgentLatestMetricsForAdmin(id).catch(() => ({data: null})),
|
||||
]);
|
||||
|
||||
setAgent(agentRes.data);
|
||||
setAuditResult(auditRes.data);
|
||||
setLatestMetrics(latestMetricsRes.data || null);
|
||||
} catch (error: any) {
|
||||
messageApi.error(error.response?.data?.message || '获取探针信息失败');
|
||||
} finally {
|
||||
@@ -104,6 +113,13 @@ const AgentDetail = () => {
|
||||
];
|
||||
|
||||
|
||||
const deviceIpInterfaces = (latestMetrics?.networkInterfaces || [])
|
||||
.map((netInterface) => ({
|
||||
name: netInterface.interface,
|
||||
addrs: Array.from(new Set((netInterface.addrs || []).map((addr) => addr.trim()).filter(Boolean))),
|
||||
}))
|
||||
.filter((netInterface) => netInterface.addrs.length > 0);
|
||||
|
||||
// Tab 项配置
|
||||
const tabItems: TabsProps['items'] = [
|
||||
{
|
||||
@@ -120,6 +136,24 @@ const AgentDetail = () => {
|
||||
<Descriptions.Item label="探针ID">{agent?.id}</Descriptions.Item>
|
||||
<Descriptions.Item label="主机名">{agent?.hostname}</Descriptions.Item>
|
||||
<Descriptions.Item label="通信IP">{agent?.ip}</Descriptions.Item>
|
||||
<Descriptions.Item label="设备IP" span={2}>
|
||||
{deviceIpInterfaces.length > 0 ? (
|
||||
<div className="flex flex-col gap-2">
|
||||
{deviceIpInterfaces.map((netInterface) => (
|
||||
<div key={netInterface.name} className="flex flex-col gap-1">
|
||||
<span className="text-xs text-gray-500">{netInterface.name}</span>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{netInterface.addrs.map((addr) => (
|
||||
<Tag key={`${netInterface.name}-${addr}`}>{addr}</Tag>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
'-'
|
||||
)}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="操作系统">
|
||||
<Tag color="blue">{agent?.os}</Tag>
|
||||
</Descriptions.Item>
|
||||
|
||||
@@ -78,6 +78,10 @@ export const getAgentForAdmin = (id: string) => {
|
||||
return get<Agent>(`/admin/agents/${id}`);
|
||||
};
|
||||
|
||||
export const getAgentLatestMetricsForAdmin = (agentId: string) => {
|
||||
return get<LatestMetrics>(`/admin/agents/${agentId}/metrics/latest`);
|
||||
};
|
||||
|
||||
export const getAgentMetrics = (params: GetAgentMetricsRequest) => {
|
||||
const {agentId, type, range = '1h', start, end, interface: interfaceName} = params;
|
||||
const query = new URLSearchParams();
|
||||
|
||||
@@ -207,6 +207,16 @@ export interface NetworkSummary {
|
||||
totalInterfaces: number; // 网卡数量
|
||||
}
|
||||
|
||||
export interface NetworkInterfaceMetric {
|
||||
interface: string;
|
||||
macAddress?: string;
|
||||
addrs?: string[];
|
||||
bytesSentRate: number;
|
||||
bytesRecvRate: number;
|
||||
bytesSentTotal: number;
|
||||
bytesRecvTotal: number;
|
||||
}
|
||||
|
||||
// 主机信息
|
||||
export interface HostInfo {
|
||||
hostname: string;
|
||||
@@ -416,6 +426,7 @@ export interface LatestMetrics {
|
||||
memory?: MemoryMetric;
|
||||
disk?: DiskSummary; // 改为汇总数据
|
||||
network?: NetworkSummary; // 改为汇总数据
|
||||
networkInterfaces?: NetworkInterfaceMetric[];
|
||||
networkConnection?: NetworkConnectionMetric; // 网络连接统计
|
||||
host?: HostInfo; // 主机信息
|
||||
gpu?: GPUMetric[]; // GPU 列表
|
||||
|
||||
Reference in new issue
Block a user