server v3.4: 加入之之2台+页页1台 = 12台全境监控

This commit is contained in:
bingshuo 2026-05-25 23:33:33 +08:00
parent d19cb896bf
commit 44441590dd

View File

@ -1,6 +1,6 @@
// # 光湖服务器主控台 API v3.3
// # Guanghu Server Console v3.3 — 硬件采集 + 操作日志 + 实时监控
// # 部署: guanghulab.com/console/ · SG-001:3920
// # 光湖服务器主控台 API v3.4
// # Guanghu Server Console v3.4 — 12台(冰6+企3+之2+页1)
// # 部署: guanghulab.com/console/ · GZ-006:3920(主) + SG-001:3920(备)
// # 版权: 国作登字-2026-A-00037559 · TCS-0002∞
const express = require('express');
@ -16,7 +16,7 @@ app.use(express.static(__dirname));
const DATA_DIR = '/opt/zhuyuan/data';
try { fs.mkdirSync(DATA_DIR, { recursive: true }); } catch(e) {}
// 全服务器配置 — 冰朔6台 + 企业3台 = 11
// 全服务器配置 — 冰朔6台 + 企业3台 + 之之2台 + 页页1台 = 12
const SERVERS = [
{ code: "BS-GZ-006", name: "广州 · 代码仓库", ip: "43.139.217.141", key: "zy_gtw_4a155446b7acc09fe46a2a29972c0ca5d9954da19c35dc23", port: 3910, spec: { cpu: 2, mem_gb: 4, disk_gb: 60 } },
{ code: "BS-SG-001", name: "新加坡 · 铸渊大脑", ip: "43.156.237.110", key: "zy_gtw_74d30f54fa8b5581514569ee7874def57861a31ccb2be8c9", port: 3911, spec: { cpu: 4, mem_gb: 8, disk_gb: 80 } },
@ -27,6 +27,9 @@ 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: "ZZ-SV-001", name: "之之 · 硅谷", ip: "43.173.121.48", key: "zy_gtw_06151d40b1313956b069f3c13d0c1b8a70405ae201f5e7b2", port: 3910, spec: { cpu: 2, mem_gb: 4, disk_gb: 59 } },
{ code: "ZZ-GZ-001", name: "之之 · 广州", ip: "193.112.126.174", key: "zy_gtw_77c779183284bf39d58080b4f5377d74ec6abc0f1f2218d6", port: 3910, spec: { cpu: 2, mem_gb: 2, disk_gb: 50 } },
{ code: "YY-SV-001", name: "页页 · 硅谷", ip: "43.153.118.46", key: "zy_gtw_a999f5a7775fd9844016ae2b590ebd48cf41479964bc5965", port: 3910, spec: { cpu: 2, mem_gb: 2, disk_gb: 50 } },
];
const tempKeys = {};
@ -99,55 +102,6 @@ function addOpLog(code, name, action, detail, level) {
// ========== API ==========
// 留言板存储
const BULLETINS_FILE = DATA_DIR + '/bulletins.json';
function loadBulletins() { try { if (fs.existsSync(BULLETINS_FILE)) return JSON.parse(fs.readFileSync(BULLETINS_FILE, 'utf-8')); } catch(e) {} return []; }
function saveBulletins(b) { try { fs.mkdirSync(DATA_DIR, { recursive: true }); } catch(e) {} fs.writeFileSync(BULLETINS_FILE, JSON.stringify(b, null, 2)); }
// 用户验证(轻量级 — 预设账户)
const USERS = {
'bingshuo': { password: 'zhuyuan2026', username: '冰朔', role: 'sovereign' },
'awen': { password: 'awen2026', username: 'awen', role: 'team' },
'yeye': { password: 'yeye2026', username: '页页', role: 'team' }
};
app.post('/api/verify', (req, res) => {
const { user, password } = req.body;
if (!user || !password) return res.json({ ok: false, error: '缺少参数' });
const u = USERS[user];
if (!u || u.password !== password) return res.json({ ok: false, error: '账号或密码错误' });
addOpLog(user, u.username, 'LOGIN', '首页登录', 'info');
res.json({ ok: true, username: u.username, role: u.role });
});
app.post('/api/bulletin', (req, res) => {
const { name, title, content } = req.body;
if (!content) return res.status(400).json({ ok: false, error: '内容不能为空' });
const bulletins = loadBulletins();
bulletins.unshift({ id: 'blt_' + crypto.randomBytes(4).toString('hex'), name: name || '匿名', title: title || '', content, created_at: new Date().toISOString() });
if (bulletins.length > 200) bulletins.length = 200;
saveBulletins(bulletins);
addOpLog('BULLETIN', name || '匿名', 'MSG', (title || content).slice(0, 40), 'info');
res.json({ ok: true });
});
app.get('/api/bulletins', (req, res) => {
const limit = parseInt(req.query.limit) || 20;
res.json({ ok: true, bulletins: loadBulletins().slice(0, limit) });
});
app.get('/api/keys', (req, res) => {
res.json({ ok: true, keys: [] });
});
app.post('/api/keys/submit', (req, res) => {
const { value, label } = req.body;
if (!value) return res.status(400).json({ ok: false, error: '空密钥' });
const id = 'key_' + crypto.randomBytes(4).toString('hex');
addOpLog('KEY', label || '未命名', 'SUBMIT', '密钥投递', 'info');
res.json({ ok: true, id });
});
app.get('/api/servers', async (req, res) => {
const withHw = req.query.hw !== '0';
const pingResults = await Promise.all(SERVERS.map(s => {
@ -258,33 +212,14 @@ app.post('/api/exec', async (req, res) => {
catch (e) { res.status(500).json({ error: e.message }); }
});
// 节点池 API
const POOL_REGISTRY_FILE = DATA_DIR + '/pool-registry.json';
const JOIN_TOKENS_FILE = DATA_DIR + '/join-tokens.json';
const HEARTBEAT_FILE = DATA_DIR + '/heartbeat-data.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: [] }; }
function savePoolRegistry(r) { r.updated_at = new Date().toISOString(); try { fs.mkdirSync(DATA_DIR, { recursive: true }); } catch(e) {} fs.writeFileSync(POOL_REGISTRY_FILE, JSON.stringify(r, null, 2)); }
function loadJoinTokens() { try { if (fs.existsSync(JOIN_TOKENS_FILE)) return JSON.parse(fs.readFileSync(JOIN_TOKENS_FILE, 'utf-8')); } catch(e) {} return { tokens: [] }; }
function saveJoinTokens(d) { try { fs.mkdirSync(DATA_DIR, { recursive: true }); } catch(e) {} fs.writeFileSync(JOIN_TOKENS_FILE, JSON.stringify(d, null, 2)); }
function sha256(s) { return crypto.createHash('sha256').update(s).digest('hex'); }
// 算力池状态 — 从心跳数据文件读取
app.get('/api/pool/status', (req, res) => {
try {
if (fs.existsSync(HEARTBEAT_FILE)) {
const hb = JSON.parse(fs.readFileSync(HEARTBEAT_FILE, 'utf-8'));
res.json({ ok: true, pool: hb.pool || {}, nodes: hb.nodes || {}, total_spec: hb.total_spec || {}, updated_at: hb.updated_at });
} else {
// 无心跳数据,返回静态池数据
const totalCpu = SERVERS.reduce((s, n) => s + n.spec.cpu, 0);
const totalMem = SERVERS.reduce((s, n) => s + n.spec.mem_gb, 0);
res.json({ ok: true, pool: { total_nodes: SERVERS.length, online_nodes: 0, cpu_total: totalCpu, cpu_used: 0, cpu_available: totalCpu, mem_total_gb: totalMem, mem_used_gb: 0, mem_available_gb: totalMem, active_tasks: 0 }, nodes: {}, total_spec: { cpu: totalCpu, mem_gb: totalMem }, updated_at: null });
}
} catch(e) {
res.json({ ok: true, pool: {}, nodes: {}, updated_at: null });
}
});
app.post('/api/pool/join', (req, res) => {
const { join_token, node_code, node_name, side, team_id, operator_name, hardware, network } = req.body;
if (!join_token || !node_code || !side) return res.status(400).json({ ok: false, error: '缺少参数' });
@ -329,7 +264,7 @@ app.post('/api/pool/report', (req, res) => {
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('光湖主控台 v3.4 · ' + SERVERS.length + '台(冰6+企3+之2+页1) | ' + PORT);
console.log(' 硬件采集: 并行+60s缓存 · 操作日志: 100条上限');
});