- 代理支持SOCKS5和HTTP两种类型切换 - 落地IP查询显示国家、城市、ISP信息 - 设置页面不再隐藏已配置的值 - Airwallex API异常统一返回400+详细错误信息
64 lines
2.4 KiB
Python
64 lines
2.4 KiB
Python
"""Airwallex client with proxy support (HTTP and SOCKS5)."""
|
|
import logging
|
|
import httpx
|
|
from datetime import datetime, timezone, timedelta
|
|
from airwallex.client import AirwallexClient
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
class ProxiedAirwallexClient(AirwallexClient):
|
|
"""AirwallexClient that routes requests through an HTTP or SOCKS5 proxy."""
|
|
|
|
def __init__(self, proxy_url: str | None = None, **kwargs):
|
|
self._proxy_url = proxy_url
|
|
super().__init__(**kwargs)
|
|
# Replace the default httpx client with a proxied one
|
|
if proxy_url:
|
|
self._client.close()
|
|
self._client = httpx.Client(
|
|
base_url=self.base_url,
|
|
timeout=self.request_timeout,
|
|
proxy=proxy_url,
|
|
)
|
|
safe_url = proxy_url.split("@")[-1] if "@" in proxy_url else proxy_url
|
|
logger.info("Airwallex client initialized with proxy: %s", safe_url)
|
|
else:
|
|
logger.info("Airwallex client initialized without proxy")
|
|
|
|
def authenticate(self) -> None:
|
|
"""Override authenticate to use proxy for auth requests too."""
|
|
if self._token and self._token_expiry and datetime.now(timezone.utc) < self._token_expiry:
|
|
return
|
|
|
|
logger.info("Authenticating with Airwallex API at %s (proxy: %s)",
|
|
self.auth_url, bool(self._proxy_url))
|
|
|
|
auth_kwargs: dict = {"timeout": self.request_timeout}
|
|
if self._proxy_url:
|
|
auth_kwargs["proxy"] = self._proxy_url
|
|
|
|
auth_client = httpx.Client(**auth_kwargs)
|
|
try:
|
|
response = auth_client.post(
|
|
self.auth_url,
|
|
headers={
|
|
"Content-Type": "application/json",
|
|
"x-client-id": self.client_id,
|
|
"x-api-key": self.api_key,
|
|
},
|
|
content="{}",
|
|
)
|
|
|
|
if response.status_code != 200:
|
|
body = response.text[:300]
|
|
logger.error("Auth failed: HTTP %d, body: %s", response.status_code, body)
|
|
response.raise_for_status()
|
|
|
|
data = response.json()
|
|
self._token = data.get("token")
|
|
self._token_expiry = datetime.now(timezone.utc) + timedelta(minutes=28)
|
|
logger.info("Authentication successful, token expires in 28 minutes")
|
|
finally:
|
|
auth_client.close()
|