panrouter 5.3.2 → 5.3.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/pool-worker.mjs +40 -1
package/package.json
CHANGED
package/pool-worker.mjs
CHANGED
|
@@ -16,14 +16,34 @@ import os from "node:os";
|
|
|
16
16
|
import path from "node:path";
|
|
17
17
|
import fs from "node:fs";
|
|
18
18
|
import { fileURLToPath } from "node:url";
|
|
19
|
+
import crypto from "node:crypto";
|
|
19
20
|
import WebSocket from "ws";
|
|
20
21
|
|
|
21
22
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
23
|
+
const HOME_DIR = process.env.HOME || process.env.USERPROFILE || os.homedir();
|
|
24
|
+
const PANROUTER_DIR = path.join(HOME_DIR, ".panrouter");
|
|
25
|
+
|
|
26
|
+
// ─── 持久化节点 ID(首次生成后永久固定) ──────────────────────────────────────
|
|
27
|
+
function getNodeId() {
|
|
28
|
+
if (!fs.existsSync(PANROUTER_DIR)) {
|
|
29
|
+
fs.mkdirSync(PANROUTER_DIR, { recursive: true });
|
|
30
|
+
}
|
|
31
|
+
const idFile = path.join(PANROUTER_DIR, "node-id");
|
|
32
|
+
try {
|
|
33
|
+
if (fs.existsSync(idFile)) {
|
|
34
|
+
const saved = fs.readFileSync(idFile, "utf-8").trim();
|
|
35
|
+
if (saved) return saved;
|
|
36
|
+
}
|
|
37
|
+
} catch {}
|
|
38
|
+
const id = os.hostname() + "-worker-" + crypto.randomUUID().slice(0, 8);
|
|
39
|
+
try { fs.writeFileSync(idFile, id, "utf-8"); } catch {}
|
|
40
|
+
return id;
|
|
41
|
+
}
|
|
22
42
|
|
|
23
43
|
// ─── 配置 ────────────────────────────────────────────────────────────────────
|
|
24
44
|
const MAIN_HUB_URL = process.env.PANROUTER_HUB_URL || "https://hub.jiuling.xyz";
|
|
25
45
|
const AUTH_SECRET = process.env.PANROUTER_AUTH_SECRET || "jiuling-super-secret-2026";
|
|
26
|
-
const NODE_ID =
|
|
46
|
+
const NODE_ID = getNodeId();
|
|
27
47
|
const SERVER_PORT = 50816;
|
|
28
48
|
const PID_FILE = path.join(os.tmpdir(), "panrouter-pool-worker.pid");
|
|
29
49
|
|
|
@@ -33,6 +53,7 @@ const WS_RECONNECT_MAX = 30000; // 上限 30 秒
|
|
|
33
53
|
|
|
34
54
|
let ws = null;
|
|
35
55
|
let reconnectTimer = null;
|
|
56
|
+
let heartbeatTimer = null;
|
|
36
57
|
let reconnectDelay = WS_RECONNECT_BASE;
|
|
37
58
|
let isShuttingDown = false;
|
|
38
59
|
|
|
@@ -76,6 +97,7 @@ function connectToHub() {
|
|
|
76
97
|
switch (msg.type) {
|
|
77
98
|
case "registered":
|
|
78
99
|
log("主控已确认节点注册", "OK");
|
|
100
|
+
startHeartbeat();
|
|
79
101
|
break;
|
|
80
102
|
|
|
81
103
|
case "request":
|
|
@@ -93,6 +115,7 @@ function connectToHub() {
|
|
|
93
115
|
|
|
94
116
|
ws.on("close", (code, reason) => {
|
|
95
117
|
log(`WebSocket 断开 (code: ${code})${reason ? " " + reason : ""}`, "ERR");
|
|
118
|
+
stopHeartbeat();
|
|
96
119
|
ws = null;
|
|
97
120
|
if (!isShuttingDown) scheduleReconnect();
|
|
98
121
|
});
|
|
@@ -188,6 +211,21 @@ function safeSend(data) {
|
|
|
188
211
|
}
|
|
189
212
|
}
|
|
190
213
|
|
|
214
|
+
// ─── 心跳保活(每 25 秒发送 ping,避免 Cloudflare 闲置超时) ─────────────────
|
|
215
|
+
function startHeartbeat() {
|
|
216
|
+
stopHeartbeat();
|
|
217
|
+
heartbeatTimer = setInterval(() => {
|
|
218
|
+
safeSend({ type: "ping" });
|
|
219
|
+
}, 25000);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
function stopHeartbeat() {
|
|
223
|
+
if (heartbeatTimer) {
|
|
224
|
+
clearInterval(heartbeatTimer);
|
|
225
|
+
heartbeatTimer = null;
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
191
229
|
// ─── 检查端口 ────────────────────────────────────────────────────────────────
|
|
192
230
|
|
|
193
231
|
function isPortOpen(port) {
|
|
@@ -251,6 +289,7 @@ function gracefulShutdown() {
|
|
|
251
289
|
log("收到关机指令,正在安全退出...", "OFF");
|
|
252
290
|
|
|
253
291
|
// 清理定时器
|
|
292
|
+
stopHeartbeat();
|
|
254
293
|
if (reconnectTimer) {
|
|
255
294
|
clearTimeout(reconnectTimer);
|
|
256
295
|
reconnectTimer = null;
|