"""Chrysopedia API — Knowledge extraction and retrieval system. Entry point for the FastAPI application. Configures middleware, structured logging, and mounts versioned API routers. """ import logging import sys from contextlib import asynccontextmanager from fastapi import FastAPI from fastapi.middleware.cors import CORSMiddleware from config import get_settings from routers import admin, auth, chat, consent, creator_chapters, creator_dashboard, creator_highlights, creators, health, highlights, ingest, pipeline, reports, search, stats, techniques, topics, videos def _setup_logging() -> None: """Configure structured logging to stdout.""" settings = get_settings() level = getattr(logging, settings.app_log_level.upper(), logging.INFO) handler = logging.StreamHandler(sys.stdout) handler.setFormatter( logging.Formatter( fmt="%(asctime)s | %(levelname)-8s | %(name)s | %(message)s", datefmt="%Y-%m-%dT%H:%M:%S", ) ) root = logging.getLogger() root.setLevel(level) # Avoid duplicate handlers on reload root.handlers.clear() root.addHandler(handler) # Quiet noisy libraries logging.getLogger("uvicorn.access").setLevel(logging.WARNING) logging.getLogger("sqlalchemy.engine").setLevel(logging.WARNING) @asynccontextmanager async def lifespan(app: FastAPI): # noqa: ARG001 """Application lifespan: setup on startup, teardown on shutdown.""" _setup_logging() logger = logging.getLogger("chrysopedia") settings = get_settings() logger.info( "Chrysopedia API starting (env=%s, log_level=%s)", settings.app_env, settings.app_log_level, ) yield logger.info("Chrysopedia API shutting down") app = FastAPI( title="Chrysopedia API", description="Knowledge extraction and retrieval for music production content", version="0.1.0", lifespan=lifespan, ) # ── Middleware ──────────────────────────────────────────────────────────────── settings = get_settings() app.add_middleware( CORSMiddleware, allow_origins=settings.cors_origins, allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) # ── Routers ────────────────────────────────────────────────────────────────── # Root-level health (no prefix) app.include_router(health.router) # Versioned API app.include_router(admin.router, prefix="/api/v1") app.include_router(auth.router, prefix="/api/v1") app.include_router(chat.router, prefix="/api/v1") app.include_router(consent.router, prefix="/api/v1") app.include_router(creator_dashboard.router, prefix="/api/v1") app.include_router(creator_chapters.router, prefix="/api/v1") app.include_router(creator_highlights.router, prefix="/api/v1") app.include_router(creators.router, prefix="/api/v1") app.include_router(highlights.router, prefix="/api/v1") app.include_router(ingest.router, prefix="/api/v1") app.include_router(pipeline.router, prefix="/api/v1") app.include_router(reports.router, prefix="/api/v1") app.include_router(search.router, prefix="/api/v1") app.include_router(stats.router, prefix="/api/v1") app.include_router(techniques.router, prefix="/api/v1") app.include_router(topics.router, prefix="/api/v1") app.include_router(videos.router, prefix="/api/v1") @app.get("/api/v1/health") async def api_health(): """Lightweight version-prefixed health endpoint (no DB check).""" return {"status": "ok", "version": "0.1.0"}