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 });
}