""" FastAPI application assembly for the AlphaX Agent | Crypto web surface. """ from contextlib import asynccontextmanager from pathlib import Path from fastapi import FastAPI, Request from fastapi.responses import JSONResponse 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_content import build_router as build_content_router from app.web.routes_market import router as market_router from app.web.routes_onchain import router as onchain_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_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 | Crypto", lifespan=lifespan) templates = Jinja2Templates(directory=str(REPO_ROOT / "static")) app.include_router(auth_router) app.include_router(recommendations_router) app.include_router(strategy_router) app.include_router(onchain_router) app.include_router(paper_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}, )