@tokenoftrust/storefront-runner 1.4.1 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/apps/storefront/astro.config.mjs +17 -1
- package/apps/storefront/env.d.ts +54 -5
- package/apps/storefront/integrations/materialize-guard.mjs +54 -0
- package/apps/storefront/migrations-apps/0002_good_switch.sql +26 -0
- package/apps/storefront/migrations-apps/0003_mute_marauders.sql +12 -0
- package/apps/storefront/migrations-apps/0004_deep_morph.sql +36 -0
- package/apps/storefront/migrations-apps/0005_common_mystique.sql +23 -0
- package/apps/storefront/migrations-apps/0006_broad_microchip.sql +16 -0
- package/apps/storefront/migrations-apps/0007_brave_outlaw_kid.sql +15 -0
- package/apps/storefront/migrations-apps/0008_blue_dazzler.sql +11 -0
- package/apps/storefront/migrations-apps/0009_bored_shinobi_shaw.sql +15 -0
- package/apps/storefront/migrations-apps/0010_ws4_verified_provider_measurement.sql +35 -0
- package/apps/storefront/migrations-apps/meta/0002_snapshot.json +1160 -0
- package/apps/storefront/migrations-apps/meta/0003_snapshot.json +1235 -0
- package/apps/storefront/migrations-apps/meta/0004_snapshot.json +1474 -0
- package/apps/storefront/migrations-apps/meta/0005_snapshot.json +1617 -0
- package/apps/storefront/migrations-apps/meta/0006_snapshot.json +1716 -0
- package/apps/storefront/migrations-apps/meta/0007_snapshot.json +1812 -0
- package/apps/storefront/migrations-apps/meta/0008_snapshot.json +1881 -0
- package/apps/storefront/migrations-apps/meta/0009_snapshot.json +1979 -0
- package/apps/storefront/migrations-apps/meta/_journal.json +56 -0
- package/apps/storefront/public/apps/tot.demo.analytics/0.1.0/entry.js +42 -0
- package/apps/storefront/public/js/dashboard-team.js +38 -2
- package/apps/storefront/public/platform/amplitude-http/v1/entry.mjs +19 -0
- package/apps/storefront/public/platform/google-measurement/v1/entry.mjs +19 -0
- package/apps/storefront/public/shared/commerce-chrome.css +20 -2
- package/apps/storefront/scripts/build-v84-clean-clone-standalone.mjs +68 -0
- package/apps/storefront/scripts/deny-v84-clean-clone-standalone-capabilities.mjs +111 -0
- package/apps/storefront/scripts/deny-v84-dogfood-capabilities.mjs +2 -0
- package/apps/storefront/scripts/deny-v84-lifecycle-capabilities.mjs +247 -0
- package/apps/storefront/scripts/dogfood-v84-nonportable-routes.ts +73 -0
- package/apps/storefront/scripts/fixed-v84-native-install-producer.ts +79 -0
- package/apps/storefront/scripts/generated/v84-clean-clone-replay.bundle.mjs +10982 -0
- package/apps/storefront/scripts/import-v84-through-ws2.ts +38 -0
- package/apps/storefront/scripts/initialize-v84-lifecycle-tooling-dependency.ts +2 -0
- package/apps/storefront/scripts/initialize-v84-lifecycle-tooling.ts +4 -0
- package/apps/storefront/scripts/install-v84-native-through-ws1.ts +185 -0
- package/apps/storefront/scripts/prove-clone-isolation.ts +506 -0
- package/apps/storefront/scripts/replay-v84-clean-clone-pass.ts +148 -0
- package/apps/storefront/scripts/replay-v84-clean-clone-standalone.ts +35 -0
- package/apps/storefront/scripts/replay-v84-clean-clone.ts +214 -0
- package/apps/storefront/scripts/run-fixed-v84-native-install-producer.mjs +18 -0
- package/apps/storefront/scripts/run-v84-clone-isolation-proof.mjs +18 -0
- package/apps/storefront/scripts/run-v84-clone-lifecycle-proof.mjs +18 -0
- package/apps/storefront/scripts/run-v84-import-through-ws2-proof.mjs +17 -0
- package/apps/storefront/scripts/run-v84-nonportable-route-dogfood.mjs +15 -0
- package/apps/storefront/scripts/sanitize-v84-proof-environment.mjs +21 -0
- package/apps/storefront/scripts/v84CleanCloneGraphPin.ts +42 -0
- package/apps/storefront/scripts/v84CleanCloneSourceGraph.ts +145 -0
- package/apps/storefront/scripts/validate-v84-clone-lifecycle.ts +18 -0
- package/apps/storefront/src/components/Img.astro +1 -1
- package/apps/storefront/src/components/Seo.astro +65 -1
- package/apps/storefront/src/components/admin/AdminBlogDetailRail.astro +878 -0
- package/apps/storefront/src/components/admin/AdminBlogTab.astro +1435 -0
- package/apps/storefront/src/components/admin/AdminCustomersTab.astro +3 -7
- package/apps/storefront/src/components/admin/AdminIntegrationsTab.astro +105 -0
- package/apps/storefront/src/components/admin/AdminPublishTab.astro +1980 -2564
- package/apps/storefront/src/components/admin/AdminSettingsTab.astro +2 -2
- package/apps/storefront/src/components/admin/AdminSubscriptionsTab.astro +1 -3
- package/apps/storefront/src/components/admin/orders/OrdersWorkspace.astro +5 -5
- package/apps/storefront/src/components/blog/BlogPaywall.astro +87 -0
- package/apps/storefront/src/components/blog/PostAccessBadge.astro +43 -0
- package/apps/storefront/src/components/blog/PostGrid.astro +57 -0
- package/apps/storefront/src/components/blog/ReactionBar.astro +214 -0
- package/apps/storefront/src/components/chrome/NavDropdown.astro +12 -0
- package/apps/storefront/src/components/chrome/SiteFooter.astro +7 -2
- package/apps/storefront/src/components/chrome/SiteHeader.astro +46 -10
- package/apps/storefront/src/components/compliance/AdultSignatureNotice.astro +1 -1
- package/apps/storefront/src/components/compliance/ComplianceNotice.astro +1 -2
- package/apps/storefront/src/components/compliance/ExciseTaxNotice.astro +1 -2
- package/apps/storefront/src/components/compliance/PactActNotice.astro +1 -1
- package/apps/storefront/src/components/compliance/Prop65Warning.astro +1 -2
- package/apps/storefront/src/components/compliance/PurchaseLimitNotice.astro +2 -3
- package/apps/storefront/src/components/compliance/ShippingRestrictionNotice.astro +2 -2
- package/apps/storefront/src/components/compliance/StateEligibilityNotice.astro +2 -3
- package/apps/storefront/src/components/content/TrustStrip.astro +4 -4
- package/apps/storefront/src/components/islands/MobileNav.tsx +26 -1
- package/apps/storefront/src/components/islands/VariantSelector.tsx +27 -2
- package/apps/storefront/src/components/islands/useCheckoutHandoffCorrelation.ts +75 -0
- package/apps/storefront/src/components/membership/IllustrativeLabel.astro +1 -2
- package/apps/storefront/src/components/membership/TierCards.astro +2 -2
- package/apps/storefront/src/components/membership/TierComparisonTable.astro +2 -2
- package/apps/storefront/src/components/subscription/SavingsExplainer.astro +2 -2
- package/apps/storefront/src/config/adminTenantLookup.ts +35 -0
- package/apps/storefront/src/config/devTenantSeed.ts +9 -9
- package/apps/storefront/src/config/resolver.ts +4 -4
- package/apps/storefront/src/layouts/Layout.astro +146 -21
- package/apps/storefront/src/lib/activity/alerts.ts +17 -19
- package/apps/storefront/src/lib/activity/candidateSubmitLease.ts +87 -0
- package/apps/storefront/src/lib/activity/changeActorAttribution.ts +89 -9
- package/apps/storefront/src/lib/activity/deployVersion.ts +20 -27
- package/apps/storefront/src/lib/activity/ingest.ts +8 -8
- package/apps/storefront/src/lib/activity/ingestAuth.ts +1 -1
- package/apps/storefront/src/lib/activity/killSwitch.ts +6 -6
- package/apps/storefront/src/lib/activity/query.ts +9 -9
- package/apps/storefront/src/lib/activity/recordActivity.ts +11 -15
- package/apps/storefront/src/lib/activity/store.ts +4 -6
- package/apps/storefront/src/lib/activity/uiActor.ts +12 -12
- package/apps/storefront/src/lib/activity/workerCommit.ts +6 -6
- package/apps/storefront/src/lib/admin/adminShell.ts +292 -0
- package/apps/storefront/src/lib/admin/ai/approvalMode.ts +2 -3
- package/apps/storefront/src/lib/admin/ai/audit.ts +3 -5
- package/apps/storefront/src/lib/admin/checkoutStyleInput.ts +7 -7
- package/apps/storefront/src/lib/admin/checkoutStyleTarget.ts +3 -3
- package/apps/storefront/src/lib/admin/envelope.ts +1 -2
- package/apps/storefront/src/lib/admin/orderClient.ts +3 -5
- package/apps/storefront/src/lib/admin/services/orders.ts +17 -19
- package/apps/storefront/src/lib/admin/viewmodels/aiWorkflows.ts +4 -6
- package/apps/storefront/src/lib/admin/viewmodels/settings.ts +1 -1
- package/apps/storefront/src/lib/apps/adminService.ts +14 -4
- package/apps/storefront/src/lib/apps/adminSession.ts +54 -45
- package/apps/storefront/src/lib/apps/apiAuth.ts +2 -2
- package/apps/storefront/src/lib/apps/apiRoute.ts +2 -2
- package/apps/storefront/src/lib/apps/appVersionStore.ts +281 -0
- package/apps/storefront/src/lib/apps/catalogMapper.ts +1 -1
- package/apps/storefront/src/lib/apps/consentConfigStore.ts +228 -0
- package/apps/storefront/src/lib/apps/credentials.ts +2 -2
- package/apps/storefront/src/lib/apps/gatewayKeys.ts +4 -4
- package/apps/storefront/src/lib/apps/gtmMigrationInstall.ts +447 -0
- package/apps/storefront/src/lib/apps/gtmMigrationReport.ts +367 -0
- package/apps/storefront/src/lib/apps/gtmMigrationTab.ts +315 -0
- package/apps/storefront/src/lib/apps/healthAggregate.ts +5 -5
- package/apps/storefront/src/lib/apps/integrationLifecycle.ts +614 -0
- package/apps/storefront/src/lib/apps/integrationLifecycleController.ts +125 -0
- package/apps/storefront/src/lib/apps/integrationReadModel.ts +563 -0
- package/apps/storefront/src/lib/apps/integrationsTab.ts +547 -0
- package/apps/storefront/src/lib/apps/integrationsView.ts +106 -0
- package/apps/storefront/src/lib/apps/managedAnalyticsService.ts +599 -0
- package/apps/storefront/src/lib/apps/orders/attributionService.ts +2 -2
- package/apps/storefront/src/lib/apps/orders/customerHash.ts +1 -1
- package/apps/storefront/src/lib/apps/orders/foxyOrderClient.ts +12 -12
- package/apps/storefront/src/lib/apps/orders/idempotency.ts +7 -7
- package/apps/storefront/src/lib/apps/orders/orderConversionD1.ts +150 -0
- package/apps/storefront/src/lib/apps/orders/orderForwardReceiver.ts +9 -1
- package/apps/storefront/src/lib/apps/orders/orderMapper.ts +2 -2
- package/apps/storefront/src/lib/apps/orders/ordersStore.ts +7 -7
- package/apps/storefront/src/lib/apps/orders/verifiedProviderMeasurement.ts +117 -0
- package/apps/storefront/src/lib/apps/registryService.ts +270 -16
- package/apps/storefront/src/lib/apps/scopes.ts +2 -2
- package/apps/storefront/src/lib/apps/tenantIntegrationMaterialization.ts +170 -0
- package/apps/storefront/src/lib/apps/tokenIssuer.ts +4 -4
- package/apps/storefront/src/lib/apps/tokenVerifier.ts +6 -6
- package/apps/storefront/src/lib/apps/v84CleanCloneReplayContext.ts +16 -0
- package/apps/storefront/src/lib/apps/v84CloneLifecycleCacheEvidence.ts +105 -0
- package/apps/storefront/src/lib/apps/v84CloneLifecycleEvidence.ts +177 -0
- package/apps/storefront/src/lib/apps/v84CloneLifecycleMaterialization.ts +251 -0
- package/apps/storefront/src/lib/apps/v84CloneLifecycleSemantics.ts +241 -0
- package/apps/storefront/src/lib/apps/v84CloneMigrationEvidence.ts +211 -0
- package/apps/storefront/src/lib/apps/v84CloneNativeInstallEvidence.ts +409 -0
- package/apps/storefront/src/lib/apps/v84NonportableCodeCoaching.ts +368 -0
- package/apps/storefront/src/lib/apps/v84NonportableRouteContract.ts +188 -0
- package/apps/storefront/src/lib/apps/v84NonportableRouteDogfood.ts +291 -0
- package/apps/storefront/src/lib/apps/widgets/eligibility.ts +2 -2
- package/apps/storefront/src/lib/apps/widgets/frameProps.ts +3 -3
- package/apps/storefront/src/lib/apps/widgets/launchToken.ts +4 -4
- package/apps/storefront/src/lib/apps/widgets/placements.ts +2 -2
- package/apps/storefront/src/lib/apps/widgets/renderSlot.ts +1 -1
- package/apps/storefront/src/lib/apps/ws1ManagedAnalyticsClient.ts +95 -0
- package/apps/storefront/src/lib/auth/adminApiGuard.ts +66 -0
- package/apps/storefront/src/lib/auth/adminEntry.ts +11 -11
- package/apps/storefront/src/lib/auth/adminLanding.ts +88 -0
- package/apps/storefront/src/lib/auth/identityToken.ts +6 -14
- package/apps/storefront/src/lib/auth/loginGate.ts +75 -13
- package/apps/storefront/src/lib/auth/mcpClientAssertion.ts +2 -4
- package/apps/storefront/src/lib/auth/operatorSession.ts +113 -0
- package/apps/storefront/src/lib/auth/session.ts +7 -8
- package/apps/storefront/src/lib/auth/totAccessClient.ts +8 -18
- package/apps/storefront/src/lib/basePath.ts +9 -2
- package/apps/storefront/src/lib/blog/access.ts +29 -0
- package/apps/storefront/src/lib/blog/collection.ts +400 -0
- package/apps/storefront/src/lib/blog/markdown.ts +174 -0
- package/apps/storefront/src/lib/blog/provider.ts +62 -3
- package/apps/storefront/src/lib/blog/reactions.ts +87 -0
- package/apps/storefront/src/lib/blog/rss.ts +92 -0
- package/apps/storefront/src/lib/blog/teaser.ts +83 -0
- package/apps/storefront/src/lib/blog/types.ts +34 -0
- package/apps/storefront/src/lib/blog/viewCounts.ts +243 -0
- package/apps/storefront/src/lib/chrome/examples/tokenoftrust.chrome-assignments.example.json +6 -0
- package/apps/storefront/src/lib/chrome/examples/tokenoftrust.chrome.example.json +79 -0
- package/apps/storefront/src/lib/chrome/model.ts +48 -253
- package/apps/storefront/src/lib/chrome/navCurrent.ts +29 -0
- package/apps/storefront/src/lib/chrome/parseConfig.ts +18 -32
- package/apps/storefront/src/lib/chrome/variant.ts +39 -0
- package/apps/storefront/src/lib/commerce/checkoutHandoffCorrelation.ts +105 -0
- package/apps/storefront/src/lib/compliance/tamper-sentinel-boot.ts +152 -0
- package/apps/storefront/src/lib/compliance/tamper-sentinel.ts +327 -0
- package/apps/storefront/src/lib/content/index.ts +5 -5
- package/apps/storefront/src/lib/content-edit/applyDiff.ts +1 -1
- package/apps/storefront/src/lib/content-edit/route.ts +1 -1
- package/apps/storefront/src/lib/d1/catalog.ts +14 -2
- package/apps/storefront/src/lib/d1/schema-apps.ts +372 -17
- package/apps/storefront/src/lib/dev/activityIngestToken.ts +2 -4
- package/apps/storefront/src/lib/dev/activityStore.ts +3 -5
- package/apps/storefront/src/lib/dev/apiBase.ts +1 -4
- package/apps/storefront/src/lib/dev/cockpit/ask.ts +49 -0
- package/apps/storefront/src/lib/dev/cockpit/clickToSource.ts +71 -0
- package/apps/storefront/src/lib/dev/cockpit/copyCommand.ts +227 -0
- package/apps/storefront/src/lib/dev/cockpit/files.ts +249 -0
- package/apps/storefront/src/lib/dev/cockpit/hostedActivity.ts +500 -0
- package/apps/storefront/src/lib/dev/cockpit/infoPopover.ts +12 -0
- package/apps/storefront/src/lib/dev/cockpit/mcpCopy.ts +34 -0
- package/apps/storefront/src/lib/dev/cockpit/monitor.ts +107 -0
- package/apps/storefront/src/lib/dev/cockpit/stepper.ts +116 -0
- package/apps/storefront/src/lib/dev/cockpit/themeToggle.ts +23 -0
- package/apps/storefront/src/lib/dev/cockpitStore.ts +1 -9
- package/apps/storefront/src/lib/dev/devDraft.ts +1 -1
- package/apps/storefront/src/lib/dev/previewStatus.ts +9 -21
- package/apps/storefront/src/lib/dev/rendezvousBroker.ts +9 -24
- package/apps/storefront/src/lib/dev/runtimeStore.ts +1 -1
- package/apps/storefront/src/lib/edgeCache.ts +34 -5
- package/apps/storefront/src/lib/email/candidateConflictEmail.ts +80 -0
- package/apps/storefront/src/lib/http/fetchTransport.ts +63 -0
- package/apps/storefront/src/lib/i18n.ts +25 -0
- package/apps/storefront/src/lib/integration/browserEnvironment.ts +137 -0
- package/apps/storefront/src/lib/integration/managedInstallDeclarations.ts +72 -0
- package/apps/storefront/src/lib/integration/nonceStamp.ts +49 -0
- package/apps/storefront/src/lib/integration/tenantDeclarations.ts +116 -0
- package/apps/storefront/src/lib/jsonld.ts +118 -0
- package/apps/storefront/src/lib/membership/eligibility.ts +1 -1
- package/apps/storefront/src/lib/membership/model.ts +1 -2
- package/apps/storefront/src/lib/monitoring/manifest.ts +11 -11
- package/apps/storefront/src/lib/newsletter/confirmationEmail.ts +30 -0
- package/apps/storefront/src/lib/newsletter/consent.ts +23 -0
- package/apps/storefront/src/lib/newsletter/digest.ts +117 -0
- package/apps/storefront/src/lib/newsletter/resendClient.ts +79 -0
- package/apps/storefront/src/lib/newsletter/send.ts +265 -0
- package/apps/storefront/src/lib/newsletter/subscribers.ts +156 -0
- package/apps/storefront/src/lib/previewReload.ts +57 -1
- package/apps/storefront/src/lib/publish/adminPublishTab.ts +4625 -0
- package/apps/storefront/src/lib/publish/apex-readiness.ts +41 -18
- package/apps/storefront/src/lib/publish/buildStream.ts +269 -0
- package/apps/storefront/src/lib/publish/collapsePersistence.ts +72 -0
- package/apps/storefront/src/lib/publish/dispatchHealth.ts +5 -7
- package/apps/storefront/src/lib/publish/domainState.ts +22 -13
- package/apps/storefront/src/lib/publish/lifecycleState.ts +369 -0
- package/apps/storefront/src/lib/publish/liveReleaseSummary.ts +29 -0
- package/apps/storefront/src/lib/publish/mergeDoctorOnFailure.ts +78 -0
- package/apps/storefront/src/lib/publish/mergeHealthBanner.ts +62 -0
- package/apps/storefront/src/lib/publish/mergeRowFindings.ts +124 -0
- package/apps/storefront/src/lib/publish/mergeSupportEscalation.ts +75 -0
- package/apps/storefront/src/lib/publish/panelPolling.ts +78 -0
- package/apps/storefront/src/lib/publish/publish-guard.ts +88 -0
- package/apps/storefront/src/lib/publish/publishFormat.ts +165 -0
- package/apps/storefront/src/lib/publish/publishHero.ts +177 -0
- package/apps/storefront/src/lib/publish/reviewRelease/changeState.ts +66 -0
- package/apps/storefront/src/lib/publish/reviewRelease/copy.ts +141 -0
- package/apps/storefront/src/lib/publish/reviewRelease/index.ts +41 -0
- package/apps/storefront/src/lib/publish/reviewRelease/permissions.ts +47 -0
- package/apps/storefront/src/lib/publish/reviewRelease/releaseState.ts +42 -0
- package/apps/storefront/src/lib/publish/reviewRelease/types.ts +59 -0
- package/apps/storefront/src/lib/publish/reviewRelease/viewModel.ts +440 -0
- package/apps/storefront/src/lib/publish/reviewReleaseWorkspace/actions.ts +161 -0
- package/apps/storefront/src/lib/publish/reviewReleaseWorkspace/controller.ts +325 -0
- package/apps/storefront/src/lib/publish/reviewReleaseWorkspace/render.ts +241 -0
- package/apps/storefront/src/lib/publish/shipWorkspace.ts +121 -51
- package/apps/storefront/src/lib/publish/sseRoute.ts +119 -0
- package/apps/storefront/src/lib/publish/stageBundleUpload.ts +215 -0
- package/apps/storefront/src/lib/publish/streamLogConsole.ts +142 -0
- package/apps/storefront/src/lib/publish/versionNumber.ts +52 -0
- package/apps/storefront/src/lib/rate/kvWindow.ts +53 -0
- package/apps/storefront/src/lib/rawChrome.ts +125 -47
- package/apps/storefront/src/lib/rawMarketingHtml.ts +74 -3
- package/apps/storefront/src/lib/search/index.ts +2 -2
- package/apps/storefront/src/lib/seo/alternates.ts +42 -0
- package/apps/storefront/src/lib/seo/meta.ts +65 -0
- package/apps/storefront/src/lib/social/golive.ts +212 -0
- package/apps/storefront/src/lib/social/notify.ts +32 -0
- package/apps/storefront/src/lib/social/tokenStore.ts +171 -0
- package/apps/storefront/src/lib/storyblok/content-model.ts +7 -33
- package/apps/storefront/src/lib/storyblok/provider.ts +168 -60
- package/apps/storefront/src/lib/subscription/model.ts +4 -4
- package/apps/storefront/src/lib/the-build/provider.ts +32 -0
- package/apps/storefront/src/lib/the-build/readingTime.ts +35 -0
- package/apps/storefront/src/lib/the-build/toc.ts +95 -0
- package/apps/storefront/src/lib/tot/ToTClient.ts +1 -1
- package/apps/storefront/src/lib/tot/d1Client.ts +2 -2
- package/apps/storefront/src/lib/tot/totClientInterface.ts +3 -4
- package/apps/storefront/src/lib/webhooks/deliveryStore.ts +16 -17
- package/apps/storefront/src/middleware/index.ts +385 -52
- package/apps/storefront/src/pages/[...slug].astro +9 -4
- package/apps/storefront/src/pages/admin/review-release.astro +265 -0
- package/apps/storefront/src/pages/admin/select.astro +96 -0
- package/apps/storefront/src/pages/admin.astro +25 -1313
- package/apps/storefront/src/pages/api/activity.ts +8 -8
- package/apps/storefront/src/pages/api/admin/activity-alerts.ts +5 -20
- package/apps/storefront/src/pages/api/admin/orders/[id].ts +7 -2
- package/apps/storefront/src/pages/api/admin/orders.ts +8 -2
- package/apps/storefront/src/pages/api/apps/admin/gtm-migration-install.ts +174 -0
- package/apps/storefront/src/pages/api/apps/admin/gtm-migration.ts +96 -0
- package/apps/storefront/src/pages/api/apps/admin/integration-lifecycle.ts +118 -0
- package/apps/storefront/src/pages/api/apps/admin/integrations.ts +89 -0
- package/apps/storefront/src/pages/api/apps/admin/platform-adapters.ts +85 -0
- package/apps/storefront/src/pages/api/apps/internal/order-forward.ts +74 -2
- package/apps/storefront/src/pages/api/blog/[collection]/[slug]/react.ts +104 -0
- package/apps/storefront/src/pages/api/commerce/checkout-handoff.ts +77 -0
- package/apps/storefront/src/pages/api/compliance/tamper.ts +88 -0
- package/apps/storefront/src/pages/api/integrations/v1/[operation].ts +108 -0
- package/apps/storefront/src/pages/api/newsletter/[collection]/subscribe.ts +146 -0
- package/apps/storefront/src/pages/api/newsletter/confirm.ts +75 -0
- package/apps/storefront/src/pages/api/newsletter/unsubscribe.ts +90 -0
- package/apps/storefront/src/pages/api/publish/stage-bundle.ts +70 -0
- package/apps/storefront/src/pages/api/rum/vitals.ts +20 -2
- package/apps/storefront/src/pages/blog/[slug].astro +99 -19
- package/apps/storefront/src/pages/blog/author/[author].astro +140 -0
- package/apps/storefront/src/pages/blog/category/[category].astro +67 -0
- package/apps/storefront/src/pages/blog/index.astro +4 -2
- package/apps/storefront/src/pages/blog/tag/[tag].astro +65 -0
- package/apps/storefront/src/pages/cockpit.astro +77 -1353
- package/apps/storefront/src/pages/dashboard/[appDomain]/team.astro +15 -11
- package/apps/storefront/src/pages/index.astro +10 -5
- package/apps/storefront/src/pages/llms.txt.ts +1 -1
- package/apps/storefront/src/pages/products/[handle].astro +7 -6
- package/apps/storefront/src/pages/search.astro +1 -1
- package/apps/storefront/src/pages/sitemap.xml.ts +21 -1
- package/apps/storefront/src/pages/style-guide/[tenant]/[theme].astro +31 -27
- package/apps/storefront/src/pages/style-guide/[tenant]/chrome/[theme].astro +1 -1
- package/apps/storefront/src/pages/tenants/[id]/[...path].ts +38 -2
- package/apps/storefront/src/pages/the-build/[slug].astro +129 -0
- package/apps/storefront/src/pages/the-build/index.astro +146 -0
- package/apps/storefront/src/pages/the-build/rss.xml.ts +33 -0
- package/apps/storefront/src/styles/admin.css +1106 -0
- package/apps/storefront/tsconfig.json +6 -1
- package/package.json +1 -1
- package/packages/public-runtime/package.json +3 -1
- package/packages/public-runtime/schemas/managed-analytics/v1/adapter-definition.schema.json +427 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/create-integration-install-request.schema.json +161 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/create-migration-draft-request.schema.json +157 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/delete-integration-install-request.schema.json +84 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/delete-migration-draft-request.schema.json +84 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/error-envelope.schema.json +180 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/get-adapter-definition-request.schema.json +52 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/get-integration-install-request.schema.json +52 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/get-integration-revision-request.schema.json +79 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/get-migration-draft-request.schema.json +52 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/integration-install.schema.json +167 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/integration-revision.schema.json +208 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/list-adapter-definitions-request.schema.json +53 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/list-integration-installs-request.schema.json +53 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/list-integration-revisions-request.schema.json +82 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/migration-draft.schema.json +218 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/mutation-request.schema.json +52 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/mutation-success-envelope.schema.json +49 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/read-request.schema.json +20 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/read-success-envelope.schema.json +45 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/revise-integration-install-request.schema.json +156 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/set-integration-install-status-request.schema.json +92 -0
- package/packages/public-runtime/schemas/managed-analytics/v1/update-migration-draft-request.schema.json +184 -0
- package/packages/public-runtime/scripts/generate-managed-analytics-api-v1-schemas.mjs +13 -0
- package/packages/public-runtime/src/activity/catalog.ts +89 -33
- package/packages/public-runtime/src/activity/event.ts +6 -6
- package/packages/public-runtime/src/activity/index.ts +2 -2
- package/packages/public-runtime/src/activity/redaction.ts +4 -4
- package/packages/public-runtime/src/candidate-index.ts +42 -8
- package/packages/public-runtime/src/catalog-d1.ts +1 -1
- package/packages/public-runtime/src/checkout-orders.ts +2 -2
- package/packages/public-runtime/src/checkout-style-publish.ts +2 -2
- package/packages/public-runtime/src/checkout-style.ts +7 -7
- package/packages/public-runtime/src/checkout.ts +3 -3
- package/packages/public-runtime/src/chrome.ts +777 -0
- package/packages/public-runtime/src/compliance-obligations.ts +310 -0
- package/packages/public-runtime/src/csp.ts +99 -0
- package/packages/public-runtime/src/customization-preview.ts +25 -2
- package/packages/public-runtime/src/customization-runtime.ts +156 -3
- package/packages/public-runtime/src/customization-versioning.ts +10 -0
- package/packages/public-runtime/src/destination-webhook/contract.ts +353 -0
- package/packages/public-runtime/src/embed-catalog.d.mts +9 -0
- package/packages/public-runtime/src/embed-catalog.mjs +40 -0
- package/packages/public-runtime/src/evidence.ts +288 -0
- package/packages/public-runtime/src/gtm-migration/bounds.ts +138 -0
- package/packages/public-runtime/src/gtm-migration/disposition-rules.ts +361 -0
- package/packages/public-runtime/src/gtm-migration/dispositions.ts +181 -0
- package/packages/public-runtime/src/gtm-migration/draft-generator.ts +400 -0
- package/packages/public-runtime/src/gtm-migration/graph.ts +468 -0
- package/packages/public-runtime/src/gtm-migration/index.ts +95 -0
- package/packages/public-runtime/src/gtm-migration/parse.ts +410 -0
- package/packages/public-runtime/src/gtm-migration/references.ts +266 -0
- package/packages/public-runtime/src/gtm-migration/types.ts +86 -0
- package/packages/public-runtime/src/index.ts +17 -1
- package/packages/public-runtime/src/integration/assurance-decision.ts +259 -0
- package/packages/public-runtime/src/integration/assurance.ts +228 -0
- package/packages/public-runtime/src/integration/candidate-evidence.ts +377 -0
- package/packages/public-runtime/src/integration/commerce-broker.ts +497 -0
- package/packages/public-runtime/src/integration/consent-broker.ts +337 -0
- package/packages/public-runtime/src/integration/consent-cmp-demand.ts +115 -0
- package/packages/public-runtime/src/integration/consent-config.ts +260 -0
- package/packages/public-runtime/src/integration/consent-preview.ts +125 -0
- package/packages/public-runtime/src/integration/correlation-token.ts +444 -0
- package/packages/public-runtime/src/integration/csp-compiler.ts +267 -0
- package/packages/public-runtime/src/integration/csp.ts +105 -0
- package/packages/public-runtime/src/integration/digest.ts +43 -0
- package/packages/public-runtime/src/integration/environment.ts +228 -0
- package/packages/public-runtime/src/integration/events-catalog.ts +186 -0
- package/packages/public-runtime/src/integration/events.ts +701 -0
- package/packages/public-runtime/src/integration/g2-action-boundary.ts +106 -0
- package/packages/public-runtime/src/integration/index.ts +90 -0
- package/packages/public-runtime/src/integration/interim-bridge.ts +138 -0
- package/packages/public-runtime/src/integration/lifecycle-boot.ts +317 -0
- package/packages/public-runtime/src/integration/lifecycle-runtime.ts +393 -0
- package/packages/public-runtime/src/integration/lifecycle.ts +94 -0
- package/packages/public-runtime/src/integration/managed-analytics-api-v1-safety.d.mts +2 -0
- package/packages/public-runtime/src/integration/managed-analytics-api-v1-safety.mjs +10 -0
- package/packages/public-runtime/src/integration/managed-analytics-api-v1.schemas.ts +382 -0
- package/packages/public-runtime/src/integration/managed-analytics-api-v1.ts +684 -0
- package/packages/public-runtime/src/integration/managed-analytics-bridge.ts +383 -0
- package/packages/public-runtime/src/integration/managed-analytics-value-safety.ts +14 -0
- package/packages/public-runtime/src/integration/managed-analytics.ts +277 -0
- package/packages/public-runtime/src/integration/manifest.ts +470 -0
- package/packages/public-runtime/src/integration/native-amplitude.ts +362 -0
- package/packages/public-runtime/src/integration/native-ga4.ts +352 -0
- package/packages/public-runtime/src/integration/observability.ts +451 -0
- package/packages/public-runtime/src/integration/order-conversion.ts +0 -0
- package/packages/public-runtime/src/integration/phases.ts +182 -0
- package/packages/public-runtime/src/integration/plan.ts +112 -0
- package/packages/public-runtime/src/integration/platform-adapter-runtime.ts +146 -0
- package/packages/public-runtime/src/integration/platform-adapters.ts +671 -0
- package/packages/public-runtime/src/integration/promotion-gate.ts +451 -0
- package/packages/public-runtime/src/integration/provider-checkout-extension.ts +425 -0
- package/packages/public-runtime/src/integration/receipt-measurement.ts +370 -0
- package/packages/public-runtime/src/integration/regulated-parent-cert.ts +311 -0
- package/packages/public-runtime/src/integration/render-phases.ts +267 -0
- package/packages/public-runtime/src/integration/resolve.ts +465 -0
- package/packages/public-runtime/src/integration/result.ts +239 -0
- package/packages/public-runtime/src/integration/secrets.ts +92 -0
- package/packages/public-runtime/src/integration/tamper-evidence.ts +205 -0
- package/packages/public-runtime/src/integration/tamper-recovery.ts +127 -0
- package/packages/public-runtime/src/integration/version.ts +92 -0
- package/packages/public-runtime/src/integration-capabilities.ts +520 -0
- package/packages/public-runtime/src/membership.ts +2 -2
- package/packages/public-runtime/src/raw-html-policy.ts +2 -4
- package/packages/public-runtime/src/regulated-commerce/broker/consent-dedupe.ts +144 -0
- package/packages/public-runtime/src/regulated-commerce/correlation/contract.ts +177 -0
- package/packages/public-runtime/src/regulated-commerce/diagnostics/local-dispatch.ts +24 -0
- package/packages/public-runtime/src/regulated-commerce/diagnostics/read-model.ts +69 -0
- package/packages/public-runtime/src/regulated-commerce/dispatch/core.ts +134 -0
- package/packages/public-runtime/src/regulated-commerce/events/contract.ts +113 -0
- package/packages/public-runtime/src/regulated-commerce/ledger/core.ts +81 -0
- package/packages/public-runtime/src/regulated-commerce/mappings/ga4-ads.ts +87 -0
- package/packages/public-runtime/src/regulated-commerce/provider-order/bridge.ts +52 -0
- package/packages/public-runtime/src/regulated-commerce/purchase-truth/contract.ts +252 -0
- package/packages/public-runtime/src/regulated-commerce/sources/accepted-browser-operation.ts +67 -0
- package/packages/public-runtime/src/regulated-commerce/testing/fake-destination.ts +93 -0
- package/packages/public-runtime/src/static-bundle.ts +357 -0
- package/packages/public-runtime/src/tenant-assets.ts +143 -4
- package/packages/public-runtime/src/tenant.ts +81 -17
- package/packages/public-runtime/tests/fixtures/integration/browser-environment.example.json +8 -0
- package/packages/public-runtime/tests/fixtures/integration/commerce-event.example.json +44 -0
- package/packages/public-runtime/tests/fixtures/integration/commerce-order-event.example.json +44 -0
- package/packages/public-runtime/tests/fixtures/integration/compliance-assurance-record.example.json +50 -0
- package/packages/public-runtime/tests/fixtures/integration/integration-manifest-v2.example.json +120 -0
- package/packages/public-runtime/tests/fixtures/integration/managed-analytics-v1/fake-adapter-catalog.json +35 -0
- package/packages/public-runtime/tests/fixtures/integration/tenant-action-registry.example.json +11 -0
- package/packages/public-runtime/tests/fixtures/integration/tenant-local-example/README.md +22 -0
- package/packages/public-runtime/tests/fixtures/integration/tenant-local-example/entry.js +30 -0
- package/packages/public-runtime/tests/fixtures/integration/tenant-local-example/manifest.json +75 -0
- package/packages/public-runtime/tests/fixtures/integration/tenant-local-example/public-config.json +3 -0
- package/packages/public-runtime/tests/fixtures/regulated-commerce/fake-destinations.ts +38 -0
- package/packages/public-runtime/tests/gtm-migration/fixtures/gtm-sample-container-v84.provenance.json +29 -0
- package/packages/public-runtime/tests/gtm-migration/fixtures/gtm-sample-container-v84.redacted.json +3397 -0
- package/scripts/build/copy-tenant-assets.mjs +14 -4
- package/scripts/dev/ai-edit.mjs +1 -1
- package/scripts/dev/checkout-watch.mjs +2 -2
- package/scripts/dev/file-browser.mjs +4 -6
- package/scripts/dev/file-writer.mjs +3 -3
- package/scripts/dev/git-status.mjs +2 -2
- package/scripts/dev/integration-guardrails.mjs +498 -0
- package/scripts/dev/locate-handler.mjs +3 -3
- package/scripts/dev/port-check.mjs +3 -3
- package/scripts/dev/publish.mjs +5 -9
- package/scripts/dev/safe-path.mjs +2 -2
- package/scripts/dev/shot.mjs +1 -1
- package/scripts/dev/tenant-source-root.mjs +1 -1
- package/scripts/dev/unified-diff.mjs +8 -3
- package/scripts/tot-dev.mjs +8 -11
- package/apps/storefront/src/lib/dev/vcBinding.ts +0 -80
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* POST /api/activity — the ingest endpoint of the operational activity telemetry
|
|
3
|
-
* spine
|
|
4
|
-
* the emitter cohorts — the CLI
|
|
3
|
+
* spine. Accepts one `ActivityEvent` (or a batch array) as JSON from
|
|
4
|
+
* the emitter cohorts — the CLI and the UI, both posting the wire shape
|
|
5
5
|
* of `packages/public-runtime/src/activity/event.ts` — authenticates the caller,
|
|
6
6
|
* re-redacts server-side, and fans each accepted event out to the sink(s) its
|
|
7
7
|
* catalog entry declares. See DECISIONS.md §8 for the store decision.
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
* DEFENSE IN DEPTH — every event is rebuilt through `createActivityEvent()`
|
|
19
19
|
* (`ingestEvents`), re-applying the default-deny allowlist + canary scan even
|
|
20
20
|
* though emitters already redacted. A value-canary trip is self-telemetried as
|
|
21
|
-
* `system.ingest.rejected` so a leak attempt is visible
|
|
21
|
+
* `system.ingest.rejected` so a leak attempt is visible for alerting.
|
|
22
22
|
*
|
|
23
23
|
* OFF THE CRITICAL PATH — auth + parse + validate + redact are cheap synchronous
|
|
24
24
|
* CPU; the sink WRITES (D1 + Analytics Engine) run past the response via
|
|
@@ -50,7 +50,7 @@ function json(body: unknown, status: number): Response {
|
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
export async function POST(context: APIContext): Promise<Response> {
|
|
53
|
-
// Kill switch
|
|
53
|
+
// Kill switch: telemetry off ⇒ accept-and-drop. Fail-open toward the EMITTER —
|
|
54
54
|
// a 202 (never a 4xx/5xx) so a CLI/UI poster never errors, retries, or is delayed by
|
|
55
55
|
// the switch; nothing is validated, stored, or fanned out.
|
|
56
56
|
if (isActivityDisabled()) {
|
|
@@ -96,8 +96,8 @@ export async function POST(context: APIContext): Promise<Response> {
|
|
|
96
96
|
|
|
97
97
|
// Self-telemetry: a value-canary trip during server-side redaction means an
|
|
98
98
|
// emitter leaked (or tried to leak) a secret/PII into the payload. Emit a
|
|
99
|
-
// `system.ingest.rejected` event (itself routed to logs+analytics) so
|
|
100
|
-
//
|
|
99
|
+
// `system.ingest.rejected` event (itself routed to logs+analytics) so it can be
|
|
100
|
+
// alerted on — the fact of the trip, the action, never the offending value. Rejected
|
|
101
101
|
// events get the same self-telemetry so a spoof/unknown-action attempt is
|
|
102
102
|
// visible. Built here (synchronously) but fanned out off-path below.
|
|
103
103
|
const selfTelemetry = [
|
|
@@ -127,9 +127,9 @@ export async function POST(context: APIContext): Promise<Response> {
|
|
|
127
127
|
|
|
128
128
|
// Fan every accepted event + the self-telemetry out to its sinks, PAST the
|
|
129
129
|
// response. Best-effort per sink (never throws). The adapter's per-request
|
|
130
|
-
// ExecutionContext lives at `locals.
|
|
130
|
+
// ExecutionContext lives at `locals.cfContext`; runAfterResponse floats the
|
|
131
131
|
// promise in dev/Node when it is absent.
|
|
132
|
-
const cfContext = (context.locals as {
|
|
132
|
+
const cfContext = (context.locals as { cfContext?: CfExecutionContextLike })?.cfContext ?? null;
|
|
133
133
|
const work = Promise.all([...accepted.map((a) => a.event), ...selfTelemetry].map((e) => fanOutActivity(e, sinks)));
|
|
134
134
|
runAfterResponse(work, cfContext);
|
|
135
135
|
|
|
@@ -25,9 +25,7 @@
|
|
|
25
25
|
* ops-timeline; never a URL/query value.
|
|
26
26
|
*/
|
|
27
27
|
import type { APIContext } from "astro";
|
|
28
|
-
import {
|
|
29
|
-
import { resolveAdminEntry } from "@/lib/auth/adminEntry";
|
|
30
|
-
import { resolveAdminTenantMode } from "@/lib/adminTenantEnvelope";
|
|
28
|
+
import { requireAdminApiEntry } from "@/lib/auth/adminApiGuard";
|
|
31
29
|
import { readD1 } from "@/lib/env";
|
|
32
30
|
import { fromD1 } from "@/lib/d1/catalog";
|
|
33
31
|
import { checkActivityAlerts } from "@/lib/activity/alerts";
|
|
@@ -46,24 +44,11 @@ function json(body: unknown, status = 200): Response {
|
|
|
46
44
|
|
|
47
45
|
export async function GET(context: APIContext): Promise<Response> {
|
|
48
46
|
const { tenant } = context.locals;
|
|
49
|
-
const nowSeconds = Math.floor(Date.now() / 1000);
|
|
50
47
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
record: viewerSession,
|
|
56
|
-
hostResource: tenant.appDomain,
|
|
57
|
-
nowSeconds,
|
|
58
|
-
});
|
|
59
|
-
const { isStaff } = resolveAdminTenantMode({
|
|
60
|
-
session: viewerSession,
|
|
61
|
-
adminAppTenant: tenant.appDomain,
|
|
62
|
-
requestedTarget: null,
|
|
63
|
-
nowSeconds,
|
|
64
|
-
});
|
|
65
|
-
const authorized = adminEntry.isOwnerViewer || isStaff;
|
|
66
|
-
if (!authorized) return json({ ok: false, reason: "not_authorized" }, 403);
|
|
48
|
+
// Owner/admin-of-tenant or ToT-staff only — same bar as /admin/ops-timeline,
|
|
49
|
+
// fail-closed and bound to the Host-resolved tenant.
|
|
50
|
+
const access = await requireAdminApiEntry(context, { requireOwner: true });
|
|
51
|
+
if (!access.ok) return json({ ok: false, reason: access.reason }, access.status);
|
|
67
52
|
|
|
68
53
|
const d1 = await readD1("STOREFRONT_APPS_DB");
|
|
69
54
|
if (!d1) return json({ ok: true, dbAvailable: false, alerts: [] });
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* `GET /api/admin/orders/:id` — one order's full detail for the Orders tab
|
|
2
|
+
* `GET /api/admin/orders/:id` — one order's full detail for the Orders tab.
|
|
3
3
|
*
|
|
4
4
|
* Server-side proxy to checkout `GET /commerce/orders/:orderId` for the
|
|
5
5
|
* resolved tenant + env. Bearer stays server-side; response is the CheckoutOrderDetail
|
|
6
|
-
* DTO. `verificationDetail` is empty
|
|
6
|
+
* DTO. `verificationDetail` is currently empty — consumers tolerate that.
|
|
7
7
|
* Read-only (scope-locks).
|
|
8
8
|
*/
|
|
9
9
|
import type { APIContext } from "astro";
|
|
10
10
|
import { readEnv } from "@/lib/env";
|
|
11
|
+
import { requireAdminApiEntry } from "@/lib/auth/adminApiGuard";
|
|
11
12
|
import {
|
|
12
13
|
createCheckoutOrderClient,
|
|
13
14
|
orderApiBaseUrl,
|
|
@@ -31,6 +32,10 @@ function jsonError(code: string, status: number): Response {
|
|
|
31
32
|
|
|
32
33
|
export async function GET(context: APIContext): Promise<Response> {
|
|
33
34
|
const { tenant, customizationEnv } = context.locals;
|
|
35
|
+
|
|
36
|
+
const access = await requireAdminApiEntry(context);
|
|
37
|
+
if (!access.ok) return jsonError(access.reason, access.status);
|
|
38
|
+
|
|
34
39
|
const orderId = context.params.id;
|
|
35
40
|
if (!orderId) return jsonError("order_not_found", 404);
|
|
36
41
|
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* `GET /api/admin/orders` — internal admin route backing the Orders tab
|
|
2
|
+
* `GET /api/admin/orders` — internal admin route backing the Orders tab.
|
|
3
3
|
*
|
|
4
4
|
* Server-side proxy to the checkout order READ API for the resolved tenant +
|
|
5
5
|
* environment. The per-tenant bearer key stays on the Worker (never in the
|
|
6
6
|
* response); the browser only ever sees order DTO JSON. Read-only (scope-locks).
|
|
7
|
-
*
|
|
7
|
+
* The admin UI consumes this and maps the DTO → OrdersViewModel.
|
|
8
8
|
*
|
|
9
9
|
* Access control matches the rest of the admin surface (owned by the admin
|
|
10
10
|
* host/approver layer) — same posture as `api/evidence.json.ts`; no new gate here.
|
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
import type { APIContext } from "astro";
|
|
13
13
|
import type { CheckoutOrderListFilters } from "@tot/public-runtime";
|
|
14
14
|
import { readEnv } from "@/lib/env";
|
|
15
|
+
import { requireAdminApiEntry } from "@/lib/auth/adminApiGuard";
|
|
15
16
|
import {
|
|
16
17
|
createCheckoutOrderClient,
|
|
17
18
|
orderApiBaseUrl,
|
|
@@ -36,6 +37,11 @@ function jsonError(code: string, status: number): Response {
|
|
|
36
37
|
export async function GET(context: APIContext): Promise<Response> {
|
|
37
38
|
const { tenant, customizationEnv } = context.locals;
|
|
38
39
|
|
|
40
|
+
// Fail closed: require a signed-in member/owner of THIS tenant, bound to the
|
|
41
|
+
// Host-resolved appDomain — never trusting the conditional login gate for auth.
|
|
42
|
+
const access = await requireAdminApiEntry(context);
|
|
43
|
+
if (!access.ok) return jsonError(access.reason, access.status);
|
|
44
|
+
|
|
39
45
|
const bearer = await readEnv("FOXY_ORDER_API_KEY");
|
|
40
46
|
if (!bearer) {
|
|
41
47
|
// Worker secret not provisioned for this env — a config error, not the
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* POST /api/apps/admin/gtm-migration-install — the review->install surface of
|
|
3
|
+
* the GTM migration assistant. It takes a reviewed draft's `draftId`, LOADS the
|
|
4
|
+
* stored draft server-side, and converts it into a DISABLED desired
|
|
5
|
+
* managed-analytics install + draft revision, strictly through WS1's pinned
|
|
6
|
+
* PUBLIC contract. It returns the resulting disabled install/revision + a
|
|
7
|
+
* correlation id.
|
|
8
|
+
*
|
|
9
|
+
* Auth mirrors the read-only analyze route (`gtm-migration.ts`): the tenant is
|
|
10
|
+
* `auth.tenantId` from the resolved operator session — NEVER the request body.
|
|
11
|
+
*
|
|
12
|
+
* SECURITY BOUNDARY (a reviewer enforces this):
|
|
13
|
+
* - LOAD-BY-ID ONLY: the body carries a `draftId` (+ optional
|
|
14
|
+
* `targetInstallId` and its original `expectedRevision`), never a draft
|
|
15
|
+
* object. The stable mutation identity comes from the required
|
|
16
|
+
* `Idempotency-Key` header. A body that tries to smuggle draft data
|
|
17
|
+
* (`proposedConfiguration`/`proposedAdapterId`/`findings`/`draft`/inline
|
|
18
|
+
* `configuration`) is rejected — the stored draft is loaded from WS1 and
|
|
19
|
+
* re-validated against the pinned validator; untrusted draft data never gets
|
|
20
|
+
* past review.
|
|
21
|
+
* - DISABLED-ONLY: the produced install is `status:"disabled"` with a
|
|
22
|
+
* `status:"draft"` revision. Any attempt to force enable/publish/activate in
|
|
23
|
+
* the body is refused. This route imports NO registry/D1/R2/materializer/
|
|
24
|
+
* lifecycle dependency and writes NOTHING to storefront's own stores — every
|
|
25
|
+
* effect goes through the pinned WS1 client, which exposes no enable op.
|
|
26
|
+
*/
|
|
27
|
+
import type { APIContext } from "astro";
|
|
28
|
+
import {
|
|
29
|
+
validateGetIntegrationInstallRequest,
|
|
30
|
+
validateGetMigrationDraftRequest,
|
|
31
|
+
validateMutationRequest,
|
|
32
|
+
} from "@tot/public-runtime";
|
|
33
|
+
import { resolveOperatorSession } from "@/lib/apps/adminSession.js";
|
|
34
|
+
import { json } from "@/lib/apps/adminService.js";
|
|
35
|
+
import {
|
|
36
|
+
reviewInstallDraft,
|
|
37
|
+
type ReviewInstallResult,
|
|
38
|
+
type Ws1ManagedAnalyticsClient,
|
|
39
|
+
} from "@/lib/apps/gtmMigrationInstall.js";
|
|
40
|
+
import { resolveWs1ManagedAnalyticsClient } from "@/lib/apps/ws1ManagedAnalyticsClient.js";
|
|
41
|
+
|
|
42
|
+
export const prerender = false;
|
|
43
|
+
|
|
44
|
+
/** Fields that would smuggle inline draft data past review — a body may carry only ids. */
|
|
45
|
+
const DRAFT_DATA_FIELDS = ["draft", "proposedConfiguration", "proposedAdapterId", "findings", "configuration"] as const;
|
|
46
|
+
/** Fields that would try to enable/publish/activate — this path produces a disabled draft only. */
|
|
47
|
+
const ENABLE_FIELDS = ["status", "enabled", "enable", "publish", "publishes", "activate"] as const;
|
|
48
|
+
|
|
49
|
+
interface ParsedRequest {
|
|
50
|
+
readonly draftId: string;
|
|
51
|
+
readonly targetInstallId?: string;
|
|
52
|
+
readonly expectedRevision?: number;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** Parse ids plus optional revise CAS state, and NOTHING that could enable or resubmit a draft. */
|
|
56
|
+
function parseRequest(body: unknown): ParsedRequest | { error: string } {
|
|
57
|
+
if (!body || typeof body !== "object") return { error: "A JSON object body with a draftId is required." };
|
|
58
|
+
const o = body as Record<string, unknown>;
|
|
59
|
+
|
|
60
|
+
for (const field of DRAFT_DATA_FIELDS) {
|
|
61
|
+
if (field in o) {
|
|
62
|
+
return { error: "Submit a draftId only; the reviewed draft is loaded server-side and cannot be supplied inline." };
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
for (const field of ENABLE_FIELDS) {
|
|
66
|
+
if (field in o) {
|
|
67
|
+
return { error: "This path produces a disabled draft only; it cannot enable, publish, or activate." };
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const rawDraftId = o.draftId;
|
|
72
|
+
if (typeof rawDraftId !== "string" || !rawDraftId.trim()) return { error: "A draftId is required." };
|
|
73
|
+
const draftCheck = validateGetMigrationDraftRequest({
|
|
74
|
+
apiVersion: "v1",
|
|
75
|
+
query: { draftId: rawDraftId },
|
|
76
|
+
});
|
|
77
|
+
if (!draftCheck.ok) {
|
|
78
|
+
return { error: "draftId must be a valid opaque identifier." };
|
|
79
|
+
}
|
|
80
|
+
const draftId = draftCheck.value.query.draftId;
|
|
81
|
+
|
|
82
|
+
let targetInstallId: string | undefined;
|
|
83
|
+
if ("targetInstallId" in o) {
|
|
84
|
+
const t = o.targetInstallId;
|
|
85
|
+
if (typeof t !== "string" || !t.trim()) return { error: "targetInstallId must be a non-empty string when present." };
|
|
86
|
+
const installCheck = validateGetIntegrationInstallRequest({
|
|
87
|
+
apiVersion: "v1",
|
|
88
|
+
query: { installId: t },
|
|
89
|
+
});
|
|
90
|
+
if (!installCheck.ok) {
|
|
91
|
+
return { error: "targetInstallId must be a valid opaque identifier." };
|
|
92
|
+
}
|
|
93
|
+
targetInstallId = installCheck.value.query.installId;
|
|
94
|
+
}
|
|
95
|
+
let expectedRevision: number | undefined;
|
|
96
|
+
if ("expectedRevision" in o) {
|
|
97
|
+
if (!Number.isInteger(o.expectedRevision) || (o.expectedRevision as number) < 0) {
|
|
98
|
+
return { error: "expectedRevision must be a non-negative integer when present." };
|
|
99
|
+
}
|
|
100
|
+
expectedRevision = o.expectedRevision as number;
|
|
101
|
+
}
|
|
102
|
+
if (targetInstallId !== undefined && expectedRevision === undefined) {
|
|
103
|
+
return { error: "expectedRevision is required when targetInstallId is present so retries preserve the original CAS request." };
|
|
104
|
+
}
|
|
105
|
+
if (targetInstallId === undefined && expectedRevision !== undefined) {
|
|
106
|
+
return { error: "expectedRevision is only accepted when targetInstallId is present." };
|
|
107
|
+
}
|
|
108
|
+
return { draftId, targetInstallId, expectedRevision };
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export async function POST(context: APIContext): Promise<Response> {
|
|
112
|
+
const url = new URL(context.request.url);
|
|
113
|
+
const requestedTenant = url.searchParams.get("tenant");
|
|
114
|
+
const auth = await resolveOperatorSession(context, requestedTenant);
|
|
115
|
+
if (!auth.ok) return json({ error: auth.message }, auth.status);
|
|
116
|
+
|
|
117
|
+
const correlationId = crypto.randomUUID();
|
|
118
|
+
|
|
119
|
+
let body: unknown;
|
|
120
|
+
try {
|
|
121
|
+
body = await context.request.json();
|
|
122
|
+
} catch {
|
|
123
|
+
return json({ ok: false, correlationId, error: "The request body must be JSON." }, 400);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const parsed = parseRequest(body);
|
|
127
|
+
if ("error" in parsed) return json({ ok: false, correlationId, error: parsed.error }, 400);
|
|
128
|
+
|
|
129
|
+
const idempotencyKey = context.request.headers.get("idempotency-key")?.trim();
|
|
130
|
+
const keyCheck = validateMutationRequest({
|
|
131
|
+
apiVersion: "v1",
|
|
132
|
+
idempotencyKey,
|
|
133
|
+
expectedRevision: 0,
|
|
134
|
+
body: {},
|
|
135
|
+
});
|
|
136
|
+
if (!keyCheck.ok) {
|
|
137
|
+
return json({ ok: false, correlationId, error: "A valid Idempotency-Key header is required." }, 400);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
let client: Ws1ManagedAnalyticsClient | null;
|
|
141
|
+
try {
|
|
142
|
+
client = await resolveWs1ManagedAnalyticsClient(context, auth.tenantId);
|
|
143
|
+
} catch {
|
|
144
|
+
return json({ ok: false, correlationId, error: "Managed analytics service resolution failed safely.", code: "internal_error" }, 502);
|
|
145
|
+
}
|
|
146
|
+
if (!client) return json({ ok: false, correlationId, error: "Managed analytics service is not configured." }, 503);
|
|
147
|
+
|
|
148
|
+
let result: ReviewInstallResult;
|
|
149
|
+
try {
|
|
150
|
+
result = await reviewInstallDraft(client, {
|
|
151
|
+
draftId: parsed.draftId,
|
|
152
|
+
targetInstallId: parsed.targetInstallId,
|
|
153
|
+
expectedRevision: parsed.expectedRevision,
|
|
154
|
+
idempotencyKey: keyCheck.value.idempotencyKey,
|
|
155
|
+
correlationId,
|
|
156
|
+
});
|
|
157
|
+
} catch {
|
|
158
|
+
return json({ ok: false, correlationId, error: "Managed analytics review failed safely.", code: "internal_error" }, 502);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
if (!result.ok) {
|
|
162
|
+
return json({ ok: false, correlationId, error: result.error, code: result.code }, result.status);
|
|
163
|
+
}
|
|
164
|
+
return json(
|
|
165
|
+
{
|
|
166
|
+
ok: true,
|
|
167
|
+
correlationId,
|
|
168
|
+
install: result.install,
|
|
169
|
+
revision: result.revision,
|
|
170
|
+
idempotencyReplayed: result.idempotencyReplayed,
|
|
171
|
+
},
|
|
172
|
+
200,
|
|
173
|
+
);
|
|
174
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* POST /api/apps/admin/gtm-migration — the READ-ONLY "Migrate from GTM" analyze
|
|
3
|
+
* surface. Accepts an uploaded GTM container export (a JSON request body, or a
|
|
4
|
+
* `multipart/form-data` file part), runs the SAFE `@tot/public-runtime` pipeline
|
|
5
|
+
* (parse -> graph -> classify) over it, and returns a REDACTED report: object
|
|
6
|
+
* accounting, the disposition summary, per-object dispositions, the dependency
|
|
7
|
+
* graph, unresolved findings, and recommendations. `?format=bundle` returns the
|
|
8
|
+
* same report as a downloadable JSON attachment with a provenance stamp.
|
|
9
|
+
*
|
|
10
|
+
* Auth mirrors the read-only `integrations` route: the tenant is `auth.tenantId`
|
|
11
|
+
* from the resolved operator session — NEVER the request body — and any tenant
|
|
12
|
+
* membership admits (analyze is a diagnostic read, not a manage action).
|
|
13
|
+
*
|
|
14
|
+
* READ-ONLY BOUNDARY (a reviewer enforces this): this route writes NOTHING,
|
|
15
|
+
* builds no integration or draft, publishes nothing, activates nothing, and
|
|
16
|
+
* emits no markup/script/network-to-third-parties/event-listener. It imports
|
|
17
|
+
* only the operator-session gate, the JSON responder, and the pure, inert
|
|
18
|
+
* pipeline + report builder — no registry, D1, R2, or lifecycle dependency is
|
|
19
|
+
* imported or reachable. It parses inert data and returns a report. Ids from a
|
|
20
|
+
* foreign container are reflected back only; they are never used to look
|
|
21
|
+
* anything up about this tenant.
|
|
22
|
+
*/
|
|
23
|
+
import type { APIContext } from "astro";
|
|
24
|
+
import { resolveOperatorSession } from "@/lib/apps/adminSession.js";
|
|
25
|
+
import { json } from "@/lib/apps/adminService.js";
|
|
26
|
+
import { analyzeGtmExport } from "@/lib/apps/gtmMigrationReport.js";
|
|
27
|
+
|
|
28
|
+
export const prerender = false;
|
|
29
|
+
|
|
30
|
+
/** The multipart field name the upload UI posts the export file under. */
|
|
31
|
+
const FILE_FIELD = "file";
|
|
32
|
+
|
|
33
|
+
/** Read the uploaded export text from a JSON body or a multipart file part. */
|
|
34
|
+
async function readUploadText(request: Request): Promise<string | { error: string }> {
|
|
35
|
+
const contentType = (request.headers.get("content-type") ?? "").toLowerCase();
|
|
36
|
+
if (contentType.includes("multipart/form-data")) {
|
|
37
|
+
let form: FormData;
|
|
38
|
+
try {
|
|
39
|
+
form = await request.formData();
|
|
40
|
+
} catch {
|
|
41
|
+
return { error: "could not read the uploaded form" };
|
|
42
|
+
}
|
|
43
|
+
const part = form.get(FILE_FIELD);
|
|
44
|
+
if (part == null) return { error: `no "${FILE_FIELD}" part in the upload` };
|
|
45
|
+
if (typeof part === "string") return part;
|
|
46
|
+
return await part.text();
|
|
47
|
+
}
|
|
48
|
+
// Any other content type: the raw request body IS the export document.
|
|
49
|
+
return await request.text();
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export async function POST(context: APIContext): Promise<Response> {
|
|
53
|
+
const url = new URL(context.request.url);
|
|
54
|
+
const requestedTenant = url.searchParams.get("tenant");
|
|
55
|
+
const auth = await resolveOperatorSession(context, requestedTenant);
|
|
56
|
+
if (!auth.ok) return json({ error: auth.message }, auth.status);
|
|
57
|
+
|
|
58
|
+
const upload = await readUploadText(context.request);
|
|
59
|
+
const correlationId = crypto.randomUUID();
|
|
60
|
+
const generatedAt = new Date().toISOString();
|
|
61
|
+
|
|
62
|
+
if (typeof upload !== "string") {
|
|
63
|
+
return json({ ok: false, correlationId, error: upload.error, violations: [] }, 400);
|
|
64
|
+
}
|
|
65
|
+
if (upload.trim() === "") {
|
|
66
|
+
return json(
|
|
67
|
+
{ ok: false, correlationId, error: "The upload was empty.", violations: [] },
|
|
68
|
+
400,
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const result = analyzeGtmExport(upload, { tenantId: auth.tenantId, correlationId, generatedAt });
|
|
73
|
+
|
|
74
|
+
if (!result.ok) {
|
|
75
|
+
// Structured parse violations + a stable, safe error and the correlation id.
|
|
76
|
+
return json(
|
|
77
|
+
{ ok: false, correlationId, error: result.error, violations: result.violations },
|
|
78
|
+
422,
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const wantsBundle = url.searchParams.get("format") === "bundle";
|
|
83
|
+
const responseBody = { ok: true, correlationId, report: result.report };
|
|
84
|
+
if (!wantsBundle) return json(responseBody, 200);
|
|
85
|
+
|
|
86
|
+
// Support-bundle download: the same redacted report as a JSON attachment.
|
|
87
|
+
const filename = `gtm-migration-report-${correlationId}.json`;
|
|
88
|
+
return new Response(JSON.stringify(responseBody, null, 2), {
|
|
89
|
+
status: 200,
|
|
90
|
+
headers: {
|
|
91
|
+
"content-type": "application/json",
|
|
92
|
+
"cache-control": "no-store",
|
|
93
|
+
"content-disposition": `attachment; filename="${filename}"`,
|
|
94
|
+
},
|
|
95
|
+
});
|
|
96
|
+
}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* POST /api/apps/admin/integration-lifecycle — execute an integration-config
|
|
3
|
+
* lifecycle write action (ext-admin-07): `preview` a candidate (its effective
|
|
4
|
+
* code/config/CSP/consent/assurance diff), `publish` it to live THROUGH the
|
|
5
|
+
* policy-06 promotion gate, or `rollback` live to a known-good revision. The
|
|
6
|
+
* install-status actions (`suspend` / `resume` / `uninstall`) keep their own
|
|
7
|
+
* registry-backed routes; this route owns the three that ride the
|
|
8
|
+
* candidate/change-control substrate.
|
|
9
|
+
*
|
|
10
|
+
* Authorization mirrors every mutating admin route: `resolveOperatorSession`
|
|
11
|
+
* resolves the tenant from the host/console session (never the body), and
|
|
12
|
+
* `requireManageRole` denies a read-only console `developer` — so a developer or
|
|
13
|
+
* vendor never holds tenant publish authority. `requireOwnedInstall` rejects a
|
|
14
|
+
* foreign-tenant `installId` with the same 404 a missing one gets.
|
|
15
|
+
*
|
|
16
|
+
* The pure {@link handleLifecycleRequest} decides and persists ONLY on success —
|
|
17
|
+
* a blocked publish leaves the ACTUAL last-good state untouched. The lifecycle
|
|
18
|
+
* state + candidate-evidence + gate-input materialization is the control plane's
|
|
19
|
+
* job (the same materialization ext-admin-02's read model resolves per install);
|
|
20
|
+
* until an install is materialized the controller answers a 409, never a
|
|
21
|
+
* fabricated success.
|
|
22
|
+
*/
|
|
23
|
+
import type { APIContext } from "astro";
|
|
24
|
+
import { resolveOperatorSession } from "@/lib/apps/adminSession.js";
|
|
25
|
+
import {
|
|
26
|
+
getRegistryService,
|
|
27
|
+
requireOwnedInstall,
|
|
28
|
+
requireManageRole,
|
|
29
|
+
parseInstallId,
|
|
30
|
+
json,
|
|
31
|
+
} from "@/lib/apps/adminService.js";
|
|
32
|
+
import {
|
|
33
|
+
handleLifecycleRequest,
|
|
34
|
+
type LifecycleDeps,
|
|
35
|
+
type LifecycleWriteAction,
|
|
36
|
+
} from "@/lib/apps/integrationLifecycleController.js";
|
|
37
|
+
import { parseEnvironment, type IntegrationEnvironment } from "@tot/public-runtime";
|
|
38
|
+
import { loadTenantLocalLifecycleState } from "@/lib/apps/tenantIntegrationMaterialization.js";
|
|
39
|
+
import { resolveAdminTenant } from "@/config/adminTenantLookup";
|
|
40
|
+
|
|
41
|
+
export const prerender = false;
|
|
42
|
+
|
|
43
|
+
const WRITE_ACTIONS: ReadonlySet<string> = new Set(["preview", "publish", "rollback"]);
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Build the control-plane materialization seam for one tenant's installs. A tenant's
|
|
47
|
+
* OWN (tenant-local) declaration is materialized through the interim bridge (ext-kernel-09):
|
|
48
|
+
* `load` returns its REAL live-by-config lifecycle state instead of the former blanket
|
|
49
|
+
* `null`, so a declared install is no longer reported `not_materialized`. It carries no
|
|
50
|
+
* separate candidate/promotion gate — an on-by-default declaration is already effective —
|
|
51
|
+
* so `resolveCandidateEvidence`/`resolveGate` stay null and a `preview`/`publish` refuses
|
|
52
|
+
* fail-closed (no candidate) rather than fabricating one. A managed-app install with a
|
|
53
|
+
* persisted candidate lifecycle remains the control plane's separate concern.
|
|
54
|
+
*/
|
|
55
|
+
function integrationLifecycleDeps(_context: APIContext, tenantId: string, stage: IntegrationEnvironment): LifecycleDeps {
|
|
56
|
+
return {
|
|
57
|
+
load: async (installId) => {
|
|
58
|
+
// Best-effort: a resolver hiccup answers "unmaterialized" (fail-closed 409),
|
|
59
|
+
// never a 500 — an install with no materialized declaration is null either way.
|
|
60
|
+
try {
|
|
61
|
+
const tenant = await resolveAdminTenant(tenantId);
|
|
62
|
+
if (!tenant || tenant.tenant_id !== tenantId) return null;
|
|
63
|
+
return loadTenantLocalLifecycleState(tenant, installId, stage);
|
|
64
|
+
} catch (err) {
|
|
65
|
+
console.error(`[integration-lifecycle] materialization skipped for ${tenantId}: ${String(err)}`);
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
},
|
|
69
|
+
save: async () => {},
|
|
70
|
+
resolveCandidateEvidence: async () => null,
|
|
71
|
+
resolveGate: async () => null,
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export async function POST(context: APIContext): Promise<Response> {
|
|
76
|
+
const requestUrl = new URL(context.request.url);
|
|
77
|
+
const requestedTenant = requestUrl.searchParams.get("tenant");
|
|
78
|
+
const auth = await resolveOperatorSession(context, requestedTenant);
|
|
79
|
+
if (!auth.ok) return json({ error: auth.message }, auth.status);
|
|
80
|
+
const manageDenied = requireManageRole(auth);
|
|
81
|
+
if (manageDenied) return manageDenied;
|
|
82
|
+
|
|
83
|
+
let body: unknown;
|
|
84
|
+
try {
|
|
85
|
+
body = await context.request.json();
|
|
86
|
+
} catch {
|
|
87
|
+
return json({ error: "invalid JSON body" }, 400);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const action = (body as { action?: unknown }).action;
|
|
91
|
+
if (typeof action !== "string" || !WRITE_ACTIONS.has(action)) {
|
|
92
|
+
return json({ error: "action must be one of: preview, publish, rollback" }, 400);
|
|
93
|
+
}
|
|
94
|
+
const installId = parseInstallId(body);
|
|
95
|
+
if (!installId) return json({ error: "installId is required" }, 400);
|
|
96
|
+
|
|
97
|
+
const registry = await getRegistryService();
|
|
98
|
+
if (registry instanceof Response) return registry;
|
|
99
|
+
|
|
100
|
+
const owned = await requireOwnedInstall(registry, installId, auth.tenantId);
|
|
101
|
+
if (owned instanceof Response) return owned;
|
|
102
|
+
|
|
103
|
+
const { expectedLiveRevision, targetRevision } = body as {
|
|
104
|
+
expectedLiveRevision?: string | null;
|
|
105
|
+
targetRevision?: string;
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
const stage: IntegrationEnvironment = parseEnvironment(requestUrl.searchParams.get("stage")) ?? "live";
|
|
109
|
+
const deps = integrationLifecycleDeps(context, auth.tenantId, stage);
|
|
110
|
+
const result = await handleLifecycleRequest(deps, {
|
|
111
|
+
action: action as LifecycleWriteAction,
|
|
112
|
+
installId,
|
|
113
|
+
actor: auth.actor,
|
|
114
|
+
...(expectedLiveRevision !== undefined ? { expectedLiveRevision } : {}),
|
|
115
|
+
...(typeof targetRevision === "string" ? { targetRevision } : {}),
|
|
116
|
+
});
|
|
117
|
+
return json(result.body, result.status);
|
|
118
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GET /api/apps/admin/integrations — the effective integration admin read model
|
|
3
|
+
* (ext-admin-02): the tenant-scoped data behind ext-admin-01's `/admin#integrations`
|
|
4
|
+
* IA. Combines each install's desired config, resolved effective plan, compiled CSP,
|
|
5
|
+
* consent, assurance, candidate, and rollback availability into ONE safe view.
|
|
6
|
+
*
|
|
7
|
+
* Auth mirrors `list.ts` exactly: the `?tenant=` query param is only ever a CANDIDATE
|
|
8
|
+
* into `resolveOperatorSession` — the tenant actually used is `auth.tenantId` from the
|
|
9
|
+
* resolved session, never the raw param — so another tenant, or a vendor with no
|
|
10
|
+
* membership, is denied (401/403) before any read model is assembled. Read-only: any
|
|
11
|
+
* membership role admits. The pure {@link assembleIntegrationReadModel} is the second,
|
|
12
|
+
* defense-in-depth gate: it re-asserts every materialized input belongs to
|
|
13
|
+
* `auth.tenantId` and redacts secrets, so a mis-plumbed control plane cannot leak.
|
|
14
|
+
*
|
|
15
|
+
* The control-plane materialization of effective plans, candidate evidence, and
|
|
16
|
+
* assurance records is resolved per install where present; an install with none yet
|
|
17
|
+
* surfaces honestly as `configuring` (no effective authority, no rollback), never a
|
|
18
|
+
* fabricated live state.
|
|
19
|
+
*/
|
|
20
|
+
import type { APIContext } from "astro";
|
|
21
|
+
import { resolveOperatorSession } from "@/lib/apps/adminSession.js";
|
|
22
|
+
import { getRegistryService, json } from "@/lib/apps/adminService.js";
|
|
23
|
+
import { parseEnvironment, type IntegrationEnvironment } from "@tot/public-runtime";
|
|
24
|
+
import {
|
|
25
|
+
assembleIntegrationReadModel,
|
|
26
|
+
type IntegrationReadModelInputItem,
|
|
27
|
+
} from "@/lib/apps/integrationReadModel.js";
|
|
28
|
+
import { tenantLocalReadModelItems } from "@/lib/apps/tenantIntegrationMaterialization.js";
|
|
29
|
+
import { resolveAdminTenant } from "@/config/adminTenantLookup";
|
|
30
|
+
|
|
31
|
+
export const prerender = false;
|
|
32
|
+
|
|
33
|
+
/** A merchant-legible purpose line for an install, never an internal adapter name. */
|
|
34
|
+
function purposeFor(appId: string): string {
|
|
35
|
+
return `Integration: ${appId}`;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export async function GET(context: APIContext): Promise<Response> {
|
|
39
|
+
const url = new URL(context.request.url);
|
|
40
|
+
const requestedTenant = url.searchParams.get("tenant");
|
|
41
|
+
const auth = await resolveOperatorSession(context, requestedTenant);
|
|
42
|
+
if (!auth.ok) return json({ error: auth.message }, auth.status);
|
|
43
|
+
|
|
44
|
+
const registry = await getRegistryService();
|
|
45
|
+
if (registry instanceof Response) return registry;
|
|
46
|
+
|
|
47
|
+
const stage: IntegrationEnvironment = parseEnvironment(url.searchParams.get("stage")) ?? "staged";
|
|
48
|
+
|
|
49
|
+
const installs = await registry.listByTenant(auth.tenantId);
|
|
50
|
+
const items: IntegrationReadModelInputItem[] = installs
|
|
51
|
+
.filter((install) => install.status !== "uninstalled")
|
|
52
|
+
.map((install) => ({
|
|
53
|
+
install: {
|
|
54
|
+
installId: install.installId,
|
|
55
|
+
appId: install.appId,
|
|
56
|
+
appVersion: install.appVersion,
|
|
57
|
+
tenantId: install.tenantId,
|
|
58
|
+
status: install.status,
|
|
59
|
+
},
|
|
60
|
+
purpose: purposeFor(install.appId),
|
|
61
|
+
// The desired config is the tenant's own input; when the manifest snapshot is not
|
|
62
|
+
// a structured object it contributes nothing — never a fabricated value.
|
|
63
|
+
desiredConfig: {},
|
|
64
|
+
}));
|
|
65
|
+
|
|
66
|
+
// ext-kernel-09: a tenant's OWN (tenant-local) declarations are materialized through
|
|
67
|
+
// the interim bridge and appended with their REAL effective plan + compiled CSP, so a
|
|
68
|
+
// declared integration surfaces effective state — not `configuring`. Resolved off the
|
|
69
|
+
// auth-bound tenant config only (the tenant id is `auth.tenantId`, never the param).
|
|
70
|
+
// Best-effort + additive: a resolver hiccup degrades to the managed-app view rather
|
|
71
|
+
// than failing the whole read model closed.
|
|
72
|
+
try {
|
|
73
|
+
const tenantConfig = await resolveAdminTenant(auth.tenantId);
|
|
74
|
+
if (tenantConfig && tenantConfig.tenant_id === auth.tenantId) {
|
|
75
|
+
items.push(...tenantLocalReadModelItems(tenantConfig, stage));
|
|
76
|
+
}
|
|
77
|
+
} catch (err) {
|
|
78
|
+
console.error(`[admin-integrations] tenant-local materialization skipped for ${auth.tenantId}: ${String(err)}`);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const result = assembleIntegrationReadModel({ tenantPublicId: auth.tenantId, stage, items });
|
|
82
|
+
if (!result.ok) {
|
|
83
|
+
// A cross-tenant input or surviving secret is a server-side integrity failure, not
|
|
84
|
+
// something to echo back field-by-field.
|
|
85
|
+
return json({ error: "integration read model failed integrity checks" }, 500);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return json({ readModel: result.value });
|
|
89
|
+
}
|