from __future__ import annotations import logging from contextlib import asynccontextmanager from pathlib import Path from fastapi import FastAPI from fastapi.staticfiles import StaticFiles from fastapi.templating import Jinja2Templates from starlette.middleware.sessions import SessionMiddleware from app.api.routes import build_router from app.bot.runtime import BotRuntime from app.config import get_settings from app.container import build_container from app.db import ensure_db from app.security import hash_password logging.basicConfig( level=logging.INFO, format="%(asctime)s %(levelname)s [%(name)s] %(message)s", ) LOGGER = logging.getLogger(__name__) def create_app(settings=None) -> FastAPI: settings = settings or get_settings() templates = Jinja2Templates(directory=str(Path(__file__).parent / "templates")) @asynccontextmanager async def lifespan(app: FastAPI): ensure_db(settings) container = await build_container(settings) container.users.upsert(settings.web_admin_login, hash_password(settings.web_admin_password), role="admin") bot_runtime = BotRuntime(container) await bot_runtime.startup() app.state.settings = settings app.state.container = container app.state.bot_runtime = bot_runtime LOGGER.info("Application startup complete.") try: yield finally: await bot_runtime.shutdown() LOGGER.info("Application shutdown complete.") app = FastAPI(title=settings.app_name, lifespan=lifespan) app.add_middleware(SessionMiddleware, secret_key=settings.session_secret, same_site="lax") app.mount("/static", StaticFiles(directory=str(Path(__file__).parent / "static")), name="static") app.include_router(build_router(templates)) return app app = create_app()