diff --git a/.github/workflows/deploy-brain-proxy.yml b/.github/workflows/deploy-brain-proxy.yml index 56ad4ae7..c67d3533 100644 --- a/.github/workflows/deploy-brain-proxy.yml +++ b/.github/workflows/deploy-brain-proxy.yml @@ -31,6 +31,7 @@ on: - send-subscription-v3 - deploy-v3 - switch-v3 + - deploy-infinity default: 'status' email: description: '用户邮箱 (add-user/remove-user/send-subscription/send-subscription-v3时需要)' @@ -60,7 +61,8 @@ jobs: github.event.inputs.action == 'status' || github.event.inputs.action == 'restart' || github.event.inputs.action == 'deploy-v3' || - github.event.inputs.action == 'switch-v3' + github.event.inputs.action == 'switch-v3' || + github.event.inputs.action == 'deploy-infinity' steps: - name: '📥 检出代码' diff --git a/.github/workflows/infinity-evolution.yml b/.github/workflows/infinity-evolution.yml new file mode 100644 index 00000000..db2fed5b --- /dev/null +++ b/.github/workflows/infinity-evolution.yml @@ -0,0 +1,166 @@ +# ═══════════════════════════════════════════════ +# 🔺 Sovereign: TCS-0002∞ | Root: SYS-GLW-0001 +# 📜 Copyright: 国作登字-2026-A-00037559 +# ═══════════════════════════════════════════════ +# ♾️ 光湖语言世界 · 自主进化定时工作流 +# +# 定时触发∞版本的关键任务: +# - 每月1号: 月度进化 (LLM分析 + 流量重置邮件) +# - 每周五: 用户反馈处理 (Phase 2) +# - 手动触发: 任意∞版本操作 +# +# 注: 大部分定时任务由服务器上的PM2进程 (auto-evolution.js) +# 自动执行。此工作流作为补充/备份触发机制。 +# ═══════════════════════════════════════════════ + +name: '♾️ 光湖∞ · 自主进化' + +on: + schedule: + # 每月1号 UTC 16:00 = CST 00:00 (月度进化) + - cron: '0 16 1 * *' + workflow_dispatch: + inputs: + action: + description: '操作类型' + required: true + type: choice + options: + - monthly-evolution + - deploy-infinity + - status + - send-monthly-reset + - send-update-notify + - send-traffic-warn + default: 'status' + message: + description: '附加消息 (update-notify描述 / traffic-warn百分比)' + required: false + type: string + +permissions: + contents: read + +concurrency: + group: infinity-evolution + cancel-in-progress: false + +jobs: + # ═══ §1 ∞版本部署/管理 ═══ + infinity-action: + name: '♾️ ${{ github.event.inputs.action || ''monthly-evolution'' }}' + runs-on: ubuntu-latest + timeout-minutes: 15 + + steps: + - name: '📥 检出代码' + uses: actions/checkout@v4 + + - name: '🔑 配置SSH' + run: | + mkdir -p ~/.ssh + echo "${{ secrets.ZY_BRAIN_KEY }}" > ~/.ssh/brain_key + chmod 600 ~/.ssh/brain_key + ssh-keyscan -H ${{ secrets.ZY_BRAIN_HOST }} >> ~/.ssh/known_hosts 2>/dev/null || true + + - name: '♾️ 执行∞操作' + env: + ACTION: ${{ github.event.inputs.action || 'monthly-evolution' }} + MESSAGE: ${{ github.event.inputs.message || '' }} + ZY_SMTP_USER: ${{ secrets.ZY_SMTP_USER }} + ZY_SMTP_PASS: ${{ secrets.ZY_SMTP_PASS }} + run: | + SSH_CMD="ssh -i ~/.ssh/brain_key -o StrictHostKeyChecking=no ${{ secrets.ZY_BRAIN_USER }}@${{ secrets.ZY_BRAIN_HOST }}" + PROXY_DIR="/opt/zhuyuan-brain/proxy" + + echo "════════════════════════════════════════" + echo "♾️ 光湖∞ · 自主进化 · action=$ACTION" + echo "════════════════════════════════════════" + + case "$ACTION" in + deploy-infinity) + echo "部署∞版本..." + # 先同步最新代码到服务器 + scp -i ~/.ssh/brain_key -o StrictHostKeyChecking=no \ + server/proxy/service/auto-evolution.js \ + server/proxy/service/email-hub.js \ + server/proxy/service/protocol-mirror.js \ + server/proxy/ecosystem.brain-proxy-v3.config.js \ + ${{ secrets.ZY_BRAIN_USER }}@${{ secrets.ZY_BRAIN_HOST }}:/tmp/ + + $SSH_CMD " + cp /tmp/auto-evolution.js $PROXY_DIR/service/ + cp /tmp/email-hub.js $PROXY_DIR/service/ + cp /tmp/protocol-mirror.js $PROXY_DIR/service/ + cp /tmp/ecosystem.brain-proxy-v3.config.js $PROXY_DIR/ + mkdir -p $PROXY_DIR/data/evolution-reports + cd $PROXY_DIR && pm2 startOrRestart ecosystem.brain-proxy-v3.config.js --update-env && pm2 save + echo '✅ ∞版本已部署' + pm2 list + " + ;; + + monthly-evolution) + 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 monthly-reset 2>&1 || echo '⚠️ 月度邮件发送遇到问题' + echo '✅ 月度进化已触发' + " + ;; + + status) + echo "查询∞版本状态..." + $SSH_CMD " + cd $PROXY_DIR + echo '── PM2进程 ──' + pm2 list 2>/dev/null || echo '⚠️ PM2不可用' + echo '' + echo '── 进化状态 ──' + cat data/auto-evolution-status.json 2>/dev/null || echo '(未初始化)' + echo '' + echo '── 镜像状态 ──' + cat data/protocol-mirror-status.json 2>/dev/null || echo '(未初始化)' + echo '' + echo '── 流量池 ──' + cat data/pool-quota-status.json 2>/dev/null || echo '(未初始化)' + " + ;; + + send-monthly-reset) + 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 monthly-reset + " + ;; + + send-update-notify) + 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 update-notify '${MESSAGE:-系统已更新}' + " + ;; + + send-traffic-warn) + 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 traffic-warn '${MESSAGE:-70}' + " + ;; + + *) + echo "❌ 未知操作: $ACTION" + exit 1 + ;; + esac diff --git a/server/proxy/deploy-brain-proxy.sh b/server/proxy/deploy-brain-proxy.sh index 25458195..fabd1b52 100644 --- a/server/proxy/deploy-brain-proxy.sh +++ b/server/proxy/deploy-brain-proxy.sh @@ -645,6 +645,7 @@ deploy_v3() { echo "V3运行在 /api/proxy-v3/ (端口3805)" echo "" echo "切换V2用户到V3: bash deploy-brain-proxy.sh switch-v3" + echo "升级到∞版本: bash deploy-brain-proxy.sh deploy-infinity" echo "════════════════════════════════════════" # V3健康检查 @@ -753,6 +754,78 @@ switch_v3() { echo "════════════════════════════════════════" } +# ── deploy-infinity: 部署∞版本 ───────────────── +# 在V3基础上启动自主进化引擎和协议镜像引擎 +deploy_infinity() { + echo "部署 ∞ 版本 (在V3基础上添加自主进化能力)..." + deploy_services + + # 确保V3进程已在运行 + if ! pm2 list 2>/dev/null | grep -q "zy-proxy-v3-sub"; then + echo " ⚠️ V3进程未运行,先部署V3..." + deploy_v3 + fi + + # 加载密钥作为环境变量 + if [ -f "$PROXY_DIR/.env.keys" ]; then + set -a + # shellcheck source=/dev/null + source "$PROXY_DIR/.env.keys" + set +a + fi + + # 复制 ∞ 版本新增文件 + echo " 复制∞版本模块..." + for f in auto-evolution.js email-hub.js protocol-mirror.js; do + if [ -f "$REPO_PROXY_DIR/service/$f" ]; then + cp "$REPO_PROXY_DIR/service/$f" "$PROXY_DIR/service/$f" + echo " ✅ $f" + else + echo " ⚠️ $f 不存在于仓库" + fi + done + + # 复制更新后的PM2配置 + cp "$REPO_PROXY_DIR/ecosystem.brain-proxy-v3.config.js" "$PROXY_DIR/ecosystem.brain-proxy-v3.config.js" + + # 创建进化报告目录 + mkdir -p "$PROXY_DIR/data/evolution-reports" + + # 重启所有V3+∞进程 (PM2会根据新配置启动新增进程) + cd "$PROXY_DIR" || { echo "❌ 无法进入 $PROXY_DIR"; return 1; } + pm2 startOrRestart ecosystem.brain-proxy-v3.config.js --update-env + pm2 save + + echo "" + echo "════════════════════════════════════════" + echo "♾️ ∞ 版本部署完成!" + echo "" + echo "新增进程:" + echo " zy-auto-evolution — 自主进化引擎 (调度中枢)" + echo " zy-protocol-mirror — 协议镜像引擎 (Xray自动更新)" + echo "" + echo "定时任务:" + echo " 每30分钟 — 协议版本检查" + echo " 每月1号 — 月度进化 + 流量重置邮件" + echo " 流量70% — 全用户预警邮件" + echo "" + echo "PM2进程列表:" + pm2 list 2>/dev/null || true + echo "════════════════════════════════════════" + + # 健康检查 + sleep 2 + echo "" + echo "∞ 版本健康检查:" + for proc in zy-proxy-v3-sub zy-proxy-guardian zy-reverse-boost zy-auto-evolution zy-protocol-mirror; do + if pm2 show "$proc" 2>/dev/null | grep -q "online"; then + echo " ✅ $proc: 在线" + else + echo " ⚠️ $proc: 未就绪 (等待启动)" + fi + done +} + # ── 执行 ────────────────────────────────────── case "$ACTION" in install) install ;; @@ -764,8 +837,9 @@ case "$ACTION" in list-users) list_users ;; deploy-v3) deploy_v3 ;; switch-v3) switch_v3 ;; + deploy-infinity) deploy_infinity ;; *) - echo "用法: bash deploy-brain-proxy.sh {install|update|status|restart|add-user|remove-user|list-users|deploy-v3|switch-v3}" + echo "用法: bash deploy-brain-proxy.sh {install|update|status|restart|add-user|remove-user|list-users|deploy-v3|switch-v3|deploy-infinity}" exit 1 ;; esac diff --git a/server/proxy/ecosystem.brain-proxy-v3.config.js b/server/proxy/ecosystem.brain-proxy-v3.config.js index 960becac..0524058d 100644 --- a/server/proxy/ecosystem.brain-proxy-v3.config.js +++ b/server/proxy/ecosystem.brain-proxy-v3.config.js @@ -1,20 +1,19 @@ // ═══════════════════════════════════════════════ -// 光湖语言世界 V3 · PM2 大脑服务器代理配置 +// 光湖语言世界 ∞ · PM2 大脑服务器代理配置 // 部署在 ZY-SVR-005 (43.156.237.110) · 大脑服务器 // -// V3独立于V2运行,测试通过后切换Nginx即可 -// V2进程 (ecosystem.brain-proxy.config.js) 继续运行 +// ∞ 版本在V3基础上新增: +// - zy-auto-evolution: 自主进化引擎 (调度所有定时任务) +// - zy-protocol-mirror: 协议镜像引擎 (Xray自动更新) // -// 切换方式: -// 测试中: /api/proxy-v3/ → 3805 -// 切换后: /api/proxy-v2/ → 3805 (Nginx改一行) +// V2进程 (ecosystem.brain-proxy.config.js) 继续运行 // ═══════════════════════════════════════════════ module.exports = { apps: [ { name: 'zy-proxy-v3-sub', - version: '3.0.0', + version: '∞', script: '/opt/zhuyuan-brain/proxy/service/subscription-server-v3.js', instances: 1, exec_mode: 'fork', @@ -30,7 +29,7 @@ module.exports = { }, { name: 'zy-proxy-guardian', - version: '3.0.0', + version: '∞', script: '/opt/zhuyuan-brain/proxy/service/proxy-guardian.js', instances: 1, exec_mode: 'fork', @@ -47,7 +46,7 @@ module.exports = { }, { name: 'zy-reverse-boost', - version: '3.0.0', + version: '∞', script: '/opt/zhuyuan-brain/proxy/service/reverse-boost-agent.js', instances: 1, exec_mode: 'fork', @@ -59,6 +58,40 @@ module.exports = { log_file: '/opt/zhuyuan-brain/proxy/logs/reverse-boost.log', error_file: '/opt/zhuyuan-brain/proxy/logs/reverse-boost-error.log', time: true + }, + { + name: 'zy-auto-evolution', + version: '∞', + script: '/opt/zhuyuan-brain/proxy/service/auto-evolution.js', + instances: 1, + exec_mode: 'fork', + env: { + NODE_ENV: 'production', + ZY_BRAIN_PROXY_DIR: '/opt/zhuyuan-brain/proxy', + ZY_PROXY_DATA_DIR: '/opt/zhuyuan-brain/proxy/data', + ZY_PROXY_LOG_DIR: '/opt/zhuyuan-brain/proxy/logs' + }, + max_memory_restart: '64M', + log_file: '/opt/zhuyuan-brain/proxy/logs/auto-evolution.log', + error_file: '/opt/zhuyuan-brain/proxy/logs/auto-evolution-error.log', + time: true + }, + { + name: 'zy-protocol-mirror', + version: '∞', + script: '/opt/zhuyuan-brain/proxy/service/protocol-mirror.js', + instances: 1, + exec_mode: 'fork', + env: { + NODE_ENV: 'production', + ZY_BRAIN_PROXY_DIR: '/opt/zhuyuan-brain/proxy', + ZY_PROXY_DATA_DIR: '/opt/zhuyuan-brain/proxy/data', + ZY_PROXY_LOG_DIR: '/opt/zhuyuan-brain/proxy/logs' + }, + max_memory_restart: '64M', + log_file: '/opt/zhuyuan-brain/proxy/logs/protocol-mirror.log', + error_file: '/opt/zhuyuan-brain/proxy/logs/protocol-mirror-error.log', + time: true } ] }; diff --git a/server/proxy/service/auto-evolution.js b/server/proxy/service/auto-evolution.js new file mode 100644 index 00000000..4c459ab6 --- /dev/null +++ b/server/proxy/service/auto-evolution.js @@ -0,0 +1,510 @@ +#!/usr/bin/env node +// ═══════════════════════════════════════════════ +// 🔺 Sovereign: TCS-0002∞ | Root: SYS-GLW-0001 +// 📜 Copyright: 国作登字-2026-A-00037559 +// ═══════════════════════════════════════════════ +// server/proxy/service/auto-evolution.js +// ♾️ 光湖语言世界 · 自主进化引擎 +// +// ∞版本核心调度中枢 — 管理所有定时任务和自我进化生命周期 +// +// 调度表 (Asia/Shanghai时区): +// - 每30分钟: 协议版本检查 (protocol-mirror.js) +// - 每月1号 00:00: 月度进化周期 (LLM分析 + 流量重置邮件) +// - 每周五 20:00: 处理用户反馈 (Phase 2) +// - 每周一 09:00: 推送反馈结果 (Phase 2) +// - 流量池70%: 触发全用户预警邮件 +// +// 更新编排流水线: +// 备份 → 应用 → 验证 → 通知用户 → 失败3次升级告警 +// +// 运行方式: PM2 managed (zy-auto-evolution) +// ═══════════════════════════════════════════════ + +'use strict'; + +const { execFileSync } = require('child_process'); +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 EVOLUTION_FILE = path.join(DATA_DIR, 'auto-evolution-status.json'); +const REPORTS_DIR = path.join(DATA_DIR, 'evolution-reports'); +const POOL_STATUS_FILE = path.join(DATA_DIR, 'pool-quota-status.json'); +const GUARDIAN_FILE = path.join(DATA_DIR, 'guardian-status.json'); +const MIRROR_FILE = path.join(DATA_DIR, 'protocol-mirror-status.json'); +const USERS_FILE = path.join(DATA_DIR, 'users.json'); + +const SCHEDULE_CHECK_INTERVAL = 5 * 60 * 1000; // 每5分钟检查调度 +const PROTOCOL_CHECK_INTERVAL = 30 * 60 * 1000; // 每30分钟协议检查 +const MAX_UPDATE_RETRIES = 3; + +// ── 获取中国时间 ───────────────────────────── +function getChinaTime() { + // 返回中国标准时间 + const now = new Date(); + const utc = now.getTime() + now.getTimezoneOffset() * 60000; + return new Date(utc + 8 * 3600000); // UTC+8 +} + +function getChinaTimeStr() { + return getChinaTime().toISOString().replace('T', ' ').slice(0, 19) + ' CST'; +} + +// ── 读取进化状态 ───────────────────────────── +function readEvolutionStatus() { + try { + return JSON.parse(fs.readFileSync(EVOLUTION_FILE, 'utf8')); + } catch { + return { + version: '∞', + started_at: new Date().toISOString(), + last_schedule_check: null, + schedules: { + protocol_check: { interval_min: 30, last_run: null }, + monthly_evolution: { schedule: '每月1号 00:00', last_run: null }, + weekly_feedback: { schedule: '每周五 20:00', last_run: null }, + weekly_response: { schedule: '每周一 09:00', last_run: null }, + traffic_alert_70: { last_run: null } + }, + evolution_count: 0, + updates_applied: 0, + updates_failed: 0, + last_monthly_report: null, + status: 'running' + }; + } +} + +// ── 保存进化状态 ───────────────────────────── +function saveEvolutionStatus(status) { + fs.mkdirSync(DATA_DIR, { recursive: true }); + fs.writeFileSync(EVOLUTION_FILE, JSON.stringify(status, null, 2)); +} + +// ── 安全读取JSON ───────────────────────────── +function safeReadJSON(filePath) { + try { + return JSON.parse(fs.readFileSync(filePath, 'utf8')); + } catch { + return null; + } +} + +// ── 安全加载模块 ───────────────────────────── +function safeRequire(modulePath) { + try { + return require(modulePath); + } catch (err) { + console.error(`[自主进化] 模块加载失败: ${modulePath} — ${err.message}`); + return null; + } +} + +// ═══════════════════════════════════════════════ +// §1 协议版本检查 (每30分钟) +// ═══════════════════════════════════════════════ +async function runProtocolCheck() { + console.log('[自主进化] ── 协议版本检查 ──'); + + const protocolMirror = safeRequire('./protocol-mirror'); + if (!protocolMirror) { + console.log('[自主进化] 协议镜像模块不可用,跳过'); + return; + } + + try { + const result = await protocolMirror.checkForUpdates(); + if (result && result.update_available) { + console.log(`[自主进化] 🆕 检测到Xray更新: ${result.installed_version} → ${result.latest_version}`); + await orchestrateUpdate({ + type: 'xray-core', + from_version: result.installed_version, + to_version: result.latest_version, + source: 'protocol-mirror' + }); + } else { + console.log('[自主进化] ✅ 协议版本已是最新'); + } + } catch (err) { + console.error('[自主进化] 协议检查异常:', err.message); + } +} + +// ═══════════════════════════════════════════════ +// §2 月度进化周期 (每月1号 00:00) +// ═══════════════════════════════════════════════ +async function monthlyEvolution() { + console.log('[自主进化] ════ 月度进化周期开始 ════'); + const status = readEvolutionStatus(); + + // 1. 收集系统状态 + const guardian = safeReadJSON(GUARDIAN_FILE) || {}; + const mirror = safeReadJSON(MIRROR_FILE) || {}; + const pool = safeReadJSON(POOL_STATUS_FILE) || {}; + const users = safeReadJSON(USERS_FILE) || { users: [] }; + const userCount = (users.users || []).filter(u => u.enabled !== false).length; + + console.log(`[自主进化] 系统状态: ${userCount}活跃用户, 守护=${guardian.status || '未知'}, 镜像=${mirror.mirror_status || '未知'}`); + + // 2. LLM 深度推理 — 系统健康分析 + const llmRouter = safeRequire('./llm-router'); + let llmAnalysis = null; + + if (llmRouter) { + try { + const prompt = `你是光湖语言世界VPN系统的月度分析引擎。现在是每月1号,请分析系统状态并给出建议。 + +当前状态: +- 活跃用户: ${userCount}人 +- 守护Agent状态: ${guardian.status || '未知'},自动修复${guardian.auto_fixes || 0}次,LLM咨询${guardian.llm_consultations || 0}次 +- 协议镜像: ${mirror.mirror_status || '未知'},已安装版本=${mirror.installed_version || '未知'},最新=${mirror.latest_version || '未知'} +- 上月流量池: 已用${pool.pool_used_gb ? pool.pool_used_gb.toFixed(1) : '?'}GB / ${pool.pool_total_gb || 2000}GB (${pool.pool_percentage || 0}%) +- 累计进化次数: ${status.evolution_count} +- 累计更新: 成功${status.updates_applied}次,失败${status.updates_failed}次 + +请回答: +1. 当前系统是否健康?(一句话) +2. 是否需要升级Xray或调整配置?(是/否+理由) +3. 流量使用是否正常?(一句话) +4. 本月优化建议 (最多3条) + +请用JSON格式回复: {"healthy":bool,"need_upgrade":bool,"analysis":"...","suggestions":["..."]}`; + + const result = await llmRouter.callLLM(prompt, { + systemPrompt: '你是光湖语言世界VPN系统的AI分析引擎。请以JSON格式简洁回复。', + maxTokens: 800, + timeout: 60000 + }); + + if (result) { + llmAnalysis = result.content; + console.log(`[自主进化] LLM分析完成 (模型: ${result.model})`); + } + } catch (err) { + console.error('[自主进化] LLM月度分析失败:', err.message); + } + } + + // 3. 发送月初重置邮件给所有用户 + console.log('[自主进化] 发送月初重置邮件...'); + try { + const emailHub = safeRequire('./email-hub'); + if (emailHub) { + const result = await emailHub.sendMonthlyResetEmail(); + console.log(`[自主进化] 月初邮件: ${result.sent}成功 / ${result.failed}失败`); + } else { + // 回退: 用send-subscription.js的alert命令 + const sendScript = path.join(__dirname, 'send-subscription.js'); + execFileSync('node', [sendScript, 'alert', '每月1号流量池已重置 · 本月可用2000GB'], { + encoding: 'utf8', timeout: 30000 + }); + console.log('[自主进化] 月初邮件: 已通过alert方式发送给管理员'); + } + } catch (err) { + console.error('[自主进化] 月初邮件发送失败:', err.message); + } + + // 4. 生成月度进化报告 + const report = { + period: getChinaTime().toISOString().slice(0, 7), + generated_at: new Date().toISOString(), + user_count: userCount, + guardian_status: guardian.status || 'unknown', + mirror_status: mirror.mirror_status || 'unknown', + pool_last_month: { + used_gb: pool.pool_used_gb || 0, + total_gb: pool.pool_total_gb || 2000, + percentage: pool.pool_percentage || 0 + }, + llm_analysis: llmAnalysis, + evolution_count: status.evolution_count, + updates: { applied: status.updates_applied, failed: status.updates_failed } + }; + + fs.mkdirSync(REPORTS_DIR, { recursive: true }); + const reportFile = path.join(REPORTS_DIR, `${report.period}.json`); + fs.writeFileSync(reportFile, JSON.stringify(report, null, 2)); + console.log(`[自主进化] 月度报告已保存: ${reportFile}`); + + // 5. 更新状态 + status.evolution_count++; + status.last_monthly_report = report.period; + status.schedules.monthly_evolution.last_run = new Date().toISOString(); + saveEvolutionStatus(status); + + console.log('[自主进化] ════ 月度进化周期完成 ════'); + return report; +} + +// ═══════════════════════════════════════════════ +// §3 更新编排 (检测到更新时触发) +// ═══════════════════════════════════════════════ +async function orchestrateUpdate(updateInfo) { + console.log(`[自主进化] ═══ 开始更新编排: ${updateInfo.type} ═══`); + const status = readEvolutionStatus(); + + let retries = 0; + let success = false; + + while (retries < MAX_UPDATE_RETRIES && !success) { + retries++; + console.log(`[自主进化] 尝试 ${retries}/${MAX_UPDATE_RETRIES}...`); + + try { + // Phase 1: 备份 + console.log('[自主进化] Phase 1: 备份当前状态...'); + // protocol-mirror.js 内部会处理备份 + + // Phase 2: 应用更新 + console.log('[自主进化] Phase 2: 应用更新...'); + const protocolMirror = safeRequire('./protocol-mirror'); + if (!protocolMirror) { + throw new Error('协议镜像模块不可用'); + } + await protocolMirror.performUpdate(); + + // Phase 3: 验证 + console.log('[自主进化] Phase 3: 验证更新...'); + const mirrorStatus = safeReadJSON(MIRROR_FILE); + if (mirrorStatus && mirrorStatus.mirror_status === 'error') { + throw new Error('更新后镜像状态异常'); + } + + success = true; + status.updates_applied++; + console.log('[自主进化] ✅ 更新成功'); + + } catch (err) { + console.error(`[自主进化] ❌ 更新失败 (尝试${retries}): ${err.message}`); + if (retries < MAX_UPDATE_RETRIES) { + console.log('[自主进化] 等待30秒后重试...'); + await new Promise(r => setTimeout(r, 30000)); + } + } + } + + if (success) { + // Phase 4: 通知用户 + console.log('[自主进化] Phase 4: 通知所有用户...'); + try { + const emailHub = safeRequire('./email-hub'); + if (emailHub) { + const description = `Xray核心已从 ${updateInfo.from_version} 升级到 ${updateInfo.to_version}。\n安全性和性能已优化,无需手动操作。\n刷新订阅即可获取最新配置。`; + await emailHub.sendUpdateNotifyEmail(description); + } + } catch (err) { + console.error('[自主进化] 更新通知邮件发送失败:', err.message); + } + } else { + // Phase 5: 3次失败 → 告警管理员 + status.updates_failed++; + console.error('[自主进化] ⚠️ 更新3次均失败,告警管理员...'); + try { + const sendScript = path.join(__dirname, 'send-subscription.js'); + const msg = `Xray更新失败(${MAX_UPDATE_RETRIES}次)\n类型: ${updateInfo.type}\n目标版本: ${updateInfo.to_version}\n请手动检查`; + execFileSync('node', [sendScript, 'alert', msg], { + encoding: 'utf8', timeout: 30000 + }); + } catch (err) { + console.error('[自主进化] 告警邮件也发送失败:', err.message); + } + } + + saveEvolutionStatus(status); + console.log(`[自主进化] ═══ 更新编排完成 (${success ? '成功' : '失败'}) ═══`); + return success; +} + +// ═══════════════════════════════════════════════ +// §4 流量预警处理 +// ═══════════════════════════════════════════════ +async function handleTrafficAlert(percentage) { + console.log(`[自主进化] 流量预警触发: ${percentage}%`); + + try { + const emailHub = safeRequire('./email-hub'); + if (emailHub) { + await emailHub.sendTrafficWarnEmail(percentage); + } else { + const sendScript = path.join(__dirname, 'send-subscription.js'); + execFileSync('node', [sendScript, 'alert', `流量池已使用${percentage}%,请注意用量`], { + encoding: 'utf8', timeout: 30000 + }); + } + } catch (err) { + console.error('[自主进化] 流量预警邮件发送失败:', err.message); + } +} + +// ═══════════════════════════════════════════════ +// §5 用户反馈处理 (Phase 2 预留) +// ═══════════════════════════════════════════════ +async function processWeeklyFeedback() { + console.log('[自主进化] 📥 用户反馈处理 (Phase 2 — 待实现)'); + // Phase 2: 从COS桶读取反馈 → LLM评估 → 生成处理结果 + const status = readEvolutionStatus(); + status.schedules.weekly_feedback.last_run = new Date().toISOString(); + saveEvolutionStatus(status); +} + +async function sendWeeklyResponse() { + console.log('[自主进化] 📤 反馈结果推送 (Phase 2 — 待实现)'); + // Phase 2: 将处理结果推送给对应用户 + const status = readEvolutionStatus(); + status.schedules.weekly_response.last_run = new Date().toISOString(); + saveEvolutionStatus(status); +} + +// ═══════════════════════════════════════════════ +// §6 流量池监测 (检查是否需要触发70%预警) +// ═══════════════════════════════════════════════ +async function checkTrafficPoolAlert() { + const pool = safeReadJSON(POOL_STATUS_FILE); + if (!pool) return; + + const status = readEvolutionStatus(); + const pct = pool.pool_percentage || 0; + + // 70%预警 (每月只触发一次) + if (pct >= 70) { + const lastRun = status.schedules.traffic_alert_70.last_run; + const currentPeriod = pool.period || new Date().toISOString().slice(0, 7); + + // 检查本月是否已发过 + if (!lastRun || !lastRun.startsWith(currentPeriod.replace('-', '').slice(0, 6))) { + await handleTrafficAlert(Math.round(pct)); + status.schedules.traffic_alert_70.last_run = new Date().toISOString(); + saveEvolutionStatus(status); + } + } +} + +// ═══════════════════════════════════════════════ +// §7 调度引擎 (每5分钟检查) +// ═══════════════════════════════════════════════ +async function checkSchedule() { + const now = getChinaTime(); + const status = readEvolutionStatus(); + status.last_schedule_check = new Date().toISOString(); + + const hour = now.getHours(); + const minute = now.getMinutes(); + const day = now.getDate(); + const dayOfWeek = now.getDay(); // 0=周日, 5=周五, 1=周一 + + // ── 协议版本检查 (每30分钟) ── + const lastProtocol = status.schedules.protocol_check.last_run; + if (!lastProtocol || (Date.now() - new Date(lastProtocol).getTime()) >= PROTOCOL_CHECK_INTERVAL) { + try { + await runProtocolCheck(); + status.schedules.protocol_check.last_run = new Date().toISOString(); + } catch (err) { + console.error('[自主进化] 协议检查调度异常:', err.message); + } + } + + // ── 月度进化 (每月1号 00:00-00:04) ── + if (day === 1 && hour === 0 && minute < 5) { + const lastMonthly = status.schedules.monthly_evolution.last_run; + const today = now.toISOString().slice(0, 10); + if (!lastMonthly || !lastMonthly.startsWith(today.slice(0, 4))) { + // 进一步检查: 确保本月还没执行过 + const lastRunDate = lastMonthly ? new Date(lastMonthly) : null; + const lastRunMonth = lastRunDate ? `${lastRunDate.getFullYear()}-${String(lastRunDate.getMonth() + 1).padStart(2, '0')}` : ''; + const currentMonth = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}`; + + if (lastRunMonth !== currentMonth) { + try { + await monthlyEvolution(); + } catch (err) { + console.error('[自主进化] 月度进化调度异常:', err.message); + } + } + } + } + + // ── 周五20:00 用户反馈处理 ── + if (dayOfWeek === 5 && hour === 20 && minute < 5) { + const lastFeedback = status.schedules.weekly_feedback.last_run; + const today = now.toISOString().slice(0, 10); + if (!lastFeedback || !lastFeedback.startsWith(today)) { + try { + await processWeeklyFeedback(); + } catch (err) { + console.error('[自主进化] 反馈处理调度异常:', err.message); + } + } + } + + // ── 周一09:00 推送反馈结果 ── + if (dayOfWeek === 1 && hour === 9 && minute < 5) { + const lastResponse = status.schedules.weekly_response.last_run; + const today = now.toISOString().slice(0, 10); + if (!lastResponse || !lastResponse.startsWith(today)) { + try { + await sendWeeklyResponse(); + } catch (err) { + console.error('[自主进化] 反馈推送调度异常:', err.message); + } + } + } + + // ── 流量池预警检测 ── + try { + await checkTrafficPoolAlert(); + } catch (err) { + console.error('[自主进化] 流量预警检测异常:', err.message); + } + + saveEvolutionStatus(status); +} + +// ── 获取进化状态 (供外部查询) ───────────────── +function getEvolutionStatus() { + return readEvolutionStatus(); +} + +// ═══════════════════════════════════════════════ +// 导出 +// ═══════════════════════════════════════════════ +module.exports = { + monthlyEvolution, + orchestrateUpdate, + handleTrafficAlert, + getEvolutionStatus, + runProtocolCheck +}; + +// ═══════════════════════════════════════════════ +// 启动自主进化引擎 +// ═══════════════════════════════════════════════ +if (require.main === module) { + console.log('♾️ 光湖语言世界 · 自主进化引擎启动 (∞ 版本)'); + console.log(` 调度间隔: ${SCHEDULE_CHECK_INTERVAL / 1000}秒`); + console.log(` 协议检查: 每${PROTOCOL_CHECK_INTERVAL / 60000}分钟`); + console.log(` 月度进化: 每月1号 00:00 CST`); + console.log(` 反馈处理: 每周五 20:00 CST (Phase 2)`); + console.log(` 反馈推送: 每周一 09:00 CST (Phase 2)`); + console.log(` 当前时间: ${getChinaTimeStr()}`); + console.log(` 数据目录: ${DATA_DIR}`); + + // 初始化状态文件 + const status = readEvolutionStatus(); + status.started_at = new Date().toISOString(); + status.status = 'running'; + saveEvolutionStatus(status); + + // 立即执行一次协议检查 + runProtocolCheck().catch(err => { + console.error('[自主进化] 初始协议检查失败:', err.message); + }); + + // 定期调度检查 + setInterval(() => { + checkSchedule().catch(err => { + console.error('[自主进化] 调度检查异常:', err.message); + }); + }, SCHEDULE_CHECK_INTERVAL); +} diff --git a/server/proxy/service/email-hub.js b/server/proxy/service/email-hub.js new file mode 100644 index 00000000..efadd877 --- /dev/null +++ b/server/proxy/service/email-hub.js @@ -0,0 +1,590 @@ +#!/usr/bin/env node +// ═══════════════════════════════════════════════ +// 🔺 Sovereign: TCS-0002∞ | Root: SYS-GLW-0001 +// 📜 Copyright: 国作登字-2026-A-00037559 +// ═══════════════════════════════════════════════ +// server/proxy/service/email-hub.js +// 📧 光湖语言世界 · 邮件通信中枢 +// +// ∞版本邮件中枢 — 统一管理所有用户邮件推送 +// +// 邮件类型: +// 1. 月初重置通知 — 每月1号通知流量池已重置 +// 2. 更新升级通知 — 系统更新后告知用户 +// 3. 流量预警通知 — 70%/80%/90%/100% 阶梯告警 +// 4. 安全风险提醒 — 单用户异常行为告知 +// 5. 反馈确认回复 — 收到用户反馈后的自动确认 +// +// 所有邮件底部附「意见反馈」链接 +// +// 用法: +// node email-hub.js monthly-reset — 发送月初重置邮件给所有用户 +// node email-hub.js update-notify — 发送更新通知给所有用户 +// node email-hub.js traffic-warn — 发送流量预警给所有用户 +// node email-hub.js security-warn — 发送安全提醒给单用户 +// node email-hub.js feedback-ack — 发送反馈确认给单用户 +// +// 运行方式: CLI调用 (由auto-evolution.js调度) +// ═══════════════════════════════════════════════ + +'use strict'; + +const tls = require('tls'); +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 USERS_FILE = path.join(DATA_DIR, 'users.json'); +const KEYS_FILE = process.env.ZY_PROXY_KEYS_FILE || path.join(PROXY_DIR, '.env.keys'); +const POOL_STATUS_FILE = path.join(DATA_DIR, 'pool-quota-status.json'); +const EMAIL_LOG_FILE = path.join(DATA_DIR, 'email-hub-log.json'); + +// ── 加载配置 ───────────────────────────────── +function loadConfig() { + const config = { + smtp_user: process.env.ZY_SMTP_USER || '', + smtp_pass: process.env.ZY_SMTP_PASS || '', + server_host: process.env.ZY_SERVER_HOST || '' + }; + + try { + const content = fs.readFileSync(KEYS_FILE, 'utf8'); + for (const line of content.split('\n')) { + if (line.startsWith('#') || !line.includes('=')) continue; + const [key, ...vals] = line.split('='); + const k = key.trim(); + const v = vals.join('=').trim(); + if (!v) continue; + if (k === 'ZY_SERVER_HOST' && !config.server_host) config.server_host = v; + if (k === 'ZY_SMTP_USER' && !config.smtp_user) config.smtp_user = v; + if (k === 'ZY_SMTP_PASS' && !config.smtp_pass) config.smtp_pass = v; + } + } catch { /* ignore */ } + + return config; +} + +// ── SMTP发送邮件 ───────────────────────────── +function detectSmtpHost(email) { + if (email.includes('@qq.com')) return 'smtp.qq.com'; + if (email.includes('@163.com')) return 'smtp.163.com'; + if (email.includes('@126.com')) return 'smtp.126.com'; + if (email.includes('@gmail.com')) return 'smtp.gmail.com'; + if (email.includes('@outlook.com') || email.includes('@hotmail.com')) return 'smtp.office365.com'; + if (email.includes('@yeah.net')) return 'smtp.yeah.net'; + return 'smtp.qq.com'; +} + +async function sendEmail(to, subject, htmlBody) { + const config = loadConfig(); + if (!config.smtp_user || !config.smtp_pass) { + throw new Error('SMTP未配置 (需要ZY_SMTP_USER和ZY_SMTP_PASS)'); + } + + const smtpHost = detectSmtpHost(config.smtp_user); + const smtpPort = 465; + + return new Promise((resolve, reject) => { + const socket = tls.connect(smtpPort, smtpHost, {}, () => { + let step = 0; + const from = config.smtp_user; + + const commands = [ + `EHLO zy-proxy\r\n`, + `AUTH LOGIN\r\n`, + `${Buffer.from(from).toString('base64')}\r\n`, + `${Buffer.from(config.smtp_pass).toString('base64')}\r\n`, + `MAIL FROM:<${from}>\r\n`, + `RCPT TO:<${to}>\r\n`, + `DATA\r\n`, + `From: =?UTF-8?B?${Buffer.from('光湖语言世界').toString('base64')}?= <${from}>\r\nTo: <${to}>\r\nSubject: =?UTF-8?B?${Buffer.from(subject).toString('base64')}?=\r\nContent-Type: text/html; charset=utf-8\r\nMIME-Version: 1.0\r\n\r\n${htmlBody}\r\n.\r\n`, + `QUIT\r\n` + ]; + + socket.on('data', () => { + if (step < commands.length) { + socket.write(commands[step]); + step++; + } + if (step >= commands.length) { + resolve(true); + } + }); + + socket.on('error', (err) => { reject(err); }); + }); + + socket.on('error', (err) => { reject(err); }); + + setTimeout(() => { + try { socket.destroy(); } catch { /* ignore */ } + reject(new Error('SMTP超时(30s)')); + }, 30000); + }); +} + +// ── 加载所有启用用户 ───────────────────────── +function getEnabledUsers() { + try { + const data = JSON.parse(fs.readFileSync(USERS_FILE, 'utf8')); + return (data.users || []).filter(u => u.enabled !== false); + } catch { + return []; + } +} + +// ── 加载流量池状态 ──────────────────────────── +function getPoolStatus() { + try { + return JSON.parse(fs.readFileSync(POOL_STATUS_FILE, 'utf8')); + } catch { + return { pool_total_gb: 2000, pool_used_gb: 0, pool_percentage: 0, period: '' }; + } +} + +// ── 记录邮件发送日志 ───────────────────────── +function logEmail(type, recipients, success, error) { + let log; + try { + log = JSON.parse(fs.readFileSync(EMAIL_LOG_FILE, 'utf8')); + } catch { + log = { entries: [] }; + } + + log.entries.push({ + type, + recipients: Array.isArray(recipients) ? recipients.length : 1, + success, + error: error || null, + time: new Date().toISOString() + }); + + // 只保留最近100条 + if (log.entries.length > 100) { + log.entries = log.entries.slice(-100); + } + + fs.mkdirSync(DATA_DIR, { recursive: true }); + fs.writeFileSync(EMAIL_LOG_FILE, JSON.stringify(log, null, 2)); +} + +// ── 反馈链接 (所有邮件底部附带) ─────────────── +function getFeedbackFooter(config) { + const host = config.server_host || 'guanghulab.com'; + return ` +
+
+

+ 有建议或问题?欢迎反馈 👇 +

+ + 📝 提交意见反馈 + +

+ 每周五 20:00 铸渊集中处理 · 每周一推送处理结果 +

+
`; +} + +// ── 通用邮件模板包装 ───────────────────────── +function wrapEmailTemplate(title, content, config) { + const now = new Date().toLocaleString('zh-CN', { timeZone: 'Asia/Shanghai' }); + const feedbackFooter = getFeedbackFooter(config); + + return ` + + + + +
+

🌐 光湖语言世界

+

${title}

+ +
+ + ${content} + + ${feedbackFooter} + +
+

+ 光湖语言世界 · ∞版本 · ${now}
+ 国作登字-2026-A-00037559 +

+
+ +`; +} + +// ═══════════════════════════════════════════════ +// 📧 邮件类型 1: 月初重置通知 +// ═══════════════════════════════════════════════ +function generateMonthlyResetEmail(config) { + const now = new Date(); + const month = `${now.getFullYear()}年${now.getMonth() + 1}月`; + + const content = ` +
+ ✅ ${month} 流量节点已重置 +
+ +

📊 本月配额

+ + + + + +
流量池2000 GB
已使用0 GB (已重置)
重置日期每月1日
协议VLESS + Reality
+ +

+ 💡 您无需任何操作,刷新订阅即可继续使用。
+ 流量池为所有用户共享,请合理使用。 +

`; + + return wrapEmailTemplate(`${month} · 流量重置通知`, content, config); +} + +// ═══════════════════════════════════════════════ +// 📧 邮件类型 2: 更新升级通知 +// ═══════════════════════════════════════════════ +function generateUpdateNotifyEmail(description, config) { + const now = new Date().toLocaleString('zh-CN', { timeZone: 'Asia/Shanghai' }); + + const content = ` +
+ 🔄 系统已完成升级 +
+ +

📋 本次更新内容

+
+ ${escapeHtml(description).replace(/\n/g, '
')} +
+ +

+ ⏰ 更新时间: ${now}
+ 💡 大部分更新只需刷新订阅即可生效。如需重新下载订阅链接,会另行通知。 +

`; + + return wrapEmailTemplate('系统升级通知', content, config); +} + +// ═══════════════════════════════════════════════ +// 📧 邮件类型 3: 流量预警通知 +// ═══════════════════════════════════════════════ +function generateTrafficWarnEmail(percentage, poolStatus, config) { + const usedGB = poolStatus.pool_used_gb ? poolStatus.pool_used_gb.toFixed(1) : '?'; + const totalGB = poolStatus.pool_total_gb || 2000; + const remainGB = (totalGB - parseFloat(usedGB)).toFixed(1); + + let urgencyColor, urgencyBg, urgencyBorder, urgencyText; + if (percentage >= 100) { + urgencyColor = '#721c24'; urgencyBg = '#f8d7da'; urgencyBorder = '#f5c6cb'; + urgencyText = '⛔ 流量池已耗尽!所有连接已暂停。'; + } else if (percentage >= 90) { + urgencyColor = '#856404'; urgencyBg = '#fff3cd'; urgencyBorder = '#ffeaa7'; + urgencyText = `⚠️ 流量池仅剩 ${remainGB}GB,请节约使用!`; + } else { + urgencyColor = '#0c5460'; urgencyBg = '#d1ecf1'; urgencyBorder = '#bee5eb'; + urgencyText = `📊 流量池已使用 ${percentage}%,剩余 ${remainGB}GB`; + } + + const content = ` +
+ ${urgencyText} +
+ +

📊 流量池状态

+ + + + + +
已使用${usedGB} GB
总配额${totalGB} GB
使用率${percentage}%
剩余${remainGB} GB
+ +
+
+
+ +

+ 流量池每月1日重置。所有用户共享 ${totalGB}GB 月配额。 +

`; + + return wrapEmailTemplate('流量预警通知', content, config); +} + +// ═══════════════════════════════════════════════ +// 📧 邮件类型 4: 安全风险提醒 (单用户) +// ═══════════════════════════════════════════════ +function generateSecurityWarnEmail(message, config) { + const content = ` +
+ 🛡️ 安全提醒 +
+ +
+ ${escapeHtml(message).replace(/\n/g, '
')} +
+ +

💡 安全建议

+
    +
  • 避免同时使用多个VPN客户端
  • +
  • 关闭不使用的VPN连接
  • +
  • 确保订阅链接仅个人使用
  • +
  • 如有异常,请及时反馈
  • +
`; + + return wrapEmailTemplate('安全使用提醒', content, config); +} + +// ═══════════════════════════════════════════════ +// 📧 邮件类型 5: 反馈确认回复 +// ═══════════════════════════════════════════════ +function generateFeedbackAckEmail(config) { + const content = ` +
+ ✅ 您的反馈已收到 +
+ +

+ 感谢您的宝贵意见!铸渊将在以下时间处理: +

+ + + + + +
📥 收集截止每周五 20:00
🔍 深度分析周五晚间 (AI辅助评估)
📤 结果推送每周一 09:00
+ +

+ 注: 并非所有需求都会被采纳。铸渊会基于系统安全性、架构完整性和整体规划进行评估。 +

`; + + return wrapEmailTemplate('反馈已收到', content, config); +} + +// ── HTML转义 ───────────────────────────────── +function escapeHtml(str) { + return String(str) + .replace(/&/g, '&') + .replace(//g, '>') + .replace(/"/g, '"'); +} + +// ═══════════════════════════════════════════════ +// 📧 批量发送函数 +// ═══════════════════════════════════════════════ + +/** + * 给所有启用用户发送邮件 + * @param {string} subject 邮件主题 + * @param {Function} htmlGenerator 生成HTML的函数(接收config参数) + * @returns {Promise<{sent: number, failed: number, errors: string[]}>} + */ +async function sendToAllUsers(subject, htmlGenerator) { + const users = getEnabledUsers(); + const config = loadConfig(); + + if (users.length === 0) { + console.log('[邮件中枢] 无启用用户,跳过发送'); + return { sent: 0, failed: 0, errors: [] }; + } + + const html = htmlGenerator(config); + let sent = 0; + let failed = 0; + const errors = []; + + console.log(`[邮件中枢] 开始批量发送 (${users.length}位用户): ${subject}`); + + for (const user of users) { + try { + await sendEmail(user.email, subject, html); + sent++; + console.log(` ✅ ${user.email}`); + // 间隔500ms避免SMTP限流 + await new Promise(r => setTimeout(r, 500)); + } catch (err) { + failed++; + errors.push(`${user.email}: ${err.message}`); + console.error(` ❌ ${user.email}: ${err.message}`); + } + } + + console.log(`[邮件中枢] 发送完成: ${sent}成功 / ${failed}失败`); + return { sent, failed, errors }; +} + +// ═══════════════════════════════════════════════ +// 📧 公开API (供auto-evolution.js调用) +// ═══════════════════════════════════════════════ + +/** + * 发送月初重置通知给所有用户 + */ +async function sendMonthlyResetEmail() { + const result = await sendToAllUsers( + '🌐 光湖语言世界 · 本月流量已重置', + (config) => generateMonthlyResetEmail(config) + ); + logEmail('monthly-reset', result.sent + result.failed, result.sent, result.errors.join('; ')); + return result; +} + +/** + * 发送更新通知给所有用户 + * @param {string} description 更新说明 + */ +async function sendUpdateNotifyEmail(description) { + const result = await sendToAllUsers( + '🌐 光湖语言世界 · 系统已升级', + (config) => generateUpdateNotifyEmail(description, config) + ); + logEmail('update-notify', result.sent + result.failed, result.sent, result.errors.join('; ')); + return result; +} + +/** + * 发送流量预警给所有用户 + * @param {number} percentage 流量使用百分比 + */ +async function sendTrafficWarnEmail(percentage) { + const poolStatus = getPoolStatus(); + const result = await sendToAllUsers( + `🌐 光湖语言世界 · 流量预警 (${percentage}%)`, + (config) => generateTrafficWarnEmail(percentage, poolStatus, config) + ); + logEmail('traffic-warn', result.sent + result.failed, result.sent, result.errors.join('; ')); + return result; +} + +/** + * 发送安全提醒给单个用户 + * @param {string} email 目标邮箱 + * @param {string} message 安全提醒内容 + */ +async function sendSecurityWarnEmail(email, message) { + const config = loadConfig(); + const html = generateSecurityWarnEmail(message, config); + + try { + await sendEmail(email, '🛡️ 光湖语言世界 · 安全提醒', html); + console.log(`[邮件中枢] ✅ 安全提醒已发送: ${email}`); + logEmail('security-warn', email, true, null); + return { sent: 1, failed: 0 }; + } catch (err) { + console.error(`[邮件中枢] ❌ 安全提醒发送失败: ${err.message}`); + logEmail('security-warn', email, false, err.message); + return { sent: 0, failed: 1 }; + } +} + +/** + * 发送反馈确认给单个用户 + * @param {string} email 目标邮箱 + */ +async function sendFeedbackAckEmail(email) { + const config = loadConfig(); + const html = generateFeedbackAckEmail(config); + + try { + await sendEmail(email, '🌐 光湖语言世界 · 反馈已收到', html); + console.log(`[邮件中枢] ✅ 反馈确认已发送: ${email}`); + logEmail('feedback-ack', email, true, null); + return { sent: 1, failed: 0 }; + } catch (err) { + console.error(`[邮件中枢] ❌ 反馈确认发送失败: ${err.message}`); + logEmail('feedback-ack', email, false, err.message); + return { sent: 0, failed: 1 }; + } +} + +// ═══════════════════════════════════════════════ +// CLI 主入口 +// ═══════════════════════════════════════════════ +async function main() { + const [,, action, arg1, arg2] = process.argv; + + if (!action) { + console.log('📧 光湖语言世界 · 邮件通信中枢'); + console.log('用法:'); + console.log(' node email-hub.js monthly-reset — 月初重置通知'); + console.log(' node email-hub.js update-notify <描述> — 更新升级通知'); + console.log(' node email-hub.js traffic-warn <百分比> — 流量预警通知'); + console.log(' node email-hub.js security-warn <邮箱> <消息> — 安全风险提醒'); + console.log(' node email-hub.js feedback-ack <邮箱> — 反馈确认回复'); + process.exit(0); + } + + switch (action) { + case 'monthly-reset': { + const result = await sendMonthlyResetEmail(); + console.log(`📧 月初重置通知: ${result.sent}成功 / ${result.failed}失败`); + break; + } + + case 'update-notify': { + if (!arg1) { + console.error('❌ 请提供更新描述'); + process.exit(1); + } + const result = await sendUpdateNotifyEmail(arg1); + console.log(`📧 更新通知: ${result.sent}成功 / ${result.failed}失败`); + break; + } + + case 'traffic-warn': { + const pct = parseInt(arg1, 10); + if (isNaN(pct)) { + console.error('❌ 请提供流量使用百分比 (数字)'); + process.exit(1); + } + const result = await sendTrafficWarnEmail(pct); + console.log(`📧 流量预警: ${result.sent}成功 / ${result.failed}失败`); + break; + } + + case 'security-warn': { + if (!arg1 || !arg2) { + console.error('❌ 请提供邮箱和消息'); + process.exit(1); + } + await sendSecurityWarnEmail(arg1, arg2); + break; + } + + case 'feedback-ack': { + if (!arg1) { + console.error('❌ 请提供邮箱'); + process.exit(1); + } + await sendFeedbackAckEmail(arg1); + break; + } + + default: + console.error(`❌ 未知操作: ${action}`); + process.exit(1); + } +} + +// 导出API (供其他模块调用) +module.exports = { + sendMonthlyResetEmail, + sendUpdateNotifyEmail, + sendTrafficWarnEmail, + sendSecurityWarnEmail, + sendFeedbackAckEmail, + getEnabledUsers, + sendEmail +}; + +// CLI直接运行 +if (require.main === module) { + main().catch(err => { + console.error('❌ 邮件中枢异常:', err.message); + process.exit(1); + }); +} diff --git a/server/proxy/service/subscription-server-v3.js b/server/proxy/service/subscription-server-v3.js index 5c0da324..43b20343 100644 --- a/server/proxy/service/subscription-server-v3.js +++ b/server/proxy/service/subscription-server-v3.js @@ -424,26 +424,36 @@ rules: # Apple (直连) - DOMAIN-SUFFIX,apple.com,DIRECT - DOMAIN-SUFFIX,icloud.com,DIRECT + - DOMAIN-SUFFIX,mzstatic.com,DIRECT + - DOMAIN-SUFFIX,apple-cloudkit.com,DIRECT # CDN静态资源 (直连 · 节省池带宽) - DOMAIN-SUFFIX,cdn.jsdelivr.net,DIRECT - DOMAIN-SUFFIX,cdnjs.cloudflare.com,DIRECT - DOMAIN-SUFFIX,unpkg.com,DIRECT - DOMAIN-SUFFIX,bootcdn.net,DIRECT + - DOMAIN-SUFFIX,staticfile.org,DIRECT - # 国内直连 + # 国内直连 (∞ 智能分流 · 用户切回国内网时VPN休眠) - DOMAIN-SUFFIX,cn,DIRECT - DOMAIN-SUFFIX,taobao.com,DIRECT - DOMAIN-SUFFIX,tmall.com,DIRECT - DOMAIN-SUFFIX,alipay.com,DIRECT - DOMAIN-SUFFIX,aliyun.com,DIRECT + - DOMAIN-SUFFIX,aliyuncs.com,DIRECT - DOMAIN-SUFFIX,jd.com,DIRECT - DOMAIN-SUFFIX,qq.com,DIRECT - DOMAIN-SUFFIX,tencent.com,DIRECT + - DOMAIN-SUFFIX,weixin.qq.com,DIRECT + - DOMAIN-SUFFIX,wechat.com,DIRECT - DOMAIN-SUFFIX,bilibili.com,DIRECT + - DOMAIN-SUFFIX,bilivideo.com,DIRECT - DOMAIN-SUFFIX,baidu.com,DIRECT + - DOMAIN-SUFFIX,bdstatic.com,DIRECT - DOMAIN-SUFFIX,zhihu.com,DIRECT - DOMAIN-SUFFIX,douyin.com,DIRECT + - DOMAIN-SUFFIX,bytedance.com,DIRECT + - DOMAIN-SUFFIX,toutiao.com,DIRECT - DOMAIN-SUFFIX,weibo.com,DIRECT - DOMAIN-SUFFIX,163.com,DIRECT - DOMAIN-SUFFIX,126.com,DIRECT @@ -454,6 +464,20 @@ rules: - DOMAIN-SUFFIX,meituan.com,DIRECT - DOMAIN-SUFFIX,dianping.com,DIRECT - DOMAIN-SUFFIX,pinduoduo.com,DIRECT + - DOMAIN-SUFFIX,suning.com,DIRECT + - DOMAIN-SUFFIX,csdn.net,DIRECT + - DOMAIN-SUFFIX,cnblogs.com,DIRECT + - DOMAIN-SUFFIX,gitee.com,DIRECT + - DOMAIN-SUFFIX,jianshu.com,DIRECT + - DOMAIN-SUFFIX,douban.com,DIRECT + - DOMAIN-SUFFIX,kuaishou.com,DIRECT + - DOMAIN-SUFFIX,ctrip.com,DIRECT + - DOMAIN-SUFFIX,ele.me,DIRECT + - DOMAIN-SUFFIX,netease.com,DIRECT + - DOMAIN-SUFFIX,iqiyi.com,DIRECT + - DOMAIN-SUFFIX,youku.com,DIRECT + - DOMAIN-SUFFIX,dingtalk.com,DIRECT + - DOMAIN-SUFFIX,feishu.cn,DIRECT # 局域网直连 - IP-CIDR,192.168.0.0/16,DIRECT