chrysopedia/backend/main.py
jlightner 243a7a3eb6 feat: Follow system + tier config page (M022/S02)
- CreatorFollow model + pure SQL migration 022
- Follow router: POST/DELETE /follows/{creator_id}, GET /follows/me, GET /follows/{id}/status
- follower_count on creator detail endpoint
- Follow button on CreatorDetail (authenticated users only)
- CreatorTiers page with Free/Pro/Premium cards, Coming Soon modals
- Tiers link in creator sidebar nav
- Route /creator/tiers (protected)
2026-04-04 07:34:03 +00:00

104 lines
3.7 KiB
Python

"""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, follows, 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(follows.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"}