"""FastAPI application — HTTP surface for the registry. T014 ships a minimal app with two routes: * ``GET /`` — service banner. * ``GET /health`` — registry liveness + DB connectivity + storage backend. Richer endpoints (package CRUD, file upload, manifest retrieval, event stream) land in workplan WP-0002. The app is built through :func:`create_app` so tests can inject their own settings. """ from __future__ import annotations from contextlib import asynccontextmanager from typing import Any from fastapi import Depends, FastAPI, Request from artifactstore import __version__ from artifactstore.app import build_registry from artifactstore.config import Settings from artifactstore.registry import Registry __all__ = ["app", "create_app"] def get_registry(request: Request) -> Registry: return request.app.state.registry # type: ignore[no-any-return] def create_app(settings: Settings | None = None) -> FastAPI: """Build the FastAPI app. Lifespan owns the registry instance.""" @asynccontextmanager async def lifespan(application: FastAPI) -> Any: registry = build_registry(settings) application.state.registry = registry try: yield finally: await registry.dispose() application = FastAPI( title="artifact-store", version=__version__, lifespan=lifespan, ) @application.get("/") def root() -> dict[str, str]: return { "service": "artifact-store", "version": __version__, "status": "scaffold", } @application.get("/health") async def health(registry: Registry = Depends(get_registry)) -> dict[str, Any]: db_ok, db_detail = await registry.db_health() backend_status = await registry.backend_health() overall = "ok" if db_ok and backend_status.healthy else "degraded" return { "service": "artifact-store", "version": __version__, "status": overall, "db": {"healthy": db_ok, "detail": db_detail}, "backend": { "backend_id": backend_status.backend_id, "healthy": backend_status.healthy, "detail": backend_status.detail, "free_bytes": backend_status.free_bytes, "total_bytes": backend_status.total_bytes, }, } return application app = create_app()