sst 2.22.11 → 2.23.1

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.
@@ -8,15 +8,14 @@ import { execSync } from "child_process";
8
8
  import { Construct } from "constructs";
9
9
  import { Fn, Token, Duration as CdkDuration, RemovalPolicy, CustomResource, } from "aws-cdk-lib/core";
10
10
  import { BlockPublicAccess, Bucket, } from "aws-cdk-lib/aws-s3";
11
- import { Role, Effect, Policy, PolicyStatement, AccountPrincipal, ServicePrincipal, CompositePrincipal, } from "aws-cdk-lib/aws-iam";
11
+ import { Role, Policy, PolicyStatement, AccountPrincipal, ServicePrincipal, CompositePrincipal, } from "aws-cdk-lib/aws-iam";
12
12
  import { Function as CdkFunction, Code, Runtime, FunctionUrlAuthType, InvokeMode, } from "aws-cdk-lib/aws-lambda";
13
- import { HostedZone, ARecord, AaaaRecord, RecordTarget, } from "aws-cdk-lib/aws-route53";
14
13
  import { Asset } from "aws-cdk-lib/aws-s3-assets";
15
- import { Distribution, ViewerProtocolPolicy, AllowedMethods, CachedMethods, LambdaEdgeEventType, CachePolicy, CacheQueryStringBehavior, CacheHeaderBehavior, CacheCookieBehavior, OriginRequestPolicy, Function as CfFunction, FunctionCode as CfFunctionCode, FunctionEventType as CfFunctionEventType, } from "aws-cdk-lib/aws-cloudfront";
14
+ import { ViewerProtocolPolicy, AllowedMethods, CachedMethods, LambdaEdgeEventType, CachePolicy, CacheQueryStringBehavior, CacheHeaderBehavior, CacheCookieBehavior, OriginRequestPolicy, Function as CfFunction, FunctionCode as CfFunctionCode, FunctionEventType as CfFunctionEventType, } from "aws-cdk-lib/aws-cloudfront";
16
15
  import { AwsCliLayer } from "aws-cdk-lib/lambda-layer-awscli";
17
16
  import { S3Origin, HttpOrigin } from "aws-cdk-lib/aws-cloudfront-origins";
18
- import { CloudFrontTarget } from "aws-cdk-lib/aws-route53-targets";
19
17
  import { Stack } from "./Stack.js";
18
+ import { Distribution } from "./Distribution.js";
20
19
  import { Logger } from "../logger.js";
21
20
  import { createAppContext } from "./context.js";
22
21
  import { isCDKConstruct } from "./Construct.js";
@@ -24,8 +23,6 @@ import { Secret } from "./Secret.js";
24
23
  import { SsrFunction } from "./SsrFunction.js";
25
24
  import { getBuildCmdEnvironment, } from "./BaseSite.js";
26
25
  import { useDeferredTasks } from "./deferred_task.js";
27
- import { HttpsRedirect } from "./cdk/website-redirect.js";
28
- import { DnsValidatedCertificate } from "./cdk/dns-validated-certificate.js";
29
26
  import { toCdkDuration } from "./util/duration.js";
30
27
  import { attachPermissionsToRole } from "./util/permission.js";
31
28
  import { getParameterPath, } from "./util/functionBinding.js";
@@ -55,9 +52,6 @@ export class SsrSite extends Construct {
55
52
  cfFunction;
56
53
  s3Origin;
57
54
  distribution;
58
- hostedZone;
59
- certificate;
60
- streaming;
61
55
  constructor(scope, id, props) {
62
56
  super(scope, props?.cdk?.id || id);
63
57
  const app = scope.node.root;
@@ -73,12 +67,11 @@ export class SsrSite extends Construct {
73
67
  };
74
68
  this.doNotDeploy =
75
69
  !stack.isActive || (app.mode === "dev" && !this.props.dev?.deploy);
76
- this.streaming = props?.streaming ?? false;
77
70
  this.buildConfig = this.initBuildConfig();
78
71
  this.validateSiteExists();
79
72
  this.validateTimeout();
80
73
  this.writeTypesFile();
81
- useSites().add(id, this.constructor.name, this.props);
74
+ useSites().add(stack.stackName, id, this.constructor.name, this.props);
82
75
  if (this.doNotDeploy) {
83
76
  // @ts-ignore
84
77
  this.cfFunction = this.bucket = this.s3Origin = this.distribution = null;
@@ -95,20 +88,13 @@ export class SsrSite extends Construct {
95
88
  this.serverLambdaForRegional = this.createFunctionForRegional();
96
89
  }
97
90
  this.grantServerS3Permissions();
98
- // Create Custom Domain
99
- this.validateCustomDomainSettings();
100
- this.hostedZone = this.lookupHostedZone();
101
- this.certificate = this.createCertificate();
102
91
  // Create CloudFront
103
- this.validateCloudFrontDistributionSettings();
104
92
  this.s3Origin = this.createCloudFrontS3Origin();
105
93
  this.cfFunction = this.createCloudFrontFunction();
106
94
  this.distribution = this.props.edge
107
95
  ? this.createCloudFrontDistributionForEdge()
108
96
  : this.createCloudFrontDistributionForRegional();
109
97
  this.grantServerCloudFrontPermissions();
110
- // Connect Custom Domain to CloudFront Distribution
111
- this.createRoute53Records();
112
98
  useDeferredTasks().add(async () => {
113
99
  // Build app
114
100
  this.buildApp();
@@ -124,7 +110,7 @@ export class SsrSite extends Construct {
124
110
  // Add static file behaviors
125
111
  this.addStaticFileBehaviors();
126
112
  // Invalidate CloudFront
127
- this.createCloudFrontInvalidation();
113
+ this.distribution.createInvalidation(this.generateBuildId());
128
114
  for (const task of this.deferredTaskCallbacks) {
129
115
  await task();
130
116
  }
@@ -139,7 +125,7 @@ export class SsrSite extends Construct {
139
125
  get url() {
140
126
  if (this.doNotDeploy)
141
127
  return this.props.dev?.url;
142
- return `https://${this.distribution.distributionDomainName}`;
128
+ return this.distribution.url;
143
129
  }
144
130
  /**
145
131
  * If the custom domain is enabled, this is the URL of the website with the
@@ -148,15 +134,7 @@ export class SsrSite extends Construct {
148
134
  get customDomainUrl() {
149
135
  if (this.doNotDeploy)
150
136
  return;
151
- const { customDomain } = this.props;
152
- if (!customDomain)
153
- return;
154
- if (typeof customDomain === "string") {
155
- return `https://${customDomain}`;
156
- }
157
- else {
158
- return `https://${customDomain.domainName}`;
159
- }
137
+ return this.distribution.customDomainUrl;
160
138
  }
161
139
  /**
162
140
  * The internally created CDK resources.
@@ -168,9 +146,9 @@ export class SsrSite extends Construct {
168
146
  function: this.serverLambdaForEdge?.function ||
169
147
  this.serverLambdaForRegional?.function,
170
148
  bucket: this.bucket,
171
- distribution: this.distribution,
172
- hostedZone: this.hostedZone,
173
- certificate: this.certificate,
149
+ distribution: this.distribution.cdk.distribution,
150
+ hostedZone: this.distribution.cdk.hostedZone,
151
+ certificate: this.distribution.cdk.certificate,
174
152
  };
175
153
  }
176
154
  /////////////////////
@@ -489,7 +467,7 @@ export class SsrSite extends Construct {
489
467
  new PolicyStatement({
490
468
  actions: ["cloudfront:CreateInvalidation"],
491
469
  resources: [
492
- `arn:${stack.partition}:cloudfront::${stack.account}:distribution/${this.distribution.distributionId}`,
470
+ `arn:${stack.partition}:cloudfront::${stack.account}:distribution/${this.distribution.cdk.distribution.distributionId}`,
493
471
  ],
494
472
  }),
495
473
  ],
@@ -499,15 +477,6 @@ export class SsrSite extends Construct {
499
477
  /////////////////////
500
478
  // CloudFront Distribution
501
479
  /////////////////////
502
- validateCloudFrontDistributionSettings() {
503
- const { cdk } = this.props;
504
- if (cdk?.distribution?.certificate) {
505
- throw new Error(`Do not configure the "cfDistribution.certificate". Use the "customDomain" to configure the domain certificate.`);
506
- }
507
- if (cdk?.distribution?.domainNames) {
508
- throw new Error(`Do not configure the "cfDistribution.domainNames". Use the "customDomain" to configure the domain name.`);
509
- }
510
- }
511
480
  createCloudFrontS3Origin() {
512
481
  return new S3Origin(this.bucket, {
513
482
  originPath: "/" + (this.buildConfig.clientBuildS3KeyPrefix ?? ""),
@@ -525,66 +494,57 @@ function handler(event) {
525
494
  });
526
495
  }
527
496
  createCloudFrontDistributionForRegional() {
528
- const { cdk } = this.props;
497
+ const { customDomain, cdk } = this.props;
529
498
  const cfDistributionProps = cdk?.distribution || {};
530
499
  const cachePolicy = cdk?.serverCachePolicy ?? this.buildServerCachePolicy();
531
- return new Distribution(this, "Distribution", {
532
- // these values can be overwritten by cfDistributionProps
533
- defaultRootObject: "",
534
- // Override props.
535
- ...cfDistributionProps,
536
- // these values can NOT be overwritten by cfDistributionProps
537
- domainNames: this.buildDistributionDomainNames(),
538
- certificate: this.certificate,
539
- defaultBehavior: this.buildDefaultBehaviorForRegional(cachePolicy),
540
- additionalBehaviors: {
541
- ...(cfDistributionProps.additionalBehaviors || {}),
500
+ return new Distribution(this, "CDN", {
501
+ scopeOverride: this,
502
+ customDomain,
503
+ cdk: {
504
+ distribution: {
505
+ // these values can be overwritten by cfDistributionProps
506
+ defaultRootObject: "",
507
+ // Override props.
508
+ ...cfDistributionProps,
509
+ // these values can NOT be overwritten by cfDistributionProps
510
+ defaultBehavior: this.buildDefaultBehaviorForRegional(cachePolicy),
511
+ additionalBehaviors: {
512
+ ...(cfDistributionProps.additionalBehaviors || {}),
513
+ },
514
+ },
542
515
  },
543
516
  });
544
517
  }
545
518
  createCloudFrontDistributionForEdge() {
546
- const { cdk } = this.props;
519
+ const { customDomain, cdk } = this.props;
547
520
  const cfDistributionProps = cdk?.distribution || {};
548
521
  const cachePolicy = cdk?.serverCachePolicy ?? this.buildServerCachePolicy();
549
- return new Distribution(this, "Distribution", {
550
- // these values can be overwritten by cfDistributionProps
551
- defaultRootObject: "",
552
- // Override props.
553
- ...cfDistributionProps,
554
- // these values can NOT be overwritten by cfDistributionProps
555
- domainNames: this.buildDistributionDomainNames(),
556
- certificate: this.certificate,
557
- defaultBehavior: this.buildDefaultBehaviorForEdge(cachePolicy),
558
- additionalBehaviors: {
559
- ...(cfDistributionProps.additionalBehaviors || {}),
522
+ return new Distribution(this, "CDN", {
523
+ scopeOverride: this,
524
+ customDomain,
525
+ cdk: {
526
+ distribution: {
527
+ // these values can be overwritten by cfDistributionProps
528
+ defaultRootObject: "",
529
+ // Override props.
530
+ ...cfDistributionProps,
531
+ // these values can NOT be overwritten by cfDistributionProps
532
+ defaultBehavior: this.buildDefaultBehaviorForEdge(cachePolicy),
533
+ additionalBehaviors: {
534
+ ...(cfDistributionProps.additionalBehaviors || {}),
535
+ },
536
+ },
560
537
  },
561
538
  });
562
539
  }
563
- buildDistributionDomainNames() {
564
- const { customDomain } = this.props;
565
- const domainNames = [];
566
- if (!customDomain) {
567
- // no domain
568
- }
569
- else if (typeof customDomain === "string") {
570
- domainNames.push(customDomain);
571
- }
572
- else {
573
- domainNames.push(customDomain.domainName);
574
- if (customDomain.alternateNames) {
575
- if (!customDomain.cdk?.certificate)
576
- throw new Error("Certificates for alternate domains cannot be automatically created. Please specify certificate to use");
577
- domainNames.push(...customDomain.alternateNames);
578
- }
579
- }
580
- return domainNames;
581
- }
582
540
  buildDefaultBehaviorForRegional(cachePolicy) {
583
541
  const { timeout, cdk } = this.props;
584
542
  const cfDistributionProps = cdk?.distribution || {};
585
543
  const fnUrl = this.serverLambdaForRegional.addFunctionUrl({
586
544
  authType: FunctionUrlAuthType.NONE,
587
- invokeMode: this.streaming ? InvokeMode.RESPONSE_STREAM : undefined,
545
+ invokeMode: this.supportsStreaming()
546
+ ? InvokeMode.RESPONSE_STREAM
547
+ : undefined,
588
548
  });
589
549
  return {
590
550
  viewerProtocolPolicy: ViewerProtocolPolicy.REDIRECT_TO_HTTPS,
@@ -647,7 +607,7 @@ function handler(event) {
647
607
  const publicDir = path.join(this.props.path, this.buildConfig.clientBuildOutputDir);
648
608
  for (const item of fs.readdirSync(publicDir)) {
649
609
  const isDir = fs.statSync(path.join(publicDir, item)).isDirectory();
650
- this.distribution.addBehavior(isDir ? `${item}/*` : item, this.s3Origin, {
610
+ this.distribution.cdk.distribution.addBehavior(isDir ? `${item}/*` : item, this.s3Origin, {
651
611
  viewerProtocolPolicy: ViewerProtocolPolicy.REDIRECT_TO_HTTPS,
652
612
  allowedMethods: AllowedMethods.ALLOW_GET_HEAD_OPTIONS,
653
613
  cachedMethods: CachedMethods.CACHE_GET_HEAD_OPTIONS,
@@ -676,158 +636,6 @@ function handler(event) {
676
636
  // CloudFront's Managed-AllViewerExceptHostHeader policy
677
637
  return OriginRequestPolicy.fromOriginRequestPolicyId(this, "ServerOriginRequestPolicy", "b689b0a8-53d0-40ab-baf2-68738e2966ac");
678
638
  }
679
- createCloudFrontInvalidation() {
680
- const stack = Stack.of(this);
681
- const policy = new Policy(this, "CloudFrontInvalidatorPolicy", {
682
- statements: [
683
- new PolicyStatement({
684
- effect: Effect.ALLOW,
685
- actions: [
686
- "cloudfront:GetInvalidation",
687
- "cloudfront:CreateInvalidation",
688
- ],
689
- resources: [
690
- `arn:${stack.partition}:cloudfront::${stack.account}:distribution/${this.distribution.distributionId}`,
691
- ],
692
- }),
693
- ],
694
- });
695
- stack.customResourceHandler.role?.attachInlinePolicy(policy);
696
- const resource = new CustomResource(this, "CloudFrontInvalidator", {
697
- serviceToken: stack.customResourceHandler.functionArn,
698
- resourceType: "Custom::CloudFrontInvalidator",
699
- properties: {
700
- buildId: this.generateBuildId(),
701
- distributionId: this.distribution.distributionId,
702
- paths: ["/*"],
703
- waitForInvalidation: this.props.waitForInvalidation,
704
- },
705
- });
706
- resource.node.addDependency(policy);
707
- return resource;
708
- }
709
- /////////////////////
710
- // Custom Domain
711
- /////////////////////
712
- validateCustomDomainSettings() {
713
- const { customDomain } = this.props;
714
- if (!customDomain) {
715
- return;
716
- }
717
- if (typeof customDomain === "string") {
718
- return;
719
- }
720
- if (customDomain.isExternalDomain === true) {
721
- if (!customDomain.cdk?.certificate) {
722
- throw new Error(`A valid certificate is required when "isExternalDomain" is set to "true".`);
723
- }
724
- if (customDomain.domainAlias) {
725
- throw new Error(`Domain alias is only supported for domains hosted on Amazon Route 53. Do not set the "customDomain.domainAlias" when "isExternalDomain" is enabled.`);
726
- }
727
- if (customDomain.hostedZone) {
728
- throw new Error(`Hosted zones can only be configured for domains hosted on Amazon Route 53. Do not set the "customDomain.hostedZone" when "isExternalDomain" is enabled.`);
729
- }
730
- }
731
- }
732
- lookupHostedZone() {
733
- const { customDomain } = this.props;
734
- // Skip if customDomain is not configured
735
- if (!customDomain) {
736
- return;
737
- }
738
- let hostedZone;
739
- if (typeof customDomain === "string") {
740
- hostedZone = HostedZone.fromLookup(this, "HostedZone", {
741
- domainName: customDomain,
742
- });
743
- }
744
- else if (customDomain.cdk?.hostedZone) {
745
- hostedZone = customDomain.cdk.hostedZone;
746
- }
747
- else if (typeof customDomain.hostedZone === "string") {
748
- hostedZone = HostedZone.fromLookup(this, "HostedZone", {
749
- domainName: customDomain.hostedZone,
750
- });
751
- }
752
- else if (typeof customDomain.domainName === "string") {
753
- // Skip if domain is not a Route53 domain
754
- if (customDomain.isExternalDomain === true) {
755
- return;
756
- }
757
- hostedZone = HostedZone.fromLookup(this, "HostedZone", {
758
- domainName: customDomain.domainName,
759
- });
760
- }
761
- else {
762
- hostedZone = customDomain.hostedZone;
763
- }
764
- return hostedZone;
765
- }
766
- createCertificate() {
767
- const { customDomain } = this.props;
768
- if (!customDomain) {
769
- return;
770
- }
771
- let acmCertificate;
772
- // HostedZone is set for Route 53 domains
773
- if (this.hostedZone) {
774
- if (typeof customDomain === "string") {
775
- acmCertificate = new DnsValidatedCertificate(this, "Certificate", {
776
- domainName: customDomain,
777
- hostedZone: this.hostedZone,
778
- region: "us-east-1",
779
- });
780
- }
781
- else if (customDomain.cdk?.certificate) {
782
- acmCertificate = customDomain.cdk.certificate;
783
- }
784
- else {
785
- acmCertificate = new DnsValidatedCertificate(this, "Certificate", {
786
- domainName: customDomain.domainName,
787
- hostedZone: this.hostedZone,
788
- region: "us-east-1",
789
- });
790
- }
791
- }
792
- // HostedZone is NOT set for non-Route 53 domains
793
- else {
794
- if (typeof customDomain !== "string") {
795
- acmCertificate = customDomain.cdk?.certificate;
796
- }
797
- }
798
- return acmCertificate;
799
- }
800
- createRoute53Records() {
801
- const { customDomain } = this.props;
802
- if (!customDomain || !this.hostedZone) {
803
- return;
804
- }
805
- let recordName;
806
- let domainAlias;
807
- if (typeof customDomain === "string") {
808
- recordName = customDomain;
809
- }
810
- else {
811
- recordName = customDomain.domainName;
812
- domainAlias = customDomain.domainAlias;
813
- }
814
- // Create DNS record
815
- const recordProps = {
816
- recordName,
817
- zone: this.hostedZone,
818
- target: RecordTarget.fromAlias(new CloudFrontTarget(this.distribution)),
819
- };
820
- new ARecord(this, "AliasRecord", recordProps);
821
- new AaaaRecord(this, "AliasRecordAAAA", recordProps);
822
- // Create Alias redirect record
823
- if (domainAlias) {
824
- new HttpsRedirect(this, "Redirect", {
825
- zone: this.hostedZone,
826
- recordNames: [domainAlias],
827
- targetDomain: recordName,
828
- });
829
- }
830
- }
831
639
  /////////////////////
832
640
  // Helper Functions
833
641
  /////////////////////
@@ -901,12 +709,15 @@ function handler(event) {
901
709
  Logger.debug(`Generated build ID ${buildId}`);
902
710
  return buildId;
903
711
  }
712
+ supportsStreaming() {
713
+ return false;
714
+ }
904
715
  }
905
716
  export const useSites = createAppContext(() => {
906
717
  const sites = [];
907
718
  return {
908
- add(name, type, props) {
909
- sites.push({ name, type, props });
719
+ add(stack, name, type, props) {
720
+ sites.push({ stack, name, type, props });
910
721
  },
911
722
  get all() {
912
723
  return sites;
@@ -1,9 +1,8 @@
1
1
  import { Construct } from "constructs";
2
2
  import { Bucket, BucketProps, IBucket } from "aws-cdk-lib/aws-s3";
3
- import { ICertificate } from "aws-cdk-lib/aws-certificatemanager";
4
- import { IHostedZone } from "aws-cdk-lib/aws-route53";
5
- import { Distribution, IDistribution } from "aws-cdk-lib/aws-cloudfront";
6
- import { BaseSiteDomainProps, BaseSiteFileOptions, BaseSiteReplaceProps, BaseSiteCdkDistributionProps } from "./BaseSite.js";
3
+ import { IDistribution } from "aws-cdk-lib/aws-cloudfront";
4
+ import { DistributionDomainProps } from "./Distribution.js";
5
+ import { BaseSiteFileOptions, BaseSiteReplaceProps, BaseSiteCdkDistributionProps } from "./BaseSite.js";
7
6
  import { SSTConstruct } from "./Construct.js";
8
7
  import { FunctionBindingProps } from "./util/functionBinding.js";
9
8
  export interface StaticSiteProps {
@@ -266,7 +265,7 @@ export interface StaticSiteProps {
266
265
  distribution?: IDistribution | StaticSiteCdkDistributionProps;
267
266
  };
268
267
  }
269
- export interface StaticSiteDomainProps extends BaseSiteDomainProps {
268
+ export interface StaticSiteDomainProps extends DistributionDomainProps {
270
269
  }
271
270
  export interface StaticSiteFileOptions extends BaseSiteFileOptions {
272
271
  }
@@ -274,6 +273,9 @@ export interface StaticSiteReplaceProps extends BaseSiteReplaceProps {
274
273
  }
275
274
  export interface StaticSiteCdkDistributionProps extends BaseSiteCdkDistributionProps {
276
275
  }
276
+ type StaticSiteNormalizedProps = StaticSiteProps & {
277
+ path: Exclude<StaticSiteProps["path"], undefined>;
278
+ };
277
279
  /**
278
280
  * The `StaticSite` construct is a higher level CDK construct that makes it easy to create a static website.
279
281
  *
@@ -295,8 +297,6 @@ export declare class StaticSite extends Construct implements SSTConstruct {
295
297
  private doNotDeploy;
296
298
  private bucket;
297
299
  private distribution;
298
- private hostedZone?;
299
- private certificate?;
300
300
  constructor(scope: Construct, id: string, props?: StaticSiteProps);
301
301
  /**
302
302
  * The CloudFront URL of the website.
@@ -311,9 +311,9 @@ export declare class StaticSite extends Construct implements SSTConstruct {
311
311
  */
312
312
  get cdk(): {
313
313
  bucket: Bucket;
314
- distribution: Distribution;
315
- hostedZone: IHostedZone | undefined;
316
- certificate: ICertificate | undefined;
314
+ distribution: IDistribution;
315
+ hostedZone: import("aws-cdk-lib/aws-route53").IHostedZone | undefined;
316
+ certificate: import("aws-cdk-lib/aws-certificatemanager").ICertificate | undefined;
317
317
  } | undefined;
318
318
  getConstructMetadata(): {
319
319
  type: "StaticSite";
@@ -333,12 +333,16 @@ export declare class StaticSite extends Construct implements SSTConstruct {
333
333
  private createS3Bucket;
334
334
  private createS3Deployment;
335
335
  private createCfDistribution;
336
- private createCloudFrontInvalidation;
337
- protected buildDistributionDomainNames(): string[];
336
+ private generateInvalidationId;
338
337
  private buildDistributionBehavior;
339
- protected validateCustomDomainSettings(): void;
340
- protected lookupHostedZone(): IHostedZone | undefined;
341
- private createCertificate;
342
- protected createRoute53Records(): void;
343
338
  private getS3ContentReplaceValues;
344
339
  }
340
+ export declare const useSites: () => {
341
+ add(stack: string, name: string, props: StaticSiteNormalizedProps): void;
342
+ readonly all: {
343
+ stack: string;
344
+ name: string;
345
+ props: StaticSiteNormalizedProps;
346
+ }[];
347
+ };
348
+ export {};