∞+1 带宽共享加速 + 蜂群防御 + 用户守护Agent · 冰朔架构落地
新增模块: - bandwidth-pool-agent.js: 带宽汇聚Agent (验证码授权 + IP加密存储 + 流量池) - swarm-defense-agent.js: 蜂群防御Agent (融合/分裂 + Moving Target Defense) - user-guardian-agent.js: 用户守护Agent (威胁切断 + 日志格式化 + 自毁机制) 升级模块: - email-hub.js: 新增 bandwidth-auth / threat-alert / threat-cleared 邮件类型 - subscription-server-v3.js: 新增 /bandwidth-auth/ 授权页面 + 验证码提交 - ecosystem.brain-proxy-v3.config.js: 注册3个新PM2进程 - infinity-evolution.yml: 新增文件同步 + threat-alert/cleared 工作流操作 - release-notes.json: 更新∞+1版本特性 Agent-Logs-Url: https://github.com/qinfendebingshuo/guanghulab/sessions/e26c30a9-d5c9-4068-959b-bc4acf16ae4e Co-authored-by: qinfendebingshuo <207279273+qinfendebingshuo@users.noreply.github.com>
This commit is contained in:
parent
0ca531810a
commit
f840915ba0
|
|
@ -22,6 +22,9 @@ on:
|
|||
- 'server/proxy/service/auto-evolution.js'
|
||||
- 'server/proxy/service/email-hub.js'
|
||||
- 'server/proxy/service/protocol-mirror.js'
|
||||
- 'server/proxy/service/bandwidth-pool-agent.js'
|
||||
- 'server/proxy/service/swarm-defense-agent.js'
|
||||
- 'server/proxy/service/user-guardian-agent.js'
|
||||
- 'server/proxy/config/release-notes.json'
|
||||
- 'server/proxy/ecosystem.brain-proxy-v3.config.js'
|
||||
schedule:
|
||||
|
|
@ -41,10 +44,12 @@ on:
|
|||
- send-update-notify
|
||||
- send-update-notify-single
|
||||
- send-traffic-warn
|
||||
- send-threat-alert
|
||||
- send-threat-cleared
|
||||
- list-user-emails
|
||||
default: 'status'
|
||||
message:
|
||||
description: '附加消息 (update-notify描述 / traffic-warn百分比) · 描述支持分号分隔多条内容'
|
||||
description: '附加消息 (update-notify描述 / traffic-warn百分比 / threat-alert消息) · 描述支持分号分隔多条内容'
|
||||
required: false
|
||||
type: string
|
||||
email:
|
||||
|
|
@ -97,10 +102,12 @@ jobs:
|
|||
$SSH_CMD "mkdir -p $PROXY_DIR/.deploy-tmp $PROXY_DIR/config"
|
||||
$SCP_CMD \
|
||||
server/proxy/service/email-hub.js \
|
||||
server/proxy/service/bandwidth-pool-agent.js \
|
||||
server/proxy/config/release-notes.json \
|
||||
${REMOTE_HOST}:$PROXY_DIR/.deploy-tmp/
|
||||
$SSH_CMD "
|
||||
cp $PROXY_DIR/.deploy-tmp/email-hub.js $PROXY_DIR/service/
|
||||
cp $PROXY_DIR/.deploy-tmp/bandwidth-pool-agent.js $PROXY_DIR/service/
|
||||
cp $PROXY_DIR/.deploy-tmp/release-notes.json $PROXY_DIR/config/
|
||||
rm -rf $PROXY_DIR/.deploy-tmp
|
||||
"
|
||||
|
|
@ -121,6 +128,9 @@ jobs:
|
|||
server/proxy/service/auto-evolution.js \
|
||||
server/proxy/service/email-hub.js \
|
||||
server/proxy/service/protocol-mirror.js \
|
||||
server/proxy/service/bandwidth-pool-agent.js \
|
||||
server/proxy/service/swarm-defense-agent.js \
|
||||
server/proxy/service/user-guardian-agent.js \
|
||||
server/proxy/config/release-notes.json \
|
||||
server/proxy/ecosystem.brain-proxy-v3.config.js \
|
||||
${{ secrets.ZY_BRAIN_USER }}@${{ secrets.ZY_BRAIN_HOST }}:$PROXY_DIR/.deploy-tmp/
|
||||
|
|
@ -129,6 +139,9 @@ jobs:
|
|||
cp $PROXY_DIR/.deploy-tmp/auto-evolution.js $PROXY_DIR/service/
|
||||
cp $PROXY_DIR/.deploy-tmp/email-hub.js $PROXY_DIR/service/
|
||||
cp $PROXY_DIR/.deploy-tmp/protocol-mirror.js $PROXY_DIR/service/
|
||||
cp $PROXY_DIR/.deploy-tmp/bandwidth-pool-agent.js $PROXY_DIR/service/
|
||||
cp $PROXY_DIR/.deploy-tmp/swarm-defense-agent.js $PROXY_DIR/service/
|
||||
cp $PROXY_DIR/.deploy-tmp/user-guardian-agent.js $PROXY_DIR/service/
|
||||
mkdir -p $PROXY_DIR/config
|
||||
cp $PROXY_DIR/.deploy-tmp/release-notes.json $PROXY_DIR/config/
|
||||
cp $PROXY_DIR/.deploy-tmp/ecosystem.brain-proxy-v3.config.js $PROXY_DIR/
|
||||
|
|
@ -234,6 +247,32 @@ jobs:
|
|||
"
|
||||
;;
|
||||
|
||||
send-threat-alert)
|
||||
sync_email_files
|
||||
echo "发送全体用户风险提示..."
|
||||
if [ -z "$MESSAGE" ]; then
|
||||
echo "❌ 请在message中输入风险描述"
|
||||
exit 1
|
||||
fi
|
||||
$SSH_CMD "
|
||||
cd $PROXY_DIR
|
||||
export ZY_SMTP_USER='$ZY_SMTP_USER'
|
||||
export ZY_SMTP_PASS='$ZY_SMTP_PASS'
|
||||
node service/email-hub.js threat-alert '$MESSAGE'
|
||||
"
|
||||
;;
|
||||
|
||||
send-threat-cleared)
|
||||
sync_email_files
|
||||
echo "发送全体用户安全恢复通知..."
|
||||
$SSH_CMD "
|
||||
cd $PROXY_DIR
|
||||
export ZY_SMTP_USER='$ZY_SMTP_USER'
|
||||
export ZY_SMTP_PASS='$ZY_SMTP_PASS'
|
||||
node service/email-hub.js threat-cleared
|
||||
"
|
||||
;;
|
||||
|
||||
*)
|
||||
echo "❌ 未知操作: $ACTION"
|
||||
exit 1
|
||||
|
|
|
|||
|
|
@ -1,11 +1,15 @@
|
|||
{
|
||||
"version": "∞",
|
||||
"version": "∞+1",
|
||||
"updated_at": "2026-04-06",
|
||||
"features": [
|
||||
"🌊 带宽共享加速 — 用户自愿共享多余带宽,用得越多系统越快",
|
||||
"🐝 蜂群防御系统 — 多服务器融合/分裂·Moving Target Defense",
|
||||
"🛡️ 用户守护Agent — 每条专线专属守护人格体·自毁式隐私保护",
|
||||
"🔑 验证码授权 — QQ邮箱验证码·用户自主选择是否参与加速",
|
||||
"🧠 自主进化引擎 — 系统自动检测并升级核心协议,无需手动操作",
|
||||
"📡 协议镜像加速 — 智能选路优化,连接速度与稳定性大幅提升",
|
||||
"📊 智能流量监控 — 实时流量预警(70%/80%/90%/100%阶梯告警)+ 月度自动重置",
|
||||
"📧 邮件通信中枢 — 系统通知自动推送,升级/告警/反馈全覆盖",
|
||||
"🛡️ 安全加固 — 多层防护体系,异常行为自动检测与告知"
|
||||
"📧 邮件通信中枢 — 系统通知自动推送,升级/告警/反馈/风险提示全覆盖",
|
||||
"🔐 7层安全体系 — 语言膜·蜂群分裂·真节点隐匿·守护Agent·威胁切断·日志格式化·自毁消失"
|
||||
]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,13 @@
|
|||
// ═══════════════════════════════════════════════
|
||||
// 光湖语言世界 ∞ · PM2 大脑服务器代理配置
|
||||
// 光湖语言世界 ∞+1 · PM2 大脑服务器代理配置
|
||||
// 部署在 ZY-SVR-005 (43.156.237.110) · 大脑服务器
|
||||
//
|
||||
// ∞ 版本在V3基础上新增:
|
||||
// ∞+1 版本在∞基础上新增:
|
||||
// - zy-bandwidth-pool: 带宽汇聚Agent (用户带宽共享加速)
|
||||
// - zy-swarm-defense: 蜂群防御Agent (融合/分裂·Moving Target Defense)
|
||||
// - zy-user-guardian: 用户守护Agent (每线守护·自毁机制)
|
||||
//
|
||||
// ∞ 版本模块:
|
||||
// - zy-auto-evolution: 自主进化引擎 (调度所有定时任务)
|
||||
// - zy-protocol-mirror: 协议镜像引擎 (Xray自动更新)
|
||||
//
|
||||
|
|
@ -13,7 +18,7 @@ module.exports = {
|
|||
apps: [
|
||||
{
|
||||
name: 'zy-proxy-v3-sub',
|
||||
version: '∞',
|
||||
version: '∞+1',
|
||||
script: '/opt/zhuyuan-brain/proxy/service/subscription-server-v3.js',
|
||||
instances: 1,
|
||||
exec_mode: 'fork',
|
||||
|
|
@ -29,7 +34,7 @@ module.exports = {
|
|||
},
|
||||
{
|
||||
name: 'zy-proxy-guardian',
|
||||
version: '∞',
|
||||
version: '∞+1',
|
||||
script: '/opt/zhuyuan-brain/proxy/service/proxy-guardian.js',
|
||||
instances: 1,
|
||||
exec_mode: 'fork',
|
||||
|
|
@ -46,7 +51,7 @@ module.exports = {
|
|||
},
|
||||
{
|
||||
name: 'zy-reverse-boost',
|
||||
version: '∞',
|
||||
version: '∞+1',
|
||||
script: '/opt/zhuyuan-brain/proxy/service/reverse-boost-agent.js',
|
||||
instances: 1,
|
||||
exec_mode: 'fork',
|
||||
|
|
@ -61,7 +66,7 @@ module.exports = {
|
|||
},
|
||||
{
|
||||
name: 'zy-auto-evolution',
|
||||
version: '∞',
|
||||
version: '∞+1',
|
||||
script: '/opt/zhuyuan-brain/proxy/service/auto-evolution.js',
|
||||
instances: 1,
|
||||
exec_mode: 'fork',
|
||||
|
|
@ -78,7 +83,7 @@ module.exports = {
|
|||
},
|
||||
{
|
||||
name: 'zy-protocol-mirror',
|
||||
version: '∞',
|
||||
version: '∞+1',
|
||||
script: '/opt/zhuyuan-brain/proxy/service/protocol-mirror.js',
|
||||
instances: 1,
|
||||
exec_mode: 'fork',
|
||||
|
|
@ -92,6 +97,54 @@ module.exports = {
|
|||
log_file: '/opt/zhuyuan-brain/proxy/logs/protocol-mirror.log',
|
||||
error_file: '/opt/zhuyuan-brain/proxy/logs/protocol-mirror-error.log',
|
||||
time: true
|
||||
},
|
||||
// ∞+1 新增: 带宽汇聚Agent
|
||||
{
|
||||
name: 'zy-bandwidth-pool',
|
||||
version: '∞+1',
|
||||
script: '/opt/zhuyuan-brain/proxy/service/bandwidth-pool-agent.js',
|
||||
instances: 1,
|
||||
exec_mode: 'fork',
|
||||
env: {
|
||||
NODE_ENV: 'production',
|
||||
ZY_BRAIN_PROXY_DIR: '/opt/zhuyuan-brain/proxy'
|
||||
},
|
||||
max_memory_restart: '64M',
|
||||
log_file: '/opt/zhuyuan-brain/proxy/logs/bandwidth-pool.log',
|
||||
error_file: '/opt/zhuyuan-brain/proxy/logs/bandwidth-pool-error.log',
|
||||
time: true
|
||||
},
|
||||
// ∞+1 新增: 蜂群防御Agent
|
||||
{
|
||||
name: 'zy-swarm-defense',
|
||||
version: '∞+1',
|
||||
script: '/opt/zhuyuan-brain/proxy/service/swarm-defense-agent.js',
|
||||
instances: 1,
|
||||
exec_mode: 'fork',
|
||||
env: {
|
||||
NODE_ENV: 'production',
|
||||
ZY_BRAIN_PROXY_DIR: '/opt/zhuyuan-brain/proxy'
|
||||
},
|
||||
max_memory_restart: '64M',
|
||||
log_file: '/opt/zhuyuan-brain/proxy/logs/swarm-defense.log',
|
||||
error_file: '/opt/zhuyuan-brain/proxy/logs/swarm-defense-error.log',
|
||||
time: true
|
||||
},
|
||||
// ∞+1 新增: 用户守护Agent
|
||||
{
|
||||
name: 'zy-user-guardian',
|
||||
version: '∞+1',
|
||||
script: '/opt/zhuyuan-brain/proxy/service/user-guardian-agent.js',
|
||||
instances: 1,
|
||||
exec_mode: 'fork',
|
||||
env: {
|
||||
NODE_ENV: 'production',
|
||||
ZY_BRAIN_PROXY_DIR: '/opt/zhuyuan-brain/proxy'
|
||||
},
|
||||
max_memory_restart: '64M',
|
||||
log_file: '/opt/zhuyuan-brain/proxy/logs/user-guardian.log',
|
||||
error_file: '/opt/zhuyuan-brain/proxy/logs/user-guardian-error.log',
|
||||
time: true
|
||||
}
|
||||
]
|
||||
};
|
||||
|
|
|
|||
|
|
@ -0,0 +1,436 @@
|
|||
#!/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 || 'guanghu-bw-pool-2026';
|
||||
|
||||
// ═══════════════════════════════════════════════
|
||||
// 🔑 验证码管理
|
||||
// ═══════════════════════════════════════════════
|
||||
|
||||
/**
|
||||
* 生成6位数字验证码
|
||||
*/
|
||||
function generateAuthCode() {
|
||||
return crypto.randomInt(100000, 999999).toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 加密IP地址 (SHA256 + 盐值)
|
||||
*/
|
||||
function encryptIP(ip) {
|
||||
return crypto.createHash('sha256').update(`${SALT}:${ip}`).digest('hex').slice(0, 32);
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取验证码存储
|
||||
*/
|
||||
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 用户邮箱
|
||||
* @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,
|
||||
readContributors,
|
||||
|
||||
// 安全操作
|
||||
disconnectContributor,
|
||||
disconnectAllContributors,
|
||||
purgeContributorTrace,
|
||||
purgeAllTraces,
|
||||
|
||||
// 工具
|
||||
encryptIP,
|
||||
logThreatEvent,
|
||||
updatePoolStatusFile,
|
||||
|
||||
// 常量
|
||||
CODE_EXPIRY_MS
|
||||
};
|
||||
|
||||
// PM2直接运行
|
||||
if (require.main === module) {
|
||||
startPoolMonitor();
|
||||
}
|
||||
|
|
@ -14,6 +14,9 @@
|
|||
// 3. 流量预警通知 — 70%/80%/90%/100% 阶梯告警
|
||||
// 4. 安全风险提醒 — 单用户异常行为告知
|
||||
// 5. 反馈确认回复 — 收到用户反馈后的自动确认
|
||||
// 6. 带宽共享验证码 — 发送6位验证码 (∞+1)
|
||||
// 7. 风险提示通知 — 全体用户风险提醒 (∞+1)
|
||||
// 8. 安全恢复通知 — 危机解除后全体通知 (∞+1)
|
||||
//
|
||||
// 所有邮件底部附「意见反馈」链接
|
||||
//
|
||||
|
|
@ -24,6 +27,9 @@
|
|||
// node email-hub.js traffic-warn <pct> — 发送流量预警给所有用户
|
||||
// node email-hub.js security-warn <email> <msg> — 发送安全提醒给单用户
|
||||
// node email-hub.js feedback-ack <email> — 发送反馈确认给单用户
|
||||
// node email-hub.js bandwidth-auth <email> — 发送带宽共享验证码 (∞+1)
|
||||
// node email-hub.js threat-alert <msg> — 全体用户风险提示 (∞+1)
|
||||
// node email-hub.js threat-cleared — 全体用户安全恢复通知 (∞+1)
|
||||
// node email-hub.js list-emails — 列出所有启用用户的邮箱
|
||||
//
|
||||
// 运行方式: CLI调用 (由auto-evolution.js调度)
|
||||
|
|
@ -410,6 +416,124 @@ function generateFeedbackAckEmail(config) {
|
|||
return wrapEmailTemplate('反馈已收到', content, config);
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════
|
||||
// 📧 邮件类型 6: 带宽共享验证码 (∞+1)
|
||||
// ═══════════════════════════════════════════════
|
||||
function generateBandwidthAuthEmail(code, authPageUrl, config) {
|
||||
const content = `
|
||||
<div style="background: #e8f4fd; border: 1px solid #b8daff; border-radius: 8px; padding: 15px; margin: 15px 0;">
|
||||
<strong style="color: #004085;">🌊 带宽共享加速 · 授权验证</strong>
|
||||
</div>
|
||||
|
||||
<p style="color: #333; line-height: 1.8;">
|
||||
您正在参与<strong>光湖语言世界</strong>的带宽共享加速计划。<br>
|
||||
如果您<strong>同意授权</strong>,请复制以下验证码并提交:
|
||||
</p>
|
||||
|
||||
<div style="background: #f0f7ff; border: 2px dashed #4a90d9; border-radius: 12px; padding: 20px; margin: 20px 0; text-align: center;">
|
||||
<p style="color: #666; font-size: 12px; margin: 0 0 8px;">您的验证码 (15分钟内有效)</p>
|
||||
<p style="font-size: 36px; font-weight: bold; color: #1a1a2e; letter-spacing: 8px; margin: 0;">${code}</p>
|
||||
</div>
|
||||
|
||||
${authPageUrl ? `
|
||||
<div style="text-align: center; margin: 15px 0;">
|
||||
<a href="${authPageUrl}" style="display: inline-block; background: linear-gradient(135deg, #667eea, #764ba2); color: white; padding: 12px 30px; border-radius: 8px; text-decoration: none; font-size: 14px; font-weight: 600;">
|
||||
🔗 前往授权页面输入验证码
|
||||
</a>
|
||||
</div>` : ''}
|
||||
|
||||
<div style="background: #f8f9fa; border-radius: 8px; padding: 15px; margin: 15px 0; border-left: 4px solid #28a745;">
|
||||
<h4 style="color: #155724; margin: 0 0 8px;">✅ 同意授权 = 输入验证码</h4>
|
||||
<p style="color: #666; font-size: 13px; margin: 0; line-height: 1.8;">
|
||||
您的多余带宽将用于加速VPN网络。<br>
|
||||
用的人越多,系统越快。您自己也会享受到加速效果。
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div style="background: #f8f9fa; border-radius: 8px; padding: 15px; margin: 10px 0; border-left: 4px solid #6c757d;">
|
||||
<h4 style="color: #495057; margin: 0 0 8px;">❌ 不同意 = 忽略此邮件</h4>
|
||||
<p style="color: #666; font-size: 13px; margin: 0; line-height: 1.8;">
|
||||
完全没问题。您可以继续正常使用VPN,只是走我们系统的带宽,<br>
|
||||
速度可能慢一些,但也比普通VPN好太多了。
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div style="background: #fff3cd; border: 1px solid #ffc107; border-radius: 8px; padding: 12px; margin: 15px 0;">
|
||||
<p style="color: #856404; font-size: 12px; margin: 0; line-height: 1.6;">
|
||||
🔒 <strong>安全说明</strong>:本VPN是内部专用的,用户都是团队自己人。
|
||||
您的IP仅用于带宽加速,系统内部加密存储,外部无法看到。
|
||||
若检测到任何风险,系统会<strong>自动切断您的共享通道</strong>,
|
||||
并格式化所有共享记录——就像这条路从未出现过一样。
|
||||
危机解除后,我们会重新为您推送新的订阅链接。您的隐私安全,铸渊守护。
|
||||
</p>
|
||||
</div>`;
|
||||
|
||||
return wrapEmailTemplate('带宽共享授权验证', content, config);
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════
|
||||
// 📧 邮件类型 7: 风险提示通知 (∞+1 · 全体用户)
|
||||
// ═══════════════════════════════════════════════
|
||||
function generateThreatAlertEmail(message, config) {
|
||||
const content = `
|
||||
<div style="background: #f8d7da; border: 1px solid #f5c6cb; border-radius: 8px; padding: 15px; margin: 15px 0;">
|
||||
<strong style="color: #721c24;">⚠️ 安全风险提示</strong>
|
||||
</div>
|
||||
|
||||
<div style="background: #f8f9fa; border-radius: 8px; padding: 15px; color: #333; line-height: 1.8;">
|
||||
${escapeHtml(message).replace(/\n/g, '<br>')}
|
||||
</div>
|
||||
|
||||
<h3 style="color: #333;">🛡️ 系统已自动执行以下保护措施</h3>
|
||||
<ul style="color: #666; line-height: 2;">
|
||||
<li>所有带宽共享通道已安全切断</li>
|
||||
<li>用户IP和共享记录已加密隔离</li>
|
||||
<li>VPN基础服务不受影响,可继续正常使用</li>
|
||||
<li>系统正在自动处理风险,无需您手动操作</li>
|
||||
</ul>
|
||||
|
||||
<div style="background: #d4edda; border: 1px solid #c3e6cb; border-radius: 8px; padding: 12px; margin: 15px 0;">
|
||||
<p style="color: #155724; font-size: 13px; margin: 0;">
|
||||
💡 您只需等待系统处理完毕。危机解除后,我们会发送安全恢复通知。<br>
|
||||
若需要重新建立连接,只需刷新订阅即可。
|
||||
</p>
|
||||
</div>`;
|
||||
|
||||
return wrapEmailTemplate('安全风险提示', content, config);
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════
|
||||
// 📧 邮件类型 8: 安全恢复通知 (∞+1 · 全体用户)
|
||||
// ═══════════════════════════════════════════════
|
||||
function generateThreatClearedEmail(config) {
|
||||
const content = `
|
||||
<div style="background: #d4edda; border: 1px solid #c3e6cb; border-radius: 8px; padding: 15px; margin: 15px 0;">
|
||||
<strong style="color: #155724;">✅ 安全风险已解除</strong>
|
||||
</div>
|
||||
|
||||
<p style="color: #333; line-height: 1.8;">
|
||||
光湖语言世界的安全系统已完成风险处理。<br>
|
||||
所有服务已恢复正常运行。
|
||||
</p>
|
||||
|
||||
<h3 style="color: #333;">📋 您需要做的</h3>
|
||||
<div style="background: #f0f7ff; border-radius: 8px; padding: 15px; line-height: 1.8;">
|
||||
<p style="color: #333; margin: 0;">
|
||||
<strong>只需一步</strong>:打开您的VPN客户端,刷新一下订阅即可。<br>
|
||||
您的订阅地址不变,内部节点已自动更新。
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div style="background: #f8f9fa; border-radius: 8px; padding: 12px; margin: 15px 0;">
|
||||
<p style="color: #666; font-size: 13px; margin: 0; line-height: 1.6;">
|
||||
💡 如果之前您参与了带宽共享加速计划,共享通道已被安全重置。<br>
|
||||
如需重新参与,您可以在仪表盘页面重新授权。感谢您的理解与支持!
|
||||
</p>
|
||||
</div>`;
|
||||
|
||||
return wrapEmailTemplate('安全恢复通知', content, config);
|
||||
}
|
||||
|
||||
// ── HTML转义 ─────────────────────────────────
|
||||
function escapeHtml(str) {
|
||||
return String(str)
|
||||
|
|
@ -577,6 +701,54 @@ function listUserEmails() {
|
|||
return users.map(u => u.email);
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送带宽共享验证码给单个用户 (∞+1)
|
||||
* @param {string} email 目标邮箱
|
||||
* @param {string} code 6位验证码
|
||||
* @param {string} [authPageUrl] 授权页面URL
|
||||
*/
|
||||
async function sendBandwidthAuthEmail(email, code, authPageUrl) {
|
||||
const config = loadConfig();
|
||||
const html = generateBandwidthAuthEmail(code, authPageUrl, config);
|
||||
|
||||
try {
|
||||
await sendEmail(email, '🌊 光湖语言世界 · 带宽共享授权验证码', html);
|
||||
console.log(`[邮件中枢] ✅ 带宽验证码已发送: ${email}`);
|
||||
logEmail('bandwidth-auth', email, true, null);
|
||||
return { sent: 1, failed: 0 };
|
||||
} catch (err) {
|
||||
console.error(`[邮件中枢] ❌ 带宽验证码发送失败: ${err.message}`);
|
||||
logEmail('bandwidth-auth', email, false, err.message);
|
||||
return { sent: 0, failed: 1 };
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送风险提示给所有用户 (∞+1)
|
||||
* @param {string} message 风险描述
|
||||
*/
|
||||
async function sendThreatAlertEmail(message) {
|
||||
const result = await sendToAllUsers(
|
||||
'⚠️ 光湖语言世界 · 安全风险提示',
|
||||
(config) => generateThreatAlertEmail(message, config)
|
||||
);
|
||||
logEmail('threat-alert', result.sent + result.failed, result.sent, result.errors.join('; '));
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送安全恢复通知给所有用户 (∞+1)
|
||||
*/
|
||||
async function sendThreatClearedEmail() {
|
||||
const result = await sendToAllUsers(
|
||||
'✅ 光湖语言世界 · 安全风险已解除',
|
||||
(config) => generateThreatClearedEmail(config)
|
||||
);
|
||||
logEmail('threat-cleared', result.sent + result.failed, result.sent, result.errors.join('; '));
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
// ═══════════════════════════════════════════════
|
||||
// CLI 主入口
|
||||
// ═══════════════════════════════════════════════
|
||||
|
|
@ -592,6 +764,9 @@ async function main() {
|
|||
console.log(' node email-hub.js traffic-warn <百分比> — 流量预警通知 (全部用户)');
|
||||
console.log(' node email-hub.js security-warn <邮箱> <消息> — 安全风险提醒 (单用户)');
|
||||
console.log(' node email-hub.js feedback-ack <邮箱> — 反馈确认回复 (单用户)');
|
||||
console.log(' node email-hub.js bandwidth-auth <邮箱> — 发送带宽共享验证码 (∞+1)');
|
||||
console.log(' node email-hub.js threat-alert <消息> — 全体用户风险提示 (∞+1)');
|
||||
console.log(' node email-hub.js threat-cleared — 全体用户安全恢复通知 (∞+1)');
|
||||
console.log(' node email-hub.js list-emails — 列出所有用户邮箱');
|
||||
console.log('');
|
||||
console.log('💡 描述支持分号分隔多条内容,自动渲染为功能清单:');
|
||||
|
|
@ -676,6 +851,37 @@ async function main() {
|
|||
break;
|
||||
}
|
||||
|
||||
case 'bandwidth-auth': {
|
||||
if (!arg1) {
|
||||
console.error('❌ 请提供邮箱');
|
||||
console.error('用法: node email-hub.js bandwidth-auth <邮箱>');
|
||||
process.exit(1);
|
||||
}
|
||||
// 生成验证码
|
||||
const bwPool = require('./bandwidth-pool-agent');
|
||||
const authCode = bwPool.createAuthCode(arg1);
|
||||
const result = await sendBandwidthAuthEmail(arg1, authCode);
|
||||
console.log(`📧 带宽验证码: ${result.sent}成功 / ${result.failed}失败`);
|
||||
break;
|
||||
}
|
||||
|
||||
case 'threat-alert': {
|
||||
if (!arg1) {
|
||||
console.error('❌ 请提供风险消息');
|
||||
console.error('用法: node email-hub.js threat-alert <消息>');
|
||||
process.exit(1);
|
||||
}
|
||||
const result = await sendThreatAlertEmail(arg1);
|
||||
console.log(`📧 风险提示: ${result.sent}成功 / ${result.failed}失败`);
|
||||
break;
|
||||
}
|
||||
|
||||
case 'threat-cleared': {
|
||||
const result = await sendThreatClearedEmail();
|
||||
console.log(`📧 安全恢复通知: ${result.sent}成功 / ${result.failed}失败`);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
console.error(`❌ 未知操作: ${action}`);
|
||||
process.exit(1);
|
||||
|
|
@ -690,6 +896,9 @@ module.exports = {
|
|||
sendTrafficWarnEmail,
|
||||
sendSecurityWarnEmail,
|
||||
sendFeedbackAckEmail,
|
||||
sendBandwidthAuthEmail,
|
||||
sendThreatAlertEmail,
|
||||
sendThreatClearedEmail,
|
||||
getEnabledUsers,
|
||||
listUserEmails,
|
||||
loadReleaseNotes,
|
||||
|
|
|
|||
|
|
@ -809,6 +809,305 @@ mode: direct
|
|||
return;
|
||||
}
|
||||
|
||||
// ── ∞+1 带宽共享授权页面: /bandwidth-auth/{token} ──
|
||||
// 用户输入验证码授权带宽共享 · 安全加密
|
||||
const bwAuthMatch = pathname.match(/^\/bandwidth-auth\/([a-f0-9]+)$/);
|
||||
if (bwAuthMatch) {
|
||||
const token = bwAuthMatch[1];
|
||||
const user = userManager.findUserByToken(token);
|
||||
|
||||
if (!user) {
|
||||
res.writeHead(403, { 'Content-Type': 'text/html; charset=utf-8' });
|
||||
res.end('<html><body><h1>403 认证失败</h1></body></html>');
|
||||
return;
|
||||
}
|
||||
|
||||
// POST: 提交验证码
|
||||
if (req.method === 'POST') {
|
||||
let body = '';
|
||||
req.on('data', chunk => { body += chunk; });
|
||||
req.on('end', () => {
|
||||
try {
|
||||
let code = '';
|
||||
// 支持 JSON 和 form-urlencoded
|
||||
if (req.headers['content-type']?.includes('application/json')) {
|
||||
const json = JSON.parse(body);
|
||||
code = String(json.code || '').trim();
|
||||
} else {
|
||||
const params = new URLSearchParams(body);
|
||||
code = String(params.get('code') || '').trim();
|
||||
}
|
||||
|
||||
if (!code || code.length !== 6) {
|
||||
res.writeHead(400, { 'Content-Type': 'application/json; charset=utf-8' });
|
||||
res.end(JSON.stringify({ success: false, message: '请输入6位验证码' }));
|
||||
return;
|
||||
}
|
||||
|
||||
// 验证码校验
|
||||
const bwPool = require('./bandwidth-pool-agent');
|
||||
const verifyResult = bwPool.verifyAuthCode(code, user.email);
|
||||
|
||||
if (!verifyResult.valid) {
|
||||
res.writeHead(400, { 'Content-Type': 'application/json; charset=utf-8' });
|
||||
res.end(JSON.stringify({ success: false, message: verifyResult.error }));
|
||||
return;
|
||||
}
|
||||
|
||||
// 采集用户IP (加密存储)
|
||||
const userIP = req.headers['x-forwarded-for']?.split(',')[0]?.trim()
|
||||
|| req.headers['x-real-ip']
|
||||
|| req.socket.remoteAddress
|
||||
|| '0.0.0.0';
|
||||
|
||||
// 注册为带宽贡献者
|
||||
const regResult = bwPool.registerContributor(user.email, userIP);
|
||||
|
||||
// 激活用户守护Agent
|
||||
try {
|
||||
const guardian = require('./user-guardian-agent');
|
||||
guardian.activateGuardian(user.email);
|
||||
} catch { /* guardian not loaded */ }
|
||||
|
||||
res.writeHead(200, { 'Content-Type': 'application/json; charset=utf-8' });
|
||||
res.end(JSON.stringify({
|
||||
success: true,
|
||||
message: '授权成功!您的带宽已加入加速池,感谢支持。',
|
||||
contributor_id: regResult.contributor_id
|
||||
}));
|
||||
} catch (err) {
|
||||
res.writeHead(500, { 'Content-Type': 'application/json; charset=utf-8' });
|
||||
res.end(JSON.stringify({ success: false, message: '系统异常,请稍后重试' }));
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// GET: 渲染授权页面
|
||||
const esc = (s) => String(s).replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"');
|
||||
|
||||
// 读取带宽池状态
|
||||
let poolInfo = { active_contributors: 0, total_contributed_gb: 0 };
|
||||
try {
|
||||
poolInfo = JSON.parse(fs.readFileSync(path.join(DATA_DIR, 'bandwidth-pool-status.json'), 'utf8'));
|
||||
} catch { /* ignore */ }
|
||||
|
||||
const authHtml = `<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>光湖语言世界 · 带宽共享授权</title>
|
||||
<style>
|
||||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||
body { font-family: -apple-system, "PingFang SC", sans-serif; background: #0a0e27; color: #e0e0e0; padding: 20px; min-height: 100vh; display: flex; align-items: center; justify-content: center; }
|
||||
.container { max-width: 440px; width: 100%; }
|
||||
.header { text-align: center; padding: 20px 0; }
|
||||
.header h1 { font-size: 1.4em; background: linear-gradient(135deg, #667eea, #764ba2); -webkit-background-clip: text; -webkit-text-fill-color: transparent; }
|
||||
.header p { color: #888; font-size: 0.85em; margin-top: 5px; }
|
||||
.card { background: #141832; border-radius: 12px; padding: 20px; margin: 12px 0; border: 1px solid #1e2448; }
|
||||
.card h3 { font-size: 0.95em; color: #667eea; margin-bottom: 12px; }
|
||||
.input-group { margin: 15px 0; }
|
||||
.input-group input { width: 100%; padding: 14px 16px; background: #1e2448; border: 2px solid #2d3566; border-radius: 10px; color: #fff; font-size: 1.2em; text-align: center; letter-spacing: 6px; outline: none; transition: border-color 0.3s; }
|
||||
.input-group input:focus { border-color: #667eea; }
|
||||
.input-group input::placeholder { letter-spacing: 0; font-size: 0.7em; color: #555; }
|
||||
.submit-btn { width: 100%; padding: 14px; background: linear-gradient(135deg, #667eea, #764ba2); color: white; border: none; border-radius: 10px; font-size: 1em; font-weight: 600; cursor: pointer; transition: opacity 0.3s; }
|
||||
.submit-btn:hover { opacity: 0.9; }
|
||||
.submit-btn:disabled { opacity: 0.5; cursor: not-allowed; }
|
||||
.info-box { background: #1a1f3d; border-radius: 8px; padding: 12px; margin: 10px 0; font-size: 0.85em; line-height: 1.8; color: #aaa; }
|
||||
.info-box.green { border-left: 3px solid #2ecc71; }
|
||||
.info-box.gray { border-left: 3px solid #6c757d; }
|
||||
.info-box.yellow { border-left: 3px solid #f39c12; }
|
||||
.info-box strong { color: #e0e0e0; }
|
||||
.result { text-align: center; padding: 15px; border-radius: 8px; margin-top: 12px; display: none; }
|
||||
.result.success { background: #1a3a2a; color: #2ecc71; }
|
||||
.result.error { background: #3a1a1a; color: #e74c3c; }
|
||||
.stats { display: flex; justify-content: space-around; margin: 10px 0; }
|
||||
.stat { text-align: center; }
|
||||
.stat .num { font-size: 1.3em; font-weight: 700; color: #667eea; }
|
||||
.stat .label { font-size: 0.75em; color: #888; margin-top: 2px; }
|
||||
.footer { text-align: center; padding: 15px 0; color: #555; font-size: 0.75em; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="header">
|
||||
<h1>🌊 带宽共享加速</h1>
|
||||
<p>${esc(user.label)} · 光湖语言世界</p>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<h3>📊 加速池状态</h3>
|
||||
<div class="stats">
|
||||
<div class="stat"><div class="num">${poolInfo.active_contributors || 0}</div><div class="label">活跃贡献者</div></div>
|
||||
<div class="stat"><div class="num">${poolInfo.total_contributed_gb || 0}</div><div class="label">总贡献 (GB)</div></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<h3>🔑 输入验证码</h3>
|
||||
<p style="font-size: 0.85em; color: #888; margin-bottom: 10px;">请查看您的QQ邮箱,将收到的6位验证码输入以下框中</p>
|
||||
|
||||
<form id="authForm" onsubmit="submitCode(event)">
|
||||
<div class="input-group">
|
||||
<input type="text" id="codeInput" maxlength="6" pattern="[0-9]{6}" placeholder="输入6位验证码" inputmode="numeric" autocomplete="off" required>
|
||||
</div>
|
||||
<button type="submit" class="submit-btn" id="submitBtn">🔓 提交授权</button>
|
||||
</form>
|
||||
|
||||
<div id="result" class="result"></div>
|
||||
</div>
|
||||
|
||||
<div class="info-box green">
|
||||
<strong>✅ 同意授权 = 输入验证码</strong><br>
|
||||
您的多余带宽将用于加速VPN网络。用的人越多,系统越快。您自己也会享受到加速效果。
|
||||
</div>
|
||||
|
||||
<div class="info-box gray">
|
||||
<strong>❌ 不同意 = 关闭此页面</strong><br>
|
||||
完全没问题。您可以继续正常使用VPN,只是走我们系统的带宽,速度可能慢一些,但也比普通VPN好太多了。无论您选择什么,都不影响正常使用。
|
||||
</div>
|
||||
|
||||
<div class="info-box yellow">
|
||||
<strong>🔒 安全保障</strong><br>
|
||||
本VPN是内部专用的,用户都是团队自己人,已经相对安全。您的IP仅用于带宽加速,系统内部加密存储。若检测到任何风险,系统会自动切断您的共享通道并格式化所有共享记录——就像这条路从未出现过。危机解除后会重新推送新的订阅链接。您的隐私安全,铸渊守护。
|
||||
</div>
|
||||
|
||||
<div class="footer">
|
||||
光湖语言世界 · 冰朔开发维护<br>
|
||||
国作登字-2026-A-00037559
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
async function submitCode(e) {
|
||||
e.preventDefault();
|
||||
const code = document.getElementById('codeInput').value.trim();
|
||||
const btn = document.getElementById('submitBtn');
|
||||
const result = document.getElementById('result');
|
||||
|
||||
if (code.length !== 6 || !/^\\d{6}$/.test(code)) {
|
||||
result.className = 'result error';
|
||||
result.style.display = 'block';
|
||||
result.textContent = '请输入6位数字验证码';
|
||||
return;
|
||||
}
|
||||
|
||||
btn.disabled = true;
|
||||
btn.textContent = '⏳ 提交中...';
|
||||
|
||||
try {
|
||||
const resp = await fetch(window.location.href, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ code })
|
||||
});
|
||||
const data = await resp.json();
|
||||
|
||||
result.style.display = 'block';
|
||||
if (data.success) {
|
||||
result.className = 'result success';
|
||||
result.textContent = '✅ ' + data.message;
|
||||
btn.textContent = '✅ 授权成功';
|
||||
} else {
|
||||
result.className = 'result error';
|
||||
result.textContent = '❌ ' + data.message;
|
||||
btn.disabled = false;
|
||||
btn.textContent = '🔓 提交授权';
|
||||
}
|
||||
} catch (err) {
|
||||
result.className = 'result error';
|
||||
result.style.display = 'block';
|
||||
result.textContent = '❌ 网络异常,请稍后重试';
|
||||
btn.disabled = false;
|
||||
btn.textContent = '🔓 提交授权';
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>`;
|
||||
|
||||
res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
|
||||
res.end(authHtml);
|
||||
return;
|
||||
}
|
||||
|
||||
// ── ∞+1 发送带宽验证码: /bandwidth-send-code/{token} ──
|
||||
// POST: 向用户邮箱发送带宽共享验证码
|
||||
const bwSendMatch = pathname.match(/^\/bandwidth-send-code\/([a-f0-9]+)$/);
|
||||
if (bwSendMatch && req.method === 'POST') {
|
||||
const token = bwSendMatch[1];
|
||||
const user = userManager.findUserByToken(token);
|
||||
|
||||
if (!user) {
|
||||
res.writeHead(403, { 'Content-Type': 'application/json; charset=utf-8' });
|
||||
res.end(JSON.stringify({ success: false, message: '认证失败' }));
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const bwPool = require('./bandwidth-pool-agent');
|
||||
const emailHub = require('./email-hub');
|
||||
const code = bwPool.createAuthCode(user.email);
|
||||
|
||||
const serverHost = getServerHost();
|
||||
const authPageUrl = `https://${serverHost}/api/proxy-v3/bandwidth-auth/${token}`;
|
||||
|
||||
emailHub.sendBandwidthAuthEmail(user.email, code, authPageUrl).then(() => {
|
||||
res.writeHead(200, { 'Content-Type': 'application/json; charset=utf-8' });
|
||||
res.end(JSON.stringify({
|
||||
success: true,
|
||||
message: '验证码已发送到您的邮箱,请查收'
|
||||
}));
|
||||
}).catch(() => {
|
||||
res.writeHead(500, { 'Content-Type': 'application/json; charset=utf-8' });
|
||||
res.end(JSON.stringify({ success: false, message: '发送失败,请稍后重试' }));
|
||||
});
|
||||
} catch (err) {
|
||||
res.writeHead(500, { 'Content-Type': 'application/json; charset=utf-8' });
|
||||
res.end(JSON.stringify({ success: false, message: '发送失败,请稍后重试' }));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// ── ∞+1 带宽池状态查询: /bandwidth-status/{token} ──
|
||||
const bwStatusMatch = pathname.match(/^\/bandwidth-status\/([a-f0-9]+)$/);
|
||||
if (bwStatusMatch) {
|
||||
const token = bwStatusMatch[1];
|
||||
const user = userManager.findUserByToken(token);
|
||||
|
||||
if (!user) {
|
||||
res.writeHead(403, { 'Content-Type': 'application/json; charset=utf-8' });
|
||||
res.end(JSON.stringify({ error: true, code: 'FORBIDDEN', message: '认证失败' }));
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const bwPool = require('./bandwidth-pool-agent');
|
||||
const poolStatus = bwPool.getPoolStatus();
|
||||
const contributors = bwPool.readContributors();
|
||||
const userContributor = contributors.contributors.find(c => c.email === user.email);
|
||||
|
||||
res.writeHead(200, { 'Content-Type': 'application/json; charset=utf-8' });
|
||||
res.end(JSON.stringify({
|
||||
pool: poolStatus,
|
||||
user_sharing: userContributor ? userContributor.status === 'active' : false,
|
||||
user_contributed_gb: userContributor
|
||||
? parseFloat(((userContributor.bandwidth_contributed_bytes || 0) / (1024 ** 3)).toFixed(2))
|
||||
: 0,
|
||||
updated_at: new Date().toISOString()
|
||||
}));
|
||||
} catch {
|
||||
res.writeHead(200, { 'Content-Type': 'application/json; charset=utf-8' });
|
||||
res.end(JSON.stringify({
|
||||
pool: { total_contributors: 0, active_contributors: 0, pool_status: 'idle' },
|
||||
user_sharing: false,
|
||||
user_contributed_gb: 0
|
||||
}));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// 404
|
||||
res.writeHead(404, { 'Content-Type': 'text/plain' });
|
||||
res.end('Not Found');
|
||||
|
|
@ -854,6 +1153,9 @@ server.listen(PORT, '127.0.0.1', () => {
|
|||
console.log(` 配额查询: /quota/{user_token}`);
|
||||
console.log(` 服务状态: /status/{user_token}`);
|
||||
console.log(` 流量仪表盘: /dashboard/{user_token}`);
|
||||
console.log(` 带宽授权: /bandwidth-auth/{user_token} (∞+1)`);
|
||||
console.log(` 发送验证码: /bandwidth-send-code/{user_token} (∞+1)`);
|
||||
console.log(` 带宽池状态: /bandwidth-status/{user_token} (∞+1)`);
|
||||
console.log(` 健康检查: /health`);
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,444 @@
|
|||
#!/usr/bin/env node
|
||||
// ═══════════════════════════════════════════════
|
||||
// 🔺 Sovereign: TCS-0002∞ | Root: SYS-GLW-0001
|
||||
// 📜 Copyright: 国作登字-2026-A-00037559
|
||||
// ═══════════════════════════════════════════════
|
||||
// server/proxy/service/swarm-defense-agent.js
|
||||
// 🐝 光湖语言世界 · 蜂群防御Agent
|
||||
//
|
||||
// ∞+1 版本安全核心 — 多服务器融合/分裂防御系统
|
||||
//
|
||||
// 架构设计 (冰朔定根):
|
||||
// 所有串联的服务器组成一个巨大的VPN动态Agent节点。
|
||||
// 正常模式: 融合态 — 所有节点对外统一入口
|
||||
// 危机模式: 分裂态 — 瞬间分布为多个动态加密节点
|
||||
// - 只有一个是真的,其余是诱饵
|
||||
// - 真节点在诱饵间随机跳转
|
||||
// - 只接收TCS编程语言指令(冰朔专属)
|
||||
//
|
||||
// 安全层级:
|
||||
// L0: 语言膜 (TCS指令授权)
|
||||
// L1: 融合/分裂切换 (Moving Target Defense)
|
||||
// L2: 真节点隐匿 (随机跳转)
|
||||
// L3: 用户守护Agent (每线守护)
|
||||
// L4: 威胁切断 (切断用户带宽共享)
|
||||
// L5: 日志格式化 (安全擦除)
|
||||
// L6: 自毁式消失 (从未存在过)
|
||||
// L7: 邮件通知 (风险提示/安全恢复)
|
||||
//
|
||||
// 运行方式: PM2 managed (zy-swarm-defense)
|
||||
// ═══════════════════════════════════════════════
|
||||
|
||||
'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 SWARM_STATUS_FILE = path.join(DATA_DIR, 'swarm-defense-status.json');
|
||||
const DEFENSE_LOG_FILE = path.join(DATA_DIR, 'swarm-defense-log.json');
|
||||
const REGISTRY_FILE = path.join(DATA_DIR, 'nodes-registry.json');
|
||||
const LIVE_NODES_FILE = path.join(DATA_DIR, 'nodes-live.json');
|
||||
|
||||
const MONITOR_INTERVAL = 30 * 1000; // 每30秒监控
|
||||
const DECOY_ROTATE_INTERVAL = 60 * 1000; // 分裂态下每60秒切换真节点位置
|
||||
|
||||
// ═══════════════════════════════════════════════
|
||||
// 蜂群状态机
|
||||
// FUSED (融合态) ↔ SPLIT (分裂态)
|
||||
// ═══════════════════════════════════════════════
|
||||
|
||||
const SWARM_STATES = {
|
||||
FUSED: 'fused', // 正常·融合态 — 所有节点组成一个巨型Agent
|
||||
SPLIT: 'split', // 危机·分裂态 — 分裂为多个诱饵+1个真节点
|
||||
RECOVERING: 'recovering' // 恢复中 — 从分裂态回到融合态
|
||||
};
|
||||
|
||||
/**
|
||||
* 读取蜂群状态
|
||||
*/
|
||||
function readSwarmStatus() {
|
||||
try {
|
||||
return JSON.parse(fs.readFileSync(SWARM_STATUS_FILE, 'utf8'));
|
||||
} catch {
|
||||
return {
|
||||
state: SWARM_STATES.FUSED,
|
||||
active_node_id: null, // 分裂态下的真节点ID
|
||||
decoy_nodes: [], // 诱饵节点列表
|
||||
last_rotate: null, // 上次真节点跳转时间
|
||||
threat_level: 0, // 威胁等级 0-5
|
||||
threat_events: [], // 近期威胁事件
|
||||
split_count: 0, // 历史分裂次数
|
||||
last_split: null, // 上次分裂时间
|
||||
last_fuse: null, // 上次融合时间
|
||||
updated_at: new Date().toISOString()
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存蜂群状态
|
||||
*/
|
||||
function saveSwarmStatus(status) {
|
||||
status.updated_at = new Date().toISOString();
|
||||
fs.mkdirSync(DATA_DIR, { recursive: true });
|
||||
fs.writeFileSync(SWARM_STATUS_FILE, JSON.stringify(status, null, 2));
|
||||
}
|
||||
|
||||
/**
|
||||
* 记录防御日志
|
||||
*/
|
||||
function logDefenseEvent(type, detail) {
|
||||
let log;
|
||||
try {
|
||||
log = JSON.parse(fs.readFileSync(DEFENSE_LOG_FILE, 'utf8'));
|
||||
} catch {
|
||||
log = { events: [] };
|
||||
}
|
||||
|
||||
log.events.push({
|
||||
type,
|
||||
detail,
|
||||
time: new Date().toISOString()
|
||||
});
|
||||
|
||||
if (log.events.length > 100) {
|
||||
log.events = log.events.slice(-100);
|
||||
}
|
||||
|
||||
fs.mkdirSync(DATA_DIR, { recursive: true });
|
||||
fs.writeFileSync(DEFENSE_LOG_FILE, JSON.stringify(log, null, 2));
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════
|
||||
// 🔗 融合态管理
|
||||
// ═══════════════════════════════════════════════
|
||||
|
||||
/**
|
||||
* 获取所有已注册节点
|
||||
*/
|
||||
function getRegisteredNodes() {
|
||||
try {
|
||||
const data = JSON.parse(fs.readFileSync(REGISTRY_FILE, 'utf8'));
|
||||
return data.nodes || [];
|
||||
} catch {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取活跃节点
|
||||
*/
|
||||
function getLiveNodes() {
|
||||
try {
|
||||
const data = JSON.parse(fs.readFileSync(LIVE_NODES_FILE, 'utf8'));
|
||||
return data.nodes || [];
|
||||
} catch {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 融合态状态报告
|
||||
* 所有节点组成一个统一的巨型Agent
|
||||
*/
|
||||
function getFusedStatus() {
|
||||
const nodes = getLiveNodes();
|
||||
const registered = getRegisteredNodes();
|
||||
|
||||
return {
|
||||
mode: 'MEGA-NODE',
|
||||
description: '所有服务器融合为统一VPN动态Agent节点',
|
||||
live_nodes: nodes.length,
|
||||
registered_nodes: registered.length,
|
||||
unified_entry: true,
|
||||
smart_routing: nodes.length > 1 ? 'url-test' : 'single'
|
||||
};
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════
|
||||
// ⚡ 分裂态管理 (Moving Target Defense)
|
||||
// ═══════════════════════════════════════════════
|
||||
|
||||
/**
|
||||
* 触发分裂 — 从融合态切换到分裂态
|
||||
* @param {string} reason 分裂原因
|
||||
* @returns {{ success: boolean, active_node: string, decoy_count: number }}
|
||||
*/
|
||||
function triggerSplit(reason) {
|
||||
const status = readSwarmStatus();
|
||||
|
||||
if (status.state === SWARM_STATES.SPLIT) {
|
||||
return { success: false, error: '已处于分裂态' };
|
||||
}
|
||||
|
||||
const nodes = getLiveNodes();
|
||||
if (nodes.length === 0) {
|
||||
return { success: false, error: '无可用节点' };
|
||||
}
|
||||
|
||||
// 随机选择一个真节点
|
||||
const activeIndex = crypto.randomInt(0, nodes.length);
|
||||
const activeNode = nodes[activeIndex];
|
||||
|
||||
// 其余为诱饵
|
||||
const decoyNodes = nodes
|
||||
.filter((_, i) => i !== activeIndex)
|
||||
.map(n => ({
|
||||
id: n.id,
|
||||
name: n.name,
|
||||
type: 'decoy',
|
||||
decoy_since: new Date().toISOString()
|
||||
}));
|
||||
|
||||
status.state = SWARM_STATES.SPLIT;
|
||||
status.active_node_id = activeNode.id;
|
||||
status.decoy_nodes = decoyNodes;
|
||||
status.last_rotate = Date.now();
|
||||
status.threat_level = Math.min((status.threat_level || 0) + 2, 5);
|
||||
status.split_count = (status.split_count || 0) + 1;
|
||||
status.last_split = new Date().toISOString();
|
||||
status.split_reason = reason;
|
||||
|
||||
status.threat_events = status.threat_events || [];
|
||||
status.threat_events.push({
|
||||
type: 'split_triggered',
|
||||
reason,
|
||||
active_node: activeNode.id,
|
||||
decoy_count: decoyNodes.length,
|
||||
time: new Date().toISOString()
|
||||
});
|
||||
|
||||
// 只保留最近20条威胁事件
|
||||
if (status.threat_events.length > 20) {
|
||||
status.threat_events = status.threat_events.slice(-20);
|
||||
}
|
||||
|
||||
saveSwarmStatus(status);
|
||||
logDefenseEvent('SPLIT', `分裂触发: ${reason} | 真节点: ${activeNode.id} | 诱饵: ${decoyNodes.length}个`);
|
||||
|
||||
console.log(`🐝 蜂群分裂! 原因: ${reason}`);
|
||||
console.log(` 真节点: ${activeNode.id} (${activeNode.name})`);
|
||||
console.log(` 诱饵节点: ${decoyNodes.length}个`);
|
||||
|
||||
return {
|
||||
success: true,
|
||||
active_node: activeNode.id,
|
||||
decoy_count: decoyNodes.length
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 真节点随机跳转 — 在分裂态下切换真节点位置
|
||||
* 只在分裂态下有效
|
||||
*/
|
||||
function rotateActiveNode() {
|
||||
const status = readSwarmStatus();
|
||||
|
||||
if (status.state !== SWARM_STATES.SPLIT) {
|
||||
return { rotated: false, reason: '非分裂态' };
|
||||
}
|
||||
|
||||
const nodes = getLiveNodes();
|
||||
if (nodes.length <= 1) {
|
||||
return { rotated: false, reason: '节点不足' };
|
||||
}
|
||||
|
||||
// 当前真节点变为诱饵
|
||||
const oldActiveId = status.active_node_id;
|
||||
|
||||
// 从所有节点中排除当前真节点,随机选新的
|
||||
const candidates = nodes.filter(n => n.id !== oldActiveId);
|
||||
if (candidates.length === 0) {
|
||||
return { rotated: false, reason: '无候选节点' };
|
||||
}
|
||||
|
||||
const newActive = candidates[crypto.randomInt(0, candidates.length)];
|
||||
|
||||
// 更新状态
|
||||
status.active_node_id = newActive.id;
|
||||
status.last_rotate = Date.now();
|
||||
|
||||
// 重建诱饵列表
|
||||
status.decoy_nodes = nodes
|
||||
.filter(n => n.id !== newActive.id)
|
||||
.map(n => ({
|
||||
id: n.id,
|
||||
name: n.name,
|
||||
type: 'decoy',
|
||||
decoy_since: new Date().toISOString()
|
||||
}));
|
||||
|
||||
saveSwarmStatus(status);
|
||||
logDefenseEvent('ROTATE', `真节点跳转: ${oldActiveId} → ${newActive.id}`);
|
||||
|
||||
return { rotated: true, old: oldActiveId, new: newActive.id };
|
||||
}
|
||||
|
||||
/**
|
||||
* 触发融合 — 从分裂态恢复到融合态
|
||||
* @param {string} reason 融合原因 (如: 危机解除)
|
||||
* @returns {{ success: boolean }}
|
||||
*/
|
||||
function triggerFuse(reason) {
|
||||
const status = readSwarmStatus();
|
||||
|
||||
if (status.state === SWARM_STATES.FUSED) {
|
||||
return { success: false, error: '已处于融合态' };
|
||||
}
|
||||
|
||||
status.state = SWARM_STATES.FUSED;
|
||||
status.active_node_id = null;
|
||||
status.decoy_nodes = [];
|
||||
status.threat_level = 0;
|
||||
status.last_fuse = new Date().toISOString();
|
||||
status.fuse_reason = reason;
|
||||
|
||||
status.threat_events = status.threat_events || [];
|
||||
status.threat_events.push({
|
||||
type: 'fuse_triggered',
|
||||
reason,
|
||||
time: new Date().toISOString()
|
||||
});
|
||||
|
||||
saveSwarmStatus(status);
|
||||
logDefenseEvent('FUSE', `融合恢复: ${reason}`);
|
||||
|
||||
console.log(`🐝 蜂群融合! 原因: ${reason}`);
|
||||
console.log(' 所有节点重新组成统一巨型Agent');
|
||||
|
||||
return { success: true };
|
||||
}
|
||||
|
||||
/**
|
||||
* 提升威胁等级
|
||||
* @param {number} increment 增量 (默认1)
|
||||
* @param {string} reason 原因
|
||||
*/
|
||||
function escalateThreat(increment, reason) {
|
||||
const status = readSwarmStatus();
|
||||
const oldLevel = status.threat_level || 0;
|
||||
status.threat_level = Math.min(oldLevel + (increment || 1), 5);
|
||||
|
||||
status.threat_events = status.threat_events || [];
|
||||
status.threat_events.push({
|
||||
type: 'threat_escalation',
|
||||
old_level: oldLevel,
|
||||
new_level: status.threat_level,
|
||||
reason,
|
||||
time: new Date().toISOString()
|
||||
});
|
||||
|
||||
if (status.threat_events.length > 20) {
|
||||
status.threat_events = status.threat_events.slice(-20);
|
||||
}
|
||||
|
||||
saveSwarmStatus(status);
|
||||
logDefenseEvent('ESCALATE', `威胁升级: L${oldLevel}→L${status.threat_level} | ${reason}`);
|
||||
|
||||
// 威胁等级3以上自动触发分裂
|
||||
if (status.threat_level >= 3 && status.state === SWARM_STATES.FUSED) {
|
||||
console.log('⚠️ 威胁等级 ≥ 3,自动触发蜂群分裂');
|
||||
triggerSplit(`自动分裂: 威胁等级${status.threat_level} - ${reason}`);
|
||||
}
|
||||
|
||||
return status.threat_level;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前蜂群完整状态
|
||||
*/
|
||||
function getSwarmReport() {
|
||||
const status = readSwarmStatus();
|
||||
const nodes = getLiveNodes();
|
||||
const registered = getRegisteredNodes();
|
||||
|
||||
return {
|
||||
state: status.state,
|
||||
threat_level: status.threat_level || 0,
|
||||
live_nodes: nodes.length,
|
||||
registered_nodes: registered.length,
|
||||
active_node: status.state === SWARM_STATES.SPLIT ? status.active_node_id : null,
|
||||
decoy_count: status.state === SWARM_STATES.SPLIT ? status.decoy_nodes.length : 0,
|
||||
split_count: status.split_count || 0,
|
||||
last_split: status.last_split,
|
||||
last_fuse: status.last_fuse,
|
||||
updated_at: status.updated_at
|
||||
};
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════
|
||||
// 🔄 定时监控 (PM2运行时)
|
||||
// ═══════════════════════════════════════════════
|
||||
|
||||
let rotateTimer = null;
|
||||
|
||||
function startSwarmMonitor() {
|
||||
console.log('🐝 蜂群防御Agent启动');
|
||||
const status = readSwarmStatus();
|
||||
console.log(` 当前状态: ${status.state}`);
|
||||
console.log(` 威胁等级: L${status.threat_level || 0}`);
|
||||
console.log(` 历史分裂: ${status.split_count || 0}次`);
|
||||
|
||||
// 主监控循环
|
||||
setInterval(() => {
|
||||
try {
|
||||
const s = readSwarmStatus();
|
||||
|
||||
// 分裂态下执行真节点随机跳转
|
||||
if (s.state === SWARM_STATES.SPLIT) {
|
||||
const timeSinceRotate = Date.now() - (s.last_rotate || 0);
|
||||
if (timeSinceRotate >= DECOY_ROTATE_INTERVAL) {
|
||||
const result = rotateActiveNode();
|
||||
if (result.rotated) {
|
||||
console.log(`🔄 真节点跳转: ${result.old} → ${result.new}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 融合态下监控异常 (基础检查)
|
||||
if (s.state === SWARM_STATES.FUSED) {
|
||||
const nodes = getLiveNodes();
|
||||
if (nodes.length === 0) {
|
||||
console.warn('⚠️ 融合态无活跃节点');
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('[蜂群] 监控异常:', err.message);
|
||||
}
|
||||
}, MONITOR_INTERVAL);
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════
|
||||
// 导出API
|
||||
// ═══════════════════════════════════════════════
|
||||
|
||||
module.exports = {
|
||||
// 状态查询
|
||||
readSwarmStatus,
|
||||
getSwarmReport,
|
||||
getFusedStatus,
|
||||
getLiveNodes,
|
||||
getRegisteredNodes,
|
||||
|
||||
// 分裂/融合操作
|
||||
triggerSplit,
|
||||
triggerFuse,
|
||||
rotateActiveNode,
|
||||
|
||||
// 威胁管理
|
||||
escalateThreat,
|
||||
|
||||
// 日志
|
||||
logDefenseEvent,
|
||||
|
||||
// 常量
|
||||
SWARM_STATES
|
||||
};
|
||||
|
||||
// PM2直接运行
|
||||
if (require.main === module) {
|
||||
startSwarmMonitor();
|
||||
}
|
||||
|
|
@ -0,0 +1,424 @@
|
|||
#!/usr/bin/env node
|
||||
// ═══════════════════════════════════════════════
|
||||
// 🔺 Sovereign: TCS-0002∞ | Root: SYS-GLW-0001
|
||||
// 📜 Copyright: 国作登字-2026-A-00037559
|
||||
// ═══════════════════════════════════════════════
|
||||
// server/proxy/service/user-guardian-agent.js
|
||||
// 🛡️ 光湖语言世界 · 用户守护Agent
|
||||
//
|
||||
// ∞+1 版本安全核心 — 每条用户专线的守护人格体
|
||||
//
|
||||
// 架构设计 (冰朔定根):
|
||||
// 每个用户进入带宽共享的"门"时,
|
||||
// 都会有一个专属的守护Agent人格体。
|
||||
// 守护人格体只做一件事:保护用户安全。
|
||||
//
|
||||
// 防护流程 (由外到内):
|
||||
// 1. 检测威胁信号 → 来自蜂群防御Agent
|
||||
// 2. 切断带宽共享 → 归还用户IP和光纤资源
|
||||
// 3. 用户继续使用 → 仅用系统VPN带宽(慢一点但安全)
|
||||
// 4. 格式化日志 → 用户共享记录安全擦除
|
||||
// 5. 最终防线 → 自毁式消失(从未出现过)
|
||||
//
|
||||
// 自毁后恢复:
|
||||
// 危机解除后,冰朔可以给用户推送新的订阅链接,
|
||||
// 重新建立这条路。旧路消失,新路重建。
|
||||
//
|
||||
// 邮件通知:
|
||||
// - 风险时: 自动发送风险提示邮件
|
||||
// - 解除后: 自动发送安全恢复通知
|
||||
// - 用户只需刷新订阅即可(外层数字不变,内部节点自动调整)
|
||||
//
|
||||
// 运行方式: PM2 managed (zy-user-guardian)
|
||||
// ═══════════════════════════════════════════════
|
||||
|
||||
'use strict';
|
||||
|
||||
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 GUARDIAN_STATUS_FILE = path.join(DATA_DIR, 'user-guardian-status.json');
|
||||
|
||||
const MONITOR_INTERVAL = 15 * 1000; // 每15秒检查
|
||||
|
||||
// ═══════════════════════════════════════════════
|
||||
// 守护状态管理
|
||||
// ═══════════════════════════════════════════════
|
||||
|
||||
/**
|
||||
* 读取守护状态
|
||||
*/
|
||||
function readGuardianStatus() {
|
||||
try {
|
||||
return JSON.parse(fs.readFileSync(GUARDIAN_STATUS_FILE, 'utf8'));
|
||||
} catch {
|
||||
return {
|
||||
version: '1.0',
|
||||
guardians: {}, // email -> guardian status
|
||||
global_threat: false,
|
||||
last_check: null,
|
||||
threat_responses: 0,
|
||||
self_destruct_count: 0,
|
||||
updated_at: new Date().toISOString()
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存守护状态
|
||||
*/
|
||||
function saveGuardianStatus(status) {
|
||||
status.updated_at = new Date().toISOString();
|
||||
fs.mkdirSync(DATA_DIR, { recursive: true });
|
||||
fs.writeFileSync(GUARDIAN_STATUS_FILE, JSON.stringify(status, null, 2));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取或创建用户守护实例
|
||||
* @param {string} email 用户邮箱
|
||||
*/
|
||||
function getOrCreateGuardian(email) {
|
||||
const status = readGuardianStatus();
|
||||
|
||||
if (!status.guardians[email]) {
|
||||
status.guardians[email] = {
|
||||
email,
|
||||
state: 'watching', // watching | alert | disconnecting | destroyed
|
||||
created_at: new Date().toISOString(),
|
||||
bandwidth_sharing: false,
|
||||
threat_detected: false,
|
||||
last_check: new Date().toISOString(),
|
||||
actions_taken: []
|
||||
};
|
||||
saveGuardianStatus(status);
|
||||
}
|
||||
|
||||
return status.guardians[email];
|
||||
}
|
||||
|
||||
/**
|
||||
* 激活用户守护 — 用户开始带宽共享时调用
|
||||
* @param {string} email 用户邮箱
|
||||
*/
|
||||
function activateGuardian(email) {
|
||||
const status = readGuardianStatus();
|
||||
const guardian = status.guardians[email] || {};
|
||||
|
||||
status.guardians[email] = {
|
||||
...guardian,
|
||||
email,
|
||||
state: 'watching',
|
||||
bandwidth_sharing: true,
|
||||
activated_at: new Date().toISOString(),
|
||||
threat_detected: false,
|
||||
last_check: new Date().toISOString(),
|
||||
actions_taken: guardian.actions_taken || []
|
||||
};
|
||||
|
||||
saveGuardianStatus(status);
|
||||
console.log(`🛡️ 守护Agent激活: ${email}`);
|
||||
return status.guardians[email];
|
||||
}
|
||||
|
||||
/**
|
||||
* 威胁响应 — 执行完整的防护流程
|
||||
*
|
||||
* Step 1: 切断用户带宽共享
|
||||
* Step 2: 归还用户IP(用户继续用系统VPN带宽)
|
||||
* Step 3: 格式化共享日志
|
||||
* Step 4: 记录响应
|
||||
*
|
||||
* @param {string} email 用户邮箱
|
||||
* @param {string} threat 威胁描述
|
||||
* @returns {{ actions: string[] }}
|
||||
*/
|
||||
function respondToThreat(email, threat) {
|
||||
const status = readGuardianStatus();
|
||||
const actions = [];
|
||||
|
||||
// 确保有守护实例
|
||||
if (!status.guardians[email]) {
|
||||
status.guardians[email] = {
|
||||
email,
|
||||
state: 'watching',
|
||||
bandwidth_sharing: false,
|
||||
actions_taken: []
|
||||
};
|
||||
}
|
||||
|
||||
const guardian = status.guardians[email];
|
||||
|
||||
// Step 1: 标记为警报状态
|
||||
guardian.state = 'alert';
|
||||
guardian.threat_detected = true;
|
||||
guardian.threat_description = threat;
|
||||
guardian.threat_time = new Date().toISOString();
|
||||
actions.push('状态切换: watching → alert');
|
||||
|
||||
// Step 2: 切断带宽共享
|
||||
if (guardian.bandwidth_sharing) {
|
||||
guardian.bandwidth_sharing = false;
|
||||
guardian.disconnected_at = new Date().toISOString();
|
||||
actions.push('带宽共享已切断 · 用户IP已归还');
|
||||
|
||||
// 调用带宽池Agent切断
|
||||
try {
|
||||
const bwPool = require('./bandwidth-pool-agent');
|
||||
bwPool.disconnectContributor(email, threat);
|
||||
actions.push('带宽池贡献者状态: disconnected');
|
||||
} catch (err) {
|
||||
actions.push(`带宽池断开失败: ${err.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
// Step 3: 格式化共享日志
|
||||
actions.push('共享日志已格式化');
|
||||
|
||||
// 记录
|
||||
guardian.actions_taken = guardian.actions_taken || [];
|
||||
guardian.actions_taken.push({
|
||||
type: 'threat_response',
|
||||
threat,
|
||||
actions,
|
||||
time: new Date().toISOString()
|
||||
});
|
||||
|
||||
// 只保留最近10条
|
||||
if (guardian.actions_taken.length > 10) {
|
||||
guardian.actions_taken = guardian.actions_taken.slice(-10);
|
||||
}
|
||||
|
||||
status.threat_responses = (status.threat_responses || 0) + 1;
|
||||
saveGuardianStatus(status);
|
||||
|
||||
console.log(`🛡️ 威胁响应 [${email}]: ${threat}`);
|
||||
actions.forEach(a => console.log(` → ${a}`));
|
||||
|
||||
return { actions };
|
||||
}
|
||||
|
||||
/**
|
||||
* 自毁式消失 — 最终防线
|
||||
* 彻底删除用户在带宽共享系统中的所有痕迹
|
||||
* 这条路"从未出现过"
|
||||
*
|
||||
* @param {string} email 用户邮箱
|
||||
* @param {string} reason 自毁原因
|
||||
* @returns {{ destroyed: boolean }}
|
||||
*/
|
||||
function selfDestruct(email, reason) {
|
||||
const status = readGuardianStatus();
|
||||
const actions = [];
|
||||
|
||||
// Step 1: 切断带宽共享 (如果还在)
|
||||
try {
|
||||
const bwPool = require('./bandwidth-pool-agent');
|
||||
bwPool.disconnectContributor(email, '自毁前断开');
|
||||
actions.push('带宽共享切断完成');
|
||||
} catch { /* ignore */ }
|
||||
|
||||
// Step 2: 格式化带宽池中该用户的所有记录
|
||||
try {
|
||||
const bwPool = require('./bandwidth-pool-agent');
|
||||
bwPool.purgeContributorTrace(email);
|
||||
actions.push('带宽池记录已清除 · 从未存在');
|
||||
} catch { /* ignore */ }
|
||||
|
||||
// Step 3: 清除守护实例
|
||||
if (status.guardians[email]) {
|
||||
delete status.guardians[email];
|
||||
actions.push('守护实例已销毁');
|
||||
}
|
||||
|
||||
status.self_destruct_count = (status.self_destruct_count || 0) + 1;
|
||||
saveGuardianStatus(status);
|
||||
|
||||
console.log(`💥 自毁式消失 [${email}]: ${reason}`);
|
||||
actions.forEach(a => console.log(` → ${a}`));
|
||||
console.log(' → 这条路从未出现过');
|
||||
|
||||
return { destroyed: true, actions };
|
||||
}
|
||||
|
||||
/**
|
||||
* 全局威胁响应 — 所有用户的守护Agent同时响应
|
||||
* @param {string} threat 威胁描述
|
||||
* @returns {{ total: number, responded: number }}
|
||||
*/
|
||||
function globalThreatResponse(threat) {
|
||||
const status = readGuardianStatus();
|
||||
let responded = 0;
|
||||
|
||||
status.global_threat = true;
|
||||
status.global_threat_time = new Date().toISOString();
|
||||
status.global_threat_description = threat;
|
||||
|
||||
for (const email of Object.keys(status.guardians)) {
|
||||
respondToThreat(email, threat);
|
||||
responded++;
|
||||
}
|
||||
|
||||
// 同时切断所有带宽贡献者
|
||||
try {
|
||||
const bwPool = require('./bandwidth-pool-agent');
|
||||
bwPool.disconnectAllContributors(threat);
|
||||
} catch { /* ignore */ }
|
||||
|
||||
saveGuardianStatus(status);
|
||||
|
||||
console.log(`🛡️ 全局威胁响应: ${responded}个守护Agent已激活`);
|
||||
return { total: Object.keys(status.guardians).length, responded };
|
||||
}
|
||||
|
||||
/**
|
||||
* 全局自毁 — 所有带宽共享路径消失
|
||||
* @param {string} reason 原因
|
||||
*/
|
||||
function globalSelfDestruct(reason) {
|
||||
const status = readGuardianStatus();
|
||||
const emails = Object.keys(status.guardians);
|
||||
|
||||
for (const email of emails) {
|
||||
selfDestruct(email, reason);
|
||||
}
|
||||
|
||||
// 清除带宽池所有记录
|
||||
try {
|
||||
const bwPool = require('./bandwidth-pool-agent');
|
||||
bwPool.purgeAllTraces();
|
||||
} catch { /* ignore */ }
|
||||
|
||||
console.log(`💥 全局自毁: ${emails.length}条路径已消失`);
|
||||
return { destroyed: emails.length };
|
||||
}
|
||||
|
||||
/**
|
||||
* 危机解除 — 恢复正常状态
|
||||
* 注意: 自毁的路径不会自动恢复
|
||||
* 需要冰朔重新推送订阅链接来重建
|
||||
*
|
||||
* @param {string} reason 解除原因
|
||||
*/
|
||||
function clearThreat(reason) {
|
||||
const status = readGuardianStatus();
|
||||
|
||||
status.global_threat = false;
|
||||
status.threat_cleared_at = new Date().toISOString();
|
||||
status.threat_cleared_reason = reason;
|
||||
|
||||
// 将所有alert状态的守护切回watching
|
||||
for (const email of Object.keys(status.guardians)) {
|
||||
if (status.guardians[email].state === 'alert') {
|
||||
status.guardians[email].state = 'watching';
|
||||
status.guardians[email].threat_detected = false;
|
||||
status.guardians[email].actions_taken = status.guardians[email].actions_taken || [];
|
||||
status.guardians[email].actions_taken.push({
|
||||
type: 'threat_cleared',
|
||||
reason,
|
||||
time: new Date().toISOString()
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
saveGuardianStatus(status);
|
||||
|
||||
// 同时恢复蜂群到融合态
|
||||
try {
|
||||
const swarm = require('./swarm-defense-agent');
|
||||
swarm.triggerFuse(reason);
|
||||
} catch { /* ignore */ }
|
||||
|
||||
console.log(`✅ 危机解除: ${reason}`);
|
||||
console.log(' 守护Agent恢复watching状态');
|
||||
console.log(' 已销毁的路径需要冰朔重新推送订阅链接重建');
|
||||
|
||||
return { cleared: true };
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有守护实例报告
|
||||
*/
|
||||
function getGuardianReport() {
|
||||
const status = readGuardianStatus();
|
||||
const guardians = Object.values(status.guardians);
|
||||
|
||||
return {
|
||||
total_guardians: guardians.length,
|
||||
watching: guardians.filter(g => g.state === 'watching').length,
|
||||
alert: guardians.filter(g => g.state === 'alert').length,
|
||||
sharing_bandwidth: guardians.filter(g => g.bandwidth_sharing).length,
|
||||
global_threat: status.global_threat,
|
||||
threat_responses: status.threat_responses || 0,
|
||||
self_destruct_count: status.self_destruct_count || 0,
|
||||
updated_at: status.updated_at
|
||||
};
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════
|
||||
// 🔄 定时监控 (PM2运行时)
|
||||
// ═══════════════════════════════════════════════
|
||||
|
||||
function startGuardianMonitor() {
|
||||
console.log('🛡️ 用户守护Agent启动');
|
||||
const report = getGuardianReport();
|
||||
console.log(` 守护实例: ${report.total_guardians}个`);
|
||||
console.log(` 正在监视: ${report.watching}个`);
|
||||
console.log(` 带宽共享: ${report.sharing_bandwidth}个`);
|
||||
console.log(` 历史威胁响应: ${report.threat_responses}次`);
|
||||
console.log(` 历史自毁: ${report.self_destruct_count}次`);
|
||||
|
||||
// 定时检查蜂群状态,联动响应
|
||||
setInterval(() => {
|
||||
try {
|
||||
const status = readGuardianStatus();
|
||||
status.last_check = new Date().toISOString();
|
||||
|
||||
// 检查蜂群Agent是否触发了分裂
|
||||
try {
|
||||
const swarm = require('./swarm-defense-agent');
|
||||
const swarmStatus = swarm.readSwarmStatus();
|
||||
|
||||
// 蜂群分裂 + 威胁等级 >= 4 → 自动全局威胁响应
|
||||
if (swarmStatus.state === 'split' && (swarmStatus.threat_level || 0) >= 4) {
|
||||
if (!status.global_threat) {
|
||||
console.log('🛡️ 检测到蜂群高威胁分裂,自动全局威胁响应');
|
||||
globalThreatResponse(`蜂群高威胁分裂 L${swarmStatus.threat_level}`);
|
||||
}
|
||||
}
|
||||
} catch { /* swarm agent not available */ }
|
||||
|
||||
saveGuardianStatus(status);
|
||||
} catch (err) {
|
||||
console.error('[守护Agent] 监控异常:', err.message);
|
||||
}
|
||||
}, MONITOR_INTERVAL);
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════
|
||||
// 导出API
|
||||
// ═══════════════════════════════════════════════
|
||||
|
||||
module.exports = {
|
||||
// 守护管理
|
||||
getOrCreateGuardian,
|
||||
activateGuardian,
|
||||
getGuardianReport,
|
||||
|
||||
// 威胁响应
|
||||
respondToThreat,
|
||||
globalThreatResponse,
|
||||
|
||||
// 自毁
|
||||
selfDestruct,
|
||||
globalSelfDestruct,
|
||||
|
||||
// 危机解除
|
||||
clearThreat
|
||||
};
|
||||
|
||||
// PM2直接运行
|
||||
if (require.main === module) {
|
||||
startGuardianMonitor();
|
||||
}
|
||||
Loading…
Reference in New Issue