判定按状态码白名单而不是只认 200:探测不带鉴权,OpenAI 回 401、Anthropic 回 405 才是正常应答,说明 TLS 握手、路由、目标服务全都正常。清单和白名单照 sub2api 的 代理质量检查抄来。 失败文案不照抄 Bun:本机端口没人监听时它给的是「Unable to connect. Is the computer able to access the url?」,把矛头指向目标网址,而真正的原因在本机。 对话框留在 InboundDetail 而不是提到 RouterPage——它不写配置文件也不改内核状态, 不需要走那条 commit 链路。firstPort 挪进 shared/inbounds.ts,两个页面都要用它了。
This commit is contained in:
+36
-2
@@ -1,11 +1,12 @@
|
||||
/**
|
||||
* 主机信息与公网 IP 检测。
|
||||
* 主机信息,以及经由某一路入站发出的探测。
|
||||
*
|
||||
* Patchbay 服务端和 mihomo 跑在同一台机器上,所以这两件事由它代答:
|
||||
* Patchbay 服务端和 mihomo 跑在同一台机器上,所以这几件事由它代答:
|
||||
* - 主机指标:mihomo 的管理接口只报自己的内存,CPU、主机内存、网关这些
|
||||
* 从 os 模块和 /proc 里读(解析在 shared/host-info.ts,那边有测试)
|
||||
* - 公网 IP 检测:浏览器没法「经由某个入站端口」发请求,服务端可以——把本机
|
||||
* 对应端口设成代理去访问 ip.sb,返回的就是那一路在外面的地址
|
||||
* - 可访问性测试:同一条路子,换成访问几个常用 AI 服务,判定在 shared/reachability.ts
|
||||
*/
|
||||
import os from 'node:os'
|
||||
import {
|
||||
@@ -16,6 +17,8 @@ import {
|
||||
parseNameservers,
|
||||
} from '../shared/host-info.ts'
|
||||
import type { CpuTimes, ExitProbe, HostInfo } from '../shared/host-info.ts'
|
||||
import { describeProbeFailure, judge } from '../shared/reachability.ts'
|
||||
import type { ReachResult, ReachTarget } from '../shared/reachability.ts'
|
||||
|
||||
function cpuTimesNow(): CpuTimes {
|
||||
let busy = 0
|
||||
@@ -98,3 +101,34 @@ export async function probeExit(port: number): Promise<ExitProbe> {
|
||||
return { ok: false, message: error instanceof Error ? error.message : String(error) }
|
||||
}
|
||||
}
|
||||
|
||||
/** 单个目标的探测超时,毫秒。超时的说法由服务端生成,所以只有这里需要这个数。 */
|
||||
const REACH_TIMEOUT_MS = 10_000
|
||||
|
||||
/**
|
||||
* 经由本机某个入站端口探测一个目标能不能访问。同 probeExit,只对说 HTTP 的入站有效。
|
||||
*
|
||||
* 不带任何鉴权头,判定全交给 shared/reachability.ts 的 judge()。正文只读前 8 KB:
|
||||
* 判定只需要认拦截页开头的标记,而目标本身可能返回很大的 JSON。
|
||||
*/
|
||||
export async function probeReach(port: number, target: ReachTarget): Promise<ReachResult> {
|
||||
const started = Date.now()
|
||||
try {
|
||||
const res = await fetch(target.url, {
|
||||
proxy: `http://127.0.0.1:${String(port)}`,
|
||||
headers: { Accept: 'application/json,text/html,*/*' },
|
||||
signal: AbortSignal.timeout(REACH_TIMEOUT_MS),
|
||||
})
|
||||
const body = (await res.text()).slice(0, 8192)
|
||||
const { verdict, message } = judge(target, res.status, res.headers, body)
|
||||
return { target: target.id, verdict, status: res.status, ms: Date.now() - started, message }
|
||||
} catch (error) {
|
||||
return {
|
||||
target: target.id,
|
||||
verdict: 'unreachable',
|
||||
status: null,
|
||||
ms: Date.now() - started,
|
||||
message: describeProbeFailure(port, REACH_TIMEOUT_MS, error),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user