142 lines
4.7 KiB
JavaScript
142 lines
4.7 KiB
JavaScript
|
|
#!/usr/bin/env node
|
||
|
|
/**
|
||
|
|
* DStatus macOS Agent - Mac mini M2
|
||
|
|
* 模拟 Go agent 上报格式: POST /stats/update, Header Key, Body {sid, data}
|
||
|
|
*/
|
||
|
|
const os = require('os');
|
||
|
|
const https = require('https');
|
||
|
|
const http = require('http');
|
||
|
|
const { execSync } = require('child_process');
|
||
|
|
|
||
|
|
const CONFIG = {
|
||
|
|
server: 'http://51.81.222.43:5555',
|
||
|
|
apiKey: 'macmini_report_key_2026',
|
||
|
|
sid: '3b24c3cb-d06b-4747-83a8-a4c1ca11d4df',
|
||
|
|
interval: 2,
|
||
|
|
device: 'en0'
|
||
|
|
};
|
||
|
|
|
||
|
|
let prevRx = 0, prevTx = 0, prevTime = 0, firstRx = 0, firstTx = 0;
|
||
|
|
|
||
|
|
function getCpu() {
|
||
|
|
try {
|
||
|
|
const out = execSync("ps -A -o %cpu | awk '{s+=$1} END {print s/100}'", { encoding: 'utf8', timeout: 3000 });
|
||
|
|
const v = parseFloat(out.trim()) || 0;
|
||
|
|
return { multi: Math.min(v, 1), single: os.cpus().map(() => Math.min(v, 1)) };
|
||
|
|
} catch { return { multi: 0, single: [0] }; }
|
||
|
|
}
|
||
|
|
|
||
|
|
function getDisk() {
|
||
|
|
try {
|
||
|
|
const out = execSync("df -k / | tail -1", { encoding: 'utf8', timeout: 3000 });
|
||
|
|
const p = out.trim().split(/\s+/);
|
||
|
|
const total = parseInt(p[1]) * 1024;
|
||
|
|
const used = parseInt(p[2]) * 1024;
|
||
|
|
const free = parseInt(p[3]) * 1024;
|
||
|
|
const pct = parseFloat(p[4]) || 0;
|
||
|
|
return {
|
||
|
|
free, total, used,
|
||
|
|
disks: [{ device: p[0], free, total, used, fstype: 'apfs', mount: '/', percent: pct }]
|
||
|
|
};
|
||
|
|
} catch { return { free: 0, total: 0, used: 0, disks: [] }; }
|
||
|
|
}
|
||
|
|
|
||
|
|
function getMem() {
|
||
|
|
const total = os.totalmem();
|
||
|
|
const free = os.freemem();
|
||
|
|
const used = total - free;
|
||
|
|
return {
|
||
|
|
virtual: {
|
||
|
|
total, available: free, used, usedPercent: (used / total) * 100,
|
||
|
|
free, active: 0, inactive: 0, wired: 0, laundry: 0,
|
||
|
|
buffers: 0, cached: 0, writeBack: 0, dirty: 0,
|
||
|
|
writeBackTmp: 0, shared: 0, slab: 0, sreclaimable: 0,
|
||
|
|
sunreclaim: 0, pageTables: 0, swapCached: 0,
|
||
|
|
commitLimit: 0, committedAS: 0, highTotal: 0, highFree: 0,
|
||
|
|
lowTotal: 0, lowFree: 0, swapTotal: 0, swapFree: 0,
|
||
|
|
mapped: 0, vmallocTotal: 0, vmallocUsed: 0, vmallocChunk: 0,
|
||
|
|
hugePagesTotal: 0, hugePagesFree: 0, hugePagesRsvd: 0,
|
||
|
|
hugePagesSurp: 0, hugePageSize: 0, anonHugePages: 0
|
||
|
|
},
|
||
|
|
swap: { total: 0, used: 0, free: 0, usedPercent: 0, sin: 0, sout: 0, pgIn: 0, pgOut: 0, pgFault: 0, pgMajFault: 0 }
|
||
|
|
};
|
||
|
|
}
|
||
|
|
|
||
|
|
function getNet() {
|
||
|
|
try {
|
||
|
|
const out = execSync(`netstat -ibI ${CONFIG.device} | awk 'NR==2{print $7,$10}'`, { encoding: 'utf8', timeout: 3000 });
|
||
|
|
const [rx, tx] = out.trim().split(/\s+/).map(Number);
|
||
|
|
const now = Date.now();
|
||
|
|
if (!firstRx) { firstRx = rx; firstTx = tx; }
|
||
|
|
const deltaIn = prevRx ? Math.max(0, (rx || 0) - prevRx) : 0;
|
||
|
|
const deltaOut = prevTx ? Math.max(0, (tx || 0) - prevTx) : 0;
|
||
|
|
prevRx = rx || 0; prevTx = tx || 0; prevTime = now;
|
||
|
|
return {
|
||
|
|
delta: { in: deltaIn, out: deltaOut },
|
||
|
|
total: { in: (rx || 0) - firstRx, out: (tx || 0) - firstTx }
|
||
|
|
};
|
||
|
|
} catch { return { delta: { in: 0, out: 0 }, total: { in: 0, out: 0 } }; }
|
||
|
|
}
|
||
|
|
|
||
|
|
function getHost() {
|
||
|
|
return {
|
||
|
|
hostname: os.hostname(),
|
||
|
|
uptime: Math.floor(os.uptime()),
|
||
|
|
bootTime: Math.floor(Date.now() / 1000 - os.uptime()),
|
||
|
|
procs: 0,
|
||
|
|
os: 'darwin',
|
||
|
|
platform: 'macOS',
|
||
|
|
platformFamily: 'darwin',
|
||
|
|
platformVersion: os.release(),
|
||
|
|
kernelVersion: os.release(),
|
||
|
|
kernelArch: os.arch() === 'arm64' ? 'aarch64' : os.arch(),
|
||
|
|
virtualizationSystem: '',
|
||
|
|
virtualizationRole: '',
|
||
|
|
hostId: CONFIG.sid
|
||
|
|
};
|
||
|
|
}
|
||
|
|
|
||
|
|
function collectData() {
|
||
|
|
const cpu = getCpu();
|
||
|
|
const disk = getDisk();
|
||
|
|
const mem = getMem();
|
||
|
|
const net = getNet();
|
||
|
|
const host = getHost();
|
||
|
|
return {
|
||
|
|
cpu, disk: { free: disk.free, total: disk.total, used: disk.used },
|
||
|
|
disks: disk.disks, host, hostname: os.hostname(),
|
||
|
|
mem, net, timestamp: Date.now()
|
||
|
|
};
|
||
|
|
}
|
||
|
|
|
||
|
|
function report() {
|
||
|
|
const data = collectData();
|
||
|
|
const body = JSON.stringify({ sid: CONFIG.sid, data });
|
||
|
|
const mod = CONFIG.server.startsWith('https') ? https : http;
|
||
|
|
const req = mod.request(`${CONFIG.server}/stats/update`, {
|
||
|
|
method: 'POST',
|
||
|
|
headers: {
|
||
|
|
'Content-Type': 'application/json',
|
||
|
|
'Content-Length': Buffer.byteLength(body),
|
||
|
|
'Key': CONFIG.apiKey
|
||
|
|
}
|
||
|
|
}, res => {
|
||
|
|
let d = '';
|
||
|
|
res.on('data', c => d += c);
|
||
|
|
res.on('end', () => {
|
||
|
|
try {
|
||
|
|
const r = JSON.parse(d);
|
||
|
|
if (r.success || r.status === 1) console.log(`[${new Date().toLocaleTimeString()}] ✅ 上报成功`);
|
||
|
|
else console.log(`[${new Date().toLocaleTimeString()}] ❌`, d.substring(0, 100));
|
||
|
|
} catch { console.log(`[${new Date().toLocaleTimeString()}] ❌ 解析失败:`, d.substring(0, 100)); }
|
||
|
|
});
|
||
|
|
});
|
||
|
|
req.on('error', e => console.error('上报错误:', e.message));
|
||
|
|
req.write(body);
|
||
|
|
req.end();
|
||
|
|
}
|
||
|
|
|
||
|
|
console.log('DStatus macOS Agent | SID:', CONFIG.sid);
|
||
|
|
report();
|
||
|
|
setInterval(report, CONFIG.interval * 1000);
|