88 lines
2.9 KiB
Python
88 lines
2.9 KiB
Python
"""FastAPI application entrypoint.
|
|
|
|
Opens a database connection on startup, runs Alembic migrations, registers
|
|
routers, locks CORS to the configured frontend origin(s), and attaches a
|
|
rate-limiter to the app (applied per-endpoint via decorators).
|
|
"""
|
|
|
|
from contextlib import asynccontextmanager
|
|
from collections.abc import AsyncIterator
|
|
|
|
from fastapi import FastAPI
|
|
from fastapi.middleware.cors import CORSMiddleware
|
|
from slowapi import Limiter, _rate_limit_exceeded_handler
|
|
from slowapi.errors import RateLimitExceeded
|
|
from slowapi.util import get_remote_address
|
|
|
|
from app.config import settings
|
|
from app.db.connection import connect, run_migrations
|
|
from app.routers import vacation_days
|
|
from app.routers.analytics import router as analytics_router
|
|
from app.routers.auth import router as auth_router
|
|
from app.routers.fetch_data import router as fetch_data_router
|
|
from app.routers.fetch_data import scraper_router
|
|
from app.routers.planned_days import router as planned_days_router
|
|
from app.routers.settings import break_router, child_router, settings_child_router, tips_router
|
|
from app.routers.workspace import _inv_router as invitations_router
|
|
from app.routers.workspace import router as workspace_router
|
|
|
|
limiter = Limiter(key_func=get_remote_address)
|
|
|
|
|
|
@asynccontextmanager
|
|
async def lifespan(app: FastAPI) -> AsyncIterator[None]:
|
|
# Run pending migrations before accepting requests (dev convenience).
|
|
# In production, run `alembic upgrade head` as a separate deploy step.
|
|
if settings.database_type == "sqlite":
|
|
try:
|
|
run_migrations(f"sqlite:///{settings.sqlite_path}")
|
|
except Exception:
|
|
pass # migrations may fail in test envs without alembic installed
|
|
|
|
app.state.db_connection = await connect(settings.sqlite_path)
|
|
try:
|
|
yield
|
|
finally:
|
|
await app.state.db_connection.close()
|
|
|
|
|
|
def create_app() -> FastAPI:
|
|
app = FastAPI(title="Föräldraledigplannerare API", lifespan=lifespan)
|
|
|
|
app.state.limiter = limiter
|
|
app.add_exception_handler(RateLimitExceeded, _rate_limit_exceeded_handler)
|
|
|
|
app.add_middleware(
|
|
CORSMiddleware,
|
|
allow_origins=list(settings.cors_origins),
|
|
allow_credentials=True,
|
|
allow_methods=["*"],
|
|
allow_headers=["*"],
|
|
)
|
|
|
|
_register_routers(app)
|
|
return app
|
|
|
|
|
|
def _register_routers(app: FastAPI) -> None:
|
|
app.include_router(auth_router)
|
|
app.include_router(workspace_router)
|
|
app.include_router(invitations_router)
|
|
app.include_router(fetch_data_router)
|
|
app.include_router(scraper_router)
|
|
app.include_router(analytics_router)
|
|
app.include_router(tips_router)
|
|
app.include_router(break_router)
|
|
app.include_router(child_router)
|
|
app.include_router(settings_child_router)
|
|
app.include_router(planned_days_router)
|
|
app.include_router(vacation_days.router)
|
|
|
|
|
|
app = create_app()
|
|
|
|
|
|
@app.get("/health", tags=["health"])
|
|
async def health() -> dict[str, str]:
|
|
return {"status": "ok"}
|