alphax/app/web/web_server.py
2026-06-07 20:58:35 +08:00

97 lines
3.7 KiB
Python

"""
FastAPI application assembly for the AlphaX Agent web surface.
"""
from contextlib import asynccontextmanager
from pathlib import Path
from fastapi import FastAPI, Request
from fastapi.responses import JSONResponse
from fastapi.staticfiles import StaticFiles
from fastapi.templating import Jinja2Templates
from app.db.schema import init_db
from app.db import auth_db
from app.db.system_logs import record_exception
from app.db.analytics import get_all_recommendations, get_cron_run_logs, get_cron_run_summary, get_review_stats, get_stats
from app.db.recommendation_queries import get_active_recommendations, get_active_recommendations_deduped
from app.web.routes_admin import build_router as build_admin_router
from app.web.routes_auth import router as auth_router
from app.web.routes_chat import router as chat_router
from app.web.routes_content import build_router as build_content_router
from app.web.routes_market import router as market_router
from app.web.routes_live_trading import router as live_trading_router
from app.web.routes_paper_trading import router as paper_trading_router
from app.web.routes_pages import build_router as build_pages_router
from app.web.routes_recommendations import router as recommendations_router
from app.web.routes_review_center import router as review_center_router
from app.web.routes_strategy import router as strategy_router
from app.web.shared import current_request
from app.web.shared import require_active_subscription as _require_active_subscription
REPO_ROOT = Path(__file__).resolve().parents[2]
STOCK_REPORT_TEMPLATE = (REPO_ROOT / "templates" / "stock_report_template.html").read_text(encoding="utf-8")
HTML_PAGE = (REPO_ROOT / "static" / "app.html").read_text(encoding="utf-8")
@asynccontextmanager
async def lifespan(app: FastAPI):
init_db()
bootstrap_admin = auth_db.ensure_default_admin()
if bootstrap_admin.get("created"):
print(f"默认管理员已创建: {bootstrap_admin.get('email')}")
yield
app = FastAPI(title="AlphaX Agent", lifespan=lifespan)
templates = Jinja2Templates(directory=str(REPO_ROOT / "static"))
app.mount("/static", StaticFiles(directory=str(REPO_ROOT / "static")), name="static")
app.include_router(auth_router)
app.include_router(chat_router)
app.include_router(recommendations_router)
app.include_router(review_center_router)
app.include_router(strategy_router)
app.include_router(paper_trading_router)
app.include_router(live_trading_router)
app.include_router(market_router)
app.include_router(build_admin_router(templates))
app.include_router(build_content_router(REPO_ROOT))
app.include_router(build_pages_router(templates, REPO_ROOT, STOCK_REPORT_TEMPLATE))
@app.middleware("http")
async def bind_current_request(request: Request, call_next):
token = current_request.set(request)
try:
return await call_next(request)
finally:
current_request.reset(token)
@app.exception_handler(Exception)
async def global_exception_handler(request: Request, exc: Exception):
user = None
try:
user = auth_db.get_user_by_session_token(request.cookies.get("altcoin_session", ""))
except Exception:
user = None
log_id = record_exception(
exc,
source="web",
request_method=request.method,
request_path=request.url.path,
query_string=request.url.query,
user_email=(user or {}).get("email", ""),
user_id=(user or {}).get("id", 0),
status_code=500,
context={
"client": request.client.host if request.client else "",
"user_agent": request.headers.get("user-agent", ""),
},
)
return JSONResponse(
status_code=500,
content={"detail": "系统内部错误", "error_id": log_id},
)