// 光湖 push-heartbeat v1.0 — 每台服务器主动上报本机状态 // 部署: 各服务器 PM2 cron模式,每5分钟运行一次 // 原理: 不扫描别人,只报自己。主控台(BS-GZ-006:3920)集中收。 // 冰朔: "不要扫描,让每个服务器自己主动往外发" 'use strict'; const http = require('http'); const os = require('os'); const fs = require('fs'); // ═══════════════════════════════════════ // 配置 — 部署时修改 // ═══════════════════════════════════════ const MASTER_HOST = process.env.MASTER_HOST || '43.139.217.141'; const MASTER_PORT = parseInt(process.env.MASTER_PORT || '3920'); const SERVER_CODE = process.env.SERVER_CODE || 'BS-UNKNOWN'; const SERVER_NAME = process.env.SERVER_NAME || '未命名'; const SERVER_KEY = process.env.SERVER_KEY || ''; const INTERVAL_MS = 5 * 60 * 1000; // 5分钟 // ═══════════════════════════════════════ // 硬件采集(本机,不连外网) // ═══════════════════════════════════════ function collectHardware() { const cpuCores = os.cpus().length; const loadAvg = os.loadavg(); const totalMem = os.totalmem(); const freeMem = os.freemem(); const usedMem = totalMem - freeMem; const uptime = os.uptime(); return { cpu_cores: cpuCores, cpu_load: parseFloat(loadAvg[0].toFixed(2)), cpu_load5: parseFloat(loadAvg[1].toFixed(2)), cpu_load15: parseFloat(loadAvg[2].toFixed(2)), mem_total_mb: Math.floor(totalMem / 1024 / 1024), mem_used_mb: Math.floor(usedMem / 1024 / 1024), mem_avail_mb: Math.floor(freeMem / 1024 / 1024), mem_used_pct: parseFloat((usedMem / totalMem * 100).toFixed(1)), uptime_h: Math.floor(uptime / 3600), hostname: os.hostname(), }; } // ═══════════════════════════════════════ // 上报到主控台(只发POST,不扫描) // ═══════════════════════════════════════ function reportToMaster(data) { return new Promise((resolve) => { const payload = JSON.stringify(data); const opts = { hostname: MASTER_HOST, port: MASTER_PORT, path: '/api/heartbeat', method: 'POST', headers: { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(payload), 'X-Server-Code': SERVER_CODE, 'X-Server-Key': SERVER_KEY, }, timeout: 8000, }; const req = http.request(opts, (res) => { let body = ''; res.on('data', (c) => (body += c)); res.on('end', () => resolve({ ok: res.statusCode === 200, status: res.statusCode, body })); }); req.on('error', (e) => resolve({ ok: false, error: e.message })); req.on('timeout', () => { req.destroy(); resolve({ ok: false, error: 'timeout' }); }); req.write(payload); req.end(); }); } // ═══════════════════════════════════════ // 主逻辑 // ═══════════════════════════════════════ async function push() { const hw = collectHardware(); const data = { server_code: SERVER_CODE, server_name: SERVER_NAME, timestamp: new Date().toISOString(), hardware: hw, }; console.log(`[${data.timestamp}] ${SERVER_CODE} → ${MASTER_HOST}:${MASTER_PORT}`); const result = await reportToMaster(data); if (result.ok) { console.log(` ✅ 上报成功 · CPU ${hw.cpu_load} · 内存 ${hw.mem_used_pct}% · 运行 ${hw.uptime_h}h`); } else { console.log(` ❌ 上报失败: ${result.error || result.status}`); } } // 立即发一次 push(); // 定时发 if (process.argv.includes('--once')) { // 一次性模式 — PM2 cron 使用 } else { setInterval(push, INTERVAL_MS); }