feat: SOCKS5代理支持、落地IP国家查询、设置页优化
- 代理支持SOCKS5和HTTP两种类型切换 - 落地IP查询显示国家、城市、ISP信息 - 设置页面不再隐藏已配置的值 - Airwallex API异常统一返回400+详细错误信息
This commit is contained in:
@@ -23,16 +23,18 @@ def _get_setting(db: Session, key: str, default: str = "") -> str:
|
||||
|
||||
|
||||
def _build_proxy_url(db: Session) -> Optional[str]:
|
||||
"""Build proxy URL from settings."""
|
||||
"""Build proxy URL from settings. Supports http and socks5."""
|
||||
proxy_ip = _get_setting(db, "proxy_ip")
|
||||
proxy_port = _get_setting(db, "proxy_port")
|
||||
if not proxy_ip or not proxy_port:
|
||||
return None
|
||||
proxy_type = _get_setting(db, "proxy_type", "socks5") # default socks5
|
||||
scheme = "socks5" if proxy_type == "socks5" else "http"
|
||||
proxy_user = _get_setting(db, "proxy_username")
|
||||
proxy_pass = _get_setting(db, "proxy_password")
|
||||
if proxy_user and proxy_pass:
|
||||
return f"http://{proxy_user}:{proxy_pass}@{proxy_ip}:{proxy_port}"
|
||||
return f"http://{proxy_ip}:{proxy_port}"
|
||||
return f"{scheme}://{proxy_user}:{proxy_pass}@{proxy_ip}:{proxy_port}"
|
||||
return f"{scheme}://{proxy_ip}:{proxy_port}"
|
||||
|
||||
|
||||
def get_client(db: Session) -> ProxiedAirwallexClient:
|
||||
@@ -71,7 +73,19 @@ def get_client(db: Session) -> ProxiedAirwallexClient:
|
||||
def ensure_authenticated(db: Session) -> ProxiedAirwallexClient:
|
||||
"""Get client and ensure it's authenticated."""
|
||||
client = get_client(db)
|
||||
client.authenticate()
|
||||
try:
|
||||
client.authenticate()
|
||||
except HTTPException:
|
||||
raise
|
||||
except Exception as e:
|
||||
error_msg = str(e)
|
||||
if hasattr(e, "response"):
|
||||
try:
|
||||
detail = e.response.json()
|
||||
error_msg = f"Airwallex API 错误 ({e.response.status_code}): {detail.get('message', detail)}"
|
||||
except Exception:
|
||||
error_msg = f"Airwallex API 错误 ({e.response.status_code}): {e.response.text[:200]}"
|
||||
raise HTTPException(status_code=400, detail=error_msg)
|
||||
return client
|
||||
|
||||
|
||||
@@ -286,7 +300,7 @@ def test_proxy(db: Session) -> Dict[str, Any]:
|
||||
masked = f"{scheme_user}:****@{rest}"
|
||||
result["proxy_url"] = masked
|
||||
|
||||
# Query outbound IP — with proxy if configured, otherwise direct
|
||||
# Query outbound IP + country — with proxy if configured, otherwise direct
|
||||
for label, use_proxy in [("proxy", True), ("direct", False)]:
|
||||
if label == "proxy" and not proxy_url:
|
||||
continue
|
||||
@@ -295,12 +309,18 @@ def test_proxy(db: Session) -> Dict[str, Any]:
|
||||
if use_proxy and proxy_url:
|
||||
client_kwargs["proxy"] = proxy_url
|
||||
with httpx.Client(**client_kwargs) as client:
|
||||
resp = client.get("https://api.ipify.org?format=json")
|
||||
# Use ip-api.com for IP + country info
|
||||
resp = client.get("http://ip-api.com/json/?fields=query,country,countryCode,city,isp")
|
||||
ip_data = resp.json()
|
||||
result[f"{label}_ip"] = ip_data.get("ip", "unknown")
|
||||
result[f"{label}_ip"] = ip_data.get("query", "unknown")
|
||||
result[f"{label}_country"] = ip_data.get("country", "unknown")
|
||||
result[f"{label}_country_code"] = ip_data.get("countryCode", "")
|
||||
result[f"{label}_city"] = ip_data.get("city", "")
|
||||
result[f"{label}_isp"] = ip_data.get("isp", "")
|
||||
result[f"{label}_status"] = "ok"
|
||||
except Exception as e:
|
||||
result[f"{label}_ip"] = None
|
||||
result[f"{label}_country"] = None
|
||||
result[f"{label}_status"] = f"failed: {str(e)[:150]}"
|
||||
|
||||
result["success"] = result.get("proxy_status", result.get("direct_status")) == "ok"
|
||||
|
||||
Reference in New Issue
Block a user