This commit is contained in:
Baobhan Sith
2025-04-20 21:53:17 +08:00
parent 7b00a111dd
commit b862e85ea5
7 changed files with 174 additions and 27 deletions
@@ -1,6 +1,7 @@
import { Client } from 'ssh2';
import { WebSocket } from 'ws';
import { ClientState } from '../websocket'; // 导入统一的 ClientState
import { settingsService } from './settings.service'; // +++ 导入 settingsService +++
// 定义服务器状态的数据结构 (与前端 StatusMonitor.vue 匹配)
interface ServerStatus {
@@ -31,7 +32,7 @@ interface NetworkStats {
}
}
const DEFAULT_POLLING_INTERVAL = 1000; // 修改为 1 秒轮询间隔 (毫秒)
// const DEFAULT_POLLING_INTERVAL = 3000; // --- 移除常量,将从 settingsService 获取 ---
// 用于存储上一次的网络统计信息以计算速率
const previousNetStats = new Map<string, { rx: number, tx: number, timestamp: number }>();
@@ -45,9 +46,9 @@ export class StatusMonitorService {
/**
* 启动指定会话的状态轮询
* @param sessionId 会话 ID
* @param interval 轮询间隔 (毫秒),可选,默认为 DEFAULT_POLLING_INTERVAL
* @param interval 轮询间隔 (毫秒),可选,默认为 DEFAULT_POLLING_INTERVAL // --- 参数移除 ---
*/
startStatusPolling(sessionId: string, interval: number = DEFAULT_POLLING_INTERVAL): void {
async startStatusPolling(sessionId: string): Promise<void> { // --- 改为 async, 移除 interval 参数 ---
const state = this.clientStates.get(sessionId);
if (!state || !state.sshClient) {
//console.warn(`[StatusMonitor] 无法为会话 ${sessionId} 启动状态轮询:状态无效或 SSH 客户端不存在。`);
@@ -57,11 +58,23 @@ export class StatusMonitorService {
//console.warn(`[StatusMonitor] 会话 ${sessionId} 的状态轮询已在运行中。`);
return;
}
//console.warn(`[StatusMonitor] 为会话 ${sessionId} 启动状态轮询,间隔 ${interval}ms`);
// +++ 从 settingsService 获取轮询间隔 +++
let intervalMs: number;
try {
const intervalSeconds = await settingsService.getStatusMonitorIntervalSeconds();
intervalMs = intervalSeconds * 1000;
console.log(`[StatusMonitor ${sessionId}] 使用配置的轮询间隔: ${intervalSeconds} 秒 (${intervalMs}ms)`);
} catch (error) {
console.error(`[StatusMonitor ${sessionId}] 获取轮询间隔设置失败,将使用默认值 3000ms:`, error);
intervalMs = 3000; // 出错时回退到 3 秒
}
//console.warn(`[StatusMonitor] 为会话 ${sessionId} 启动状态轮询,间隔 ${intervalMs}ms`);
// 移除立即执行,让 setInterval 负责第一次调用,给连接更多准备时间
state.statusIntervalId = setInterval(() => {
this.fetchAndSendServerStatus(sessionId);
}, interval);
}, intervalMs); // --- 使用获取到的间隔 ---
}
/**
@@ -94,7 +107,8 @@ export class StatusMonitorService {
const status = await this.fetchServerStatus(state.sshClient, sessionId);
state.ws.send(JSON.stringify({ type: 'status_update', payload: { connectionId: state.dbConnectionId, status } }));
} catch (error: any) {
//console.warn(`[StatusMonitor] 获取会话 ${sessionId} 服务器状态失败:`, error);
// --- 移除 console.warn ---
// console.warn(`[StatusMonitor] 获取会话 ${sessionId} 服务器状态失败:`, error);
state.ws.send(JSON.stringify({ type: 'status_error', payload: { connectionId: state.dbConnectionId, message: `获取状态失败: ${error.message}` } }));
}
}
@@ -116,7 +130,7 @@ export class StatusMonitorService {
const osReleaseOutput = await this.executeSshCommand(sshClient, 'cat /etc/os-release');
const nameMatch = osReleaseOutput.match(/^PRETTY_NAME="?([^"]+)"?/m);
status.osName = nameMatch ? nameMatch[1] : (osReleaseOutput.match(/^NAME="?([^"]+)"?/m)?.[1] ?? 'Unknown');
} catch (err) { console.warn(`[StatusMonitor ${sessionId}] Failed to get OS name:`, err); }
} catch (err) { /* 静默处理 */ } // --- 移除 console.warn ---
// --- CPU Model (Try /proc/cpuinfo first, fallback to lscpu) ---
try {
@@ -126,13 +140,13 @@ export class StatusMonitorService {
cpuModelOutput = await this.executeSshCommand(sshClient, "cat /proc/cpuinfo | grep 'model name' | head -n 1");
status.cpuModel = cpuModelOutput.match(/model name\s*:\s*(.*)/i)?.[1].trim();
} catch (procErr) {
console.warn(`[StatusMonitor ${sessionId}] Failed to get CPU model from /proc/cpuinfo, trying lscpu...`, procErr);
// console.warn(`[StatusMonitor ${sessionId}] Failed to get CPU model from /proc/cpuinfo, trying lscpu...`, procErr); // --- 移除 console.warn ---
// Fallback to lscpu if /proc/cpuinfo fails
try {
cpuModelOutput = await this.executeSshCommand(sshClient, "lscpu | grep 'Model name:'");
status.cpuModel = cpuModelOutput.match(/Model name:\s+(.*)/)?.[1].trim();
} catch (lscpuErr) {
console.warn(`[StatusMonitor ${sessionId}] Failed to get CPU model from lscpu as well:`, lscpuErr);
// console.warn(`[StatusMonitor ${sessionId}] Failed to get CPU model from lscpu as well:`, lscpuErr); // --- 移除 console.warn ---
}
}
// If still no model found after both attempts
@@ -140,7 +154,7 @@ export class StatusMonitorService {
status.cpuModel = 'Unknown';
}
} catch (err) { // Catch any unexpected error during the process
console.warn(`[StatusMonitor ${sessionId}] Error getting CPU model:`, err);
// console.warn(`[StatusMonitor ${sessionId}] Error getting CPU model:`, err); // --- 移除 console.warn ---
status.cpuModel = 'Unknown';
}
@@ -172,7 +186,7 @@ export class StatusMonitorService {
}
}
} else { status.swapTotal = 0; status.swapUsed = 0; status.swapPercent = 0; }
} catch (err) { console.warn(`[StatusMonitor ${sessionId}] Failed to get memory/swap usage:`, err); }
} catch (err) { /* 静默处理 */ } // --- 移除 console.warn ---
// --- Disk Usage (Root Partition, POSIX format for compatibility) ---
try {
@@ -193,7 +207,7 @@ export class StatusMonitorService {
}
}
}
} catch (err) { console.warn(`[StatusMonitor ${sessionId}] Failed to get disk usage:`, err); }
} catch (err) { /* 静默处理 */ } // --- 移除 console.warn ---
// --- CPU Usage (Simplified from top) ---
try {
@@ -203,14 +217,14 @@ export class StatusMonitorService {
const idlePercent = parseFloat(idleMatch[1]);
status.cpuPercent = parseFloat((100 - idlePercent).toFixed(1));
}
} catch (err) { console.warn(`[StatusMonitor ${sessionId}] Failed to get CPU usage from top:`, err); }
} catch (err) { /* 静默处理 */ } // --- 移除 console.warn ---
// --- Load Average ---
try {
const uptimeOutput = await this.executeSshCommand(sshClient, 'uptime');
const match = uptimeOutput.match(/load average(?:s)?:\s*([\d.]+)[, ]?\s*([\d.]+)[, ]?\s*([\d.]+)/);
if (match) status.loadAvg = [parseFloat(match[1]), parseFloat(match[2]), parseFloat(match[3])];
} catch (err) { console.warn(`[StatusMonitor ${sessionId}] Failed to get uptime/load average:`, err); }
} catch (err) { /* 静默处理 */ } // --- 移除 console.warn ---
// --- Network Rates ---
try {
@@ -233,9 +247,9 @@ export class StatusMonitorService {
} else { status.netRxRate = 0; status.netTxRate = 0; } // First run or no time diff
previousNetStats.set(sessionId, { rx: currentRx, tx: currentTx, timestamp });
} else { console.warn(`[StatusMonitor ${sessionId}] Could not find stats for default interface ${defaultInterface}`); }
} else { /* 静默处理 */ } // --- 移除 console.warn ---
}
} catch (err) { console.warn(`[StatusMonitor ${sessionId}] Failed to get network stats:`, err); }
} catch (err) { /* 静默处理 */ } // --- 移除 console.warn ---
} catch (error) {
console.error(`[StatusMonitor ${sessionId}] General error fetching server status:`, error);
@@ -256,7 +270,7 @@ export class StatusMonitorService {
output = await this.executeSshCommand(sshClient, 'cat /proc/net/dev');
} catch (error) {
// 如果命令失败,记录警告并返回 null
console.warn("[StatusMonitor] Failed to execute 'cat /proc/net/dev':", error);
// console.warn("[StatusMonitor] Failed to execute 'cat /proc/net/dev':", error); // --- 移除 console.warn ---
return null;
}
// 如果命令成功,继续解析
@@ -276,7 +290,7 @@ export class StatusMonitorService {
return Object.keys(stats).length > 0 ? stats : null;
} catch (parseError) {
// 如果解析失败,记录错误并返回 null
console.error("[StatusMonitor] Error parsing /proc/net/dev output:", parseError);
// console.error("[StatusMonitor] Error parsing /proc/net/dev output:", parseError); // --- 移除 console.error ---
return null;
}
}
@@ -293,10 +307,10 @@ export class StatusMonitorService {
const interfaceName = output.trim();
if (interfaceName) return interfaceName;
// 如果 ip route 没返回有效接口名,也尝试 fallback
console.warn("[StatusMonitor] 'ip route' did not return a valid interface name. Falling back...");
// console.warn("[StatusMonitor] 'ip route' did not return a valid interface name. Falling back..."); // --- 移除 console.warn ---
} catch (error) {
console.warn("[StatusMonitor] Failed to get default interface using 'ip route', falling back:", error);
// console.warn("[StatusMonitor] Failed to get default interface using 'ip route', falling back:", error); // --- 移除 console.warn ---
// Fallback: 尝试查找第一个非 lo 接口
try {
const netDevOutput = await this.executeSshCommand(sshClient, 'cat /proc/net/dev');
@@ -308,7 +322,7 @@ export class StatusMonitorService {
}
}
} catch (fallbackError) {
console.error("[StatusMonitor] Failed to fallback to /proc/net/dev for interface:", fallbackError);
// console.error("[StatusMonitor] Failed to fallback to /proc/net/dev for interface:", fallbackError); // --- 移除 console.error ---
}
// Ensure null is returned if both primary and fallback fail within the outer catch
return null;
@@ -341,7 +355,8 @@ export class StatusMonitorService {
}).on('data', (data: Buffer) => {
output += data.toString('utf8');
}).stderr.on('data', (data: Buffer) => {
//console.warn(`[StatusMonitor] Command '${command}' stderr: ${data.toString('utf8').trim()}`);
// --- 移除 console.warn ---
// console.warn(`[StatusMonitor] Command '${command}' stderr: ${data.toString('utf8').trim()}`);
});
});
});