feat: Airwallex 发卡管理后台完整实现
- 后端: FastAPI + SQLAlchemy + SQLite, JWT认证, 代理支持的AirwallexClient - 前端: React 18 + Vite + Ant Design 5, 中文界面 - 功能: 卡片管理, 持卡人管理, 交易记录, API令牌, 系统设置, 审计日志 - 第三方API: X-API-Key认证, 权限控制 - Docker部署: docker-compose编排前后端
This commit is contained in:
51
backend/app/proxy_client.py
Normal file
51
backend/app/proxy_client.py
Normal file
@@ -0,0 +1,51 @@
|
||||
"""Airwallex client with proxy support."""
|
||||
import httpx
|
||||
from airwallex.client import AirwallexClient
|
||||
|
||||
|
||||
class ProxiedAirwallexClient(AirwallexClient):
|
||||
"""AirwallexClient that routes requests through an HTTP 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,
|
||||
)
|
||||
|
||||
def authenticate(self) -> None:
|
||||
"""Override authenticate to use proxy for auth requests too."""
|
||||
from datetime import datetime, timezone
|
||||
|
||||
if self._token and self._token_expiry and datetime.now(timezone.utc) < self._token_expiry:
|
||||
return
|
||||
|
||||
# Use a proxied client for authentication
|
||||
auth_kwargs = {"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="{}",
|
||||
)
|
||||
response.raise_for_status()
|
||||
data = response.json()
|
||||
self._token = data.get("token")
|
||||
# Token valid for 30 minutes, refresh a bit early
|
||||
from datetime import timedelta
|
||||
self._token_expiry = datetime.now(timezone.utc) + timedelta(minutes=28)
|
||||
finally:
|
||||
auth_client.close()
|
||||
Reference in New Issue
Block a user