From 53dd67a2eabecea5e28931a2d7d0e4e061a4a17e Mon Sep 17 00:00:00 2001 From: bingshuo <565183519@qq.com> Date: Mon, 25 May 2026 23:16:34 +0800 Subject: [PATCH] =?UTF-8?q?server=20v3.3:=20=E5=B9=B6=E8=A1=8C=E7=A1=AC?= =?UTF-8?q?=E4=BB=B6=E9=87=87=E9=9B=86=E4=BF=AE=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- _deploy/console-server/server.js | 55 +++++++++----------------------- 1 file changed, 15 insertions(+), 40 deletions(-) diff --git a/_deploy/console-server/server.js b/_deploy/console-server/server.js index 6f1c972..a025af6 100644 --- a/_deploy/console-server/server.js +++ b/_deploy/console-server/server.js @@ -11,7 +11,7 @@ const fs = require('fs'); const app = express(); app.use(express.json()); -app.use(express.static(path.join(__dirname, 'public'))); +app.use(express.static(__dirname)); const DATA_DIR = '/opt/zhuyuan/data'; try { fs.mkdirSync(DATA_DIR, { recursive: true }); } catch(e) {} @@ -27,19 +27,12 @@ const SERVERS = [ { code: "AW-GZ-001", name: "企业主服务器 · 广州", ip: "43.139.251.175", key: "zy_gtw_cab20e8c1b957aaad7507bf542231521bba30665461aa540", port: 3910, spec: { cpu: 4, mem_gb: 16, disk_gb: 80 } }, { code: "AW-SH-002", name: "企业灾备 · 上海", ip: "124.222.54.198", key: "zy_gtw_242f1dd3b14c1260fb01196083abde3dcb85e53532d27666", port: 3910, spec: { cpu: 4, mem_gb: 4, disk_gb: 40 } }, { code: "AW-GZ-003", name: "企业灾备 · 广州", ip: "119.29.181.132", key: "zy_gtw_8c9a8b439af823690ae6ad57f52734771ff39493ab986032", port: 3910, spec: { cpu: 2, mem_gb: 8, disk_gb: 50 } }, - // 页页 · 硅谷(待接入) - // { code: "YY-SV-001", name: "页页 · 硅谷", ip: "TBD", key: "TBD", port: 3910, spec: { cpu: 2, mem_gb: 4, disk_gb: 50 } }, - // 之之 · 待定 - // { code: "ZZ-XX-001", name: "之之 · 待定", ip: "TBD", key: "TBD", port: 3910, spec: { cpu: 2, mem_gb: 4, disk_gb: 50 } }, ]; const tempKeys = {}; const authQueue = []; +const hwCache = {}; -// ── 硬件缓存 (60秒TTL) ── -const hwCache = {}; // { code: { data, ts } } - -// ── Gatekeeper exec 通用函数 ── async function gatekeeperExec(srv, cmd, timeoutMs) { timeoutMs = timeoutMs || 10000; const d = JSON.stringify({ cmd }); @@ -59,27 +52,21 @@ async function gatekeeperExec(srv, cmd, timeoutMs) { }); } -// ── 硬件采集(通过 Gatekeeper exec) ── async function fetchHardware(srv) { const now = Date.now(); const cached = hwCache[srv.code]; - if (cached && (now - cached.ts) < 60000) return cached.data; // 60s cache - + if (cached && (now - cached.ts) < 60000) return cached.data; try { const cmd = "echo 'CPU_CORES:'$(nproc);echo 'UPTIME_S:'$(awk '{print int($1)}' /proc/uptime 2>/dev/null||echo 0);free -m|awk '/^Mem:/{printf \"MEM_TOTAL:%d\\nMEM_USED:%d\\nMEM_AVAIL:%d\\n\",$2,$3,$7}';cat /proc/loadavg 2>/dev/null|awk '{printf \"LOAD1:%.2f\\nLOAD5:%.2f\\nLOAD15:%.2f\\n\",$1,$2,$3}';df -h / 2>/dev/null|awk 'NR==2{printf \"DISK_TOTAL:%s\\nDISK_USED:%s\\nDISK_AVAIL:%s\\nDISK_PCT:%s\\n\",$2,$3,$4,$5}'"; const r = await gatekeeperExec(srv, cmd, 8000); - if (!r.ok || !r.body || !r.body.stdout) { const fb = { cpu_cores: srv.spec.cpu || 2, uptime_h: 0, mem_total_mb: (srv.spec.mem_gb || 4) * 1024, mem_used_mb: 0, mem_avail_mb: (srv.spec.mem_gb || 4) * 1024, load_1min: 0, load_5min: 0, load_15min: 0, disk_total: (srv.spec.disk_gb || 50) + 'G', disk_used: '0G', disk_avail: (srv.spec.disk_gb || 50) + 'G', disk_pct: '0%', source: 'spec' }; hwCache[srv.code] = { data: fb, ts: now }; return fb; } - const stdout = r.body.stdout || ''; const hw = { source: 'live' }; - const m = (re) => { const match = stdout.match(re); return match ? match[1] : null; }; - hw.cpu_cores = parseInt(m(/CPU_CORES:(\d+)/)) || srv.spec.cpu || 2; hw.uptime_h = Math.floor((parseInt(m(/UPTIME_S:(\d+)/)) || 0) / 3600); hw.mem_total_mb = parseInt(m(/MEM_TOTAL:(\d+)/)) || (srv.spec.mem_gb || 4) * 1024; @@ -92,11 +79,9 @@ async function fetchHardware(srv) { hw.disk_used = m(/DISK_USED:(\S+)/) || '0G'; hw.disk_avail = m(/DISK_AVAIL:(\S+)/) || hw.disk_total; hw.disk_pct = m(/DISK_PCT:(\S+)/) || '0%'; - hw.mem_pct = hw.mem_total_mb > 0 ? Math.round(hw.mem_used_mb / hw.mem_total_mb * 100) : 0; hw.cpu_pct = hw.cpu_cores > 0 ? Math.min(Math.round(hw.load_1min / hw.cpu_cores * 100), 100) : 0; hw.disk_used_pct = parseInt(hw.disk_pct) || 0; - hwCache[srv.code] = { data: hw, ts: now }; return hw; } catch (e) { @@ -106,14 +91,13 @@ async function fetchHardware(srv) { } } -// ── 操作日志(滚动记录,最多100条) ── const opLogs = []; function addOpLog(code, name, action, detail, level) { opLogs.unshift({ time: new Date().toISOString(), code, name, action, detail, level: level || 'info' }); if (opLogs.length > 100) opLogs.length = 100; } -// ========== 基础 API ========== +// ========== API ========== app.get('/api/servers', async (req, res) => { const withHw = req.query.hw !== '0'; @@ -134,12 +118,14 @@ app.get('/api/servers', async (req, res) => { }); })); - for (const r of pingResults) { + const hwTasks = pingResults.filter(r => r.alive && withHw).map(async (r) => { const srv = SERVERS.find(s => s.code === r.code); r.last_seen = new Date().toISOString(); - if (r.alive && withHw) { - try { r.hardware = await fetchHardware(srv); } catch(e) { r.hardware = null; } - } + try { r.hardware = await fetchHardware(srv); } catch(e) { r.hardware = null; } + }); + await Promise.all(hwTasks); + for (const r of pingResults) { + if (!r.last_seen) r.last_seen = new Date().toISOString(); } res.json({ ok: true, servers: pingResults, timestamp: new Date().toISOString(), total: SERVERS.length }); @@ -176,19 +162,12 @@ app.get('/api/summary', async (req, res) => { req.write(data); req.end(); }); })); - const aliveCount = pingResults.filter(r => r.alive).length; const totalCpu = SERVERS.reduce((sum, s) => sum + (s.spec.cpu || 2), 0); const totalMem = SERVERS.reduce((sum, s) => sum + (s.spec.mem_gb || 4), 0); const totalDisk = SERVERS.reduce((sum, s) => sum + (s.spec.disk_gb || 50), 0); const alertCount = opLogs.filter(l => l.level === 'error').length; - - res.json({ - ok: true, total_servers: SERVERS.length, alive: aliveCount, - offline: SERVERS.length - aliveCount, alerts: alertCount, - total_cpu_cores: totalCpu, total_mem_gb: totalMem, total_disk_gb: totalDisk, - timestamp: new Date().toISOString() - }); + res.json({ ok: true, total_servers: SERVERS.length, alive: aliveCount, offline: SERVERS.length - aliveCount, alerts: alertCount, total_cpu_cores: totalCpu, total_mem_gb: totalMem, total_disk_gb: totalDisk, timestamp: new Date().toISOString() }); }); app.post('/api/knock', (req, res) => { @@ -226,14 +205,11 @@ app.post('/api/exec', async (req, res) => { if (!temp_key || !target || !cmd) return res.status(400).json({ error: '缺少参数' }); const ke = tempKeys[temp_key]; if (!ke || Date.now() > ke.expires_at) return res.status(401).json({ error: '密钥无效' }); const s = SERVERS.find(s => s.code === target); if (!s) return res.status(404).json({ error: '未找到' }); - try { - const r = await gatekeeperExec(s, cmd, 30000); - addOpLog(s.code, s.name, 'EXEC', cmd.slice(0, 60), 'info'); - res.json({ ok: true, result: r }); - } catch (e) { res.status(500).json({ error: e.message }); } + try { const r = await gatekeeperExec(s, cmd, 30000); addOpLog(s.code, s.name, 'EXEC', cmd.slice(0, 60), 'info'); res.json({ ok: true, result: r }); } + catch (e) { res.status(500).json({ error: e.message }); } }); -// ========== 节点池 API ========== +// 节点池 API const POOL_REGISTRY_FILE = DATA_DIR + '/pool-registry.json'; const JOIN_TOKENS_FILE = DATA_DIR + '/join-tokens.json'; function loadPoolRegistry() { try { if (fs.existsSync(POOL_REGISTRY_FILE)) return JSON.parse(fs.readFileSync(POOL_REGISTRY_FILE, 'utf-8')); } catch(e) {} return { active: {}, pending: {}, history: [] }; } @@ -284,11 +260,10 @@ app.post('/api/pool/report', (req, res) => { res.json({ ok: true, node_code }); }); -// ========== 启动 ========== const PORT = process.env.CONSOLE_PORT || 3920; app.listen(PORT, '127.0.0.1', () => { console.log('光湖主控台 v3.3 · ' + SERVERS.length + '台 | SG-001:' + PORT); - console.log(' 硬件采集: 60s缓存 · 操作日志: 100条上限'); + console.log(' 硬件采集: 并行+60s缓存 · 操作日志: 100条上限'); }); setInterval(() => {