tt-help-cli-ycl 1.4.39 → 1.4.40
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/src/webserver/server.mjs +60 -0
package/package.json
CHANGED
package/src/webserver/server.mjs
CHANGED
|
@@ -66,6 +66,10 @@ const SCRAPER_CONFIG = {
|
|
|
66
66
|
let scraperInstance = null;
|
|
67
67
|
let scraperClosing = false;
|
|
68
68
|
let scraperInitPromise = null;
|
|
69
|
+
let consecutiveErrors = 0;
|
|
70
|
+
const MAX_CONSECUTIVE_ERRORS = 10;
|
|
71
|
+
let warmTimer = null;
|
|
72
|
+
let lastWarmTime = 0;
|
|
69
73
|
|
|
70
74
|
async function getScraper() {
|
|
71
75
|
if (scraperInstance && !scraperClosing) {
|
|
@@ -82,6 +86,8 @@ async function getScraper() {
|
|
|
82
86
|
const scraper = new TikTokScraper(SCRAPER_CONFIG);
|
|
83
87
|
await scraper.init();
|
|
84
88
|
scraperInstance = scraper;
|
|
89
|
+
consecutiveErrors = 0;
|
|
90
|
+
lastWarmTime = Date.now();
|
|
85
91
|
return scraper;
|
|
86
92
|
})();
|
|
87
93
|
try {
|
|
@@ -91,6 +97,47 @@ async function getScraper() {
|
|
|
91
97
|
}
|
|
92
98
|
}
|
|
93
99
|
|
|
100
|
+
async function restartScraper(reason) {
|
|
101
|
+
console.error(`[webserver] 重启 scraper: ${reason}`);
|
|
102
|
+
if (scraperInstance) {
|
|
103
|
+
try {
|
|
104
|
+
await scraperInstance.close();
|
|
105
|
+
} catch {}
|
|
106
|
+
scraperInstance = null;
|
|
107
|
+
}
|
|
108
|
+
scraperInitPromise = null;
|
|
109
|
+
consecutiveErrors = 0;
|
|
110
|
+
// 下次请求时懒初始化
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
async function warmScraper() {
|
|
114
|
+
if (scraperClosing || !scraperInstance) return;
|
|
115
|
+
const now = Date.now();
|
|
116
|
+
if (now - lastWarmTime < SCRAPER_CONFIG.wafTTL) return;
|
|
117
|
+
try {
|
|
118
|
+
await scraperInstance.warmWaf();
|
|
119
|
+
lastWarmTime = Date.now();
|
|
120
|
+
} catch (e) {
|
|
121
|
+
console.error(`[webserver] warmWaf 失败: ${e.message}`);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function startWarmTimer() {
|
|
126
|
+
if (warmTimer) return;
|
|
127
|
+
// 每 wafTTL/2 检查一次是否需要预热
|
|
128
|
+
warmTimer = setInterval(() => {
|
|
129
|
+
warmScraper().catch(() => {});
|
|
130
|
+
}, SCRAPER_CONFIG.wafTTL / 2);
|
|
131
|
+
warmTimer.unref?.();
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function stopWarmTimer() {
|
|
135
|
+
if (warmTimer) {
|
|
136
|
+
clearInterval(warmTimer);
|
|
137
|
+
warmTimer = null;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
94
141
|
// ---- API 处理器 ----
|
|
95
142
|
|
|
96
143
|
const AVAILABLE_APIS = [
|
|
@@ -149,6 +196,10 @@ function handleHealth(req, res) {
|
|
|
149
196
|
return sendJSON(res, 200, {
|
|
150
197
|
status: "ok",
|
|
151
198
|
scraperReady: scraperInstance !== null && !scraperClosing,
|
|
199
|
+
consecutiveErrors,
|
|
200
|
+
lastWarmSec: lastWarmTime
|
|
201
|
+
? Math.round((Date.now() - lastWarmTime) / 1000)
|
|
202
|
+
: null,
|
|
152
203
|
uptime: process.uptime(),
|
|
153
204
|
});
|
|
154
205
|
}
|
|
@@ -208,6 +259,11 @@ export async function startWebserver(
|
|
|
208
259
|
try {
|
|
209
260
|
return await route.handler(req, res);
|
|
210
261
|
} catch (e) {
|
|
262
|
+
// 自动恢复:连续错误超过阈值时重启 scraper
|
|
263
|
+
consecutiveErrors++;
|
|
264
|
+
if (consecutiveErrors >= MAX_CONSECUTIVE_ERRORS) {
|
|
265
|
+
await restartScraper(`连续 ${consecutiveErrors} 次错误`);
|
|
266
|
+
}
|
|
211
267
|
if (!res.headersSent) {
|
|
212
268
|
return sendJSON(res, 500, { error: e.message });
|
|
213
269
|
}
|
|
@@ -242,6 +298,9 @@ export async function startWebserver(
|
|
|
242
298
|
});
|
|
243
299
|
server.once("error", reject);
|
|
244
300
|
});
|
|
301
|
+
|
|
302
|
+
// 启动定期 WAF 预热
|
|
303
|
+
startWarmTimer();
|
|
245
304
|
}
|
|
246
305
|
|
|
247
306
|
export async function closeServer(server) {
|
|
@@ -255,6 +314,7 @@ export async function closeServer(server) {
|
|
|
255
314
|
}
|
|
256
315
|
|
|
257
316
|
export async function closeScraper() {
|
|
317
|
+
stopWarmTimer();
|
|
258
318
|
if (scraperInstance && !scraperClosing) {
|
|
259
319
|
scraperClosing = true;
|
|
260
320
|
await scraperInstance.close();
|