feat(bootstrap,api,infra): scaffold app composition, health route, and postgres/minio/qdrant adapters

Why:
- first runnable slice of ADR-0012's resource-lifetime rules and ADR-0015's package layout: app-lifetime clients built once in the lifespan, released via explicit dependencies.

Changes:
- Settings split into per-domain nested settings (postgres/minio/ingestion/qdrant/app/logging); FastAPI app wired with /healthz, /readyz and a /v1 router; Postgres/MinIO/Qdrant adapters and SQLAlchemy models for tenants, API keys, source files, ingestion jobs/events.
This commit is contained in:
2026-08-16 11:54:14 +03:30
parent df221279a5
commit 3c660de093
28 changed files with 616 additions and 2 deletions

0
src/api/__init__.py Normal file
View File

View File

3
src/api/router.py Normal file
View File

@@ -0,0 +1,3 @@
from fastapi import APIRouter
router = APIRouter()

View File

36
src/api/routers/health.py Normal file
View File

@@ -0,0 +1,36 @@
import asyncio
from fastapi import APIRouter, Request, Response, status
from src.bootstrap.dependencies import AppResources
from src.infrastructure.minio.client import ping as ping_minio
from src.infrastructure.postgres.database import ping as ping_postgres
from src.infrastructure.qdrant.client import ping as ping_qdrant
router = APIRouter(tags=["health"])
@router.get("/healthz")
async def healthz() -> dict[str, str]:
return {"status": "ok"}
@router.get("/readyz")
async def readyz(request: Request, response: Response) -> dict[str, bool]:
resources: AppResources = request.app.state.resources
timeout = resources.settings.app.readiness_check_timeout_seconds
postgres_ready, minio_ready, qdrant_ready = await asyncio.gather(
ping_postgres(resources.db_engine, timeout),
ping_minio(resources.minio_client, timeout),
ping_qdrant(resources.qdrant_client, timeout),
)
result = {
"postgres": postgres_ready,
"minio": minio_ready,
"qdrant": qdrant_ready,
}
if not all(result.values()):
response.status_code = status.HTTP_503_SERVICE_UNAVAILABLE
return result

View File