fix: 修复代理状态检测不正确
This commit is contained in:
116
backend/main.py
116
backend/main.py
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user