from contextlib import asynccontextmanager from pathlib import Path from fastapi import FastAPI, Request from fastapi.responses import RedirectResponse from jinja2 import Environment, FileSystemLoader from starlette.middleware.base import BaseHTTPMiddleware from app.database import engine, Base, SessionLocal from app.models import User from app.seed import seed from app.routers import contracts, admin, auth, positions TEMPLATES_DIR = Path(__file__).parent / "templates" def setup_jinja(app: FastAPI): env = Environment(loader=FileSystemLoader(str(TEMPLATES_DIR))) app.state.templates = env class AuthMiddleware(BaseHTTPMiddleware): async def dispatch(self, request: Request, call_next): # Public paths if request.url.path in ("/auth/login", "/auth/logout") or request.url.path.startswith("/auth/"): return await call_next(request) # Check session user_id = request.cookies.get("ft_session") if user_id: db = SessionLocal() try: user = db.query(User).filter(User.id == int(user_id)).first() if user: request.state.user = user return await call_next(request) except (ValueError, TypeError): pass finally: db.close() # Redirect to login return RedirectResponse(f"/auth/login?next={request.url.path}", status_code=303) @asynccontextmanager async def lifespan(app: FastAPI): setup_jinja(app) Base.metadata.create_all(bind=engine) seed() yield app = FastAPI(title="期货量化系统", lifespan=lifespan) app.add_middleware(AuthMiddleware) app.include_router(auth.router) app.include_router(contracts.router) app.include_router(admin.router) app.include_router(positions.router) @app.get("/") def root(): return RedirectResponse("/contracts")