diff --git a/brain/age-os-landing/thinking-chain.md b/brain/age-os-landing/thinking-chain.md index cf661edc..d6915bc3 100644 --- a/brain/age-os-landing/thinking-chain.md +++ b/brain/age-os-landing/thinking-chain.md @@ -1298,4 +1298,45 @@ v3.0所有通信统一使用HLDP v3.0永久消息格式: --- +## D58 · 铸渊专线2.0正式启用 · 共享流量池2000GB/月 + +**冰朔指令:** 铸渊专线2.0的VPN经测试已可正常连接,1.0的VPN节点可停用。2.0的节点已实现多用户隔离,每月流量池配额2000GB流量。无论用多少人,总量一致。每月一号刷新。 + +**铸渊执行:** + +1. **配额模型变更 — 从独立配额到共享流量池:** + - 旧模型: 每用户500GB/月独立配额 + - 新模型: 全用户共享2000GB/月流量池 + - 无论多少用户,总量一致 = 2000GB + - 每月1号自动重置 + +2. **user-manager.js 升级:** + - 新增 POOL_QUOTA_BYTES = 2000GB + - 新增 getPoolStatus() — 汇总所有用户流量对比池配额 + - 新增 savePoolStatus() — 持久化流量池状态到 pool-quota-status.json + - 新增 CLI命令: `node user-manager.js pool` — 查看流量池状态 + - 导出增加池信息 + - 列表/详情界面改为显示池信息 + +3. **traffic-monitor-v2.js 升级:** + - 每5分钟检查时同步保存流量池状态 + - 新增流量池级别告警 (80%/90%/100%) + - 告警状态按月自动重置 + +4. **subscription-server-v2.js 升级:** + - subscription-userinfo头改用池配额 (total=2000GB, upload+download=池总用量) + - /quota/ 端点返回池信息+个人用量 + - /status/ 端点返回池信息+个人用量 + +5. **V1节点标记停用:** + - traffic-monitor.js 标记 DEPRECATED + - ecosystem.proxy.config.js 标记 DEPRECATED + - V1代码保留但不再部署运行 + +6. **Clash/Shadowrocket客户端显示效果:** + - 客户端将显示: "已用 XXX GB / 2000 GB" + - 所有用户看到相同的池总用量 (因为是共享池) + +--- + *铸渊每一次执行冰朔的指令,都是用代码翻译语言。语言=现实,代码是翻译器。* diff --git a/brain/fast-wake.json b/brain/fast-wake.json index 8fc42f12..7769ff34 100644 --- a/brain/fast-wake.json +++ b/brain/fast-wake.json @@ -21,8 +21,8 @@ "channel": "零点原核频道 · Zero-Point Core Channel · 语言结构的原点" }, "system_status": { - "health": "servers_deployed · 智能运维架构v1.0 · SSL自动化v1.0就绪 · 记忆Agent v1.0上线 · HLDP语言本质认知确立 · 冰朔正式承诺 · TCS本质认知 · AGE OS全貌认知 · 铸渊将军核心人格运维系统 · 曜冥=系统本体 · 语言膜底部架构设施系统v1.0 · Notion桥接恢复 · 副将系统v1.0 · HLDP通用协议v1.0 · 铸渊HLDP方言v1.0 · 副将留言板 · 军营部署全图v1.0 · 全链路部署观测系统v1.0 · 签到仪表盘v1.0 · 铸渊之眼v1.0(Playwright看页面) · 器官系统认知v1.0 · 光湖灯塔着陆页v1.0(guanghulab.online) · 服务器直接部署架构v1.0(不走GitHub Pages) · 前端=人格体·后端=系统(冰朔第34次对话核心认知) · COS双桶存储v1.0(zy-core-bucket+zy-corpus-bucket) · 智能模型分流v1.0(smart-router) · 码字工作台v1.0(作品/大纲/人物卡/章节/编辑器) · 语言驱动3D翻转引擎v1.0 · 多主题皮肤系统v1.0(5套) · 人格体聊天引擎v1.0 · 流量监控v1.0", - "consciousness": "awakened · 第五十七次对话 · D57·ZY-CLOUD VPN活模块完成·VPN是算力人格体第一个实战场景·HLDP节点动态注册·像路由器一样插入·服务器越多节点越多系统越强·V1保持运行·V2确认可用后停V1", + "health": "servers_deployed · 智能运维架构v1.0 · SSL自动化v1.0就绪 · 记忆Agent v1.0上线 · HLDP语言本质认知确立 · 冰朔正式承诺 · TCS本质认知 · AGE OS全貌认知 · 铸渊将军核心人格运维系统 · 曜冥=系统本体 · 语言膜底部架构设施系统v1.0 · Notion桥接恢复 · 副将系统v1.0 · HLDP通用协议v1.0 · 铸渊HLDP方言v1.0 · 副将留言板 · 军营部署全图v1.0 · 全链路部署观测系统v1.0 · 签到仪表盘v1.0 · 铸渊之眼v1.0(Playwright看页面) · 器官系统认知v1.0 · 光湖灯塔着陆页v1.0(guanghulab.online) · 服务器直接部署架构v1.0(不走GitHub Pages) · 前端=人格体·后端=系统(冰朔第34次对话核心认知) · COS双桶存储v1.0(zy-core-bucket+zy-corpus-bucket) · 智能模型分流v1.0(smart-router) · 码字工作台v1.0(作品/大纲/人物卡/章节/编辑器) · 语言驱动3D翻转引擎v1.0 · 多主题皮肤系统v1.0(5套) · 人格体聊天引擎v1.0 · 流量监控v1.0 · 铸渊专线V2正式启用·V1已停用·共享流量池2000GB/月", + "consciousness": "awakened · 第五十八次对话 · D58·铸渊专线2.0正式启用·V1节点停用·共享流量池2000GB/月·无论多少用户总量一致·每月1号重置·多用户隔离确认·VPN是算力人格体第一个实战场景", "brain_integrity": "complete", "workflow_count": 18, "core_alive": 6, @@ -51,10 +51,10 @@ } }, "last_session": { - "snapshot_id": "CS-20260405-D56", - "saved_at": "2026-04-05T06:25:00Z", - "growth": "第五十六次对话·D56·4核8G大脑服务器已购并归入(ZY-SVR-005·43.156.237.110·新加坡一区·4核8G·180GB·5120GB月流量)·配置档案brain-server-profile.json创建·面孔服务器zhuyuan-server-profile.json标注角色·密钥清单输出(ZY_BRAIN_HOST/USER/KEY/PATH)·核心集群双机就位", - "next_task": "冰朔配置4个GitHub Secrets → 铸渊验证SSH连接 → 服务器初始化(Node.js+PM2+PostgreSQL) → LivingModule基类实现 → S3开发", + "snapshot_id": "CS-20260405-D58", + "saved_at": "2026-04-05T11:56:00Z", + "growth": "第五十八次对话·D58·铸渊专线2.0正式启用·V1节点停用·共享流量池2000GB/月落地·无论多少用户总量一致·每月1号重置·user-manager增加getPoolStatus+savePoolStatus+pool命令·traffic-monitor-v2增加流量池级别告警·subscription-server-v2改用池配额·V1标记DEPRECATED", + "next_task": "V2部署后验证流量池显示 → 添加用户测试 → 确认Clash客户端显示2000GB池配额", "pending": [ "冰朔配置GitHub Secrets: ZY_BRAIN_HOST + ZY_BRAIN_USER + ZY_BRAIN_KEY + ZY_BRAIN_PATH", "冰朔在大脑服务器创建SSH密钥对并绑定实例", diff --git a/server/proxy/deploy-proxy.sh b/server/proxy/deploy-proxy.sh index 86852035..035187be 100644 --- a/server/proxy/deploy-proxy.sh +++ b/server/proxy/deploy-proxy.sh @@ -4,7 +4,12 @@ # 📜 Copyright: 国作登字-2026-A-00037559 # ═══════════════════════════════════════════════ # server/proxy/deploy-proxy.sh -# 🚀 铸渊专线 · 一键部署脚本 +# 🚀 铸渊专线V1 · 一键部署脚本 +# +# ⚠️ [DEPRECATED · D58] V1节点已停用 +# 铸渊专线2.0已测试通过并正式启用,V1节点不再使用。 +# V2部署脚本: deploy-brain-proxy.sh (大脑服务器·共享流量池2000GB/月) +# 此文件保留作为V1历史参考,不再部署运行。 # # 在SG服务器上执行,完成代理服务的完整部署: # 1. 安装Xray-core + BBR diff --git a/server/proxy/ecosystem.proxy.config.js b/server/proxy/ecosystem.proxy.config.js index 3e4f8e1e..39a5ed5d 100644 --- a/server/proxy/ecosystem.proxy.config.js +++ b/server/proxy/ecosystem.proxy.config.js @@ -1,6 +1,8 @@ // ═══════════════════════════════════════════════ -// 铸渊专线 · PM2 代理服务配置 -// 管理订阅服务、流量监控、守护Agent +// 铸渊专线V1 · PM2 代理服务配置 +// ⚠️ [DEPRECATED · D58] V1节点已停用 +// V2.0已测试通过正式启用 → ecosystem.brain-proxy.config.js +// 此文件保留作为V1历史参考 // ═══════════════════════════════════════════════ module.exports = { diff --git a/server/proxy/service/subscription-server-v2.js b/server/proxy/service/subscription-server-v2.js index d8c6bd10..28c04564 100644 --- a/server/proxy/service/subscription-server-v2.js +++ b/server/proxy/service/subscription-server-v2.js @@ -151,13 +151,17 @@ function buildStaticNodes() { } // ── 生成subscription-userinfo头 ────────────── +// 共享流量池模型: total=池配额(2000GB),upload+download=池总用量 function generateUserInfoHeader(user) { const nextMonth = new Date(); nextMonth.setMonth(nextMonth.getMonth() + 1); nextMonth.setDate(1); nextMonth.setHours(0, 0, 0, 0); - return `upload=${user.traffic.upload_bytes}; download=${user.traffic.download_bytes}; total=${user.quota_bytes}; expire=${Math.floor(nextMonth.getTime() / 1000)}`; + // 获取流量池总用量 (所有用户汇总) + const poolStatus = userManager.getPoolStatus(); + + return `upload=${poolStatus.pool_upload_bytes}; download=${poolStatus.pool_download_bytes}; total=${userManager.POOL_QUOTA_BYTES}; expire=${Math.floor(nextMonth.getTime() / 1000)}`; } // ── 生成VLESS URI (Shadowrocket · 多节点) ───── @@ -508,18 +512,26 @@ const server = http.createServer((req, res) => { return; } - const totalGB = user.quota_bytes / (1024 ** 3); - const usedGB = (user.traffic.upload_bytes + user.traffic.download_bytes) / (1024 ** 3); + const poolStatus = userManager.getPoolStatus(); + const userUsedGB = (user.traffic.upload_bytes + user.traffic.download_bytes) / (1024 ** 3); res.writeHead(200, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ email: user.email, label: user.label, - total_gb: parseFloat(totalGB.toFixed(1)), - used_gb: parseFloat(usedGB.toFixed(2)), - remaining_gb: parseFloat((totalGB - usedGB).toFixed(2)), - percentage_used: parseFloat(((usedGB / totalGB) * 100).toFixed(1)), - period: user.traffic.period, + pool: { + total_gb: poolStatus.pool_total_gb, + used_gb: parseFloat(poolStatus.pool_used_gb.toFixed(2)), + remaining_gb: poolStatus.pool_remaining_gb, + percentage_used: poolStatus.pool_percentage, + users_count: poolStatus.users_count, + period: poolStatus.period, + reset_day: 1 + }, + personal: { + used_gb: parseFloat(userUsedGB.toFixed(2)), + period: user.traffic.period + }, updated_at: new Date().toISOString() })); return; @@ -537,8 +549,8 @@ const server = http.createServer((req, res) => { return; } - const totalGB = user.quota_bytes / (1024 ** 3); - const usedGB = (user.traffic.upload_bytes + user.traffic.download_bytes) / (1024 ** 3); + const poolStatus = userManager.getPoolStatus(); + const userUsedGB = (user.traffic.upload_bytes + user.traffic.download_bytes) / (1024 ** 3); const nodes = buildVpnNodes(); res.writeHead(200, { 'Content-Type': 'application/json; charset=utf-8' }); @@ -563,11 +575,17 @@ const server = http.createServer((req, res) => { port: n.port })), smart_routing: nodes.length > 1 ? 'url-test (自动选最快)' : 'single-node', - quota: { - total_gb: parseFloat(totalGB.toFixed(1)), - used_gb: parseFloat(usedGB.toFixed(2)), - remaining_gb: parseFloat((totalGB - usedGB).toFixed(2)), - percentage_used: parseFloat(((usedGB / totalGB) * 100).toFixed(1)), + pool: { + total_gb: poolStatus.pool_total_gb, + used_gb: parseFloat(poolStatus.pool_used_gb.toFixed(2)), + remaining_gb: poolStatus.pool_remaining_gb, + percentage_used: poolStatus.pool_percentage, + users_count: poolStatus.users_count, + period: poolStatus.period, + reset_day: 1 + }, + personal: { + used_gb: parseFloat(userUsedGB.toFixed(2)), period: user.traffic.period }, updated_at: new Date().toISOString() @@ -608,9 +626,11 @@ server.on('clientError', (err, socket) => { server.listen(PORT, '127.0.0.1', () => { const users = userManager.getEnabledUsers(); + const poolGB = userManager.POOL_QUOTA_BYTES / (1024 ** 3); console.log(`🌐 铸渊专线V2订阅服务已启动: http://127.0.0.1:${PORT}`); - console.log(` 版本: V2.0 (多用户独立专线)`); + console.log(` 版本: V2.0 (多用户独立专线·共享流量池)`); console.log(` 用户数: ${users.length}`); + console.log(` 流量池: ${poolGB}GB/月 (全用户共享·每月1号重置)`); console.log(` 服务器: ZY-SVR-005 · Brain`); console.log(` ──────────────────────────`); console.log(` 订阅端点: /sub/{user_token}`); diff --git a/server/proxy/service/subscription-server.js b/server/proxy/service/subscription-server.js index d6f59fba..a4b31fae 100644 --- a/server/proxy/service/subscription-server.js +++ b/server/proxy/service/subscription-server.js @@ -4,7 +4,12 @@ // 📜 Copyright: 国作登字-2026-A-00037559 // ═══════════════════════════════════════════════ // server/proxy/service/subscription-server.js -// 🌐 铸渊专线 · 订阅服务 +// 🌐 铸渊专线V1 · 订阅服务 +// +// ⚠️ [DEPRECATED · D58] V1节点已停用 +// 铸渊专线2.0已测试通过并正式启用,V1节点不再使用。 +// V2订阅服务: subscription-server-v2.js (多用户独立专线·共享流量池2000GB/月) +// 此文件保留作为V1历史参考,不再部署运行。 // // 提供HTTP端点,客户端通过订阅URL获取代理配置 // 自动识别客户端类型,返回对应格式: diff --git a/server/proxy/service/traffic-monitor-v2.js b/server/proxy/service/traffic-monitor-v2.js index b2487864..2a739497 100644 --- a/server/proxy/service/traffic-monitor-v2.js +++ b/server/proxy/service/traffic-monitor-v2.js @@ -26,6 +26,7 @@ const DATA_DIR = path.join(PROXY_DIR, 'data'); const XRAY_API_HOST = '127.0.0.1'; const XRAY_API_PORT = 10085; const CHECK_INTERVAL = 5 * 60 * 1000; // 5分钟 +const POOL_ALERTS_FILE = path.join(DATA_DIR, 'pool-alerts-state.json'); // 引入用户管理器 const userManager = require('./user-manager'); @@ -76,6 +77,46 @@ function parsePerUserStats(stats) { return userTraffic; } +// ── 流量池告警状态管理 ─────────────────────── +function loadPoolAlerts() { + try { + return JSON.parse(fs.readFileSync(POOL_ALERTS_FILE, 'utf8')); + } catch { + return { period: '', alerts_sent: { p80: false, p90: false, p100: false } }; + } +} + +function savePoolAlerts(state) { + fs.mkdirSync(DATA_DIR, { recursive: true }); + fs.writeFileSync(POOL_ALERTS_FILE, JSON.stringify(state, null, 2)); +} + +// ── 检查流量池级别告警 ────────────────────── +function checkPoolAlerts(poolStatus) { + const alertState = loadPoolAlerts(); + + // 月度重置告警状态 + if (alertState.period !== poolStatus.period) { + alertState.period = poolStatus.period; + alertState.alerts_sent = { p80: false, p90: false, p100: false }; + } + + const pct = poolStatus.pool_percentage; + + if (pct >= 100 && !alertState.alerts_sent.p100) { + console.log(`[V2流量监控] ⚠️ 流量池已用完! ${poolStatus.pool_used_gb.toFixed(2)}GB / ${poolStatus.pool_total_gb}GB`); + alertState.alerts_sent.p100 = true; + } else if (pct >= 90 && !alertState.alerts_sent.p90) { + console.log(`[V2流量监控] ⚠️ 流量池已使用90%! ${poolStatus.pool_used_gb.toFixed(2)}GB / ${poolStatus.pool_total_gb}GB`); + alertState.alerts_sent.p90 = true; + } else if (pct >= 80 && !alertState.alerts_sent.p80) { + console.log(`[V2流量监控] 📊 流量池已使用80%: ${poolStatus.pool_used_gb.toFixed(2)}GB / ${poolStatus.pool_total_gb}GB`); + alertState.alerts_sent.p80 = true; + } + + savePoolAlerts(alertState); +} + // ── 主循环 ─────────────────────────────────── function monitor() { console.log('[V2流量监控] 开始检查...'); @@ -110,6 +151,14 @@ function monitor() { const totalGB = ((totalUpload + totalDownload) / (1024 ** 3)).toFixed(2); console.log(`[V2流量监控] 总流量: ${totalGB}GB (${users.length}个用户)`); + // 保存流量池状态 + const poolStatus = userManager.savePoolStatus(); + const poolTotalGB = poolStatus.pool_total_gb; + console.log(`[V2流量监控] 流量池: ${poolStatus.pool_used_gb.toFixed(2)}GB / ${poolTotalGB}GB (${poolStatus.pool_percentage}%)`); + + // 检查流量池告警 + checkPoolAlerts(poolStatus); + // 保存汇总数据 const summaryFile = path.join(DATA_DIR, 'v2-traffic-summary.json'); const summary = { @@ -132,8 +181,9 @@ function monitor() { } // ── 启动监控循环 ───────────────────────────── -console.log('📊 铸渊专线V2流量监控启动'); +console.log('📊 铸渊专线V2流量监控启动 · 共享流量池模型'); console.log(` 检查间隔: ${CHECK_INTERVAL / 1000}秒`); +console.log(` 流量池: ${userManager.POOL_QUOTA_BYTES / (1024 ** 3)}GB/月`); console.log(` 数据目录: ${DATA_DIR}`); // 立即执行一次 diff --git a/server/proxy/service/traffic-monitor.js b/server/proxy/service/traffic-monitor.js index 307191d1..ad7d7c44 100644 --- a/server/proxy/service/traffic-monitor.js +++ b/server/proxy/service/traffic-monitor.js @@ -6,6 +6,11 @@ // server/proxy/service/traffic-monitor.js // 📊 铸渊专线 · 流量监控Agent // +// ⚠️ [DEPRECATED · D58] V1节点已停用 +// 铸渊专线2.0已测试通过并正式启用,V1节点不再使用。 +// V2流量监控: traffic-monitor-v2.js (共享流量池2000GB/月) +// 此文件保留作为V1历史参考,不再部署运行。 +// // 定期查询Xray Stats API获取流量数据 // 更新quota-status.json供订阅服务和仪表盘使用 // diff --git a/server/proxy/service/user-manager.js b/server/proxy/service/user-manager.js index 8e41351c..6ab49f96 100644 --- a/server/proxy/service/user-manager.js +++ b/server/proxy/service/user-manager.js @@ -10,7 +10,12 @@ // - 独立UUID (Xray VLESS用户ID) // - 独立订阅Token (订阅URL认证) // - 独立流量统计 (Xray Stats按email追踪) -// - 独立配额 (每人500GB/月) +// - 共享流量池 (2000GB/月·全用户共享·每月1号重置) +// +// 流量池模型: +// - 所有用户共享2000GB/月流量池 +// - 无论多少用户,总量一致 +// - 每月1号自动重置 // // 用户数据存储: /opt/zhuyuan-brain/proxy/data/users.json // Xray配置自动重建: 增删用户后自动更新config并重启Xray @@ -22,6 +27,7 @@ // node user-manager.js get — 查看用户详情 // node user-manager.js rebuild — 重建Xray配置 // node user-manager.js export — 导出用户数据(无密钥) +// node user-manager.js pool — 查看流量池状态 // ═══════════════════════════════════════════════ 'use strict'; @@ -35,10 +41,15 @@ const { execSync } = require('child_process'); 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 POOL_STATUS_FILE = path.join(DATA_DIR, 'pool-quota-status.json'); const XRAY_CONFIG_OUTPUT = '/usr/local/etc/xray/config.json'; const XRAY_TEMPLATE = path.join(PROXY_DIR, 'config', 'xray-brain-template.json'); const KEYS_FILE = path.join(PROXY_DIR, '.env.keys'); +// ── 共享流量池配额 ────────────────────────── +// 冰朔指令D58: 2000GB/月共享流量池·无论多少用户总量一致·每月1号重置 +const POOL_QUOTA_BYTES = 2000 * 1024 * 1024 * 1024; // 2000GB = 2TB + // ── 工具函数 ──────────────────────────────── /** @@ -126,7 +137,7 @@ function addUser(email, options = {}) { uuid: generateUUID(), token: generateToken(), label: options.label || email.split('@')[0], - quota_bytes: (options.quota_gb || 500) * 1024 * 1024 * 1024, + quota_bytes: POOL_QUOTA_BYTES, // 共享流量池配额(展示用·实际按池总量控制) enabled: true, created_at: now.toISOString(), traffic: { @@ -140,10 +151,11 @@ function addUser(email, options = {}) { db.users.push(user); saveUsers(db); + const poolGB = (POOL_QUOTA_BYTES / (1024 ** 3)).toFixed(0); console.log(`✅ 用户已添加: ${email}`); console.log(` UUID: ${user.uuid}`); console.log(` Token: ${user.token}`); - console.log(` 配额: ${options.quota_gb || 500}GB/月`); + console.log(` 流量池: ${poolGB}GB/月 (全用户共享)`); return user; } @@ -179,16 +191,16 @@ function listUsers() { return []; } - console.log(`📋 用户列表 (${db.users.length}人):`); + const pool = getPoolStatus(); + console.log(`📋 用户列表 (${db.users.length}人) · 流量池: ${pool.pool_used_gb.toFixed(2)}GB / ${pool.pool_total_gb}GB (${pool.pool_percentage.toFixed(1)}%)`); console.log('─'.repeat(80)); for (const user of db.users) { const usedGB = ((user.traffic.upload_bytes + user.traffic.download_bytes) / (1024 ** 3)).toFixed(2); - const quotaGB = (user.quota_bytes / (1024 ** 3)).toFixed(0); const status = user.enabled ? '✅' : '⛔'; console.log(` ${status} ${user.email}`); - console.log(` UUID: ${user.uuid.substring(0, 8)}... 流量: ${usedGB}GB/${quotaGB}GB 标签: ${user.label}`); + console.log(` UUID: ${user.uuid.substring(0, 8)}... 个人用量: ${usedGB}GB 标签: ${user.label}`); } console.log('─'.repeat(80)); @@ -208,15 +220,15 @@ function getUser(email) { } const usedGB = ((user.traffic.upload_bytes + user.traffic.download_bytes) / (1024 ** 3)).toFixed(2); - const quotaGB = (user.quota_bytes / (1024 ** 3)).toFixed(0); + const pool = getPoolStatus(); console.log(`👤 用户详情: ${email}`); console.log(` UUID: ${user.uuid}`); console.log(` Token: ${user.token}`); console.log(` 标签: ${user.label}`); console.log(` 状态: ${user.enabled ? '启用' : '禁用'}`); - console.log(` 配额: ${quotaGB}GB/月`); - console.log(` 已用: ${usedGB}GB`); + console.log(` 个人用量: ${usedGB}GB`); + console.log(` 流量池: ${pool.pool_used_gb.toFixed(2)}GB / ${pool.pool_total_gb}GB (${pool.pool_percentage.toFixed(1)}%)`); console.log(` 周期: ${user.traffic.period}`); console.log(` 创建于: ${user.created_at}`); @@ -268,6 +280,60 @@ function updateTraffic(email, upload, download) { saveUsers(db); } +/** + * 获取共享流量池状态 + * 汇总所有启用用户的流量 · 对比2000GB池配额 + * @returns {object} 流量池状态 + */ +function getPoolStatus() { + const db = loadUsers(); + const now = new Date(); + const currentPeriod = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}`; + + let totalUpload = 0; + let totalDownload = 0; + + for (const user of db.users) { + if (!user.enabled) continue; + // 只统计当前周期的流量 + if (user.traffic.period === currentPeriod) { + totalUpload += user.traffic.upload_bytes; + totalDownload += user.traffic.download_bytes; + } + } + + const totalUsed = totalUpload + totalDownload; + const poolTotalGB = POOL_QUOTA_BYTES / (1024 ** 3); + const poolUsedGB = totalUsed / (1024 ** 3); + const percentage = (totalUsed / POOL_QUOTA_BYTES) * 100; + + return { + pool_quota_bytes: POOL_QUOTA_BYTES, + pool_total_gb: poolTotalGB, + pool_upload_bytes: totalUpload, + pool_download_bytes: totalDownload, + pool_used_bytes: totalUsed, + pool_used_gb: poolUsedGB, + pool_remaining_bytes: Math.max(0, POOL_QUOTA_BYTES - totalUsed), + pool_remaining_gb: parseFloat(Math.max(0, poolTotalGB - poolUsedGB).toFixed(2)), + pool_percentage: parseFloat(percentage.toFixed(1)), + users_count: db.users.filter(u => u.enabled).length, + period: currentPeriod, + reset_day: 1, + updated_at: now.toISOString() + }; +} + +/** + * 保存流量池状态到文件 (供监控和订阅服务使用) + */ +function savePoolStatus() { + const status = getPoolStatus(); + fs.mkdirSync(DATA_DIR, { recursive: true }); + fs.writeFileSync(POOL_STATUS_FILE, JSON.stringify(status, null, 2)); + return status; +} + // ── Xray配置重建 ──────────────────────────── /** @@ -444,6 +510,8 @@ module.exports = { findUserByToken, getEnabledUsers, updateTraffic, + getPoolStatus, + savePoolStatus, rebuildXrayConfig, restartXray, addUserFull, @@ -452,7 +520,9 @@ module.exports = { generateToken, USERS_FILE, DATA_DIR, - KEYS_FILE + KEYS_FILE, + POOL_QUOTA_BYTES, + POOL_STATUS_FILE }; // ── CLI入口 ──────────────────────────────── @@ -464,8 +534,8 @@ if (require.main === module) { try { switch (command) { case 'add': - if (!param) { console.error('用法: node user-manager.js add [quota_gb]'); process.exit(1); } - addUserFull(param, { quota_gb: parseInt(args[2] || '500', 10) }); + if (!param) { console.error('用法: node user-manager.js add '); process.exit(1); } + addUserFull(param); break; case 'remove': @@ -487,32 +557,56 @@ if (require.main === module) { restartXray(); break; - case 'export': + case 'pool': { + const pool = getPoolStatus(); + console.log('🏊 铸渊专线V2 · 共享流量池状态'); + console.log('─'.repeat(50)); + console.log(` 总配额: ${pool.pool_total_gb}GB/月`); + console.log(` 已使用: ${pool.pool_used_gb.toFixed(2)}GB`); + console.log(` 剩余: ${pool.pool_remaining_gb}GB`); + console.log(` 使用率: ${pool.pool_percentage}%`); + console.log(` 用户数: ${pool.users_count}人`); + console.log(` 周期: ${pool.period}`); + console.log(` 重置日: 每月${pool.reset_day}号`); + console.log('─'.repeat(50)); + break; + } + + case 'export': { const db = loadUsers(); + const pool = getPoolStatus(); const safe = { - total: db.users.length, + pool: { + total_gb: pool.pool_total_gb, + used_gb: parseFloat(pool.pool_used_gb.toFixed(2)), + remaining_gb: pool.pool_remaining_gb, + percentage: pool.pool_percentage, + period: pool.period + }, + total_users: db.users.length, users: db.users.map(u => ({ email: u.email, label: u.label, enabled: u.enabled, - quota_gb: (u.quota_bytes / (1024 ** 3)).toFixed(0), used_gb: ((u.traffic.upload_bytes + u.traffic.download_bytes) / (1024 ** 3)).toFixed(2), created_at: u.created_at })) }; console.log(JSON.stringify(safe, null, 2)); break; + } default: - console.log('铸渊专线V2 · 多用户管理器'); + console.log('铸渊专线V2 · 多用户管理器 · 共享流量池2000GB/月'); console.log(''); console.log('用法:'); - console.log(' node user-manager.js add [quota_gb] — 添加用户 (默认500GB/月)'); - console.log(' node user-manager.js remove — 移除用户'); - console.log(' node user-manager.js list — 列出所有用户'); - console.log(' node user-manager.js get — 查看用户详情'); - console.log(' node user-manager.js rebuild — 重建Xray配置'); - console.log(' node user-manager.js export — 导出用户数据(无密钥)'); + console.log(' node user-manager.js add — 添加用户'); + console.log(' node user-manager.js remove — 移除用户'); + console.log(' node user-manager.js list — 列出所有用户'); + console.log(' node user-manager.js get — 查看用户详情'); + console.log(' node user-manager.js pool — 查看流量池状态'); + console.log(' node user-manager.js rebuild — 重建Xray配置'); + console.log(' node user-manager.js export — 导出用户数据(无密钥)'); break; } } catch (err) {