import logging from contextlib import asynccontextmanager from fastapi import FastAPI from fastapi.middleware.cors import CORSMiddleware from sqlalchemy import select from slowapi import _rate_limit_exceeded_handler from slowapi.errors import RateLimitExceeded from app.config import settings from app.database import init_db, AsyncSessionLocal from app.limiter import limiter from app.routers import auth, users, entries, public, admin logging.basicConfig( level=logging.INFO, format="%(asctime)s %(levelname)s %(name)s %(message)s", ) logger = logging.getLogger("bourbonacci") async def _seed_admin() -> None: from app.models.user import User from app.utils.security import hash_password async with AsyncSessionLocal() as db: result = await db.execute(select(User).where(User.email == settings.admin_username)) user = result.scalar_one_or_none() if user is None: db.add(User( email=settings.admin_username, password_hash=hash_password(settings.admin_password), display_name="Admin", is_admin=True, )) elif not user.is_admin: user.is_admin = True await db.commit() @asynccontextmanager async def lifespan(app: FastAPI): await init_db() await _seed_admin() logger.info("Bourbonacci started") yield logger.info("Bourbonacci shutting down") app = FastAPI(title="Bourbonacci", lifespan=lifespan) app.state.limiter = limiter app.add_exception_handler(RateLimitExceeded, _rate_limit_exceeded_handler) app.add_middleware( CORSMiddleware, allow_origins=[o.strip() for o in settings.allowed_origins.split(",")], allow_methods=["*"], allow_headers=["*"], ) app.include_router(auth.router) app.include_router(users.router) app.include_router(entries.router) app.include_router(public.router) app.include_router(admin.router) @app.get("/health") async def health(): return {"status": "ok"}