fix: 修复代理状态检测不正确

This commit is contained in:
Daniel
2026-03-24 19:38:44 +08:00
parent 6a68d5b66a
commit eb8d43841c
2 changed files with 71 additions and 54 deletions

View File

@@ -421,7 +421,42 @@ PROXY_CHECK_URL = os.getenv("PROXY_CHECK_URL", "https://httpbin.org/ip")
@app.get("/api/check-proxy")
async def api_check_proxy(proxy: Optional[str] = Query(None, description="可选,指定要检测的代理 URL不传则用环境变量/隧道/KDL")):
"""检测代理是否可用:用解析到的代理请求测试页,返回是否成功及来源"""
"""检测代理是否可用:返回连通状态、出口 IP若可解析与错误原因"""
def _extract_origin_ip(resp: httpx.Response) -> Optional[str]:
"""优先解析 httpbin /ip 的 origin 字段,解析失败返回 None。"""
try:
data = resp.json()
if isinstance(data, dict):
origin = (data.get("origin") or "").strip()
return origin or None
except Exception:
pass
return None
def _format_proxy_result(ok: bool, *, source: str, preview: str, status_code: Optional[int] = None,
origin_ip: Optional[str] = None, error: Optional[str] = None, note: Optional[str] = None) -> dict:
status = "available" if ok else "unavailable"
out = {
"ok": ok,
"status": status,
"source": source,
"proxy_preview": preview,
"check_url": PROXY_CHECK_URL,
}
if status_code is not None:
out["status_code"] = status_code
if origin_ip:
out["origin_ip"] = origin_ip
if note:
out["note"] = note
if error:
out["error"] = error
# 常见 socks 认证失败给出更可执行的提示
if "User was rejected by the SOCKS5 server" in error:
out["reason"] = "proxy_auth_rejected"
out["suggestion"] = "请检查代理用户名/密码、端口、以及代理服务商白名单配置"
return out
proxy_url = (proxy or "").strip()
source = "query"
if not proxy_url:
@@ -439,11 +474,12 @@ async def api_check_proxy(proxy: Optional[str] = Query(None, description="可选
if proxy_url:
logger.info("check-proxy: using env/body, len=%s", len(proxy_url))
if not proxy_url:
return {
"ok": False,
"source": "none",
"error": "未配置代理。请填写代理、或设置 HTTP_PROXY/HTTPS_PROXY、或配置 TUNNEL_PROXY固定隧道、或 KDL 代理 API。",
}
return _format_proxy_result(
False,
source="none",
preview="(empty)",
error="未配置代理。请填写代理、或设置 HTTP_PROXY/HTTPS_PROXY、或配置 TUNNEL_PROXY固定隧道、或 KDL 代理 API。",
)
# 脱敏显示(不暴露密码)
def _preview(u: str) -> str:
if not u or "@" not in u:
@@ -475,66 +511,42 @@ async def api_check_proxy(proxy: Optional[str] = Query(None, description="可选
async with httpx.AsyncClient(trust_env=False, timeout=15.0, transport=transport) as client:
resp = await client.get(PROXY_CHECK_URL)
if resp.status_code == 200:
origin_ip = _extract_origin_ip(resp)
logger.info("check-proxy: ok (socks), status=%s", resp.status_code)
return {
"ok": True,
"source": source,
"proxy_preview": preview,
"check_url": PROXY_CHECK_URL,
"status_code": resp.status_code,
}
return _format_proxy_result(
True, source=source, preview=preview, status_code=resp.status_code, origin_ip=origin_ip
)
logger.warning("check-proxy: fail (socks), status=%s", resp.status_code)
return {
"ok": False,
"source": source,
"proxy_preview": preview,
"error": f"请求测试页返回 {resp.status_code}",
"status_code": resp.status_code,
}
return _format_proxy_result(
False, source=source, preview=preview, status_code=resp.status_code,
error=f"请求测试页返回 {resp.status_code}"
)
except ImportError:
logger.info("check-proxy: socks5 已配置,跳过连通性检测(需 pip install httpx-socks 方可检测)")
return {
"ok": True,
"source": source,
"proxy_preview": preview,
"note": "socks5 代理已配置;连通性检测需安装 pip install httpx-socks",
}
return _format_proxy_result(
True, source=source, preview=preview,
note="socks5 代理已配置;连通性检测需安装 pip install httpx-socks"
)
except Exception as e:
logger.warning("check-proxy: socks exception %s", e)
return {
"ok": False,
"source": source,
"proxy_preview": preview,
"error": str(e),
}
return _format_proxy_result(False, source=source, preview=preview, error=str(e))
try:
async with httpx.AsyncClient(trust_env=False, timeout=15.0, proxy=proxy_url) as client:
resp = await client.get(PROXY_CHECK_URL)
if resp.status_code == 200:
origin_ip = _extract_origin_ip(resp)
logger.info("check-proxy: ok, status=%s", resp.status_code)
return {
"ok": True,
"source": source,
"proxy_preview": preview,
"check_url": PROXY_CHECK_URL,
"status_code": resp.status_code,
}
return _format_proxy_result(
True, source=source, preview=preview, status_code=resp.status_code, origin_ip=origin_ip
)
logger.warning("check-proxy: fail, status=%s", resp.status_code)
return {
"ok": False,
"source": source,
"proxy_preview": preview,
"error": f"请求测试页返回 {resp.status_code}",
"status_code": resp.status_code,
}
return _format_proxy_result(
False, source=source, preview=preview, status_code=resp.status_code,
error=f"请求测试页返回 {resp.status_code}"
)
except Exception as e:
logger.warning("check-proxy: exception %s", e)
return {
"ok": False,
"source": source,
"proxy_preview": preview,
"error": str(e),
}
return _format_proxy_result(False, source=source, preview=preview, error=str(e))
def _proxy_from_env() -> str:

View File

@@ -1050,9 +1050,14 @@
try {
var data = await callApi(url);
if (data && data.ok) {
log('代理正常。来源: ' + (data.source || '') + (data.proxy_preview ? '代理: ' + data.proxy_preview : ''), 'ok');
var msg = '代理可用。状态: ' + (data.status || 'available') + '来源: ' + (data.source || '');
if (data.origin_ip) msg += '出口IP: ' + data.origin_ip;
if (data.proxy_preview) msg += ',代理: ' + data.proxy_preview;
log(msg, 'ok');
} else {
log('代理不可用: ' + (data && data.error ? data.error : JSON.stringify(data)), 'error');
var err = data && data.error ? data.error : JSON.stringify(data);
var detail = data && data.reason ? (',原因: ' + data.reason) : '';
log('代理不可用: ' + err + detail, 'error');
}
} catch (e) {
log('检测代理失败: ' + (e.message || e), 'error');