Coverage for src/cstlcore/main.py: 87%
53 statements
« prev ^ index » next coverage.py v7.9.1, created at 2026-02-19 12:46 +0000
« prev ^ index » next coverage.py v7.9.1, created at 2026-02-19 12:46 +0000
1from contextlib import asynccontextmanager
3from fastapi import FastAPI
4from fastapi.middleware.cors import CORSMiddleware
5from fastapi.responses import JSONResponse
6from sqlalchemy.exc import IntegrityError
8from cstlcore.assets import router as assets
9from cstlcore.auth import router as auth
10from cstlcore.collections import router as collections
11from cstlcore.constellations import router as constellations
12from cstlcore.feedbacks import router as feedbacks
13from cstlcore.glossary import router as glossary
14from cstlcore.logging import setup_logging
15from cstlcore.maps import router as maps
16from cstlcore.me import router as me
17from cstlcore.memberships import router as memberships
18from cstlcore.newsletter import router as newsletter
19from cstlcore.settings import settings
20from cstlcore.users import router as users
21from cstlcore.ydocs import router as ydocs
22from cstlcore.admin import router as admin
24setup_logging()
27@asynccontextmanager
28async def lifespan(app: FastAPI):
29 # On startup ↑
30 yield
31 # On shutdown ↓
34app = FastAPI(lifespan=lifespan)
37@app.exception_handler(IntegrityError)
38async def sqlalchemy_integrity_error_handler(request, exc: IntegrityError):
39 """Convert DB integrity errors (unique constraint, etc.) to HTTP 409."""
40 # We return a simple message; more detailed parsing could extract which
41 # constraint failed (e.g., unique vs. check) but that's optional.
42 return JSONResponse(status_code=409, content={"detail": "Database integrity error"})
45app.add_middleware(
46 CORSMiddleware,
47 allow_origins=settings.allowed_origins,
48 allow_credentials=True,
49 allow_methods=["*"],
50 allow_headers=["*"],
51)
53app.include_router(constellations.router, tags=["Constellations"])
54app.include_router(collections.router, tags=["Collections"])
55app.include_router(users.router, tags=["Users"])
56app.include_router(memberships.router, tags=["Memberships"])
57app.include_router(auth.router, tags=["Authentication"])
58app.include_router(ydocs.router, tags=["YDocs"])
59app.include_router(me.router, tags=["Me"])
60app.include_router(assets.router, tags=["Assets"])
61app.include_router(admin.router, tags=["Admin"])
62app.include_router(maps.router, tags=["Maps"])
63app.include_router(newsletter.router, tags=["Newsletter"])
64app.include_router(feedbacks.router, tags=["Feedbacks"])
65app.include_router(glossary.router, tags=["Glossary"])
67if settings.target == "dev":
68 from cstlcore.debug import router as debug
70 app.include_router(debug.router, tags=["Debug"])
73@app.get("/health")
74async def health_check():
75 return {"status": "ok"}
78def main():
79 import uvicorn
81 uvicorn.run(
82 app,
83 host="0.0.0.0",
84 port=8001,
85 )
88if __name__ == "__main__":
89 main()