web修复

This commit is contained in:
Alex Yang
2025-12-03 10:27:45 +08:00
parent dd01058b32
commit bd70dc1748
9 changed files with 437 additions and 134 deletions

View File

@@ -67,15 +67,18 @@ func RegisterRoutes(r *gin.Engine) {
}
}
// NetworkInterfaceMetrics 网卡监控指标
type NetworkInterfaceMetrics struct {
BytesSent uint64 `json:"bytes_sent"`
BytesReceived uint64 `json:"bytes_received"`
}
// MetricsRequest 指标请求结构
type MetricsRequest struct {
CPU float64 `json:"cpu"`
Memory float64 `json:"memory"`
Disk map[string]float64 `json:"disk"`
Network struct {
BytesSent uint64 `json:"bytes_sent"`
BytesReceived uint64 `json:"bytes_received"`
} `json:"network"`
CPU float64 `json:"cpu"`
Memory float64 `json:"memory"`
Disk map[string]float64 `json:"disk"`
Network map[string]NetworkInterfaceMetrics `json:"network"`
}
// HandleMetricsPost 处理Agent发送的指标数据
@@ -127,11 +130,20 @@ func HandleMetricsPost(c *gin.Context) {
// 处理请求体,支持单指标对象和指标数组
var metricsList []MetricsRequest
// 读取请求体到缓冲区,以便多次解析
body, err := c.GetRawData()
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{
"error": "Failed to read request body",
})
return
}
// 首先尝试解析为数组
if err := c.ShouldBindJSON(&metricsList); err != nil {
if err := json.Unmarshal(body, &metricsList); err != nil {
// 如果解析数组失败,尝试解析为单个对象
var singleMetric MetricsRequest
if err := c.ShouldBindJSON(&singleMetric); err != nil {
if err := json.Unmarshal(body, &singleMetric); err != nil {
c.JSON(http.StatusBadRequest, gin.H{
"error": "Invalid request body",
})
@@ -173,16 +185,33 @@ func HandleMetricsPost(c *gin.Context) {
}
}
// 写入网络发送指标
if err := globalStorage.WriteMetric(c.Request.Context(), deviceID, "network_sent", float64(req.Network.BytesSent), baseTags); err != nil {
// 只记录警告,不影响后续指标处理
log.Printf("Warning: Failed to write network sent metrics: %v", err)
}
// 写入网络指标,支持多个网卡
var totalBytesSent, totalBytesReceived uint64
for interfaceName, networkMetrics := range req.Network {
// 为每个网卡创建标签,包含基础标签和网卡名称
interfaceTags := make(map[string]string)
// 复制基础标签
for k, v := range baseTags {
interfaceTags[k] = v
}
// 添加网卡标签
interfaceTags["interface"] = interfaceName
// 写入网络接收指标
if err := globalStorage.WriteMetric(c.Request.Context(), deviceID, "network_received", float64(req.Network.BytesReceived), baseTags); err != nil {
// 只记录警告,不影响后续指标处理
log.Printf("Warning: Failed to write network received metrics: %v", err)
// 写入网络发送指标
if err := globalStorage.WriteMetric(c.Request.Context(), deviceID, "network_sent", float64(networkMetrics.BytesSent), interfaceTags); err != nil {
// 只记录警告,不影响后续指标处理
log.Printf("Warning: Failed to write network sent metrics for interface %s: %v", interfaceName, err)
}
// 写入网络接收指标
if err := globalStorage.WriteMetric(c.Request.Context(), deviceID, "network_received", float64(networkMetrics.BytesReceived), interfaceTags); err != nil {
// 只记录警告,不影响后续指标处理
log.Printf("Warning: Failed to write network received metrics for interface %s: %v", interfaceName, err)
}
// 累加总流量
totalBytesSent += networkMetrics.BytesSent
totalBytesReceived += networkMetrics.BytesReceived
}
// 广播指标更新消息,只广播最后一个指标
@@ -192,9 +221,10 @@ func HandleMetricsPost(c *gin.Context) {
"memory": req.Memory,
"disk": req.Disk,
"network": map[string]uint64{
"bytes_sent": req.Network.BytesSent,
"bytes_received": req.Network.BytesReceived,
"bytes_sent": totalBytesSent,
"bytes_received": totalBytesReceived,
},
"network_interfaces": req.Network,
}
broadcastMetricsUpdate(deviceID, metrics)
}
@@ -440,15 +470,61 @@ func GetNetworkMetrics(c *gin.Context) {
receivedPoints = []storage.MetricPoint{}
}
// 处理数据传递interval、startTime和endTime参数
processedSentData := ProcessMetrics(sentPoints, aggregation, interval, startTime, endTime)
processedReceivedData := ProcessMetrics(receivedPoints, aggregation, interval, startTime, endTime)
// 按网卡名称分组发送和接收的指标
sentByInterface := make(map[string][]storage.MetricPoint)
receivedByInterface := make(map[string][]storage.MetricPoint)
c.JSON(http.StatusOK, gin.H{
"data": map[string][]MetricData{
// 分组发送的网络指标
for _, point := range sentPoints {
// 获取网卡名称,默认使用"all"表示所有网卡
interfaceName := point.Tags["interface"]
if interfaceName == "" {
interfaceName = "all"
}
sentByInterface[interfaceName] = append(sentByInterface[interfaceName], point)
}
// 分组接收的网络指标
for _, point := range receivedPoints {
// 获取网卡名称,默认使用"all"表示所有网卡
interfaceName := point.Tags["interface"]
if interfaceName == "" {
interfaceName = "all"
}
receivedByInterface[interfaceName] = append(receivedByInterface[interfaceName], point)
}
// 处理数据,为每个网卡创建独立的数据集
result := make(map[string]map[string][]MetricData)
// 合并所有网卡名称
allInterfaces := make(map[string]bool)
for iface := range sentByInterface {
allInterfaces[iface] = true
}
for iface := range receivedByInterface {
allInterfaces[iface] = true
}
// 为每个网卡处理数据
for iface := range allInterfaces {
// 获取该网卡的发送和接收指标
ifaceSentPoints := sentByInterface[iface]
ifaceReceivedPoints := receivedByInterface[iface]
// 处理数据
processedSentData := ProcessMetrics(ifaceSentPoints, aggregation, interval, startTime, endTime)
processedReceivedData := ProcessMetrics(ifaceReceivedPoints, aggregation, interval, startTime, endTime)
// 保存结果
result[iface] = map[string][]MetricData{
"sent": processedSentData,
"received": processedReceivedData,
},
}
}
c.JSON(http.StatusOK, gin.H{
"data": result,
})
}

Binary file not shown.

View File

@@ -1349,11 +1349,25 @@ function updateStatusCards(metrics) {
} else if (typeof displayMetrics.network === 'object' &&
displayMetrics.network.bytes_sent !== undefined &&
displayMetrics.network.bytes_received !== undefined) {
// WebSocket消息格式
// WebSocket消息格式 - 总流量
sentRate = displayMetrics.network.bytes_sent;
receivedRate = displayMetrics.network.bytes_received;
sentTotal = displayMetrics.network.bytes_sent_total || sentRate;
receivedTotal = displayMetrics.network.bytes_received_total || receivedRate;
} else if (typeof displayMetrics.network === 'object' && !Array.isArray(displayMetrics.network)) {
// 按网卡分组的数据,计算总流量
for (const iface in displayMetrics.network) {
const ifaceMetrics = displayMetrics.network[iface];
if (ifaceMetrics.bytes_sent !== undefined && ifaceMetrics.bytes_received !== undefined) {
sentRate += ifaceMetrics.bytes_sent;
receivedRate += ifaceMetrics.bytes_received;
sentTotal += ifaceMetrics.bytes_sent_total || ifaceMetrics.bytes_sent;
receivedTotal += ifaceMetrics.bytes_received_total || ifaceMetrics.bytes_received;
} else if (ifaceMetrics.BytesSent !== undefined && ifaceMetrics.BytesReceived !== undefined) {
sentRate += ifaceMetrics.BytesSent;
receivedRate += ifaceMetrics.BytesReceived;
}
}
}
// 计算比率
@@ -1590,92 +1604,223 @@ function updateCharts(cpuData, memoryData, diskData, networkData) {
charts.disk.update();
}
// 保存当前选中的网卡
state.currentInterface = state.currentInterface || 'all';
// 更新网络流量趋势图表(发送总和和接收总和)
if (networkData && networkData.sent && networkData.received && charts.network) {
// 计算发送总和(时间段内的累积值)
if (Array.isArray(networkData.sent) && networkData.sent.length > 0) {
// 排序发送数据
const sortedSent = sortDataByTime(networkData.sent);
// 计算累积发送总和MB
let cumulativeSent = 0;
const sentSumData = sortedSent.map(item => {
// 转换为MB并累积
const mbValue = item.value / (1024 * 1024);
cumulativeSent += mbValue;
return {
time: item.time,
value: cumulativeSent
};
});
// 使用固定份数X轴数据计算
const fixedPointsSentSum = getFixedPointsData(sentSumData);
charts.network.data.datasets[0].data = fixedPointsSentSum.map(item => ({
x: formatTime(item.time),
y: item.value
}));
if (networkData && charts.network) {
let selectedNetworkData = networkData;
// 如果是按网卡分组的数据,选择当前选中的网卡数据
if (typeof networkData === 'object' && networkData.sent === undefined && networkData.received === undefined) {
selectedNetworkData = networkData[state.currentInterface] || networkData['all'] || {};
}
// 计算接收总和(时间段内的累积值)
if (Array.isArray(networkData.received) && networkData.received.length > 0) {
// 排序接收数据
const sortedReceived = sortDataByTime(networkData.received);
if (selectedNetworkData.sent && selectedNetworkData.received) {
// 计算发送总和(时间段内的累积值)
if (Array.isArray(selectedNetworkData.sent) && selectedNetworkData.sent.length > 0) {
// 排序发送数据
const sortedSent = sortDataByTime(selectedNetworkData.sent);
// 计算累积发送总和MB
let cumulativeSent = 0;
const sentSumData = sortedSent.map(item => {
// 转换为MB并累积
const mbValue = item.value / (1024 * 1024);
cumulativeSent += mbValue;
return {
time: item.time,
value: cumulativeSent
};
});
// 使用固定份数X轴数据计算
const fixedPointsSentSum = getFixedPointsData(sentSumData);
charts.network.data.datasets[0].data = fixedPointsSentSum.map(item => ({
x: formatTime(item.time),
y: item.value
}));
}
// 计算累积接收总和(MB
let cumulativeReceived = 0;
const receivedSumData = sortedReceived.map(item => {
// 转换为MB并累积
const mbValue = item.value / (1024 * 1024);
cumulativeReceived += mbValue;
return {
time: item.time,
value: cumulativeReceived
};
});
// 计算接收总和(时间段内的累积值
if (Array.isArray(selectedNetworkData.received) && selectedNetworkData.received.length > 0) {
// 排序接收数据
const sortedReceived = sortDataByTime(selectedNetworkData.received);
// 计算累积接收总和MB
let cumulativeReceived = 0;
const receivedSumData = sortedReceived.map(item => {
// 转换为MB并累积
const mbValue = item.value / (1024 * 1024);
cumulativeReceived += mbValue;
return {
time: item.time,
value: cumulativeReceived
};
});
// 使用固定份数X轴数据计算
const fixedPointsReceivedSum = getFixedPointsData(receivedSumData);
charts.network.data.datasets[1].data = fixedPointsReceivedSum.map(item => ({
x: formatTime(item.time),
y: item.value
}));
}
// 使用固定份数X轴数据计算
const fixedPointsReceivedSum = getFixedPointsData(receivedSumData);
charts.network.data.datasets[1].data = fixedPointsReceivedSum.map(item => ({
x: formatTime(item.time),
y: item.value
}));
charts.network.update();
}
charts.network.update();
}
// 更新网速趋势图表
if (networkData && networkData.sent && networkData.received && charts.speed) {
// 更新发送流量
if (Array.isArray(networkData.sent) && networkData.sent.length > 0) {
const sortedData = sortDataByTime(networkData.sent);
// 使用固定份数X轴数据计算
const fixedPointsData = getFixedPointsData(sortedData);
charts.speed.data.datasets[0].data = fixedPointsData.map(item => ({
x: formatTime(item.time),
y: item.value / (1024 * 1024) // 转换为MB/s
}));
if (networkData && charts.speed) {
let selectedNetworkData = networkData;
// 如果是按网卡分组的数据,选择当前选中的网卡数据
if (typeof networkData === 'object' && networkData.sent === undefined && networkData.received === undefined) {
selectedNetworkData = networkData[state.currentInterface] || networkData['all'] || {};
}
// 更新接收流量
if (Array.isArray(networkData.received) && networkData.received.length > 0) {
const sortedData = sortDataByTime(networkData.received);
// 使用固定份数X轴数据计算
const fixedPointsData = getFixedPointsData(sortedData);
charts.speed.data.datasets[1].data = fixedPointsData.map(item => ({
x: formatTime(item.time),
y: item.value / (1024 * 1024) // 转换为MB/s
}));
if (selectedNetworkData.sent && selectedNetworkData.received) {
// 更新发送流量
if (Array.isArray(selectedNetworkData.sent) && selectedNetworkData.sent.length > 0) {
const sortedData = sortDataByTime(selectedNetworkData.sent);
// 使用固定份数X轴数据计算
const fixedPointsData = getFixedPointsData(sortedData);
charts.speed.data.datasets[0].data = fixedPointsData.map(item => ({
x: formatTime(item.time),
y: item.value / (1024 * 1024) // 转换为MB/s
}));
}
// 更新接收流量
if (Array.isArray(selectedNetworkData.received) && selectedNetworkData.received.length > 0) {
const sortedData = sortDataByTime(selectedNetworkData.received);
// 使用固定份数X轴数据计算
const fixedPointsData = getFixedPointsData(sortedData);
charts.speed.data.datasets[1].data = fixedPointsData.map(item => ({
x: formatTime(item.time),
y: item.value / (1024 * 1024) // 转换为MB/s
}));
}
charts.speed.update();
}
charts.speed.update();
}
// 更新网卡选择下拉框
updateInterfaceDropdown(networkData);
// 初始化图表(如果尚未初始化)
initDetailedCharts();
}
// 更新网卡选择下拉框
function updateInterfaceDropdown(networkData) {
// 创建或获取网卡选择容器
let interfaceContainer = document.getElementById('interfaceSelectorContainer');
if (!interfaceContainer) {
// 获取图表选项卡导航容器
const chartTabs = document.getElementById('chartTabs');
if (!chartTabs) {
return;
}
// 创建网卡选择容器
interfaceContainer = document.createElement('div');
interfaceContainer.id = 'interfaceSelectorContainer';
interfaceContainer.className = 'flex items-center gap-2 ml-4';
// 创建标签
const label = document.createElement('label');
label.htmlFor = 'interfaceSelector';
label.className = 'text-sm text-gray-600';
label.textContent = '网卡:';
// 创建下拉框
const select = document.createElement('select');
select.id = 'interfaceSelector';
select.className = 'px-3 py-1 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 text-sm';
// 添加默认选项
const defaultOption = document.createElement('option');
defaultOption.value = 'all';
defaultOption.textContent = '所有网卡';
select.appendChild(defaultOption);
// 添加事件监听
select.addEventListener('change', (e) => {
state.currentInterface = e.target.value;
// 重新加载指标
loadMetrics();
});
// 组装容器
interfaceContainer.appendChild(label);
interfaceContainer.appendChild(select);
// 将容器添加到图表选项卡导航中
const tabsContainer = chartTabs.querySelector('.flex.flex-wrap');
if (tabsContainer) {
tabsContainer.appendChild(interfaceContainer);
}
}
// 更新下拉框选项
const select = document.getElementById('interfaceSelector');
if (select) {
// 保存当前选中的值
const currentValue = select.value;
// 清空现有选项
select.innerHTML = '';
// 添加默认选项
const defaultOption = document.createElement('option');
defaultOption.value = 'all';
defaultOption.textContent = '所有网卡';
select.appendChild(defaultOption);
// 添加所有网卡选项
// 检查是否有按网卡分组的网络数据
if (typeof networkData === 'object' && networkData.sent === undefined && networkData.received === undefined) {
// 获取所有网卡名称
const interfaces = Object.keys(networkData);
// 添加所有网卡选项
interfaces.forEach(iface => {
// 只添加实际的网卡名称,不添加"all"选项
if (iface !== 'all') {
const option = document.createElement('option');
option.value = iface;
option.textContent = iface;
select.appendChild(option);
}
});
} else {
// 如果没有按网卡分组的数据,使用模拟网卡名称
// 这里可以根据实际情况修改,比如从其他地方获取网卡名称列表
const mockInterfaces = ['eth0', 'eth1', 'lo'];
mockInterfaces.forEach(iface => {
const option = document.createElement('option');
option.value = iface;
option.textContent = iface;
select.appendChild(option);
});
}
// 恢复当前选中的值
select.value = currentValue;
}
// 检查当前选中的选项卡,如果不是"网络"或"网速",隐藏网卡选择下拉框
const activeTab = document.querySelector('.chart-tab.active');
if (activeTab && activeTab.dataset.tab !== 'network' && activeTab.dataset.tab !== 'speed') {
interfaceContainer.classList.add('hidden');
} else {
interfaceContainer.classList.remove('hidden');
}
}
// 尝试重连WebSocket
function attemptReconnect() {
if (wsReconnectAttempts < wsMaxReconnectAttempts) {
@@ -2073,6 +2218,17 @@ function initChartTabs() {
if (activeContainer) {
activeContainer.classList.remove('hidden');
}
// 显示/隐藏网卡选择下拉框
const interfaceContainer = document.getElementById('interfaceSelectorContainer');
if (interfaceContainer) {
// 只有在点击"网络"或"网速"选项卡时,显示网卡选择下拉框
if (tabId === 'network' || tabId === 'speed') {
interfaceContainer.classList.remove('hidden');
} else {
interfaceContainer.classList.add('hidden');
}
}
});
});
}