fix: 改进错误提示、代理测试和落地IP查询
- 前端所有页面显示后端真实错误信息,不再显示通用"失败" - 新增代理测试功能和落地IP查询 - 修复凭证未配置时返回500改为400+中文提示 - 修复Settings页面字段名与后端一致(proxy_ip) - 修复favicon 404、bcrypt版本兼容、tsconfig配置
This commit is contained in:
@@ -83,3 +83,12 @@ def test_connection(
|
||||
):
|
||||
"""Test Airwallex API connection with current settings."""
|
||||
return airwallex_service.test_connection(db)
|
||||
|
||||
|
||||
@router.post("/test-proxy")
|
||||
def test_proxy(
|
||||
db: Session = Depends(get_db),
|
||||
user: AdminUser = Depends(get_current_user),
|
||||
):
|
||||
"""Test proxy connectivity and query outbound IP."""
|
||||
return airwallex_service.test_proxy(db)
|
||||
|
||||
@@ -5,6 +5,7 @@ from typing import Any, Dict, List, Optional
|
||||
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from fastapi import HTTPException
|
||||
from app.models.db_models import SystemSetting
|
||||
from app.proxy_client import ProxiedAirwallexClient
|
||||
|
||||
@@ -44,7 +45,7 @@ def get_client(db: Session) -> ProxiedAirwallexClient:
|
||||
proxy_url = _build_proxy_url(db)
|
||||
|
||||
if not client_id or not api_key:
|
||||
raise ValueError("Airwallex credentials not configured. Please set client_id and api_key in Settings.")
|
||||
raise HTTPException(status_code=400, detail="Airwallex 凭证未配置,请在系统设置中填写 Client ID 和 API Key")
|
||||
|
||||
config_hash = f"{client_id}:{api_key}:{base_url}:{proxy_url}"
|
||||
if _client_instance and _client_config_hash == config_hash:
|
||||
@@ -249,10 +250,58 @@ def get_issuing_config(db: Session) -> Optional[Dict[str, Any]]:
|
||||
|
||||
|
||||
def test_connection(db: Session) -> Dict[str, Any]:
|
||||
"""Test Airwallex API connection."""
|
||||
"""Test Airwallex API connection with detailed error info."""
|
||||
try:
|
||||
client = get_client(db)
|
||||
client.authenticate()
|
||||
return {"success": True, "message": "Connection successful"}
|
||||
return {"success": True, "message": "连接成功,认证通过"}
|
||||
except HTTPException as e:
|
||||
return {"success": False, "message": e.detail}
|
||||
except Exception as e:
|
||||
return {"success": False, "message": str(e)}
|
||||
error_msg = str(e)
|
||||
# Try to extract more detail from httpx response errors
|
||||
if hasattr(e, "response"):
|
||||
try:
|
||||
detail = e.response.json()
|
||||
error_msg = f"HTTP {e.response.status_code}: {detail.get('message', detail)}"
|
||||
except Exception:
|
||||
error_msg = f"HTTP {e.response.status_code}: {e.response.text[:200]}"
|
||||
return {"success": False, "message": error_msg}
|
||||
|
||||
|
||||
def test_proxy(db: Session) -> Dict[str, Any]:
|
||||
"""Test proxy connectivity and return the outbound IP address."""
|
||||
import httpx
|
||||
|
||||
proxy_url = _build_proxy_url(db)
|
||||
result: Dict[str, Any] = {"proxy_configured": bool(proxy_url), "proxy_url": None}
|
||||
|
||||
if proxy_url:
|
||||
# Mask password in display
|
||||
masked = proxy_url
|
||||
if "@" in proxy_url:
|
||||
prefix, rest = proxy_url.rsplit("@", 1)
|
||||
if ":" in prefix:
|
||||
scheme_user = prefix.rsplit(":", 1)[0]
|
||||
masked = f"{scheme_user}:****@{rest}"
|
||||
result["proxy_url"] = masked
|
||||
|
||||
# Query outbound IP — with proxy if configured, otherwise direct
|
||||
for label, use_proxy in [("proxy", True), ("direct", False)]:
|
||||
if label == "proxy" and not proxy_url:
|
||||
continue
|
||||
try:
|
||||
client_kwargs: Dict[str, Any] = {"timeout": 10}
|
||||
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")
|
||||
ip_data = resp.json()
|
||||
result[f"{label}_ip"] = ip_data.get("ip", "unknown")
|
||||
result[f"{label}_status"] = "ok"
|
||||
except Exception as e:
|
||||
result[f"{label}_ip"] = None
|
||||
result[f"{label}_status"] = f"failed: {str(e)[:150]}"
|
||||
|
||||
result["success"] = result.get("proxy_status", result.get("direct_status")) == "ok"
|
||||
return result
|
||||
|
||||
Reference in New Issue
Block a user