Skip to content

Repository files navigation

Example MFE (Suncoast Contract)

Example micro-frontend package that:

  • follows the Suncoast UI module contract (module.definition.json)
  • includes a host adapter for the current shell CmsModuleDefinition contract
  • builds to a single browser JS file (dist/example-mfe.js)
  • provides Directus seed data for cms_modules
  • demonstrates GraphQL async communication:
    • submit via GraphQL HTTP mutation
    • receive streamed response via GraphQL WS subscription

Quick Start

npm install
npm run dev

Local preview URL:

  • http://localhost:4173/preview/ (default)

Build production artifact:

npm run build

Build output:

  • dist/example-mfe.js (single-file bundle)
  • dist/example-mfe.js.map
  • dist/module.definition.json

GraphQL Runtime Configuration

GraphQL endpoint/token values are runtime-resolved. They are not baked into the built bundle.

Resolution order:

  1. props.graphql.* from CMS block config
  2. Shell runtime values (#cms-root data attrs / window.__SUNCOAST_RUNTIME__.graphql)
  3. Browser auth storage fallback for token (for preview helper flows)

Session lifecycle ownership:

  • Token refresh scheduling and session-expiry warning UI are owned by the shell runtime.
  • This MFE only consumes shell auth globals (for example window.__SUNCOAST_AUTH__) before making requests.
  • For template guidance on logout events and local auth-state cleanup, see MFE_AUTH_INTEGRATION_REFERENCE.md.

Build metadata debug helper (devtools):

  • window.__SUNCOAST_GET_MFE_BUILD_INFO__("mfe-internal-operations")
  • Returns compile-time metadata for the loaded bundle (module version, build version, commit, timestamp, mode).
  • window.__SUNCOAST_GET_MFE_BUILD_INFO__() returns all loaded MFE build entries by module key.
  • Lookup is exact-key first, then case-insensitive.

Optional token exchange:

  • Configure graphql.tokenExchange to exchange the shell bearer token to the audience this MFE needs before GraphQL submit/stream calls.
  • Preferred: set graphql.tokenExchange.exchangeUrl to your backend exchange endpoint (for example https://login.suncoast.systems/v1/auth/token-exchange) so the browser never calls Keycloak token exchange directly.
  • When exchangeUrl is set, the MFE performs gateway exchange even if the source token already contains the requested audience (so gateway can still mint/augment required claims).
  • If module security is enabled (secured=true or requiredRole set) and the shell token lacks Hasura claims, the MFE now auto-attempts gateway token exchange using exchangeUrl + appSlug (even when tokenExchange.enabled is false).
  • If tokenExchange.tokenUrl / tokenExchange.clientId are omitted, the module falls back to shell auth runtime values (data-auth-token-url, data-auth-client-id, window.__SUNCOAST_AUTH__.config).

Local preview:

  1. Copy .env.local.example to .env.local
  2. Set optional preview auth values
  3. Run npm run dev

Supported env vars:

  • MFE_PREVIEW_AUTH_GATEWAY_URL (preview login gateway, usually https://login.suncoast.systems)
  • MFE_PREVIEW_AUTH_APP_SLUG (registered app slug in auth-gateway, for example example-mfe-dev for local/preview and example-mfe-prod for production)
  • MFE_PREVIEW_AUTH_CODE_PARAM (query key returned by gateway callback, default gateway_code)
  • MFE_PREVIEW_PORT (dev only)
  • MFE_PREVIEW_VM_STATS_BASE_URL (optional, e.g. http://localhost:8787/vm-stats or https://vm-stats.internal/vm-stats)
  • MFE_PREVIEW_VM_STATS_SYSTEMS_PATH (optional override for systems endpoint path, default /stats/systems)
  • MFE_PREVIEW_VM_STATS_SNAPSHOT_PATH (optional override for snapshot endpoint path, default /stats/snapshot)
  • MFE_PREVIEW_CLOUD_BILLING_GRAPHQL_ENDPOINT (optional override for billing Hasura GraphQL endpoint, default /graphql)
  • MFE_PREVIEW_CLOUD_BILLING_HASURA_ACTION_NAME (optional override for billing Hasura action field)
  • MFE_PREVIEW_CLOUD_BILLING_HASURA_ACTION_CANDIDATES (optional comma-separated action field candidates)
  • MFE_PREVIEW_CLOUD_BILLING_SERVICES (optional comma-separated billing service IDs to show)

Local Preview Login

The local preview page (/preview/) now includes a login helper that uses the shared auth-gateway flow:

  1. Fill Auth Gateway URL and Auth App Slug (or set MFE_PREVIEW_AUTH_* env vars).
  2. Click Login on the preview page.
  3. Gateway returns to /preview/ with a one-time code (gateway_code by default).
  4. Preview exchanges that code at /v1/auth/exchange and auto-fills Auth Token.
    • Preview requests Hasura claims (request_hasura_claims: true), and gateway resolves audience server-side.
  5. Click Apply / Remount to use that token for GraphQL HTTP/WS requests.

If your auth provider returns access_token in URL hash (implicit flow), the preview page will capture that too.

What This MFE Does

  • Module key: mfe-internal-operations
  • Renders internal operations views for system status, runtime canaries, auth gateway apps, MFE management, and cloud billing.
  • Shows normalized billing data for configured platform cloud services.
  • Includes the legacy async GraphQL chat runtime in src/module.ts for contract/reference compatibility.
  • Emits module events

Cloud Billing Backend

The backend service lives in the sibling Kubernetes workspace:

  • ../k8s-cms/cloud-billing-service
  • ../k8s-cms/manifests/74-cloud-billing-service.yaml
  • ../k8s-cms/docs/cloud-billing-service-openapi.yaml

The backend exposes REST endpoints such as GET /billing/summary and GET /healthz for service health and internal checks. The MFE runtime consumes the generated Hasura action, normally gravitee_cloud_billing_api_hasurafetchcloudbilling, through /graphql.

Configuration is provided to the Kubernetes service through BILLING_SERVICES_JSON in the cloud-billing-service-config ConfigMap and supports AWS Cost Explorer, Azure Cost Management Query, GCP Cloud Billing export in BigQuery, OCI Usage API, and mock local data.

The service returns a normalized response with totals, providerTotals, services, breakdown, budgetMonthly, forecastCost, and status fields. Keep provider credentials in the backend environment; the browser only receives normalized billing output.

Directus Setup

  1. Host dist/example-mfe.js at a URL reachable by your shell runtime.
  2. Create/update a cms_modules record using directus/cms-module.seed.json.
  3. In a cms_block_module block, choose module key mfe-internal-operations.
  4. Use directus/cms-block-module.props.example.json as your props_json baseline.
  5. Set:
    • optional module access controls:
      • secured (when true, require sign-in before module renders)
      • requiredRole (optional role required to use this MFE; when missing, module is hidden)
      • hideWhenUnauthorized (optional; hide instead of showing denied message for non-role access failures)
      • unauthorizedMessage (optional denied message when not hidden)
      • when requiredRole is set, the module also forwards that role into async request metadata so backend policy can reject unauthorized API requests
    • optional graphql.httpUrl and graphql.wsUrl overrides (leave unset to use shell runtime defaults)
    • optional graphql.authToken override (usually leave unset and let shell runtime auth provide token)
    • graphql.hasuraRole is deprecated in async mode and ignored by runtime (role comes from token claims)
    • optional graphql.tokenExchange.*:
      • enabled
      • requestedAudience (set this to the Hasura/GraphQL audience expected by auth hook)
      • requestedAudiences (optional array; request multiple audiences in one exchanged token)
      • requestedScope (optional)
      • requestHasuraClaims (optional boolean; for gateway exchange, ask backend to resolve Hasura audience and mint Hasura-claims token)
      • exchangeUrl (recommended; dedicated backend token exchange endpoint)
      • appSlug (required for backend exchange mode)
      • tokenUrl (optional; defaults from shell auth runtime)
      • clientId (optional; defaults from shell auth runtime)
    • graphql.submitMutation and graphql.streamSubscription (defaults are preconfigured for publish_async_request + graphql_client_async_messages)
    • optional async transport hints:
      • async.mode (none, graphql-stream, kafka-graphql-bridge, request-response, subscribe, mixed)
      • async.requestChannel
      • async.responseChannel
  • async.correlationIdPath
  • path mappings:
    • graphql.submitRequestIdPath
    • graphql.streamTextPath
    • graphql.streamDonePath
    • graphql.streamErrorPath
  • optional vmStats token exchange for systems action/authenticated routes:
    • vmStats.tokenExchangePath (defaults to /v1/auth/token-exchange)
    • vmStats.tokenExchangeUrl (absolute override URL for token exchange)
    • vmStats.tokenExchangeAppSlug (optional auth-gateway app slug sent to the token-exchange service for Hasura claims; leave empty to use the shell auth app slug)
    • vmStats.tokenExchangeRequestedAudience (optional; leave empty for Hasura claims so auth-gateway resolves the concrete audience)
  • optional cloudBilling config for the billing view:
    • cloudBilling.graphqlEndpoint (defaults to the shell Hasura /graphql endpoint)
    • cloudBilling.hasuraActionName (optional override)
    • cloudBilling.hasuraActionCandidates (defaults to the generated Gravitee cloud billing actions)
    • cloudBilling.baseUrl and cloudBilling.summaryPath are legacy compatibility settings; runtime billing reads use Hasura
    • cloudBilling.services (optional service ID allowlist; empty means all backend-configured services)
    • cloudBilling.currency
    • cloudBilling.lookbackDays
    • cloudBilling.requestTimeoutMs
    • cloudBilling.allowLocalFallback
  • optional platformManagement config for the Organization Management view:
    • platformManagement.graphqlEndpoint (defaults to the shell Hasura /graphql endpoint)
    • platformManagement.defaultDomain
    • platformManagement.defaultCloudProvider
    • platformManagement.defaultDeploymentStrategy
    • platformManagement.defaultTemplateSourceRepo (optional; leave blank unless there is a real app repo default)
    • platformManagement.defaultTemplateRef (defaults to the prod deploy ref)
    • platformManagement.productionHostnamePattern
    • platformManagement.previewHostnamePattern
    • platformManagement.requestTimeoutMs

The Organization Management view uses Hasura actions for both Directus registry writes and deploy execution. Creating an app from the form now saves the platform_apps row and calls the platform deploy action; saving an existing app queues an update. The GitHub repo field must point at the existing app repository to deploy, not a starter repository. Use the secondary save-only button when the Directus metadata should change without launching a deployment. Destroy resources before deleting a registry row; row deletion is disabled for apps that are still deployed, deploying, destroying, or failed. Use the Deploy secrets dialog to load and update the Terraform Cloud, Cloudflare, and source clone tokens in Vault; auth-gateway admin access uses the existing internal Vault secret. While operations are active, the view polls platform_app_operations and platform_app_operation_steps to show live step status messages.

async.requestChannel, async.responseChannel, and async.correlationIdPath are consumed by this example MFE and injected into submit/stream template variables and emitted events.

Automated Registry Publish (No Directus/GitOps PR Required)

publish.yml now publishes module artifacts directly to the module registry service POST /v1/modules/publish using multipart upload.

  1. Add repository variable(s) or secret(s):
    • MODULE_REGISTRY_SERVICE_URL_PREVIEW
    • MODULE_REGISTRY_SERVICE_URL_PROD
    • optional legacy fallback: MODULE_REGISTRY_SERVICE_URL
  2. Add repository secrets for module registry publish API auth:
    • MODULE_REGISTRY_SERVICE_GOOGLE_SERVICE_ACCOUNT_EMAIL
    • MODULE_REGISTRY_SERVICE_GOOGLE_SERVICE_ACCOUNT_PRIVATE_KEY
    • MODULE_REGISTRY_SERVICE_GOOGLE_TOKEN_AUDIENCE
  3. Optional repository variable:
    • MODULE_REGISTRY_SERVICE_PUBLISH_PATH (default /v1/modules/publish)
  4. Publish tag v* (or run Publish workflow manually).
  5. Workflow will:
    • build artifacts
    • run npm run publish:registry to generate dist/module.publish.json
    • run npm run notify:catalog to upload dist/example-mfe.js and dist/module.publish.json to the registry service
    • attach release artifacts in GitHub

publish:registry writes metadata in dist/module.publish.json:

  • module_version
  • published_at
  • release.tag
  • release.sha
  • bundle.sha256
  • full definition + seed documents

Module registry service endpoint called by workflow:

  • POST <resolved-service-url><MODULE_REGISTRY_SERVICE_PUBLISH_PATH>
  • Default path: /v1/modules/publish
  • Channel defaults:
    • all publishes use preview (tag and manual)
  • Service URL selection:
    • registry_target=preview (default): targets MODULE_REGISTRY_SERVICE_URL_PREVIEW (fallback MODULE_REGISTRY_SERVICE_URL)
    • registry_target=production: targets MODULE_REGISTRY_SERVICE_URL_PROD (fallback MODULE_REGISTRY_SERVICE_URL)

Important Runtime Note

This repo provides the MFE contract + bundle. Your shell runtime must include or load this module definition at runtime.

If your shell currently only mounts modules from an internal registry, wire this module using the exported host adapter:

  • export: createCmsModuleDefinition()

The bundle also self-registers at:

  • globalThis.SuncoastMfeRegistry["mfe-internal-operations"]

Scripts

  • npm run clean - remove dist
  • npm run clean:dev - remove dev-dist
  • npm run typecheck - TS type check
  • npm run build - compile single JS + copy module definition
  • npm run dev - local preview server with live rebuild + preview harness
  • npm run publish:registry - generate local publish manifest from built artifacts
  • npm run notify:catalog - upload built bundle + publish manifest to module registry API

GitHub Actions

Workflows included:

  • .github/workflows/ci.yml
    • runs typecheck/build on push + PR
    • uploads dist artifacts
  • .github/workflows/publish.yml
    • runs on v* tags or manual dispatch
    • manual inputs:
      • build_mode (production/local)
      • registry_target (preview/production, default preview)
    • builds bundle + publish metadata
    • uploads artifacts
    • creates a GitHub Release for tag pushes

Secrets expected by publish workflow:

  • MODULE_REGISTRY_SERVICE_GOOGLE_SERVICE_ACCOUNT_EMAIL
  • MODULE_REGISTRY_SERVICE_GOOGLE_SERVICE_ACCOUNT_PRIVATE_KEY
  • MODULE_REGISTRY_SERVICE_GOOGLE_TOKEN_AUDIENCE

Repository variables used by publish workflow:

  • MODULE_REGISTRY_SERVICE_URL_PREVIEW
  • MODULE_REGISTRY_SERVICE_URL_PROD
  • MODULE_REGISTRY_SERVICE_URL (legacy fallback)
  • MODULE_REGISTRY_SERVICE_PUBLISH_PATH (optional, default /v1/modules/publish)

About

MFE application for internal operations

Resources

Stars

Watchers

Forks

Releases

Packages

Used by

Contributors

Languages

Generated from dotcomrow/example-mfe