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:
105
backend/app/main.py
Normal file
105
backend/app/main.py
Normal file
@@ -0,0 +1,105 @@
|
||||
"""FastAPI application entry point."""
|
||||
import logging
|
||||
from contextlib import asynccontextmanager
|
||||
|
||||
from fastapi import FastAPI
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
|
||||
from .auth import get_password_hash
|
||||
from .config import settings
|
||||
from .database import SessionLocal, create_tables
|
||||
from .models.db_models import SystemSetting
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
|
||||
|
||||
def _init_admin_and_defaults() -> None:
|
||||
"""Initialize admin password hash and default settings."""
|
||||
db = SessionLocal()
|
||||
try:
|
||||
# Store admin password hash
|
||||
existing = (
|
||||
db.query(SystemSetting)
|
||||
.filter(SystemSetting.key == "admin_password_hash")
|
||||
.first()
|
||||
)
|
||||
if not existing:
|
||||
hashed = get_password_hash(settings.ADMIN_PASSWORD)
|
||||
db.add(SystemSetting(key="admin_password_hash", value=hashed, encrypted=True))
|
||||
logger.info("Admin user initialized.")
|
||||
|
||||
# Store Airwallex credentials from env if provided
|
||||
for key, env_val in [
|
||||
("airwallex_client_id", settings.AIRWALLEX_CLIENT_ID),
|
||||
("airwallex_api_key", settings.AIRWALLEX_API_KEY),
|
||||
("airwallex_base_url", settings.AIRWALLEX_BASE_URL),
|
||||
]:
|
||||
if env_val:
|
||||
s = db.query(SystemSetting).filter(SystemSetting.key == key).first()
|
||||
if not s:
|
||||
db.add(SystemSetting(key=key, value=env_val, encrypted=(key == "airwallex_api_key")))
|
||||
|
||||
# Default daily card limit
|
||||
if not db.query(SystemSetting).filter(SystemSetting.key == "daily_card_limit").first():
|
||||
db.add(SystemSetting(key="daily_card_limit", value="100"))
|
||||
|
||||
db.commit()
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
|
||||
@asynccontextmanager
|
||||
async def lifespan(app: FastAPI):
|
||||
"""Startup and shutdown logic."""
|
||||
create_tables()
|
||||
_init_admin_and_defaults()
|
||||
logger.info("Application started.")
|
||||
yield
|
||||
logger.info("Application shutting down.")
|
||||
|
||||
|
||||
app = FastAPI(
|
||||
title="Airwallex Card Management",
|
||||
version="1.0.0",
|
||||
lifespan=lifespan,
|
||||
)
|
||||
|
||||
# CORS - allow all origins for development
|
||||
app.add_middleware(
|
||||
CORSMiddleware,
|
||||
allow_origins=["*"],
|
||||
allow_credentials=True,
|
||||
allow_methods=["*"],
|
||||
allow_headers=["*"],
|
||||
)
|
||||
|
||||
|
||||
# --- Mount routers (each router defines its own prefix) ---
|
||||
from .routers import auth as auth_router
|
||||
from .routers import cards as cards_router
|
||||
from .routers import cardholders as cardholders_router
|
||||
from .routers import dashboard as dashboard_router
|
||||
from .routers import settings as settings_router
|
||||
from .routers import logs as logs_router
|
||||
from .routers import tokens as tokens_router
|
||||
from .routers import transactions as transactions_router
|
||||
from .routers import external_api as external_api_router
|
||||
|
||||
app.include_router(auth_router.router)
|
||||
app.include_router(dashboard_router.router)
|
||||
app.include_router(cards_router.router)
|
||||
app.include_router(cardholders_router.router)
|
||||
app.include_router(transactions_router.router)
|
||||
app.include_router(settings_router.router)
|
||||
app.include_router(tokens_router.router)
|
||||
app.include_router(logs_router.router)
|
||||
app.include_router(external_api_router.router)
|
||||
|
||||
|
||||
# --- Health check ---
|
||||
@app.get("/api/health")
|
||||
async def health_check():
|
||||
"""Simple health check endpoint."""
|
||||
return {"status": "ok"}
|
||||
Reference in New Issue
Block a user