diff --git a/server.js b/server.js index c6cb717..ebcb747 100644 --- a/server.js +++ b/server.js @@ -178,10 +178,10 @@ function httpStatusCheck(line, timeoutMs = 8000) { }, (res) => { res.resume(); const badGateway = [520, 521, 522, 523, 524].includes(res.statusCode); - resolve(!badGateway); + resolve({ ok: !badGateway, detail: `HTTP ${res.statusCode}` }); }); - req.on('timeout', () => { req.destroy(); resolve(false); }); - req.on('error', () => resolve(false)); + req.on('timeout', () => { req.destroy(); resolve({ ok: false, detail: 'HTTP请求超时' }); }); + req.on('error', (err) => resolve({ ok: false, detail: `HTTP错误:${err.code || err.message}` })); req.end(); }); } @@ -189,11 +189,11 @@ function wsHandshakeCheck(line, timeoutMs = 8000) { return new Promise((resolve) => { let done = false; let ws; - const finish = (ok) => { + const finish = (ok, detail) => { if (done) return; done = true; try { ws && ws.terminate(); } catch (e) {} - resolve(ok); + resolve({ ok, detail }); }; try { const port = Number(line.port) || 443; @@ -203,25 +203,31 @@ function wsHandshakeCheck(line, timeoutMs = 8000) { headers: { Host: routeName }, servername: line.sni || routeName, // TLS SNI }); - ws.on('open', () => finish(true)); - ws.on('unexpected-response', () => finish(false)); - ws.on('error', () => finish(false)); + ws.on('open', () => finish(true, 'WS握手成功')); + ws.on('unexpected-response', (req2, res2) => finish(false, `WS握手被拒绝:HTTP ${res2 && res2.statusCode}`)); + ws.on('error', (err) => finish(false, `WS错误:${err.code || err.message}`)); } catch (e) { - finish(false); + finish(false, `WS异常:${e.message}`); } - setTimeout(() => finish(false), timeoutMs + 500); + setTimeout(() => finish(false, 'WS握手超时'), timeoutMs + 500); }); } +// 返回 { ok, detail } —— detail只在vless-cdn时有意义(http/ws两段各自的失败原因), +// 方便"立即检测"排查究竟是端口/Host/SNI哪一环出的问题,而不是只有一个"异常"看不出所以然。 async function checkLineConnectivity(line) { if (line.protocol === 'vless-cdn') { - const [httpOk, wsOk] = await Promise.all([ + const [httpResult, wsResult] = await Promise.all([ httpStatusCheck(line), wsHandshakeCheck(line), ]); - return httpOk && wsOk; + return { + ok: httpResult.ok && wsResult.ok, + detail: `HTTP探测: ${httpResult.detail} | WS握手: ${wsResult.detail}`, + }; } - return tcpCheck(line.addr, line.port); + const tcpOk = await tcpCheck(line.addr, line.port); + return { ok: tcpOk, detail: tcpOk ? 'TCP连通' : 'TCP连接失败/超时' }; } // 后台定时巡检:每 JMS_CHECK_INTERVAL_MS 跑一轮,只在状态发生变化(正常↔异常)时才发Telegram, @@ -237,12 +243,12 @@ async function runJmsMonitorCycle() { for (const line of lines) { try { - const ok = await checkLineConnectivity(line); + const { ok, detail } = await checkLineConnectivity(line); const newStatus = ok ? 'normal' : 'abnormal'; if (line.monitorStatus && line.monitorStatus !== newStatus) { const name = line.name || line.addr; const msg = newStatus === 'abnormal' - ? `⚠️ ${name} 检测异常,无法连接\n协议:${line.protocol}\n地址:${line.addr}:${line.port}` + ? `⚠️ ${name} 检测异常,无法连接\n协议:${line.protocol}\n地址:${line.addr}:${line.port}\n详情:${detail}` : `✅ ${name} 已恢复正常\n协议:${line.protocol}\n地址:${line.addr}:${line.port}`; sendTelegramMessage(msg).catch(() => {}); // Telegram没配置/发送失败都不影响检测本身 } @@ -568,16 +574,16 @@ app.post('/api/jms/check-line', requireAuth, jmsCheckLimiter, async (req, res) = const line = req.body && req.body.line; if (!line || !line.addr) return res.status(400).json({ error: '线路信息不完整' }); try { - const ok = await checkLineConnectivity(line); + const { ok, detail } = await checkLineConnectivity(line); const newStatus = ok ? 'normal' : 'abnormal'; if (line.monitorStatus && line.monitorStatus !== newStatus) { const name = line.name || line.addr; const msg = newStatus === 'abnormal' - ? `⚠️ ${name} 检测异常,无法连接\n协议:${line.protocol}\n地址:${line.addr}:${line.port}` + ? `⚠️ ${name} 检测异常,无法连接\n协议:${line.protocol}\n地址:${line.addr}:${line.port}\n详情:${detail}` : `✅ ${name} 已恢复正常\n协议:${line.protocol}\n地址:${line.addr}:${line.port}`; sendTelegramMessage(msg).catch(() => {}); } - res.json({ ok: true, status: newStatus, checkedAt: Date.now() }); + res.json({ ok: true, status: newStatus, detail, checkedAt: Date.now() }); } catch (e) { res.status(500).json({ error: e.message }); }