sst 2.22.10 → 2.23.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.
@@ -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";
12
- import { Function as CdkFunction, Code, Runtime, FunctionUrlAuthType, } from "aws-cdk-lib/aws-lambda";
13
- import { HostedZone, ARecord, AaaaRecord, RecordTarget, } from "aws-cdk-lib/aws-route53";
11
+ import { Role, Policy, PolicyStatement, AccountPrincipal, ServicePrincipal, CompositePrincipal, } from "aws-cdk-lib/aws-iam";
12
+ import { Function as CdkFunction, Code, Runtime, FunctionUrlAuthType, InvokeMode, } from "aws-cdk-lib/aws-lambda";
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,8 +52,6 @@ export class SsrSite extends Construct {
55
52
  cfFunction;
56
53
  s3Origin;
57
54
  distribution;
58
- hostedZone;
59
- certificate;
60
55
  constructor(scope, id, props) {
61
56
  super(scope, props?.cdk?.id || id);
62
57
  const app = scope.node.root;
@@ -76,7 +71,7 @@ export class SsrSite extends Construct {
76
71
  this.validateSiteExists();
77
72
  this.validateTimeout();
78
73
  this.writeTypesFile();
79
- useSites().add(id, this.constructor.name, this.props);
74
+ useSites().add(stack.stackName, id, this.constructor.name, this.props);
80
75
  if (this.doNotDeploy) {
81
76
  // @ts-ignore
82
77
  this.cfFunction = this.bucket = this.s3Origin = this.distribution = null;
@@ -93,20 +88,13 @@ export class SsrSite extends Construct {
93
88
  this.serverLambdaForRegional = this.createFunctionForRegional();
94
89
  }
95
90
  this.grantServerS3Permissions();
96
- // Create Custom Domain
97
- this.validateCustomDomainSettings();
98
- this.hostedZone = this.lookupHostedZone();
99
- this.certificate = this.createCertificate();
100
91
  // Create CloudFront
101
- this.validateCloudFrontDistributionSettings();
102
92
  this.s3Origin = this.createCloudFrontS3Origin();
103
93
  this.cfFunction = this.createCloudFrontFunction();
104
94
  this.distribution = this.props.edge
105
95
  ? this.createCloudFrontDistributionForEdge()
106
96
  : this.createCloudFrontDistributionForRegional();
107
97
  this.grantServerCloudFrontPermissions();
108
- // Connect Custom Domain to CloudFront Distribution
109
- this.createRoute53Records();
110
98
  useDeferredTasks().add(async () => {
111
99
  // Build app
112
100
  this.buildApp();
@@ -122,7 +110,7 @@ export class SsrSite extends Construct {
122
110
  // Add static file behaviors
123
111
  this.addStaticFileBehaviors();
124
112
  // Invalidate CloudFront
125
- this.createCloudFrontInvalidation();
113
+ this.distribution.createInvalidation(this.generateBuildId());
126
114
  for (const task of this.deferredTaskCallbacks) {
127
115
  await task();
128
116
  }
@@ -137,7 +125,7 @@ export class SsrSite extends Construct {
137
125
  get url() {
138
126
  if (this.doNotDeploy)
139
127
  return this.props.dev?.url;
140
- return `https://${this.distribution.distributionDomainName}`;
128
+ return this.distribution.url;
141
129
  }
142
130
  /**
143
131
  * If the custom domain is enabled, this is the URL of the website with the
@@ -146,15 +134,7 @@ export class SsrSite extends Construct {
146
134
  get customDomainUrl() {
147
135
  if (this.doNotDeploy)
148
136
  return;
149
- const { customDomain } = this.props;
150
- if (!customDomain)
151
- return;
152
- if (typeof customDomain === "string") {
153
- return `https://${customDomain}`;
154
- }
155
- else {
156
- return `https://${customDomain.domainName}`;
157
- }
137
+ return this.distribution.customDomainUrl;
158
138
  }
159
139
  /**
160
140
  * The internally created CDK resources.
@@ -166,9 +146,9 @@ export class SsrSite extends Construct {
166
146
  function: this.serverLambdaForEdge?.function ||
167
147
  this.serverLambdaForRegional?.function,
168
148
  bucket: this.bucket,
169
- distribution: this.distribution,
170
- hostedZone: this.hostedZone,
171
- certificate: this.certificate,
149
+ distribution: this.distribution.cdk.distribution,
150
+ hostedZone: this.distribution.cdk.hostedZone,
151
+ certificate: this.distribution.cdk.certificate,
172
152
  };
173
153
  }
174
154
  /////////////////////
@@ -487,7 +467,7 @@ export class SsrSite extends Construct {
487
467
  new PolicyStatement({
488
468
  actions: ["cloudfront:CreateInvalidation"],
489
469
  resources: [
490
- `arn:${stack.partition}:cloudfront::${stack.account}:distribution/${this.distribution.distributionId}`,
470
+ `arn:${stack.partition}:cloudfront::${stack.account}:distribution/${this.distribution.cdk.distribution.distributionId}`,
491
471
  ],
492
472
  }),
493
473
  ],
@@ -497,15 +477,6 @@ export class SsrSite extends Construct {
497
477
  /////////////////////
498
478
  // CloudFront Distribution
499
479
  /////////////////////
500
- validateCloudFrontDistributionSettings() {
501
- const { cdk } = this.props;
502
- if (cdk?.distribution?.certificate) {
503
- throw new Error(`Do not configure the "cfDistribution.certificate". Use the "customDomain" to configure the domain certificate.`);
504
- }
505
- if (cdk?.distribution?.domainNames) {
506
- throw new Error(`Do not configure the "cfDistribution.domainNames". Use the "customDomain" to configure the domain name.`);
507
- }
508
- }
509
480
  createCloudFrontS3Origin() {
510
481
  return new S3Origin(this.bucket, {
511
482
  originPath: "/" + (this.buildConfig.clientBuildS3KeyPrefix ?? ""),
@@ -523,65 +494,57 @@ function handler(event) {
523
494
  });
524
495
  }
525
496
  createCloudFrontDistributionForRegional() {
526
- const { cdk } = this.props;
497
+ const { customDomain, cdk } = this.props;
527
498
  const cfDistributionProps = cdk?.distribution || {};
528
499
  const cachePolicy = cdk?.serverCachePolicy ?? this.buildServerCachePolicy();
529
- return new Distribution(this, "Distribution", {
530
- // these values can be overwritten by cfDistributionProps
531
- defaultRootObject: "",
532
- // Override props.
533
- ...cfDistributionProps,
534
- // these values can NOT be overwritten by cfDistributionProps
535
- domainNames: this.buildDistributionDomainNames(),
536
- certificate: this.certificate,
537
- defaultBehavior: this.buildDefaultBehaviorForRegional(cachePolicy),
538
- additionalBehaviors: {
539
- ...(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
+ },
540
515
  },
541
516
  });
542
517
  }
543
518
  createCloudFrontDistributionForEdge() {
544
- const { cdk } = this.props;
519
+ const { customDomain, cdk } = this.props;
545
520
  const cfDistributionProps = cdk?.distribution || {};
546
521
  const cachePolicy = cdk?.serverCachePolicy ?? this.buildServerCachePolicy();
547
- return new Distribution(this, "Distribution", {
548
- // these values can be overwritten by cfDistributionProps
549
- defaultRootObject: "",
550
- // Override props.
551
- ...cfDistributionProps,
552
- // these values can NOT be overwritten by cfDistributionProps
553
- domainNames: this.buildDistributionDomainNames(),
554
- certificate: this.certificate,
555
- defaultBehavior: this.buildDefaultBehaviorForEdge(cachePolicy),
556
- additionalBehaviors: {
557
- ...(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
+ },
558
537
  },
559
538
  });
560
539
  }
561
- buildDistributionDomainNames() {
562
- const { customDomain } = this.props;
563
- const domainNames = [];
564
- if (!customDomain) {
565
- // no domain
566
- }
567
- else if (typeof customDomain === "string") {
568
- domainNames.push(customDomain);
569
- }
570
- else {
571
- domainNames.push(customDomain.domainName);
572
- if (customDomain.alternateNames) {
573
- if (!customDomain.cdk?.certificate)
574
- throw new Error("Certificates for alternate domains cannot be automatically created. Please specify certificate to use");
575
- domainNames.push(...customDomain.alternateNames);
576
- }
577
- }
578
- return domainNames;
579
- }
580
540
  buildDefaultBehaviorForRegional(cachePolicy) {
581
541
  const { timeout, cdk } = this.props;
582
542
  const cfDistributionProps = cdk?.distribution || {};
583
543
  const fnUrl = this.serverLambdaForRegional.addFunctionUrl({
584
544
  authType: FunctionUrlAuthType.NONE,
545
+ invokeMode: this.supportsStreaming()
546
+ ? InvokeMode.RESPONSE_STREAM
547
+ : undefined,
585
548
  });
586
549
  return {
587
550
  viewerProtocolPolicy: ViewerProtocolPolicy.REDIRECT_TO_HTTPS,
@@ -644,7 +607,7 @@ function handler(event) {
644
607
  const publicDir = path.join(this.props.path, this.buildConfig.clientBuildOutputDir);
645
608
  for (const item of fs.readdirSync(publicDir)) {
646
609
  const isDir = fs.statSync(path.join(publicDir, item)).isDirectory();
647
- this.distribution.addBehavior(isDir ? `${item}/*` : item, this.s3Origin, {
610
+ this.distribution.cdk.distribution.addBehavior(isDir ? `${item}/*` : item, this.s3Origin, {
648
611
  viewerProtocolPolicy: ViewerProtocolPolicy.REDIRECT_TO_HTTPS,
649
612
  allowedMethods: AllowedMethods.ALLOW_GET_HEAD_OPTIONS,
650
613
  cachedMethods: CachedMethods.CACHE_GET_HEAD_OPTIONS,
@@ -673,158 +636,6 @@ function handler(event) {
673
636
  // CloudFront's Managed-AllViewerExceptHostHeader policy
674
637
  return OriginRequestPolicy.fromOriginRequestPolicyId(this, "ServerOriginRequestPolicy", "b689b0a8-53d0-40ab-baf2-68738e2966ac");
675
638
  }
676
- createCloudFrontInvalidation() {
677
- const stack = Stack.of(this);
678
- const policy = new Policy(this, "CloudFrontInvalidatorPolicy", {
679
- statements: [
680
- new PolicyStatement({
681
- effect: Effect.ALLOW,
682
- actions: [
683
- "cloudfront:GetInvalidation",
684
- "cloudfront:CreateInvalidation",
685
- ],
686
- resources: [
687
- `arn:${stack.partition}:cloudfront::${stack.account}:distribution/${this.distribution.distributionId}`,
688
- ],
689
- }),
690
- ],
691
- });
692
- stack.customResourceHandler.role?.attachInlinePolicy(policy);
693
- const resource = new CustomResource(this, "CloudFrontInvalidator", {
694
- serviceToken: stack.customResourceHandler.functionArn,
695
- resourceType: "Custom::CloudFrontInvalidator",
696
- properties: {
697
- buildId: this.generateBuildId(),
698
- distributionId: this.distribution.distributionId,
699
- paths: ["/*"],
700
- waitForInvalidation: this.props.waitForInvalidation,
701
- },
702
- });
703
- resource.node.addDependency(policy);
704
- return resource;
705
- }
706
- /////////////////////
707
- // Custom Domain
708
- /////////////////////
709
- validateCustomDomainSettings() {
710
- const { customDomain } = this.props;
711
- if (!customDomain) {
712
- return;
713
- }
714
- if (typeof customDomain === "string") {
715
- return;
716
- }
717
- if (customDomain.isExternalDomain === true) {
718
- if (!customDomain.cdk?.certificate) {
719
- throw new Error(`A valid certificate is required when "isExternalDomain" is set to "true".`);
720
- }
721
- if (customDomain.domainAlias) {
722
- 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.`);
723
- }
724
- if (customDomain.hostedZone) {
725
- 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.`);
726
- }
727
- }
728
- }
729
- lookupHostedZone() {
730
- const { customDomain } = this.props;
731
- // Skip if customDomain is not configured
732
- if (!customDomain) {
733
- return;
734
- }
735
- let hostedZone;
736
- if (typeof customDomain === "string") {
737
- hostedZone = HostedZone.fromLookup(this, "HostedZone", {
738
- domainName: customDomain,
739
- });
740
- }
741
- else if (customDomain.cdk?.hostedZone) {
742
- hostedZone = customDomain.cdk.hostedZone;
743
- }
744
- else if (typeof customDomain.hostedZone === "string") {
745
- hostedZone = HostedZone.fromLookup(this, "HostedZone", {
746
- domainName: customDomain.hostedZone,
747
- });
748
- }
749
- else if (typeof customDomain.domainName === "string") {
750
- // Skip if domain is not a Route53 domain
751
- if (customDomain.isExternalDomain === true) {
752
- return;
753
- }
754
- hostedZone = HostedZone.fromLookup(this, "HostedZone", {
755
- domainName: customDomain.domainName,
756
- });
757
- }
758
- else {
759
- hostedZone = customDomain.hostedZone;
760
- }
761
- return hostedZone;
762
- }
763
- createCertificate() {
764
- const { customDomain } = this.props;
765
- if (!customDomain) {
766
- return;
767
- }
768
- let acmCertificate;
769
- // HostedZone is set for Route 53 domains
770
- if (this.hostedZone) {
771
- if (typeof customDomain === "string") {
772
- acmCertificate = new DnsValidatedCertificate(this, "Certificate", {
773
- domainName: customDomain,
774
- hostedZone: this.hostedZone,
775
- region: "us-east-1",
776
- });
777
- }
778
- else if (customDomain.cdk?.certificate) {
779
- acmCertificate = customDomain.cdk.certificate;
780
- }
781
- else {
782
- acmCertificate = new DnsValidatedCertificate(this, "Certificate", {
783
- domainName: customDomain.domainName,
784
- hostedZone: this.hostedZone,
785
- region: "us-east-1",
786
- });
787
- }
788
- }
789
- // HostedZone is NOT set for non-Route 53 domains
790
- else {
791
- if (typeof customDomain !== "string") {
792
- acmCertificate = customDomain.cdk?.certificate;
793
- }
794
- }
795
- return acmCertificate;
796
- }
797
- createRoute53Records() {
798
- const { customDomain } = this.props;
799
- if (!customDomain || !this.hostedZone) {
800
- return;
801
- }
802
- let recordName;
803
- let domainAlias;
804
- if (typeof customDomain === "string") {
805
- recordName = customDomain;
806
- }
807
- else {
808
- recordName = customDomain.domainName;
809
- domainAlias = customDomain.domainAlias;
810
- }
811
- // Create DNS record
812
- const recordProps = {
813
- recordName,
814
- zone: this.hostedZone,
815
- target: RecordTarget.fromAlias(new CloudFrontTarget(this.distribution)),
816
- };
817
- new ARecord(this, "AliasRecord", recordProps);
818
- new AaaaRecord(this, "AliasRecordAAAA", recordProps);
819
- // Create Alias redirect record
820
- if (domainAlias) {
821
- new HttpsRedirect(this, "Redirect", {
822
- zone: this.hostedZone,
823
- recordNames: [domainAlias],
824
- targetDomain: recordName,
825
- });
826
- }
827
- }
828
639
  /////////////////////
829
640
  // Helper Functions
830
641
  /////////////////////
@@ -898,12 +709,15 @@ function handler(event) {
898
709
  Logger.debug(`Generated build ID ${buildId}`);
899
710
  return buildId;
900
711
  }
712
+ supportsStreaming() {
713
+ return false;
714
+ }
901
715
  }
902
716
  export const useSites = createAppContext(() => {
903
717
  const sites = [];
904
718
  return {
905
- add(name, type, props) {
906
- sites.push({ name, type, props });
719
+ add(stack, name, type, props) {
720
+ sites.push({ stack, name, type, props });
907
721
  },
908
722
  get all() {
909
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 {};