from __future__ import annotations from contextlib import asynccontextmanager from fastapi import FastAPI, Response, status from hub_core import __version__ from hub_core.runtime.config import RuntimeSettings from hub_core.runtime.compat import SQLCompatibilityStore, create_compatibility_router from hub_core.runtime.models import HealthResponse, ReadinessResponse from hub_core.runtime.ports import create_ports_router from hub_core.runtime.store import InMemoryPortStore, PortStore from hub_core.runtime.validation import ContractValidator def create_app( *, settings: RuntimeSettings | None = None, port_store: PortStore | None = None, ) -> FastAPI: resolved_settings = settings or RuntimeSettings.from_env() resolved_store = port_store or _create_store(resolved_settings) owns_store = port_store is None @asynccontextmanager async def lifespan(_: FastAPI): yield if owns_store and (closer := getattr(resolved_store, "aclose", None)): await closer() app = FastAPI( title="Hub Core Runtime", version=__version__, description="HelixForge hub framework and named-port runtime.", lifespan=lifespan, ) app.state.settings = resolved_settings app.state.port_store = resolved_store app.state.compat_store = ( SQLCompatibilityStore(resolved_store) if resolved_store.backend_name == "postgresql" else None ) app.state.contract_validator = ContractValidator() @app.get("/healthz", response_model=HealthResponse, tags=["system"]) async def healthz() -> HealthResponse: return HealthResponse( service="core-hub" if resolved_settings.legacy_health else "hub-core", version=__version__, ) @app.get("/readyz", response_model=ReadinessResponse, tags=["system"]) async def readyz(response: Response) -> ReadinessResponse: dependency_checks = await resolved_store.readiness_checks() ready = resolved_settings.is_ready(resolved_store.backend_name) and all( value in {"ok", "not_applicable"} for value in dependency_checks.values() ) if not ready: response.status_code = status.HTTP_503_SERVICE_UNAVAILABLE return ReadinessResponse( status="ok" if ready else "degraded", checks={ **resolved_settings.readiness_checks(resolved_store.backend_name), **dependency_checks, }, ) app.include_router(create_ports_router()) app.include_router(create_compatibility_router()) return app def _create_store(settings: RuntimeSettings) -> PortStore: if settings.backend == "memory": return InMemoryPortStore() if settings.backend == "postgresql": if not settings.database_url: raise RuntimeError("HUB_CORE_DATABASE_URL is required for PostgreSQL backend") from hub_core.runtime.postgres_store import PostgresPortStore return PostgresPortStore.from_url(settings.database_url) raise RuntimeError(f"Unsupported HUB_CORE_BACKEND '{settings.backend}'") app = create_app()