123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333 |
- /**
- * 服务器实用工具函数
- */
- const { exec } = require('child_process');
- const os = require('os');
- const fs = require('fs').promises;
- const path = require('path');
- const logger = require('./logger');
- /**
- * 安全执行系统命令
- * @param {string} command - 要执行的命令
- * @param {object} options - 执行选项
- * @returns {Promise<string>} 命令输出结果
- */
- function execCommand(command, options = { timeout: 30000 }) {
- return new Promise((resolve, reject) => {
- exec(command, options, (error, stdout, stderr) => {
- if (error) {
- if (error.killed) {
- reject(new Error('执行命令超时'));
- } else {
- reject(error);
- }
- return;
- }
- resolve(stdout.trim() || stderr.trim());
- });
- });
- }
- /**
- * 获取系统信息
- * @returns {Promise<object>} 系统信息对象
- */
- async function getSystemInfo() {
- const platform = os.platform();
- let memoryInfo = {};
- let cpuInfo = {};
- let diskInfo = {};
-
- try {
- // 内存信息 - 使用OS模块,适用于所有平台
- const totalMem = os.totalmem();
- const freeMem = os.freemem();
- const usedMem = totalMem - freeMem;
- const memPercent = Math.round((usedMem / totalMem) * 100);
-
- memoryInfo = {
- total: formatBytes(totalMem),
- free: formatBytes(freeMem),
- used: formatBytes(usedMem),
- percent: memPercent
- };
-
- // CPU信息 - 使用不同平台的方法
- await getCpuInfo(platform).then(info => {
- cpuInfo = info;
- }).catch(err => {
- logger.warn('获取CPU信息失败:', err);
- // 降级方案:使用OS模块
- const cpuLoad = os.loadavg();
- const cpuCores = os.cpus().length;
-
- cpuInfo = {
- model: os.cpus()[0].model,
- cores: cpuCores,
- load1: cpuLoad[0].toFixed(2),
- load5: cpuLoad[1].toFixed(2),
- load15: cpuLoad[2].toFixed(2),
- percent: Math.round((cpuLoad[0] / cpuCores) * 100)
- };
- });
-
- // 磁盘信息 - 根据平台调用不同方法
- await getDiskInfo(platform).then(info => {
- diskInfo = info;
- }).catch(err => {
- logger.warn('获取磁盘信息失败:', err);
- diskInfo = {
- filesystem: 'unknown',
- size: 'unknown',
- used: 'unknown',
- available: 'unknown',
- percent: '0%'
- };
- });
-
- return {
- platform,
- hostname: os.hostname(),
- memory: memoryInfo,
- cpu: cpuInfo,
- disk: diskInfo,
- uptime: formatUptime(os.uptime())
- };
- } catch (error) {
- logger.error('获取系统信息失败:', error);
- throw error;
- }
- }
- /**
- * 根据平台获取CPU信息
- * @param {string} platform - 操作系统平台
- * @returns {Promise<object>} CPU信息
- */
- async function getCpuInfo(platform) {
- if (platform === 'linux') {
- try {
- // Linux平台使用/proc/stat和/proc/cpuinfo
- const [loadData, cpuData] = await Promise.all([
- execCommand("cat /proc/loadavg"),
- execCommand("cat /proc/cpuinfo | grep 'model name' | head -1")
- ]);
-
- const cpuLoad = loadData.split(' ').slice(0, 3).map(parseFloat);
- const cpuCores = os.cpus().length;
- const modelMatch = cpuData.match(/model name\s*:\s*(.*)/);
- const model = modelMatch ? modelMatch[1].trim() : os.cpus()[0].model;
- const percent = Math.round((cpuLoad[0] / cpuCores) * 100);
-
- return {
- model,
- cores: cpuCores,
- load1: cpuLoad[0].toFixed(2),
- load5: cpuLoad[1].toFixed(2),
- load15: cpuLoad[2].toFixed(2),
- percent: percent > 100 ? 100 : percent
- };
- } catch (error) {
- throw error;
- }
- } else if (platform === 'darwin') {
- // macOS平台
- try {
- const cpuLoad = os.loadavg();
- const cpuCores = os.cpus().length;
- const model = os.cpus()[0].model;
- const systemProfilerData = await execCommand("system_profiler SPHardwareDataType | grep 'Processor Name'");
- const cpuMatch = systemProfilerData.match(/Processor Name:\s*(.*)/);
- const cpuModel = cpuMatch ? cpuMatch[1].trim() : model;
-
- return {
- model: cpuModel,
- cores: cpuCores,
- load1: cpuLoad[0].toFixed(2),
- load5: cpuLoad[1].toFixed(2),
- load15: cpuLoad[2].toFixed(2),
- percent: Math.round((cpuLoad[0] / cpuCores) * 100)
- };
- } catch (error) {
- throw error;
- }
- } else if (platform === 'win32') {
- // Windows平台
- try {
- // 使用wmic获取CPU信息
- const cpuData = await execCommand('wmic cpu get Name,NumberOfCores /value');
- const cpuLines = cpuData.split('\r\n');
-
- let model = os.cpus()[0].model;
- let cores = os.cpus().length;
-
- cpuLines.forEach(line => {
- if (line.startsWith('Name=')) {
- model = line.substring(5).trim();
- } else if (line.startsWith('NumberOfCores=')) {
- cores = parseInt(line.substring(14).trim()) || cores;
- }
- });
-
- // Windows没有直接的负载平均值,使用CPU使用率作为替代
- const perfData = await execCommand('wmic cpu get LoadPercentage /value');
- const loadMatch = perfData.match(/LoadPercentage=(\d+)/);
- const loadPercent = loadMatch ? parseInt(loadMatch[1]) : 0;
-
- return {
- model,
- cores,
- load1: '不适用',
- load5: '不适用',
- load15: '不适用',
- percent: loadPercent
- };
- } catch (error) {
- throw error;
- }
- }
-
- // 默认返回OS模块的信息
- const cpuLoad = os.loadavg();
- const cpuCores = os.cpus().length;
-
- return {
- model: os.cpus()[0].model,
- cores: cpuCores,
- load1: cpuLoad[0].toFixed(2),
- load5: cpuLoad[1].toFixed(2),
- load15: cpuLoad[2].toFixed(2),
- percent: Math.round((cpuLoad[0] / cpuCores) * 100)
- };
- }
- /**
- * 根据平台获取磁盘信息
- * @param {string} platform - 操作系统平台
- * @returns {Promise<object>} 磁盘信息
- */
- async function getDiskInfo(platform) {
- if (platform === 'linux' || platform === 'darwin') {
- try {
- // Linux/macOS使用df命令
- const diskCommand = platform === 'linux'
- ? 'df -h / | tail -1'
- : 'df -h / | tail -1';
-
- const diskData = await execCommand(diskCommand);
- const parts = diskData.trim().split(/\s+/);
-
- if (parts.length >= 5) {
- return {
- filesystem: parts[0],
- size: parts[1],
- used: parts[2],
- available: parts[3],
- percent: parts[4]
- };
- } else {
- throw new Error('磁盘信息格式不正确');
- }
- } catch (error) {
- throw error;
- }
- } else if (platform === 'win32') {
- // Windows平台
- try {
- // 使用wmic获取C盘信息
- const diskData = await execCommand('wmic logicaldisk where DeviceID="C:" get Size,FreeSpace /value');
- const lines = diskData.split(/\r\n|\n/);
- let freeSpace, totalSize;
-
- lines.forEach(line => {
- if (line.startsWith('FreeSpace=')) {
- freeSpace = parseInt(line.split('=')[1]);
- } else if (line.startsWith('Size=')) {
- totalSize = parseInt(line.split('=')[1]);
- }
- });
-
- if (freeSpace !== undefined && totalSize !== undefined) {
- const usedSpace = totalSize - freeSpace;
- const usedPercent = Math.round((usedSpace / totalSize) * 100);
-
- return {
- filesystem: 'C:',
- size: formatBytes(totalSize),
- used: formatBytes(usedSpace),
- available: formatBytes(freeSpace),
- percent: `${usedPercent}%`
- };
- } else {
- throw new Error('无法解析Windows磁盘信息');
- }
- } catch (error) {
- throw error;
- }
- }
-
- // 默认尝试df命令
- try {
- const diskData = await execCommand('df -h / | tail -1');
- const parts = diskData.trim().split(/\s+/);
-
- if (parts.length >= 5) {
- return {
- filesystem: parts[0],
- size: parts[1],
- used: parts[2],
- available: parts[3],
- percent: parts[4]
- };
- } else {
- throw new Error('磁盘信息格式不正确');
- }
- } catch (error) {
- throw error;
- }
- }
- /**
- * 将字节格式化为可读大小
- * @param {number} bytes - 字节数
- * @returns {string} 格式化后的字符串
- */
- function formatBytes(bytes) {
- if (bytes === 0) return '0 B';
-
- const sizes = ['B', 'KB', 'MB', 'GB', 'TB', 'PB'];
- const i = Math.floor(Math.log(bytes) / Math.log(1024));
-
- return parseFloat((bytes / Math.pow(1024, i)).toFixed(2)) + ' ' + sizes[i];
- }
- /**
- * 格式化运行时间
- * @param {number} seconds - 秒数
- * @returns {string} 格式化后的运行时间
- */
- function formatUptime(seconds) {
- const days = Math.floor(seconds / 86400);
- seconds %= 86400;
- const hours = Math.floor(seconds / 3600);
- seconds %= 3600;
- const minutes = Math.floor(seconds / 60);
- seconds = Math.floor(seconds % 60);
-
- const parts = [];
- if (days > 0) parts.push(`${days}天`);
- if (hours > 0) parts.push(`${hours}小时`);
- if (minutes > 0) parts.push(`${minutes}分钟`);
- if (seconds > 0 && parts.length === 0) parts.push(`${seconds}秒`);
-
- return parts.join(' ');
- }
- module.exports = {
- execCommand,
- getSystemInfo,
- formatBytes,
- formatUptime
- };
|