module Web.Controller.Api.V2.HubRegistry where -- GET /api/v2/hub-registry — list hubs with active manifest summary + GAAF indicator -- GET /api/v2/hub-registry/:hubId — single hub detail import Web.Types import Generated.Types import IHP.Prelude import IHP.ControllerPrelude import Data.Aeson (object, (.=), Value) import Web.Controller.Api.V2.Auth (requireApiConsumer) import Application.Helper.ApiRateLimit (checkRateLimitAndLog) instance Controller ApiV2HubRegistryController where action ApiV2IndexHubRegistryAction = do consumer <- requireApiConsumer checkRateLimitAndLog consumer "GET" "/api/v2/hub-registry" hubs <- query @Hub |> orderByAsc #name |> fetch rows <- mapM buildHubJson hubs renderJson rows action ApiV2ShowHubRegistryAction { hubId } = do consumer <- requireApiConsumer checkRateLimitAndLog consumer "GET" ("/api/v2/hub-registry/" <> tshow hubId) hub <- fetch hubId mManifest <- query @HubCapabilityManifest |> filterWhere (#hubId, hubId) |> filterWhere (#status, "active") |> fetchOneOrNothing mSnapshot <- query @HubHealthSnapshot |> filterWhere (#hubId, hubId) |> orderByDesc #computedAt |> limit 1 |> fetchOneOrNothing renderJson (hubDetailJson hub mManifest mSnapshot) -- | Build a summary JSON object for a hub including manifest and health data. buildHubJson :: (?modelContext :: ModelContext) => Hub -> IO Value buildHubJson hub = do mManifest <- query @HubCapabilityManifest |> filterWhere (#hubId, hub.id) |> filterWhere (#status, "active") |> fetchOneOrNothing mSnapshot <- query @HubHealthSnapshot |> filterWhere (#hubId, hub.id) |> orderByDesc #computedAt |> limit 1 |> fetchOneOrNothing pure $ hubDetailJson hub mManifest mSnapshot hubDetailJson :: Hub -> Maybe HubCapabilityManifest -> Maybe HubHealthSnapshot -> Value hubDetailJson hub mManifest mSnapshot = let gaafIndicator = case mManifest of Nothing -> "no_manifest" :: Text Just m | m.status == "active" -> "compliant" | otherwise -> "draft_only" in object [ "id" .= hub.id , "name" .= hub.name , "slug" .= hub.slug , "domain" .= hub.domain , "hubKind" .= hub.hubKind , "gaafStatus" .= gaafIndicator , "manifest" .= fmap manifestSummary mManifest , "healthScore" .= fmap (.healthScore) mSnapshot , "healthAt" .= fmap (.computedAt) mSnapshot ] manifestSummary :: HubCapabilityManifest -> Value manifestSummary m = object [ "id" .= m.id , "manifestVersion" .= m.manifestVersion , "status" .= m.status , "declaredWidgetTypes" .= m.declaredWidgetTypes , "declaredEventTypes" .= m.declaredEventTypes , "declaredAnnotationCategories" .= m.declaredAnnotationCategories , "declaredPolicyScopes" .= m.declaredPolicyScopes , "activatedAt" .= m.activatedAt ]