fix: 改进错误提示、代理测试和落地IP查询

- 前端所有页面显示后端真实错误信息,不再显示通用"失败"
- 新增代理测试功能和落地IP查询
- 修复凭证未配置时返回500改为400+中文提示
- 修复Settings页面字段名与后端一致(proxy_ip)
- 修复favicon 404、bcrypt版本兼容、tsconfig配置
This commit is contained in:
zqq61
2026-03-15 23:39:02 +08:00
parent 4f53889a8e
commit 1b8b2c0bd6
14 changed files with 3299 additions and 70 deletions

View File

@@ -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)

View File

@@ -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