D57: Address code review feedback - configurable timeouts, security fixes, dynamic specs
Agent-Logs-Url: https://github.com/qinfendebingshuo/guanghulab/sessions/24c233b1-db72-4d9c-b668-22679479825b Co-authored-by: qinfendebingshuo <207279273+qinfendebingshuo@users.noreply.github.com>
This commit is contained in:
parent
4fc20144c3
commit
bd58cf1b7b
|
|
@ -171,13 +171,13 @@ EOF
|
|||
chmod 600 "$KEYS_FILE"
|
||||
|
||||
echo ""
|
||||
echo " ══════════ V2密钥 (请添加到GitHub Secrets) ══════════"
|
||||
echo " ZY_BRAIN_PROXY_REALITY_PUBLIC_KEY=$PUBLIC_KEY"
|
||||
echo " ZY_BRAIN_PROXY_REALITY_SHORT_ID=$SHORT_ID"
|
||||
echo " ═══════════════════════════════════════════════════════"
|
||||
echo ""
|
||||
echo " ⚠️ 密钥已保存到: $KEYS_FILE"
|
||||
echo " ⚠️ Private Key不需要添加到GitHub Secrets (已保存在服务器)"
|
||||
echo " ══════════ V2密钥已生成 ══════════"
|
||||
echo " ⚠️ 密钥已保存到: $KEYS_FILE (权限600·仅root可读)"
|
||||
echo " ⚠️ 如需查看密钥,请SSH到服务器执行: cat $KEYS_FILE"
|
||||
echo " ⚠️ 部署完成后,将公钥和ShortID添加到GitHub Secrets:"
|
||||
echo " ZY_BRAIN_PROXY_REALITY_PUBLIC_KEY"
|
||||
echo " ZY_BRAIN_PROXY_REALITY_SHORT_ID"
|
||||
echo " ═══════════════════════════════════"
|
||||
}
|
||||
|
||||
# ── 部署V2服务代码 ────────────────────────────
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ const PORT = process.env.ZY_PROXY_V2_PORT || 3803;
|
|||
const PROXY_DIR = process.env.ZY_BRAIN_PROXY_DIR || '/opt/zhuyuan-brain/proxy';
|
||||
const DATA_DIR = path.join(PROXY_DIR, 'data');
|
||||
const KEYS_FILE = path.join(PROXY_DIR, '.env.keys');
|
||||
const LIVE_NODES_FRESHNESS_MS = parseInt(process.env.ZY_CLOUD_HB_EXPIRY_MS || '600000', 10);
|
||||
|
||||
// 引入用户管理器
|
||||
const userManager = require('./user-manager');
|
||||
|
|
@ -80,9 +81,9 @@ function buildVpnNodes() {
|
|||
const liveNodesFile = path.join(DATA_DIR, 'nodes-live.json');
|
||||
try {
|
||||
const liveData = JSON.parse(fs.readFileSync(liveNodesFile, 'utf8'));
|
||||
// 检查数据是否新鲜(5分钟内)
|
||||
// 检查数据是否新鲜(使用与ZY-CLOUD相同的心跳过期阈值)
|
||||
const age = Date.now() - new Date(liveData.updated_at).getTime();
|
||||
if (age < 10 * 60 * 1000 && liveData.nodes && liveData.nodes.length > 0) {
|
||||
if (age < LIVE_NODES_FRESHNESS_MS * 2 && liveData.nodes && liveData.nodes.length > 0) {
|
||||
return liveData.nodes;
|
||||
}
|
||||
} catch { /* ZY-CLOUD未运行,回退到静态配置 */ }
|
||||
|
|
|
|||
|
|
@ -120,6 +120,7 @@ function addUser(email, options = {}) {
|
|||
throw new Error(`用户已存在: ${email} (UUID: ${existing.uuid.substring(0, 8)}...)`);
|
||||
}
|
||||
|
||||
const now = new Date();
|
||||
const user = {
|
||||
email,
|
||||
uuid: generateUUID(),
|
||||
|
|
@ -127,11 +128,11 @@ function addUser(email, options = {}) {
|
|||
label: options.label || email.split('@')[0],
|
||||
quota_bytes: (options.quota_gb || 500) * 1024 * 1024 * 1024,
|
||||
enabled: true,
|
||||
created_at: new Date().toISOString(),
|
||||
created_at: now.toISOString(),
|
||||
traffic: {
|
||||
upload_bytes: 0,
|
||||
download_bytes: 0,
|
||||
period: `${new Date().getFullYear()}-${String(new Date().getMonth() + 1).padStart(2, '0')}`,
|
||||
period: `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}`,
|
||||
alerts_sent: { p80: false, p90: false, p100: false }
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -66,19 +66,16 @@ function parseConfig() {
|
|||
};
|
||||
}
|
||||
|
||||
// ── 自动检测本机公网IP ──────────────────────
|
||||
// ── 自动检测本机IP ──────────────────────────
|
||||
function getPublicIp() {
|
||||
try {
|
||||
return execSync("hostname -I | awk '{print $1}'", { encoding: 'utf8', timeout: 3000 }).trim();
|
||||
} catch {
|
||||
const nets = os.networkInterfaces();
|
||||
for (const ifaces of Object.values(nets)) {
|
||||
for (const iface of ifaces) {
|
||||
if (!iface.internal && iface.family === 'IPv4') return iface.address;
|
||||
}
|
||||
// 优先使用Node.js原生API(安全)
|
||||
const nets = os.networkInterfaces();
|
||||
for (const ifaces of Object.values(nets)) {
|
||||
for (const iface of ifaces) {
|
||||
if (!iface.internal && iface.family === 'IPv4') return iface.address;
|
||||
}
|
||||
return '0.0.0.0';
|
||||
}
|
||||
return '0.0.0.0';
|
||||
}
|
||||
|
||||
// ── 自动检测本机配置 ────────────────────────
|
||||
|
|
|
|||
|
|
@ -37,10 +37,12 @@ const REGISTRY_FILE = path.join(DATA_DIR, 'nodes-registry.json');
|
|||
const HEARTBEAT_FILE = path.join(DATA_DIR, 'zy-cloud-vpn-heartbeat.json');
|
||||
const KEYS_FILE = path.join(PROXY_DIR, '.env.keys');
|
||||
|
||||
// ── 时间间隔 ────────────────────────────────
|
||||
const HEARTBEAT_INTERVAL = 30 * 1000; // 30秒心跳
|
||||
const DIAGNOSE_INTERVAL = 5 * 60 * 1000; // 5分钟诊断
|
||||
const LEARN_INTERVAL = 30 * 60 * 1000; // 30分钟学习周期
|
||||
// ── 时间间隔(可通过环境变量调整)────────────
|
||||
const HEARTBEAT_INTERVAL = parseInt(process.env.ZY_CLOUD_HEARTBEAT_INTERVAL || '30000', 10);
|
||||
const DIAGNOSE_INTERVAL = parseInt(process.env.ZY_CLOUD_DIAGNOSE_INTERVAL || '300000', 10);
|
||||
const LEARN_INTERVAL = parseInt(process.env.ZY_CLOUD_LEARN_INTERVAL || '1800000', 10);
|
||||
const HEARTBEAT_EXPIRY_MS = parseInt(process.env.ZY_CLOUD_HB_EXPIRY_MS || '600000', 10); // 10分钟
|
||||
const NODE_UNREGISTER_MS = parseInt(process.env.ZY_CLOUD_UNREGISTER_MS || '86400000', 10); // 24小时
|
||||
|
||||
// ═══════════════════════════════════════════════
|
||||
// LivingModule 基类
|
||||
|
|
@ -273,7 +275,8 @@ class ZyCloudVpn extends LivingModule {
|
|||
const brainPbk = this._readEnvOrKey('ZY_PROXY_REALITY_PUBLIC_KEY');
|
||||
const brainSid = this._readEnvOrKey('ZY_PROXY_REALITY_SHORT_ID');
|
||||
if (brainHost && brainPbk) {
|
||||
nodes.push(this._makeNode('zy-brain-sg1', '🧠 铸渊专线V2-SG1(大脑)', brainHost, 443, brainPbk, brainSid, 'sg-zone1', 'ZY-SVR-005', 'local', '4核8G'));
|
||||
const localSpecs = `${require('os').cpus().length}核${Math.round(require('os').totalmem() / (1024 ** 3))}G`;
|
||||
nodes.push(this._makeNode('zy-brain-sg1', '🧠 铸渊专线V2-SG1(大脑)', brainHost, 443, brainPbk, brainSid, 'sg-zone1', 'ZY-SVR-005', 'local', localSpecs));
|
||||
seenIds.add('zy-brain-sg1');
|
||||
}
|
||||
|
||||
|
|
@ -299,14 +302,14 @@ class ZyCloudVpn extends LivingModule {
|
|||
for (const regNode of Object.values(this._registry.nodes)) {
|
||||
if (seenIds.has(regNode.id)) continue; // 避免重复
|
||||
|
||||
// 检查心跳是否过期(超过10分钟无心跳 → 自动注销)
|
||||
// 检查心跳是否过期
|
||||
const lastHb = new Date(regNode.last_heartbeat).getTime();
|
||||
const age = Date.now() - lastHb;
|
||||
if (age > 10 * 60 * 1000) {
|
||||
if (age > HEARTBEAT_EXPIRY_MS) {
|
||||
console.log(`[ZY-CLOUD VPN] ⏰ 节点 ${regNode.name} 心跳过期(${Math.floor(age/60000)}分钟),标记为离线`);
|
||||
// 不删除注册,只是标记离线(可能临时断网)
|
||||
// 超过24小时无心跳才自动清理
|
||||
if (age > 24 * 60 * 60 * 1000) {
|
||||
if (age > NODE_UNREGISTER_MS) {
|
||||
console.log(`[ZY-CLOUD VPN] 🗑️ 节点 ${regNode.name} 超24小时无心跳,自动注销`);
|
||||
delete this._registry.nodes[regNode.id];
|
||||
this._saveRegistry();
|
||||
|
|
|
|||
Loading…
Reference in New Issue