2025-11-21 12:00:00 +00:00
|
|
|
"""FastAPI application entry point."""
|
|
|
|
|
|
|
|
|
|
from contextlib import asynccontextmanager
|
|
|
|
|
from typing import AsyncGenerator
|
|
|
|
|
|
|
|
|
|
from fastapi import FastAPI
|
2025-12-29 09:55:30 +00:00
|
|
|
from fastapi.openapi.utils import get_openapi
|
2025-11-21 12:00:00 +00:00
|
|
|
|
2026-01-03 10:18:21 +00:00
|
|
|
from app.api.v1 import auth, health, incidents, org
|
2025-11-21 12:00:00 +00:00
|
|
|
from app.config import settings
|
|
|
|
|
from app.db import db, redis_client
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@asynccontextmanager
|
|
|
|
|
async def lifespan(app: FastAPI) -> AsyncGenerator[None, None]:
|
|
|
|
|
"""Manage application lifecycle - connect/disconnect resources."""
|
|
|
|
|
# Startup
|
|
|
|
|
await db.connect(settings.database_url)
|
|
|
|
|
await redis_client.connect(settings.redis_url)
|
|
|
|
|
yield
|
|
|
|
|
# Shutdown
|
|
|
|
|
await redis_client.disconnect()
|
|
|
|
|
await db.disconnect()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
app = FastAPI(
|
|
|
|
|
title="IncidentOps",
|
|
|
|
|
description="Incident management API with multi-tenant org support",
|
|
|
|
|
version="0.1.0",
|
2025-12-29 09:55:30 +00:00
|
|
|
docs_url="/docs",
|
|
|
|
|
redoc_url="/redoc",
|
|
|
|
|
openapi_url="/openapi.json",
|
2025-11-21 12:00:00 +00:00
|
|
|
lifespan=lifespan,
|
|
|
|
|
)
|
|
|
|
|
|
2025-12-29 09:55:30 +00:00
|
|
|
app.openapi_tags = [
|
|
|
|
|
{"name": "auth", "description": "Registration, login, token lifecycle"},
|
2026-01-03 10:18:21 +00:00
|
|
|
{"name": "org", "description": "Organization membership, services, and notifications"},
|
|
|
|
|
{"name": "incidents", "description": "Incident lifecycle and timelines"},
|
2025-12-29 09:55:30 +00:00
|
|
|
{"name": "health", "description": "Service health probes"},
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def custom_openapi() -> dict:
|
|
|
|
|
"""Add JWT bearer security scheme to the generated OpenAPI schema."""
|
|
|
|
|
|
|
|
|
|
if app.openapi_schema:
|
|
|
|
|
return app.openapi_schema
|
|
|
|
|
|
|
|
|
|
openapi_schema = get_openapi(
|
|
|
|
|
title=app.title,
|
|
|
|
|
version=app.version,
|
|
|
|
|
description=app.description,
|
|
|
|
|
routes=app.routes,
|
|
|
|
|
)
|
|
|
|
|
security_schemes = openapi_schema.setdefault("components", {}).setdefault("securitySchemes", {})
|
|
|
|
|
security_schemes["BearerToken"] = {
|
|
|
|
|
"type": "http",
|
|
|
|
|
"scheme": "bearer",
|
|
|
|
|
"bearerFormat": "JWT",
|
|
|
|
|
"description": "Paste the JWT access token returned by /auth endpoints",
|
|
|
|
|
}
|
|
|
|
|
openapi_schema["security"] = [{"BearerToken": []}]
|
|
|
|
|
app.openapi_schema = openapi_schema
|
|
|
|
|
return app.openapi_schema
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
app.openapi = custom_openapi # type: ignore[assignment]
|
|
|
|
|
|
2025-11-21 12:00:00 +00:00
|
|
|
# Include routers
|
2025-12-29 09:55:30 +00:00
|
|
|
app.include_router(auth.router, prefix=settings.api_v1_prefix)
|
2026-01-03 10:18:21 +00:00
|
|
|
app.include_router(incidents.router, prefix=settings.api_v1_prefix)
|
|
|
|
|
app.include_router(org.router, prefix=settings.api_v1_prefix)
|
2025-11-21 12:00:00 +00:00
|
|
|
app.include_router(health.router, prefix=settings.api_v1_prefix, tags=["health"])
|