修复websocket
This commit is contained in:
@@ -296,7 +296,7 @@
|
|||||||
<path
|
<path
|
||||||
d="M2 12h20M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z" />
|
d="M2 12h20M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z" />
|
||||||
</svg>
|
</svg>
|
||||||
全球服务器分布
|
全球骨干分布
|
||||||
</h2>
|
</h2>
|
||||||
<div class="chart-header-actions">
|
<div class="chart-header-actions">
|
||||||
<button class="btn-icon" id="btnExpandGlobe" title="放大显示">
|
<button class="btn-icon" id="btnExpandGlobe" title="放大显示">
|
||||||
|
|||||||
@@ -551,8 +551,10 @@
|
|||||||
const msg = JSON.parse(event.data);
|
const msg = JSON.parse(event.data);
|
||||||
if (msg.type === 'overview') {
|
if (msg.type === 'overview') {
|
||||||
allServersData = msg.data.servers || [];
|
allServersData = msg.data.servers || [];
|
||||||
|
if (msg.data.latencies) {
|
||||||
|
currentLatencies = msg.data.latencies;
|
||||||
|
}
|
||||||
updateDashboard(msg.data);
|
updateDashboard(msg.data);
|
||||||
updateMap2D(allServersData);
|
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('WS Message Error:', err);
|
console.error('WS Message Error:', err);
|
||||||
@@ -1127,9 +1129,21 @@
|
|||||||
// Update server table
|
// Update server table
|
||||||
renderFilteredServers();
|
renderFilteredServers();
|
||||||
|
|
||||||
// Update globe
|
// Update globe (latencies already updated in ws message handler)
|
||||||
updateMap2D(data.servers || []);
|
updateMap2D(data.servers || []);
|
||||||
|
|
||||||
|
// Real-time update for server detail modal if open
|
||||||
|
if (dom.serverDetailModal.classList.contains('active') && currentServerDetail.instance) {
|
||||||
|
const currentS = (data.servers || []).find(s =>
|
||||||
|
s.instance === currentServerDetail.instance &&
|
||||||
|
s.job === currentServerDetail.job &&
|
||||||
|
s.source === currentServerDetail.source
|
||||||
|
);
|
||||||
|
if (currentS) {
|
||||||
|
updateServerDetailMetrics(currentS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Flash animation
|
// Flash animation
|
||||||
if (previousMetrics) {
|
if (previousMetrics) {
|
||||||
// No flash on update
|
// No flash on update
|
||||||
@@ -1138,6 +1152,41 @@
|
|||||||
previousMetrics = data;
|
previousMetrics = data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Real-time update for core server detail metrics (called from WebSocket broadcast)
|
||||||
|
function updateServerDetailMetrics(server) {
|
||||||
|
if (!server) return;
|
||||||
|
|
||||||
|
// Update the values displayed in the metric header cards within the detail modal
|
||||||
|
const metrics = [
|
||||||
|
{ key: 'cpuBusy', label: 'CPU 使用率', value: `
|
||||||
|
<div style="display: flex; align-items: baseline; gap: 8px;">
|
||||||
|
<span style="font-weight: 700; font-size: 1.1rem;">${formatPercent(server.cpuPercent)}</span>
|
||||||
|
</div>` },
|
||||||
|
{ key: 'memUsedPct', label: '内存使用率 (RAM)', value: formatPercent(server.memPercent) },
|
||||||
|
{ key: 'rootFsUsedPct', label: '根分区使用率 (/)', value: formatPercent(server.diskPercent) },
|
||||||
|
{ key: 'netRx', label: '网络接收速率 (RX)', value: formatBandwidth(server.netRx) },
|
||||||
|
{ key: 'netTx', label: '网络发送速率 (TX)', value: formatBandwidth(server.netTx) }
|
||||||
|
];
|
||||||
|
|
||||||
|
metrics.forEach(m => {
|
||||||
|
const el = document.getElementById(`metric-${m.key}`);
|
||||||
|
if (el) {
|
||||||
|
const valEl = el.querySelector('.metric-value');
|
||||||
|
if (valEl) valEl.innerHTML = m.value;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Also update current active history charts if they are open
|
||||||
|
Object.keys(currentServerDetail.charts).forEach(key => {
|
||||||
|
const el = document.getElementById(`metric-${key}`);
|
||||||
|
if (el && el.classList.contains('active')) {
|
||||||
|
// If the chart is open, we don't automatically refresh the history (too heavy)
|
||||||
|
// But we could append the latest point if we wanted to.
|
||||||
|
// For now, updating the summary numbers is enough for "real-time".
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function renderFilteredServers() {
|
function renderFilteredServers() {
|
||||||
let filtered = allServersData;
|
let filtered = allServersData;
|
||||||
if (currentSourceFilter !== 'all') {
|
if (currentSourceFilter !== 'all') {
|
||||||
|
|||||||
@@ -1026,7 +1026,34 @@ function broadcast(data) {
|
|||||||
async function broadcastMetrics() {
|
async function broadcastMetrics() {
|
||||||
try {
|
try {
|
||||||
const overview = await getOverview();
|
const overview = await getOverview();
|
||||||
broadcast({ type: 'overview', data: overview });
|
|
||||||
|
// Also include latencies in the broadcast to make map lines real-time
|
||||||
|
const [routes] = await db.query(`
|
||||||
|
SELECT r.*, s.url, s.type as source_type
|
||||||
|
FROM latency_routes r
|
||||||
|
JOIN prometheus_sources s ON r.source_id = s.id
|
||||||
|
`);
|
||||||
|
|
||||||
|
const latencyResults = await Promise.all(routes.map(async (route) => {
|
||||||
|
let latency = await cache.get(`latency:route:${route.id}`);
|
||||||
|
if (latency === null && route.source_type === 'prometheus') {
|
||||||
|
latency = await prometheusService.getLatency(route.url, route.latency_target);
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
id: route.id,
|
||||||
|
source: route.latency_source,
|
||||||
|
dest: route.latency_dest,
|
||||||
|
latency: latency
|
||||||
|
};
|
||||||
|
}));
|
||||||
|
|
||||||
|
broadcast({
|
||||||
|
type: 'overview',
|
||||||
|
data: {
|
||||||
|
...overview,
|
||||||
|
latencies: latencyResults
|
||||||
|
}
|
||||||
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
// console.error('WS Broadcast error:', err.message);
|
// console.error('WS Broadcast error:', err.message);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user