guanghulab/server/proxy/service/bandwidth-pool-agent.js
2026-05-10 13:12:44 +08:00

469 lines
14 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env node
// ═══════════════════════════════════════════════
// 🔺 Sovereign: TCS-0002∞ | Root: SYS-GLW-0001
// 📜 Copyright: 国作登字-2026-A-00037559
// ═══════════════════════════════════════════════
// server/proxy/service/bandwidth-pool-agent.js
// 🌊 光湖语言世界 · 带宽汇聚Agent
//
// ∞+1 版本核心模块 — 用户带宽共享加速系统
//
// 架构设计 (冰朔定根):
// 用户的光纤带宽是真正的引擎。
// 通过验证码授权,用户自愿共享多余带宽,
// 汇聚到公共加速缓冲池,为其他用户加速。
// 用得用户越多,系统越快。
//
// 核心流程:
// 1. 发送验证码 → 用户QQ邮箱
// 2. 用户输入验证码 → 授权带宽共享
// 3. 采集用户IP (加密存储) → 注册为Relay节点
// 4. 用户专属直通道 (优先) + 公共加速缓冲池 (多余带宽)
//
// 安全原则:
// - 用户IP仅加密存储 (SHA256+盐值),不存明文
// - 验证码15分钟过期一次性使用
// - 用户可随时退出带宽共享
// - 威胁检测时自动切断用户共享通道
//
// 运行方式: PM2 managed (zy-bandwidth-pool)
// 状态文件: bandwidth-pool-status.json
// ═══════════════════════════════════════════════
'use strict';
const crypto = require('crypto');
const fs = require('fs');
const path = require('path');
const PROXY_DIR = process.env.ZY_BRAIN_PROXY_DIR || '/opt/zhuyuan-brain/proxy';
const DATA_DIR = path.join(PROXY_DIR, 'data');
const POOL_STATUS_FILE = path.join(DATA_DIR, 'bandwidth-pool-status.json');
const AUTH_CODES_FILE = path.join(DATA_DIR, 'bandwidth-auth-codes.json');
const CONTRIBUTORS_FILE = path.join(DATA_DIR, 'bandwidth-contributors.json');
const THREAT_LOG_FILE = path.join(DATA_DIR, 'bandwidth-threat-log.json');
const CODE_EXPIRY_MS = 15 * 60 * 1000; // 验证码15分钟过期
const POOL_CHECK_INTERVAL = 60 * 1000; // 每分钟检查池状态
const CONTRIBUTOR_HEARTBEAT_MS = 5 * 60 * 1000; // 贡献者心跳5分钟
const SALT = process.env.ZY_BW_SALT || (() => {
// 如果未设置环境变量,从密钥文件中读取或生成持久化盐值
const saltFile = path.join(DATA_DIR, '.bw-salt');
try {
return fs.readFileSync(saltFile, 'utf8').trim();
} catch {
const generated = crypto.randomBytes(32).toString('hex');
try {
fs.mkdirSync(DATA_DIR, { recursive: true });
fs.writeFileSync(saltFile, generated, { mode: 0o600 });
} catch { /* 无法持久化时使用内存值 */ }
return generated;
}
})();
// ═══════════════════════════════════════════════
// 🔑 验证码管理
// ═══════════════════════════════════════════════
/**
* 生成6位数字验证码
*/
function generateAuthCode() {
return crypto.randomInt(100000, 999999).toString();
}
/**
* 加密IP地址 (SHA256 + 盐值)
*/
function encryptIP(ip) {
return crypto.createHash('sha256').update(`${SALT}:${ip}`).digest('hex');
}
/**
* 读取验证码存储
*/
function readAuthCodes() {
try {
return JSON.parse(fs.readFileSync(AUTH_CODES_FILE, 'utf8'));
} catch {
return { codes: {} };
}
}
/**
* 保存验证码存储
*/
function saveAuthCodes(data) {
fs.mkdirSync(DATA_DIR, { recursive: true });
fs.writeFileSync(AUTH_CODES_FILE, JSON.stringify(data, null, 2));
}
/**
* 创建验证码并关联邮箱
* @param {string} email 用户邮箱
* @returns {string} 6位验证码
*/
function createAuthCode(email) {
const codes = readAuthCodes();
const code = generateAuthCode();
// 清理该邮箱的旧验证码
for (const [key, entry] of Object.entries(codes.codes)) {
if (entry.email === email) {
delete codes.codes[key];
}
}
codes.codes[code] = {
email,
created_at: Date.now(),
expires_at: Date.now() + CODE_EXPIRY_MS,
used: false
};
// 清理所有过期验证码
const now = Date.now();
for (const [key, entry] of Object.entries(codes.codes)) {
if (entry.expires_at < now || entry.used) {
delete codes.codes[key];
}
}
saveAuthCodes(codes);
return code;
}
/**
* 验证验证码
* @param {string} code 用户输入的验证码
* @param {string} email 用户邮箱 (用于交叉验证)
* @returns {{ valid: boolean, email?: string, error?: string }}
*/
function verifyAuthCode(code, email) {
const codes = readAuthCodes();
const entry = codes.codes[code];
if (!entry) {
return { valid: false, error: '验证码不存在或已过期' };
}
if (entry.used) {
return { valid: false, error: '验证码已使用' };
}
if (Date.now() > entry.expires_at) {
delete codes.codes[code];
saveAuthCodes(codes);
return { valid: false, error: '验证码已过期(15分钟)' };
}
if (entry.email !== email) {
return { valid: false, error: '验证码与邮箱不匹配' };
}
// 标记为已使用
entry.used = true;
entry.used_at = Date.now();
saveAuthCodes(codes);
return { valid: true, email: entry.email };
}
// ═══════════════════════════════════════════════
// 🌊 带宽贡献者管理
// ═══════════════════════════════════════════════
/**
* 读取贡献者列表
*/
function readContributors() {
try {
return JSON.parse(fs.readFileSync(CONTRIBUTORS_FILE, 'utf8'));
} catch {
return {
version: '1.0',
contributors: [],
updated_at: new Date().toISOString()
};
}
}
/**
* 保存贡献者列表
*/
function saveContributors(data) {
data.updated_at = new Date().toISOString();
fs.mkdirSync(DATA_DIR, { recursive: true });
fs.writeFileSync(CONTRIBUTORS_FILE, JSON.stringify(data, null, 2));
}
/**
* 注册带宽贡献者
* @param {string} email 用户邮箱
* @param {string} ip 用户真实IP (将加密存储)
* @returns {{ success: boolean, contributor_id: string }}
*/
function registerContributor(email, ip) {
const data = readContributors();
// 检查是否已注册
const existing = data.contributors.find(c => c.email === email);
if (existing) {
existing.ip_hash = encryptIP(ip);
existing.status = 'active';
existing.last_heartbeat = Date.now();
existing.reactivated_at = new Date().toISOString();
saveContributors(data);
return { success: true, contributor_id: existing.id };
}
// 新注册
const contributorId = `bw-${crypto.randomBytes(8).toString('hex')}`;
data.contributors.push({
id: contributorId,
email,
ip_hash: encryptIP(ip),
status: 'active', // active | suspended | disconnected
authorized_at: new Date().toISOString(),
last_heartbeat: Date.now(),
bandwidth_contributed_bytes: 0,
sessions_count: 0
});
saveContributors(data);
return { success: true, contributor_id: contributorId };
}
/**
* 获取活跃贡献者数量和带宽池状态
*/
function getPoolStatus() {
const data = readContributors();
const now = Date.now();
const active = data.contributors.filter(c =>
c.status === 'active' &&
(now - c.last_heartbeat) < CONTRIBUTOR_HEARTBEAT_MS * 3
);
const totalContributed = data.contributors.reduce(
(sum, c) => sum + (c.bandwidth_contributed_bytes || 0), 0
);
return {
total_contributors: data.contributors.length,
active_contributors: active.length,
total_contributed_gb: parseFloat((totalContributed / (1024 ** 3)).toFixed(2)),
pool_status: active.length > 0 ? 'active' : 'idle',
updated_at: new Date().toISOString()
};
}
/**
* 检查指定邮箱是否为活跃带宽贡献者
* @param {string} email 用户邮箱
* @returns {{ is_contributor: boolean, status: string, authorized_at: string|null }}
*/
function isContributor(email) {
const data = readContributors();
const contributor = data.contributors.find(c => c.email === email);
if (!contributor) {
return { is_contributor: false, status: 'none', authorized_at: null };
}
return {
is_contributor: true,
status: contributor.status,
authorized_at: contributor.authorized_at || null
};
}
/**
* 紧急切断指定用户的带宽共享
* @param {string} email 用户邮箱
* @param {string} reason 切断原因
* @returns {boolean} 是否成功切断
*/
function disconnectContributor(email, reason) {
const data = readContributors();
const contributor = data.contributors.find(c => c.email === email);
if (!contributor) return false;
contributor.status = 'disconnected';
contributor.disconnected_at = new Date().toISOString();
contributor.disconnect_reason = reason;
saveContributors(data);
logThreatEvent('contributor_disconnect', email, reason);
return true;
}
/**
* 紧急切断所有贡献者 (全局威胁响应)
* @param {string} reason 切断原因
* @returns {number} 切断的贡献者数量
*/
function disconnectAllContributors(reason) {
const data = readContributors();
let count = 0;
for (const c of data.contributors) {
if (c.status === 'active') {
c.status = 'disconnected';
c.disconnected_at = new Date().toISOString();
c.disconnect_reason = reason;
count++;
}
}
saveContributors(data);
logThreatEvent('global_disconnect', 'ALL', reason);
return count;
}
/**
* 自毁式清除 — 格式化指定用户的所有共享记录
* 让这条路"从未出现过"
* @param {string} email 用户邮箱
*/
function purgeContributorTrace(email) {
const data = readContributors();
data.contributors = data.contributors.filter(c => c.email !== email);
saveContributors(data);
logThreatEvent('trace_purged', email, '自毁式清除·记录已消失');
}
/**
* 全局自毁 — 格式化所有贡献者记录
*/
function purgeAllTraces() {
const data = {
version: '1.0',
contributors: [],
updated_at: new Date().toISOString(),
last_purge: new Date().toISOString()
};
saveContributors(data);
logThreatEvent('global_purge', 'ALL', '全局自毁·所有记录已清除');
}
// ═══════════════════════════════════════════════
// 🛡️ 威胁日志
// ═══════════════════════════════════════════════
function logThreatEvent(type, target, detail) {
let log;
try {
log = JSON.parse(fs.readFileSync(THREAT_LOG_FILE, 'utf8'));
} catch {
log = { events: [] };
}
log.events.push({
type,
target,
detail,
time: new Date().toISOString()
});
// 只保留最近50条
if (log.events.length > 50) {
log.events = log.events.slice(-50);
}
fs.mkdirSync(DATA_DIR, { recursive: true });
fs.writeFileSync(THREAT_LOG_FILE, JSON.stringify(log, null, 2));
}
// ═══════════════════════════════════════════════
// 📊 池状态持久化 (定时写入)
// ═══════════════════════════════════════════════
function updatePoolStatusFile() {
const status = getPoolStatus();
fs.mkdirSync(DATA_DIR, { recursive: true });
fs.writeFileSync(POOL_STATUS_FILE, JSON.stringify(status, null, 2));
return status;
}
// ═══════════════════════════════════════════════
// 🔄 定时任务 (PM2运行时)
// ═══════════════════════════════════════════════
function startPoolMonitor() {
console.log('🌊 带宽汇聚Agent启动');
console.log(` 验证码有效期: ${CODE_EXPIRY_MS / 60000}分钟`);
console.log(` 贡献者心跳: ${CONTRIBUTOR_HEARTBEAT_MS / 60000}分钟`);
console.log(` 池状态检查: ${POOL_CHECK_INTERVAL / 1000}`);
// 初始状态
const status = updatePoolStatusFile();
console.log(` 当前贡献者: ${status.total_contributors}人 (活跃: ${status.active_contributors})`);
// 定时更新池状态
setInterval(() => {
try {
const s = updatePoolStatusFile();
if (s.active_contributors > 0) {
console.log(`[带宽池] 活跃贡献者: ${s.active_contributors} | 总贡献: ${s.total_contributed_gb}GB`);
}
} catch (err) {
console.error('[带宽池] 状态更新失败:', err.message);
}
}, POOL_CHECK_INTERVAL);
// 定时清理过期验证码
setInterval(() => {
try {
const codes = readAuthCodes();
const now = Date.now();
let cleaned = 0;
for (const [key, entry] of Object.entries(codes.codes)) {
if (entry.expires_at < now || entry.used) {
delete codes.codes[key];
cleaned++;
}
}
if (cleaned > 0) {
saveAuthCodes(codes);
console.log(`[带宽池] 清理过期验证码: ${cleaned}`);
}
} catch { /* ignore */ }
}, 5 * 60 * 1000); // 每5分钟清理
}
// ═══════════════════════════════════════════════
// 导出API
// ═══════════════════════════════════════════════
module.exports = {
// 验证码
createAuthCode,
verifyAuthCode,
// 贡献者管理
registerContributor,
getPoolStatus,
isContributor,
readContributors,
// 安全操作
disconnectContributor,
disconnectAllContributors,
purgeContributorTrace,
purgeAllTraces,
// 工具
encryptIP,
logThreatEvent,
updatePoolStatusFile,
// 常量
CODE_EXPIRY_MS
};
// PM2直接运行
if (require.main === module) {
startPoolMonitor();
}