修复泄露IP的严重BUG
This commit is contained in:
@@ -489,7 +489,9 @@
|
|||||||
{ key: 'swapUsedPct', label: 'SWAP 使用率', value: formatPercent(data.swapUsedPct) },
|
{ key: 'swapUsedPct', label: 'SWAP 使用率', value: formatPercent(data.swapUsedPct) },
|
||||||
{ key: 'rootFsUsedPct', label: '根分区使用率 (/)', value: formatPercent(data.rootFsUsedPct) },
|
{ key: 'rootFsUsedPct', label: '根分区使用率 (/)', value: formatPercent(data.rootFsUsedPct) },
|
||||||
{ key: 'netRx', label: '网络接收速率 (RX)', value: formatBandwidth(data.netRx) },
|
{ key: 'netRx', label: '网络接收速率 (RX)', value: formatBandwidth(data.netRx) },
|
||||||
{ key: 'netTx', label: '网络发送速率 (TX)', value: formatBandwidth(data.netTx) }
|
{ key: 'netTx', label: '网络发送速率 (TX)', value: formatBandwidth(data.netTx) },
|
||||||
|
{ key: 'sockstatTcp', label: 'TCP 链接数 (Sockstat)', value: data.sockstatTcp.toFixed(0) },
|
||||||
|
{ key: 'sockstatTcpMem', label: 'TCP 内存占用', value: formatBytes(data.sockstatTcpMem) }
|
||||||
];
|
];
|
||||||
|
|
||||||
dom.detailMetricsList.innerHTML = metrics.map(m => `
|
dom.detailMetricsList.innerHTML = metrics.map(m => `
|
||||||
@@ -565,6 +567,7 @@
|
|||||||
let unit = '';
|
let unit = '';
|
||||||
if (metricKey.includes('Pct') || metricKey === 'cpuBusy') unit = '%';
|
if (metricKey.includes('Pct') || metricKey === 'cpuBusy') unit = '%';
|
||||||
if (metricKey.startsWith('net')) unit = 'B/s';
|
if (metricKey.startsWith('net')) unit = 'B/s';
|
||||||
|
if (metricKey === 'sockstatTcpMem') unit = 'B';
|
||||||
|
|
||||||
chart = new MetricChart(canvas, unit);
|
chart = new MetricChart(canvas, unit);
|
||||||
currentServerDetail.charts[metricKey] = chart;
|
currentServerDetail.charts[metricKey] = chart;
|
||||||
|
|||||||
@@ -380,8 +380,15 @@ class MetricChart {
|
|||||||
ctx.textAlign = 'right';
|
ctx.textAlign = 'right';
|
||||||
|
|
||||||
let label = '';
|
let label = '';
|
||||||
if (this.unit === 'B/s') {
|
if (this.unit === 'B/s' || this.unit === 'B') {
|
||||||
label = window.formatBandwidth ? window.formatBandwidth(v) : v.toFixed(0);
|
const isRate = this.unit === 'B/s';
|
||||||
|
if (window.formatBandwidth && isRate) {
|
||||||
|
label = window.formatBandwidth(v);
|
||||||
|
} else if (window.formatBytes) {
|
||||||
|
label = window.formatBytes(v) + (isRate ? '/s' : '');
|
||||||
|
} else {
|
||||||
|
label = v.toFixed(0) + this.unit;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
label = (v >= 1000 ? (v / 1000).toFixed(1) + 'k' : v.toFixed(v < 10 && v > 0 ? 1 : 0)) + this.unit;
|
label = (v >= 1000 ? (v / 1000).toFixed(1) + 'k' : v.toFixed(v < 10 && v > 0 ? 1 : 0)) + this.unit;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,9 +5,21 @@ const https = require('https');
|
|||||||
const QUERY_TIMEOUT = 10000;
|
const QUERY_TIMEOUT = 10000;
|
||||||
|
|
||||||
// Reusable agents to handle potential redirect issues and protocol mismatches
|
// Reusable agents to handle potential redirect issues and protocol mismatches
|
||||||
|
const crypto = require('crypto');
|
||||||
const httpAgent = new http.Agent({ keepAlive: true });
|
const httpAgent = new http.Agent({ keepAlive: true });
|
||||||
const httpsAgent = new https.Agent({ keepAlive: true, rejectUnauthorized: false });
|
const httpsAgent = new https.Agent({ keepAlive: true, rejectUnauthorized: false });
|
||||||
|
|
||||||
|
const serverIdMap = new Map(); // token -> { instance, job, source }
|
||||||
|
const SECRET = crypto.randomBytes(16).toString('hex');
|
||||||
|
|
||||||
|
function getServerToken(instance) {
|
||||||
|
const hash = crypto.createHmac('sha256', SECRET)
|
||||||
|
.update(instance)
|
||||||
|
.digest('hex')
|
||||||
|
.substring(0, 16);
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Normalize URL and ensure protocol
|
* Normalize URL and ensure protocol
|
||||||
*/
|
*/
|
||||||
@@ -180,10 +192,16 @@ async function getOverviewMetrics(url, sourceName) {
|
|||||||
const instances = new Map();
|
const instances = new Map();
|
||||||
|
|
||||||
const getOrCreate = (metric) => {
|
const getOrCreate = (metric) => {
|
||||||
const key = metric.instance;
|
const originalInstance = metric.instance;
|
||||||
if (!instances.has(key)) {
|
const token = getServerToken(originalInstance, sourceName);
|
||||||
instances.set(key, {
|
|
||||||
instance: key,
|
// Store mapping for detail queries
|
||||||
|
serverIdMap.set(token, { instance: originalInstance, source: sourceName, job: metric.job });
|
||||||
|
|
||||||
|
if (!instances.has(token)) {
|
||||||
|
instances.set(token, {
|
||||||
|
instance: token, // This is the masked IP SENT TO FRONTEND
|
||||||
|
originalInstance, // Keep internal for aggregation/parsing
|
||||||
job: metric.job || 'Unknown',
|
job: metric.job || 'Unknown',
|
||||||
source: sourceName,
|
source: sourceName,
|
||||||
cpuPercent: 0,
|
cpuPercent: 0,
|
||||||
@@ -197,7 +215,7 @@ async function getOverviewMetrics(url, sourceName) {
|
|||||||
up: false
|
up: false
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
const inst = instances.get(key);
|
const inst = instances.get(token);
|
||||||
// If job was Unknown but we now have a job name, update it
|
// If job was Unknown but we now have a job name, update it
|
||||||
if (inst.job === 'Unknown' && metric.job) {
|
if (inst.job === 'Unknown' && metric.job) {
|
||||||
inst.job = metric.job;
|
inst.job = metric.job;
|
||||||
@@ -311,9 +329,13 @@ async function getOverviewMetrics(url, sourceName) {
|
|||||||
},
|
},
|
||||||
traffic24h: {
|
traffic24h: {
|
||||||
rx: totalTraffic24hRx,
|
rx: totalTraffic24hRx,
|
||||||
tx: totalTraffic24hTx
|
tx: totalTraffic24hTx,
|
||||||
|
total: totalTraffic24hRx + totalTraffic24hTx
|
||||||
},
|
},
|
||||||
servers: Array.from(instances.values())
|
servers: Array.from(instances.values()).map(s => {
|
||||||
|
const { originalInstance, ...rest } = s;
|
||||||
|
return rest;
|
||||||
|
})
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -412,12 +434,19 @@ function mergeCpuHistories(histories) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function resolveToken(token) {
|
||||||
|
if (serverIdMap.has(token)) {
|
||||||
|
return serverIdMap.get(token).instance;
|
||||||
|
}
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get detailed metrics for a specific server (node)
|
* Get detailed metrics for a specific server (node)
|
||||||
*/
|
*/
|
||||||
async function getServerDetails(baseUrl, instance, job) {
|
async function getServerDetails(baseUrl, instance, job) {
|
||||||
const url = normalizeUrl(baseUrl);
|
const url = normalizeUrl(baseUrl);
|
||||||
const node = instance;
|
const node = resolveToken(instance);
|
||||||
|
|
||||||
// Queries based on the requested dashboard structure
|
// Queries based on the requested dashboard structure
|
||||||
const queries = {
|
const queries = {
|
||||||
@@ -438,7 +467,9 @@ async function getServerDetails(baseUrl, instance, job) {
|
|||||||
memTotal: `node_memory_MemTotal_bytes{instance="${node}",job="${job}"}`,
|
memTotal: `node_memory_MemTotal_bytes{instance="${node}",job="${job}"}`,
|
||||||
uptime: `node_time_seconds{instance="${node}",job="${job}"} - node_boot_time_seconds{instance="${node}",job="${job}"}`,
|
uptime: `node_time_seconds{instance="${node}",job="${job}"} - node_boot_time_seconds{instance="${node}",job="${job}"}`,
|
||||||
netRx: `sum(rate(node_network_receive_bytes_total{instance="${node}",job="${job}",device!~'tap.*|veth.*|br.*|docker.*|virbr*|podman.*|lo.*|vmbr.*|fwbr.|ip.*|gre.*|virbr.*|vnet.*'}[1m]))`,
|
netRx: `sum(rate(node_network_receive_bytes_total{instance="${node}",job="${job}",device!~'tap.*|veth.*|br.*|docker.*|virbr*|podman.*|lo.*|vmbr.*|fwbr.|ip.*|gre.*|virbr.*|vnet.*'}[1m]))`,
|
||||||
netTx: `sum(rate(node_network_transmit_bytes_total{instance="${node}",job="${job}",device!~'tap.*|veth.*|br.*|docker.*|virbr*|podman.*|lo.*|vmbr.*|fwbr.|ip.*|gre.*|virbr.*|vnet.*'}[1m]))`
|
netTx: `sum(rate(node_network_transmit_bytes_total{instance="${node}",job="${job}",device!~'tap.*|veth.*|br.*|docker.*|virbr*|podman.*|lo.*|vmbr.*|fwbr.|ip.*|gre.*|virbr.*|vnet.*'}[1m]))`,
|
||||||
|
sockstatTcp: `node_sockstat_TCP_inuse{instance="${node}",job="${job}"}`,
|
||||||
|
sockstatTcpMem: `node_sockstat_TCP_mem{instance="${node}",job="${job}"} * 4096` // Converting pages to bytes (assuming 4KB pages)
|
||||||
};
|
};
|
||||||
|
|
||||||
const results = {};
|
const results = {};
|
||||||
@@ -461,7 +492,7 @@ async function getServerDetails(baseUrl, instance, job) {
|
|||||||
*/
|
*/
|
||||||
async function getServerHistory(baseUrl, instance, job, metric, range = '1h', start = null, end = null) {
|
async function getServerHistory(baseUrl, instance, job, metric, range = '1h', start = null, end = null) {
|
||||||
const url = normalizeUrl(baseUrl);
|
const url = normalizeUrl(baseUrl);
|
||||||
const node = instance;
|
const node = resolveToken(instance);
|
||||||
|
|
||||||
// Custom multi-metric handler for CPU Busy
|
// Custom multi-metric handler for CPU Busy
|
||||||
if (metric === 'cpuBusy') {
|
if (metric === 'cpuBusy') {
|
||||||
@@ -504,7 +535,9 @@ async function getServerHistory(baseUrl, instance, job, metric, range = '1h', st
|
|||||||
swapUsedPct: `((node_memory_SwapTotal_bytes{instance="${node}",job="${job}"} - node_memory_SwapFree_bytes{instance="${node}",job="${job}"}) / (node_memory_SwapTotal_bytes{instance="${node}",job="${job}"})) * 100`,
|
swapUsedPct: `((node_memory_SwapTotal_bytes{instance="${node}",job="${job}"} - node_memory_SwapFree_bytes{instance="${node}",job="${job}"}) / (node_memory_SwapTotal_bytes{instance="${node}",job="${job}"})) * 100`,
|
||||||
rootFsUsedPct: `100 - ((node_filesystem_avail_bytes{instance="${node}",job="${job}",mountpoint="/",fstype!="rootfs"} * 100) / node_filesystem_size_bytes{instance="${node}",job="${job}",mountpoint="/",fstype!="rootfs"})`,
|
rootFsUsedPct: `100 - ((node_filesystem_avail_bytes{instance="${node}",job="${job}",mountpoint="/",fstype!="rootfs"} * 100) / node_filesystem_size_bytes{instance="${node}",job="${job}",mountpoint="/",fstype!="rootfs"})`,
|
||||||
netRx: `sum(rate(node_network_receive_bytes_total{instance="${node}",job="${job}",device!~'tap.*|veth.*|br.*|docker.*|virbr*|podman.*|lo.*|vmbr.*|fwbr.|ip.*|gre.*|virbr.*|vnet.*'}[1m]))`,
|
netRx: `sum(rate(node_network_receive_bytes_total{instance="${node}",job="${job}",device!~'tap.*|veth.*|br.*|docker.*|virbr*|podman.*|lo.*|vmbr.*|fwbr.|ip.*|gre.*|virbr.*|vnet.*'}[1m]))`,
|
||||||
netTx: `sum(rate(node_network_transmit_bytes_total{instance="${node}",job="${job}",device!~'tap.*|veth.*|br.*|docker.*|virbr*|podman.*|lo.*|vmbr.*|fwbr.|ip.*|gre.*|virbr.*|vnet.*'}[1m]))`
|
netTx: `sum(rate(node_network_transmit_bytes_total{instance="${node}",job="${job}",device!~'tap.*|veth.*|br.*|docker.*|virbr*|podman.*|lo.*|vmbr.*|fwbr.|ip.*|gre.*|virbr.*|vnet.*'}[1m]))`,
|
||||||
|
sockstatTcp: `node_sockstat_TCP_inuse{instance="${node}",job="${job}"}`,
|
||||||
|
sockstatTcpMem: `node_sockstat_TCP_mem{instance="${node}",job="${job}"} * 4096`
|
||||||
};
|
};
|
||||||
|
|
||||||
const expr = metricMap[metric];
|
const expr = metricMap[metric];
|
||||||
|
|||||||
Reference in New Issue
Block a user