@openhi/constructs 0.0.129 → 0.0.130

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/lib/index.js CHANGED
@@ -785,6 +785,7 @@ __export(src_exports, {
785
785
  DATA_STORE_CHANGE_DETAIL_MAX_UTF8_BYTES: () => DATA_STORE_CHANGE_DETAIL_MAX_UTF8_BYTES,
786
786
  DATA_STORE_CHANGE_DETAIL_TYPE: () => DATA_STORE_CHANGE_DETAIL_TYPE,
787
787
  DATA_STORE_CHANGE_EVENT_SOURCE: () => DATA_STORE_CHANGE_EVENT_SOURCE,
788
+ DEFAULT_PREVIEW_EXPIRATION_DAYS: () => DEFAULT_PREVIEW_EXPIRATION_DAYS,
788
789
  DEMO_DATA_PLANE_FIXTURES: () => DEMO_DATA_PLANE_FIXTURES,
789
790
  DEMO_PERIOD: () => DEMO_PERIOD,
790
791
  DEMO_TENANT_SPECS: () => DEMO_TENANT_SPECS,
@@ -795,6 +796,7 @@ __export(src_exports, {
795
796
  DataStorePostgresReplica: () => DataStorePostgresReplica,
796
797
  DiscoverableStringParameter: () => DiscoverableStringParameter,
797
798
  DynamoDbDataStore: () => DynamoDbDataStore,
799
+ OPENHI_PR_NUMBER_ENV_VAR: () => OPENHI_PR_NUMBER_ENV_VAR,
798
800
  OPENHI_REPO_TAG_KEY_ENV_VAR: () => OPENHI_REPO_TAG_KEY_ENV_VAR,
799
801
  OPENHI_RESOURCE_URN_SYSTEM: () => OPENHI_RESOURCE_URN_SYSTEM,
800
802
  OPENHI_TAG_KEY_PREFIX_ENV_VAR: () => OPENHI_TAG_KEY_PREFIX_ENV_VAR,
@@ -820,6 +822,7 @@ __export(src_exports, {
820
822
  OpsEventBus: () => OpsEventBus,
821
823
  OwningDeleteCascadeLambdas: () => OwningDeleteCascadeLambdas,
822
824
  OwningDeleteCascadeWorkflow: () => OwningDeleteCascadeWorkflow,
825
+ PER_BRANCH_PREVIEW_PREFIX: () => PER_BRANCH_PREVIEW_PREFIX,
823
826
  PLACEHOLDER_TENANT_ID: () => PLACEHOLDER_TENANT_ID,
824
827
  PLACEHOLDER_WORKSPACE_ID: () => PLACEHOLDER_WORKSPACE_ID,
825
828
  PLATFORM_DEPLOY_BRIDGE_ACTOR_SYSTEM: () => PLATFORM_DEPLOY_BRIDGE_ACTOR_SYSTEM,
@@ -2352,6 +2355,8 @@ var import_aws_route53_targets = require("aws-cdk-lib/aws-route53-targets");
2352
2355
  var import_aws_s3 = require("aws-cdk-lib/aws-s3");
2353
2356
  var import_constructs8 = require("constructs");
2354
2357
  var STATIC_HOSTING_SERVICE_TYPE = "website";
2358
+ var PER_BRANCH_PREVIEW_PREFIX = "admin-pr-";
2359
+ var DEFAULT_PREVIEW_EXPIRATION_DAYS = 14;
2355
2360
  var _StaticHosting = class _StaticHosting extends import_constructs8.Construct {
2356
2361
  /**
2357
2362
  * Returns true when `domainName` begins with a wildcard label (`*.`),
@@ -2361,11 +2366,19 @@ var _StaticHosting = class _StaticHosting extends import_constructs8.Construct {
2361
2366
  static isWildcardDomain(domainName) {
2362
2367
  return domainName.startsWith("*.");
2363
2368
  }
2364
- constructor(scope, id, props = {}) {
2369
+ constructor(scope, id, props) {
2365
2370
  super(scope, id);
2366
2371
  const stack = OpenHiService.of(scope);
2367
2372
  const serviceType = props.serviceType ?? STATIC_HOSTING_SERVICE_TYPE;
2368
2373
  const hostingMode = props.hostingMode ?? "spa";
2374
+ const previewLifecycleRules = props.enablePreviewLifecycle ? [
2375
+ {
2376
+ id: "expire-pr-previews",
2377
+ enabled: true,
2378
+ prefix: props.prefixPattern,
2379
+ expiration: props.previewExpiration ?? import_aws_cdk_lib11.Duration.days(DEFAULT_PREVIEW_EXPIRATION_DAYS)
2380
+ }
2381
+ ] : void 0;
2369
2382
  this.bucket = new import_aws_s3.Bucket(this, "bucket", {
2370
2383
  blockPublicAccess: {
2371
2384
  blockPublicAcls: true,
@@ -2373,6 +2386,9 @@ var _StaticHosting = class _StaticHosting extends import_constructs8.Construct {
2373
2386
  ignorePublicAcls: true,
2374
2387
  restrictPublicBuckets: true
2375
2388
  },
2389
+ ...previewLifecycleRules !== void 0 && {
2390
+ lifecycleRules: previewLifecycleRules
2391
+ },
2376
2392
  ...props.bucketProps
2377
2393
  });
2378
2394
  const handlerJs = path6.join(
@@ -7415,7 +7431,9 @@ _OpenHiGraphqlService.SERVICE_TYPE = "graphql-api";
7415
7431
  var OpenHiGraphqlService = _OpenHiGraphqlService;
7416
7432
 
7417
7433
  // src/services/open-hi-website-service.ts
7434
+ var import_config5 = __toESM(require_lib());
7418
7435
  var import_aws_s32 = require("aws-cdk-lib/aws-s3");
7436
+ var OPENHI_PR_NUMBER_ENV_VAR = "OPENHI_PR_NUMBER";
7419
7437
  var SSM_PARAM_NAME_FULL_DOMAIN = "WEBSITE_FULL_DOMAIN";
7420
7438
  var _OpenHiWebsiteService = class _OpenHiWebsiteService extends OpenHiService {
7421
7439
  /**
@@ -7475,9 +7493,16 @@ var _OpenHiWebsiteService = class _OpenHiWebsiteService extends OpenHiService {
7475
7493
  super(ohEnv, _OpenHiWebsiteService.SERVICE_TYPE, props);
7476
7494
  this.props = props;
7477
7495
  this.validateConfig(props);
7496
+ const isReleaseBranch = this.branchName === this.defaultReleaseBranch;
7497
+ this.prNumber = this.resolvePrNumber(props);
7498
+ if (!isReleaseBranch && this.prNumber === void 0) {
7499
+ throw new Error(
7500
+ `OpenHiWebsiteService: prNumber is required on non-release-branch deploys (branchName="${this.branchName}", defaultReleaseBranch="${this.defaultReleaseBranch}"). Pass the \`prNumber\` prop or set the ${OPENHI_PR_NUMBER_ENV_VAR} env var.`
7501
+ );
7502
+ }
7478
7503
  const hostedZone = this.createHostedZone();
7479
7504
  this.fullDomain = this.computeFullDomain(hostedZone);
7480
- const shouldCreateHostingInfra = props.createHostingInfrastructure ?? this.branchName === this.defaultReleaseBranch;
7505
+ const shouldCreateHostingInfra = props.createHostingInfrastructure ?? isReleaseBranch;
7481
7506
  if (shouldCreateHostingInfra) {
7482
7507
  const certificate = this.createCertificate();
7483
7508
  this.staticHosting = this.createStaticHosting({
@@ -7485,6 +7510,8 @@ var _OpenHiWebsiteService = class _OpenHiWebsiteService extends OpenHiService {
7485
7510
  hostedZone
7486
7511
  });
7487
7512
  this.createFullDomainParameter();
7513
+ } else if (!isReleaseBranch) {
7514
+ this.perBranchHostname = this.createPerBranchHostname(hostedZone);
7488
7515
  }
7489
7516
  if (props.createStaticContent !== false) {
7490
7517
  const bucket = this.resolveStaticHostingBucket();
@@ -7527,25 +7554,76 @@ var _OpenHiWebsiteService = class _OpenHiWebsiteService extends OpenHiService {
7527
7554
  return OpenHiGlobalService.rootWildcardCertificateFromConstruct(this);
7528
7555
  }
7529
7556
  /**
7530
- * Computes the full website domain from `domainPrefix` and the child
7531
- * zone name.
7557
+ * Resolves the PR number from props or the `OPENHI_PR_NUMBER` env var.
7558
+ * Returns `undefined` on release-branch deploys where no PR number is
7559
+ * needed.
7560
+ */
7561
+ resolvePrNumber(props) {
7562
+ if (props.prNumber !== void 0) {
7563
+ return props.prNumber;
7564
+ }
7565
+ const raw = process.env[OPENHI_PR_NUMBER_ENV_VAR]?.trim();
7566
+ if (!raw) {
7567
+ return void 0;
7568
+ }
7569
+ const parsed = Number.parseInt(raw, 10);
7570
+ if (!Number.isInteger(parsed) || parsed <= 0) {
7571
+ throw new Error(
7572
+ `${OPENHI_PR_NUMBER_ENV_VAR} must be a positive integer; got "${raw}".`
7573
+ );
7574
+ }
7575
+ return parsed;
7576
+ }
7577
+ /**
7578
+ * Computes the full website domain from `domainPrefix`, the PR number,
7579
+ * and the child zone name. Release-branch deploys serve at
7580
+ * `\<domainPrefix\>.\<zone\>` (e.g. `admin.dev.openhi.org`); every other
7581
+ * deploy serves a per-PR preview at `\<domainPrefix\>-pr-\<N\>.\<zone\>`
7582
+ * (e.g. `admin-pr-123.dev.openhi.org`).
7532
7583
  */
7533
7584
  computeFullDomain(hostedZone) {
7534
- const prefix = this.props.domainPrefix ?? "www";
7535
- return [prefix, hostedZone.zoneName].join(".");
7585
+ const subDomain = this.computeSubDomain();
7586
+ return [subDomain, hostedZone.zoneName].join(".");
7587
+ }
7588
+ /**
7589
+ * Returns the sub-domain label (left of the zone) for the current
7590
+ * deploy. Used both for {@link fullDomain} and for the per-branch S3
7591
+ * key prefix passed to {@link StaticContent} so the upload prefix
7592
+ * always matches the served hostname.
7593
+ *
7594
+ * Non-release deploys compose the per-PR slug from
7595
+ * {@link PER_BRANCH_PREVIEW_PREFIX} so the per-PR S3 key prefix
7596
+ * matches what `StaticHosting`'s lifecycle rule expires.
7597
+ */
7598
+ computeSubDomain() {
7599
+ const isReleaseBranch = this.branchName === this.defaultReleaseBranch;
7600
+ if (isReleaseBranch) {
7601
+ return this.props.domainPrefix ?? "www";
7602
+ }
7603
+ return `${PER_BRANCH_PREVIEW_PREFIX}${this.prNumber}`;
7536
7604
  }
7537
7605
  /**
7538
7606
  * Creates the StaticHosting infrastructure (bucket + distribution +
7539
- * Lambda@Edge + 4 SSM params + DNS).
7607
+ * Lambda@Edge + 4 SSM params + DNS). The release-branch distribution
7608
+ * adds `*.\<zone\>` as a wildcard alt-name on top of the canonical
7609
+ * hostname so per-PR previews resolve via the same distribution.
7610
+ *
7611
+ * The bucket carries an S3 lifecycle rule that expires per-PR
7612
+ * preview content (keys under {@link PER_BRANCH_PREVIEW_PREFIX})
7613
+ * on non-production stages. PROD never gets the rule — see
7614
+ * `enablePreviewLifecycle`.
7540
7615
  */
7541
7616
  createStaticHosting(deps) {
7542
7617
  const restApi = this.props.restApi === true ? this.resolveRestApi() : void 0;
7618
+ const wildcardSan = `*.${deps.hostedZone.zoneName}`;
7543
7619
  return new StaticHosting(this, "static-hosting", {
7544
7620
  serviceType: _OpenHiWebsiteService.SERVICE_TYPE,
7545
7621
  certificate: deps.certificate,
7546
7622
  hostedZone: deps.hostedZone,
7547
- domainNames: [this.fullDomain],
7623
+ domainNames: [this.fullDomain, wildcardSan],
7548
7624
  description: `OpenHI website (${this.fullDomain})`,
7625
+ prefixPattern: PER_BRANCH_PREVIEW_PREFIX,
7626
+ enablePreviewLifecycle: this.ohEnv.ohStage.stageType !== import_config5.OPEN_HI_STAGE.PROD,
7549
7627
  ...restApi !== void 0 && { restApi }
7550
7628
  });
7551
7629
  }
@@ -7579,6 +7657,12 @@ var _OpenHiWebsiteService = class _OpenHiWebsiteService extends OpenHiService {
7579
7657
  * the release-branch deploy publishes to SSM, addressed against
7580
7658
  * {@link OpenHiService.releaseBranchHash}. See
7581
7659
  * {@link resolveStaticHostingBucket}.
7660
+ *
7661
+ * The S3 key prefix is `\<sub-domain\>.\<zone\>/\<contentDest\>` so the
7662
+ * upload location matches the Host-header-derived folder the Lambda@Edge
7663
+ * viewer-request handler prepends. Passing the zone name (rather than
7664
+ * `this.fullDomain`) for the `fullDomain` prop keeps the prefix flat
7665
+ * — `admin-pr-123.dev.openhi.org/`, not `admin-pr-123.admin.dev.openhi.org/`.
7582
7666
  */
7583
7667
  createStaticContent(bucket) {
7584
7668
  const { contentSourceDirectory, contentDestinationDirectory } = this.props;
@@ -7586,7 +7670,21 @@ var _OpenHiWebsiteService = class _OpenHiWebsiteService extends OpenHiService {
7586
7670
  bucket,
7587
7671
  contentSourceDirectory,
7588
7672
  contentDestinationDirectory,
7589
- fullDomain: this.fullDomain
7673
+ subDomain: this.computeSubDomain(),
7674
+ fullDomain: this.config.zoneName
7675
+ });
7676
+ }
7677
+ /**
7678
+ * Creates the per-PR `PerBranchHostname` alias record on non-release
7679
+ * branch deploys. The record points `\<domainPrefix\>-pr-\<N\>.\<zone\>`
7680
+ * at the release-branch CloudFront distribution (resolved from SSM
7681
+ * against {@link OpenHiService.releaseBranchHash}).
7682
+ */
7683
+ createPerBranchHostname(hostedZone) {
7684
+ return new PerBranchHostname(this, "per-branch-hostname", {
7685
+ hostname: this.fullDomain,
7686
+ hostedZone,
7687
+ serviceType: _OpenHiWebsiteService.SERVICE_TYPE
7590
7688
  });
7591
7689
  }
7592
7690
  /**
@@ -8132,6 +8230,7 @@ var RenameCascadeWorkflow = class extends import_constructs25.Construct {
8132
8230
  DATA_STORE_CHANGE_DETAIL_MAX_UTF8_BYTES,
8133
8231
  DATA_STORE_CHANGE_DETAIL_TYPE,
8134
8232
  DATA_STORE_CHANGE_EVENT_SOURCE,
8233
+ DEFAULT_PREVIEW_EXPIRATION_DAYS,
8135
8234
  DEMO_DATA_PLANE_FIXTURES,
8136
8235
  DEMO_PERIOD,
8137
8236
  DEMO_TENANT_SPECS,
@@ -8142,6 +8241,7 @@ var RenameCascadeWorkflow = class extends import_constructs25.Construct {
8142
8241
  DataStorePostgresReplica,
8143
8242
  DiscoverableStringParameter,
8144
8243
  DynamoDbDataStore,
8244
+ OPENHI_PR_NUMBER_ENV_VAR,
8145
8245
  OPENHI_REPO_TAG_KEY_ENV_VAR,
8146
8246
  OPENHI_RESOURCE_URN_SYSTEM,
8147
8247
  OPENHI_TAG_KEY_PREFIX_ENV_VAR,
@@ -8167,6 +8267,7 @@ var RenameCascadeWorkflow = class extends import_constructs25.Construct {
8167
8267
  OpsEventBus,
8168
8268
  OwningDeleteCascadeLambdas,
8169
8269
  OwningDeleteCascadeWorkflow,
8270
+ PER_BRANCH_PREVIEW_PREFIX,
8170
8271
  PLACEHOLDER_TENANT_ID,
8171
8272
  PLACEHOLDER_WORKSPACE_ID,
8172
8273
  PLATFORM_DEPLOY_BRIDGE_ACTOR_SYSTEM,