module Web.Controller.HubRegistry where -- Hub Registry: browsable view over hub_capability_manifests + hub_health_snapshots + hubs -- No HubRegistry table — this is a view over existing Phase 9 data. import Web.Types import Web.View.HubRegistry.Index import Web.View.HubRegistry.Show import Generated.Types import IHP.Prelude import IHP.ControllerPrelude -- | Aggregated row for the hub registry index. data HubRegistryRow = HubRegistryRow { hub :: !Hub , mManifest :: !(Maybe HubCapabilityManifest) , mLatestSnapshot :: !(Maybe HubHealthSnapshot) } -- | GAAF compliance status derived from manifest and registry. data GaafStatus = GaafCompliant -- active manifest, all declared types registered | GaafNoManifest -- hub has no active manifest | GaafDraftOnly -- hub has a draft but no active manifest deriving (Eq, Show) gaafStatus :: Maybe HubCapabilityManifest -> GaafStatus gaafStatus Nothing = GaafNoManifest gaafStatus (Just m) | m.status == "active" = GaafCompliant | m.status == "draft" = GaafDraftOnly | otherwise = GaafNoManifest instance Controller HubRegistryController where beforeAction = ensureIsUser action HubRegistryAction = autoRefresh do hubs <- query @Hub |> orderByAsc #name |> fetch registryRows <- mapM buildRow hubs render IndexView { registryRows } action ShowHubRegistryAction { hubId } = do hub <- fetch hubId mManifest <- query @HubCapabilityManifest |> filterWhere (#hubId, hubId) |> filterWhere (#status, "active") |> fetchOneOrNothing healthHistory <- query @HubHealthSnapshot |> filterWhere (#hubId, hubId) |> orderByDesc #computedAt |> limit 10 |> fetch adoptedPatterns <- sqlQuery "SELECT wp.id, wp.name, wp.widget_type, wp.hub_id, \ \ pa.id AS adoption_id, pa.is_version_pinned, pa.adopted_at \ \ FROM pattern_adoptions pa \ \ JOIN widget_patterns wp ON wp.id = pa.widget_pattern_id \ \ WHERE pa.adopting_hub_id = ? \ \ ORDER BY pa.adopted_at DESC" (Only hubId) render ShowView { hub, mManifest, healthHistory, adoptedPatterns } -- | Build a HubRegistryRow for a hub by fetching its active manifest and latest snapshot. buildRow :: (?modelContext :: ModelContext) => Hub -> IO HubRegistryRow buildRow hub = do mManifest <- query @HubCapabilityManifest |> filterWhere (#hubId, hub.id) |> filterWhere (#status, "active") |> fetchOneOrNothing mLatestSnapshot <- query @HubHealthSnapshot |> filterWhere (#hubId, hub.id) |> orderByDesc #computedAt |> limit 1 |> fetchOneOrNothing pure HubRegistryRow { hub, mManifest, mLatestSnapshot }