const fs = require('fs'); const os = require('os'); const { execFileSync } = require('child_process'); /** * Reads and parses /proc/stat to calculate CPU usage. * To get a real-time percentage, it samples twice with a small delay. * @returns {Promise} CPU usage percent (0-100) */ const getCpuUsage = async () => { const getStats = () => { const data = fs.readFileSync('/proc/stat', 'utf8'); const lines = data.split('\n'); const cpuLine = lines[0].split(/\s+/).slice(1); const idle = parseInt(cpuLine[3], 10); const total = cpuLine.reduce((acc, val) => acc + parseInt(val, 10), 0); return { idle, total }; }; const stats1 = getStats(); await new Promise(resolve => setTimeout(resolve, 200)); const stats2 = getStats(); const idleDiff = stats2.idle - stats1.idle; const totalDiff = stats2.total - stats1.total; if (totalDiff === 0) return 0; const usage = 100 * (1 - idleDiff / totalDiff); return parseFloat(usage.toFixed(1)); }; /** * Reads /proc/meminfo to calculate memory stats in MB. * @returns {object} { total, used, free } */ const getMemoryInfo = () => { const data = fs.readFileSync('/proc/meminfo', 'utf8'); const lines = data.split('\n'); const stats = {}; lines.forEach(line => { const parts = line.split(':'); if (parts.length === 2) { stats[parts[0].trim()] = parseInt(parts[1].trim().split(' ')[0], 10); } }); const total = Math.round(stats.MemTotal / 1024); const free = Math.round(stats.MemFree / 1024); const buffers = Math.round((stats.Buffers || 0) / 1024); const cached = Math.round((stats.Cached || 0) / 1024); const available = Math.round((stats.MemAvailable || (stats.MemFree + stats.Buffers + stats.Cached)) / 1024); const used = total - available; return { total, used: Math.max(0, used), free: available }; }; const getDiskUsage = () => { try { // -T shows filesystem type const output = execFileSync('df', ['-T'], { encoding: 'utf-8' }); const lines = output.trim().split('\n').slice(1); // Skip header const allowedFS = ['ext4', 'ext3', 'xfs', 'btrfs', 'vfat']; return lines.map(line => { const parts = line.split(/\s+/); const totalBlocks = parseInt(parts[2]); const usedBlocks = parseInt(parts[3]); const availBlocks = parseInt(parts[4]); return { mount: parts[6], type: parts[1], total: (totalBlocks * 1024 / 1e9).toFixed(1), // GB used: (usedBlocks * 1024 / 1e9).toFixed(1), // GB free: (availBlocks * 1024 / 1e9).toFixed(1),// GB percent: parts[5], // Keep as string with % for now as per current frontend usage unit: 'GB' }; }).filter(d => allowedFS.includes(d.type) && parseFloat(d.total) > 0 && d.mount.startsWith('/') ); } catch (err) { console.error('Error getting disk usage:', err.message); return []; } }; /** * Calculates system uptime. * @returns {object} { seconds, human } */ const getUptime = () => { const seconds = Math.floor(os.uptime()); const days = Math.floor(seconds / (24 * 3600)); const hours = Math.floor((seconds % (24 * 3600)) / 3600); const minutes = Math.floor((seconds % 3600) / 60); let human = ''; if (days > 0) human += `${days} day${days > 1 ? 's' : ''}, `; if (hours > 0) human += `${hours} hour${hours > 1 ? 's' : ''}, `; human += `${minutes} minute${minutes > 1 ? 's' : ''}`; return { seconds, human }; }; /** * Reads /proc/loadavg to get system load. * @returns {object} { one, five, fifteen } */ const getLoadAverage = () => { const data = fs.readFileSync('/proc/loadavg', 'utf8'); const parts = data.trim().split(/\s+/); return { one: parseFloat(parts[0]), five: parseFloat(parts[1]), fifteen: parseFloat(parts[2]) }; }; module.exports = { getCpuUsage, getMemoryInfo, getDiskUsage, getUptime, getLoadAverage };