import asyncio from contextlib import asynccontextmanager from fastapi import FastAPI, Request from fastapi.exceptions import RequestValidationError from fastapi.responses import JSONResponse from loguru import logger from app.config import setup_logging from app.database import engine from app.settings import settings @asynccontextmanager async def lifespan(app: FastAPI): setup_logging() logger.info("Starting {} v{}", settings.app_name, "0.1.0") async with engine.connect() as conn: await conn.exec_driver_sql("SELECT 1") logger.info("Database connection established") from app.bot.dispatcher import create_bot, create_dispatcher from app.scheduler import setup_scheduler bot = create_bot() dp = create_dispatcher() polling_task = asyncio.create_task(dp.start_polling(bot)) logger.info("Telegram bot started") scheduler = setup_scheduler() scheduler.start() logger.info("Scheduler started") yield scheduler.shutdown(wait=True) logger.info("Scheduler stopped") polling_task.cancel() try: await polling_task except asyncio.CancelledError: pass await bot.session.close() logger.info("Telegram bot stopped") await engine.dispose() logger.info("Database engine disposed") app = FastAPI( title=settings.app_name, version="0.1.0", debug=settings.debug, lifespan=lifespan, docs_url="/docs", redoc_url="/redoc", ) @app.exception_handler(RequestValidationError) async def validation_exception_handler( request: Request, exc: RequestValidationError ): logger.warning("Validation error: {}", exc.errors()) return JSONResponse( status_code=422, content={ "detail": exc.errors(), "body": exc.body, }, ) @app.exception_handler(Exception) async def global_exception_handler(request: Request, exc: Exception): logger.error("Unhandled error: {}", exc) return JSONResponse( status_code=500, content={"detail": "Internal server error"}, ) from app.admin.router import router as admin_router from app.api.v1.router import router as v1_router app.include_router(admin_router) app.include_router(v1_router) @app.get("/health") async def health(): try: async with engine.connect() as conn: await conn.exec_driver_sql("SELECT 1") return {"status": "ok", "database": "connected"} except Exception as e: logger.error("Health check failed: {}", e) return JSONResponse( status_code=503, content={"status": "error", "database": str(e)}, )