@gradientedge/cdk-utils 8.126.0 → 8.128.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.
Files changed (25) hide show
  1. package/app/api-destined-function/node_modules/.bin/rimraf +2 -2
  2. package/app/api-destined-function/package.json +1 -1
  3. package/dist/src/lib/aws/construct/index.d.ts +1 -0
  4. package/dist/src/lib/aws/construct/index.js +1 -0
  5. package/dist/src/lib/aws/construct/site-with-lambda-backend/constants.d.ts +5 -0
  6. package/dist/src/lib/aws/construct/site-with-lambda-backend/constants.js +9 -0
  7. package/dist/src/lib/aws/construct/site-with-lambda-backend/index.d.ts +3 -0
  8. package/dist/src/lib/aws/construct/site-with-lambda-backend/index.js +19 -0
  9. package/dist/src/lib/aws/construct/site-with-lambda-backend/main.d.ts +116 -0
  10. package/dist/src/lib/aws/construct/site-with-lambda-backend/main.js +317 -0
  11. package/dist/src/lib/aws/construct/site-with-lambda-backend/types.d.ts +45 -0
  12. package/dist/src/lib/aws/construct/site-with-lambda-backend/types.js +2 -0
  13. package/dist/src/lib/aws/services/api-gateway/main.js +1 -0
  14. package/dist/src/lib/aws/services/api-gateway/types.d.ts +1 -0
  15. package/dist/src/lib/aws/services/lambda/main.d.ts +1 -0
  16. package/dist/src/lib/aws/services/lambda/main.js +6 -0
  17. package/package.json +8 -8
  18. package/src/lib/aws/construct/index.ts +1 -0
  19. package/src/lib/aws/construct/site-with-lambda-backend/constants.ts +6 -0
  20. package/src/lib/aws/construct/site-with-lambda-backend/index.ts +3 -0
  21. package/src/lib/aws/construct/site-with-lambda-backend/main.ts +433 -0
  22. package/src/lib/aws/construct/site-with-lambda-backend/types.ts +64 -0
  23. package/src/lib/aws/services/api-gateway/main.ts +2 -1
  24. package/src/lib/aws/services/api-gateway/types.ts +1 -0
  25. package/src/lib/aws/services/lambda/main.ts +15 -0
@@ -6,9 +6,9 @@ case `uname` in
6
6
  esac
7
7
 
8
8
  if [ -z "$NODE_PATH" ]; then
9
- export NODE_PATH="/home/runner/work/cdk-utils/cdk-utils/node_modules/.pnpm/rimraf@5.0.4/node_modules/rimraf/dist/esm/node_modules:/home/runner/work/cdk-utils/cdk-utils/node_modules/.pnpm/rimraf@5.0.4/node_modules/rimraf/dist/node_modules:/home/runner/work/cdk-utils/cdk-utils/node_modules/.pnpm/rimraf@5.0.4/node_modules/rimraf/node_modules:/home/runner/work/cdk-utils/cdk-utils/node_modules/.pnpm/rimraf@5.0.4/node_modules:/home/runner/work/cdk-utils/cdk-utils/node_modules/.pnpm/node_modules"
9
+ export NODE_PATH="/home/runner/work/cdk-utils/cdk-utils/node_modules/.pnpm/rimraf@5.0.5/node_modules/rimraf/dist/esm/node_modules:/home/runner/work/cdk-utils/cdk-utils/node_modules/.pnpm/rimraf@5.0.5/node_modules/rimraf/dist/node_modules:/home/runner/work/cdk-utils/cdk-utils/node_modules/.pnpm/rimraf@5.0.5/node_modules/rimraf/node_modules:/home/runner/work/cdk-utils/cdk-utils/node_modules/.pnpm/rimraf@5.0.5/node_modules:/home/runner/work/cdk-utils/cdk-utils/node_modules/.pnpm/node_modules"
10
10
  else
11
- export NODE_PATH="/home/runner/work/cdk-utils/cdk-utils/node_modules/.pnpm/rimraf@5.0.4/node_modules/rimraf/dist/esm/node_modules:/home/runner/work/cdk-utils/cdk-utils/node_modules/.pnpm/rimraf@5.0.4/node_modules/rimraf/dist/node_modules:/home/runner/work/cdk-utils/cdk-utils/node_modules/.pnpm/rimraf@5.0.4/node_modules/rimraf/node_modules:/home/runner/work/cdk-utils/cdk-utils/node_modules/.pnpm/rimraf@5.0.4/node_modules:/home/runner/work/cdk-utils/cdk-utils/node_modules/.pnpm/node_modules:$NODE_PATH"
11
+ export NODE_PATH="/home/runner/work/cdk-utils/cdk-utils/node_modules/.pnpm/rimraf@5.0.5/node_modules/rimraf/dist/esm/node_modules:/home/runner/work/cdk-utils/cdk-utils/node_modules/.pnpm/rimraf@5.0.5/node_modules/rimraf/dist/node_modules:/home/runner/work/cdk-utils/cdk-utils/node_modules/.pnpm/rimraf@5.0.5/node_modules/rimraf/node_modules:/home/runner/work/cdk-utils/cdk-utils/node_modules/.pnpm/rimraf@5.0.5/node_modules:/home/runner/work/cdk-utils/cdk-utils/node_modules/.pnpm/node_modules:$NODE_PATH"
12
12
  fi
13
13
  if [ -x "$basedir/node" ]; then
14
14
  exec "$basedir/node" "$basedir/../rimraf/dist/esm/bin.mjs" "$@"
@@ -21,6 +21,6 @@
21
21
  },
22
22
  "devDependencies": {
23
23
  "mkdirp": "^3.0.1",
24
- "rimraf": "^5.0.4"
24
+ "rimraf": "^5.0.5"
25
25
  }
26
26
  }
@@ -9,5 +9,6 @@ export * from './lambda-with-iam-access';
9
9
  export * from './rest-api-lambda';
10
10
  export * from './rest-api-lambda-with-cache';
11
11
  export * from './site-with-ecs-backend';
12
+ export * from './site-with-lambda-backend';
12
13
  export * from './static-asset-deployment';
13
14
  export * from './static-site';
@@ -25,5 +25,6 @@ __exportStar(require("./lambda-with-iam-access"), exports);
25
25
  __exportStar(require("./rest-api-lambda"), exports);
26
26
  __exportStar(require("./rest-api-lambda-with-cache"), exports);
27
27
  __exportStar(require("./site-with-ecs-backend"), exports);
28
+ __exportStar(require("./site-with-lambda-backend"), exports);
28
29
  __exportStar(require("./static-asset-deployment"), exports);
29
30
  __exportStar(require("./static-site"), exports);
@@ -0,0 +1,5 @@
1
+ export declare enum SiteWithLambdaBackendResponseHeaderPolicyType {
2
+ ORIGIN = "origin",
3
+ STATIC = "static"
4
+ }
5
+ export declare const LAMBDA_ALIAS_NAME_CURRENT = "current";
@@ -0,0 +1,9 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.LAMBDA_ALIAS_NAME_CURRENT = exports.SiteWithLambdaBackendResponseHeaderPolicyType = void 0;
4
+ var SiteWithLambdaBackendResponseHeaderPolicyType;
5
+ (function (SiteWithLambdaBackendResponseHeaderPolicyType) {
6
+ SiteWithLambdaBackendResponseHeaderPolicyType["ORIGIN"] = "origin";
7
+ SiteWithLambdaBackendResponseHeaderPolicyType["STATIC"] = "static";
8
+ })(SiteWithLambdaBackendResponseHeaderPolicyType || (exports.SiteWithLambdaBackendResponseHeaderPolicyType = SiteWithLambdaBackendResponseHeaderPolicyType = {}));
9
+ exports.LAMBDA_ALIAS_NAME_CURRENT = 'current';
@@ -0,0 +1,3 @@
1
+ export * from './constants';
2
+ export * from './main';
3
+ export * from './types';
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./constants"), exports);
18
+ __exportStar(require("./main"), exports);
19
+ __exportStar(require("./types"), exports);
@@ -0,0 +1,116 @@
1
+ import { ICertificate } from 'aws-cdk-lib/aws-certificatemanager';
2
+ import { CachePolicy, IFunction as CfIFunction, Distribution, FunctionAssociation, OriginRequestPolicy, ResponseHeadersPolicy } from 'aws-cdk-lib/aws-cloudfront';
3
+ import { HttpOrigin } from 'aws-cdk-lib/aws-cloudfront-origins';
4
+ import { PolicyDocument, Role } from 'aws-cdk-lib/aws-iam';
5
+ import { AssetCode, FunctionUrl, IFunction, ILayerVersion } from 'aws-cdk-lib/aws-lambda';
6
+ import { IHostedZone } from 'aws-cdk-lib/aws-route53';
7
+ import { IBucket } from 'aws-cdk-lib/aws-s3';
8
+ import { BucketDeployment } from 'aws-cdk-lib/aws-s3-deployment';
9
+ import { Construct } from 'constructs';
10
+ import { CommonConstruct } from '../../common';
11
+ import { SiteWithLambdaBackendCachePolicyProps, SiteWithLambdaBackendProps, SiteWithLambdaBackendResponseHeadersPolicyProps } from './types';
12
+ /**
13
+ * @classdesc Provides a construct to create and deploy a site hosted with an clustered ECS/ELB backend
14
+ * @example
15
+ * import { SiteWithLambdaBackend, SiteWithLambdaBackendProps } '@gradientedge/cdk-utils'
16
+ * import { Construct } from 'constructs'
17
+ *
18
+ * class CustomConstruct extends SiteWithLambdaBackend {
19
+ * constructor(parent: Construct, id: string, props: SiteWithLambdaBackendProps) {
20
+ * super(parent, id, props)
21
+ * this.props = props
22
+ * this.id = id
23
+ * this.initResources()
24
+ * }
25
+ * }
26
+ */
27
+ export declare class SiteWithLambdaBackend extends CommonConstruct {
28
+ props: SiteWithLambdaBackendProps;
29
+ id: string;
30
+ siteHostedZone: IHostedZone;
31
+ siteCertificate: ICertificate;
32
+ siteRegionalCertificate: ICertificate;
33
+ siteSecrets: any;
34
+ siteLogBucket: IBucket;
35
+ siteOrigin: HttpOrigin;
36
+ siteDistribution: Distribution;
37
+ siteInternalDomainName: string;
38
+ siteExternalDomainName: string;
39
+ siteDomainNames: string[];
40
+ siteCloudfrontFunction: CfIFunction;
41
+ siteFunctionAssociations: FunctionAssociation[];
42
+ siteOriginRequestPolicy: OriginRequestPolicy;
43
+ siteOriginResponseHeadersPolicy?: ResponseHeadersPolicy;
44
+ siteCachePolicy: CachePolicy;
45
+ siteStaticAssetDeployment: BucketDeployment;
46
+ siteLambdaPolicy: PolicyDocument;
47
+ siteLambdaRole: Role;
48
+ siteLambdaEnvironment: any;
49
+ siteLambdaLayers: ILayerVersion[];
50
+ siteLambdaApplication: AssetCode;
51
+ siteLambdaFunction: IFunction;
52
+ siteLambdaUrl: FunctionUrl;
53
+ constructor(parent: Construct, id: string, props: SiteWithLambdaBackendProps);
54
+ /**
55
+ * @summary Initialise and provision resources
56
+ */
57
+ protected initResources(): void;
58
+ /**
59
+ * @summary Method to resolve a hosted zone based on domain attributes
60
+ */
61
+ protected resolveHostedZone(): void;
62
+ /**
63
+ * @summary Method to resolve a certificate based on attributes
64
+ */
65
+ protected resolveCertificate(): void;
66
+ protected resolveGlobalCertificate(): void;
67
+ protected resolveRegionalCertificate(): void;
68
+ /**
69
+ * @summary Method to resolve secrets from SecretsManager
70
+ * - To be implemented in the overriding method in the implementation class
71
+ */
72
+ protected resolveSiteSecrets(): void;
73
+ /**
74
+ * @summary Method to resolve site domain names
75
+ */
76
+ protected resolveSiteDomainNames(): void;
77
+ /**
78
+ * Method to create log bucket for site distribution
79
+ */
80
+ protected createSiteLogBucket(): void;
81
+ protected createSiteCachePolicy(id: string, siteCachePolicy: SiteWithLambdaBackendCachePolicyProps): CachePolicy;
82
+ protected createSiteOriginCachePolicy(): void;
83
+ protected createSiteOriginRequestPolicy(): void;
84
+ protected createResponseHeaderPolicy(props: SiteWithLambdaBackendResponseHeadersPolicyProps): ResponseHeadersPolicy | undefined;
85
+ protected createSiteOriginResponseHeadersPolicy(): void;
86
+ protected createSiteOrigin(): void;
87
+ protected createSiteOriginResources(): void;
88
+ protected createSiteStaticAssetDeployment(): void;
89
+ protected createSiteLambdaPolicy(): void;
90
+ protected createSiteLambdaRole(): void;
91
+ protected createSiteLambdaEnvironment(): void;
92
+ protected createSiteLambdaLayers(): void;
93
+ protected createSiteLambdaApplication(): void;
94
+ protected createSiteLambda(): void;
95
+ protected createSiteLambdaUrl(): void;
96
+ /**
97
+ * @summary Method to create a site cloudfront function
98
+ */
99
+ protected createSiteCloudfrontFunction(): void;
100
+ /**
101
+ * @summary Method to create a site cloudfront function associations
102
+ */
103
+ protected resolveSiteFunctionAssociations(): void;
104
+ /**
105
+ * Method to create Site distribution
106
+ */
107
+ protected createDistribution(): void;
108
+ /**
109
+ * Method to create Route53 records for distribution
110
+ */
111
+ protected createNetworkMappings(): void;
112
+ /**
113
+ * Method to invalidation the cloudfront distribution cache after a deployment
114
+ */
115
+ protected invalidateDistributionCache(): void;
116
+ }
@@ -0,0 +1,317 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.SiteWithLambdaBackend = void 0;
7
+ const aws_cdk_lib_1 = require("aws-cdk-lib");
8
+ const aws_cloudfront_1 = require("aws-cdk-lib/aws-cloudfront");
9
+ const aws_cloudfront_origins_1 = require("aws-cdk-lib/aws-cloudfront-origins");
10
+ const aws_iam_1 = require("aws-cdk-lib/aws-iam");
11
+ const aws_lambda_1 = require("aws-cdk-lib/aws-lambda");
12
+ const lodash_1 = __importDefault(require("lodash"));
13
+ const common_1 = require("../../common");
14
+ const constants_1 = require("./constants");
15
+ /**
16
+ * @classdesc Provides a construct to create and deploy a site hosted with an clustered ECS/ELB backend
17
+ * @example
18
+ * import { SiteWithLambdaBackend, SiteWithLambdaBackendProps } '@gradientedge/cdk-utils'
19
+ * import { Construct } from 'constructs'
20
+ *
21
+ * class CustomConstruct extends SiteWithLambdaBackend {
22
+ * constructor(parent: Construct, id: string, props: SiteWithLambdaBackendProps) {
23
+ * super(parent, id, props)
24
+ * this.props = props
25
+ * this.id = id
26
+ * this.initResources()
27
+ * }
28
+ * }
29
+ */
30
+ class SiteWithLambdaBackend extends common_1.CommonConstruct {
31
+ /* site properties */
32
+ props;
33
+ id;
34
+ /* site resources */
35
+ siteHostedZone;
36
+ siteCertificate;
37
+ siteRegionalCertificate;
38
+ siteSecrets;
39
+ siteLogBucket;
40
+ siteOrigin;
41
+ siteDistribution;
42
+ siteInternalDomainName;
43
+ siteExternalDomainName;
44
+ siteDomainNames;
45
+ siteCloudfrontFunction;
46
+ siteFunctionAssociations;
47
+ siteOriginRequestPolicy;
48
+ siteOriginResponseHeadersPolicy;
49
+ siteCachePolicy;
50
+ siteStaticAssetDeployment;
51
+ siteLambdaPolicy;
52
+ siteLambdaRole;
53
+ siteLambdaEnvironment;
54
+ siteLambdaLayers;
55
+ siteLambdaApplication;
56
+ siteLambdaFunction;
57
+ siteLambdaUrl;
58
+ constructor(parent, id, props) {
59
+ super(parent, id, props);
60
+ this.props = props;
61
+ this.id = id;
62
+ }
63
+ /**
64
+ * @summary Initialise and provision resources
65
+ */
66
+ initResources() {
67
+ this.resolveHostedZone();
68
+ this.resolveCertificate();
69
+ this.resolveSiteSecrets();
70
+ this.resolveSiteDomainNames();
71
+ this.createSiteLogBucket();
72
+ this.createSiteOriginCachePolicy();
73
+ this.createSiteOriginRequestPolicy();
74
+ this.createSiteOriginResponseHeadersPolicy();
75
+ this.createSiteOrigin();
76
+ this.createSiteCloudfrontFunction();
77
+ this.resolveSiteFunctionAssociations();
78
+ this.createDistribution();
79
+ this.createNetworkMappings();
80
+ this.invalidateDistributionCache();
81
+ }
82
+ /**
83
+ * @summary Method to resolve a hosted zone based on domain attributes
84
+ */
85
+ resolveHostedZone() {
86
+ this.siteHostedZone = this.route53Manager.withHostedZoneFromFullyQualifiedDomainName(`${this.id}-hosted-zone`, this, this.props.useExistingHostedZone);
87
+ }
88
+ /**
89
+ * @summary Method to resolve a certificate based on attributes
90
+ */
91
+ resolveCertificate() {
92
+ this.resolveGlobalCertificate();
93
+ this.resolveRegionalCertificate();
94
+ }
95
+ resolveGlobalCertificate() {
96
+ if (this.props.siteCertificate.useExistingCertificate &&
97
+ this.props.siteCertificate.certificateSsmName &&
98
+ this.props.siteCertificate.certificateRegion) {
99
+ this.props.siteCertificate.certificateArn = this.ssmManager.readStringParameterFromRegion(`${this.id}-certificate-parameter`, this, this.props.siteCertificate.certificateSsmName, this.props.siteCertificate.certificateRegion);
100
+ }
101
+ this.siteCertificate = this.acmManager.resolveCertificate(`${this.id}-certificate`, this, this.props.siteCertificate);
102
+ }
103
+ resolveRegionalCertificate() {
104
+ if (this.props.siteRegionalCertificate.useExistingCertificate &&
105
+ this.props.siteRegionalCertificate.certificateSsmName &&
106
+ this.props.siteRegionalCertificate.certificateRegion) {
107
+ this.props.siteRegionalCertificate.certificateArn = this.ssmManager.readStringParameterFromRegion(`${this.id}-regional-certificate-parameter`, this, this.props.siteRegionalCertificate.certificateSsmName, this.props.siteRegionalCertificate.certificateRegion);
108
+ }
109
+ this.siteRegionalCertificate = this.acmManager.resolveCertificate(`${this.id}-regional-certificate`, this, this.props.siteRegionalCertificate, this.siteHostedZone);
110
+ }
111
+ /**
112
+ * @summary Method to resolve secrets from SecretsManager
113
+ * - To be implemented in the overriding method in the implementation class
114
+ */
115
+ resolveSiteSecrets() { }
116
+ /**
117
+ * @summary Method to resolve site domain names
118
+ */
119
+ resolveSiteDomainNames() {
120
+ /* the internal domain name used by ELB */
121
+ this.siteInternalDomainName =
122
+ this.isProductionStage() || this.props.skipStageForARecords
123
+ ? `${this.props.siteSubDomain}-internal.${this.fullyQualifiedDomainName}`
124
+ : `${this.props.siteSubDomain}-internal-${this.props.stage}.${this.fullyQualifiedDomainName}`;
125
+ /* the external domain name exposed to CloudFront */
126
+ this.siteExternalDomainName =
127
+ this.isProductionStage() || this.props.skipStageForARecords
128
+ ? `${this.props.siteSubDomain}.${this.fullyQualifiedDomainName}`
129
+ : `${this.props.siteSubDomain}-${this.props.stage}.${this.fullyQualifiedDomainName}`;
130
+ this.siteDomainNames = [this.siteExternalDomainName];
131
+ }
132
+ /**
133
+ * Method to create log bucket for site distribution
134
+ */
135
+ createSiteLogBucket() {
136
+ this.siteLogBucket = this.s3Manager.createS3Bucket(`${this.id}-site-logs`, this, this.props.siteLogBucket);
137
+ }
138
+ createSiteCachePolicy(id, siteCachePolicy) {
139
+ return new aws_cloudfront_1.CachePolicy(this, `${id}`, {
140
+ cachePolicyName: `${this.id}-${siteCachePolicy.cachePolicyName}`,
141
+ comment: `Policy for ${this.id}-distribution - ${this.props.stage} stage`,
142
+ cookieBehavior: siteCachePolicy.cookieBehavior,
143
+ enableAcceptEncodingBrotli: siteCachePolicy.enableAcceptEncodingBrotli,
144
+ enableAcceptEncodingGzip: siteCachePolicy.enableAcceptEncodingGzip,
145
+ headerBehavior: siteCachePolicy.headerBehavior,
146
+ maxTtl: aws_cdk_lib_1.Duration.seconds(siteCachePolicy.maxTtlInSeconds),
147
+ minTtl: aws_cdk_lib_1.Duration.seconds(siteCachePolicy.minTtlInSeconds),
148
+ queryStringBehavior: siteCachePolicy.queryStringBehavior,
149
+ });
150
+ }
151
+ createSiteOriginCachePolicy() {
152
+ if (!this.props.siteCachePolicy)
153
+ return;
154
+ this.siteCachePolicy = this.createSiteCachePolicy(`${this.id}-site-cache-policy`, this.props.siteCachePolicy);
155
+ lodash_1.default.assign(this.props.siteDistribution.defaultBehavior, {
156
+ cachePolicy: this.siteCachePolicy,
157
+ });
158
+ }
159
+ createSiteOriginRequestPolicy() {
160
+ if (!this.props.siteOriginRequestPolicy)
161
+ return;
162
+ this.siteOriginRequestPolicy = new aws_cloudfront_1.OriginRequestPolicy(this, `${this.id}-sorp`, {
163
+ comment: `Request Policy for ${this.id}-distribution - ${this.props.stage} stage`,
164
+ cookieBehavior: this.props.siteOriginRequestPolicy.cookieBehavior,
165
+ headerBehavior: this.props.siteOriginRequestPolicy.headerBehavior,
166
+ originRequestPolicyName: `${this.id}-origin-request`,
167
+ queryStringBehavior: this.props.siteOriginRequestPolicy.queryStringBehavior,
168
+ });
169
+ lodash_1.default.assign(this.props.siteDistribution.defaultBehavior, {
170
+ originRequestPolicy: this.siteOriginRequestPolicy,
171
+ });
172
+ }
173
+ createResponseHeaderPolicy(props) {
174
+ if (!props)
175
+ return undefined;
176
+ return new aws_cloudfront_1.ResponseHeadersPolicy(this, `${this.id}-${props.type}-srhp`, {
177
+ ...props,
178
+ comment: `Response Header Policy for ${props.type} for ${this.id}-distribution - ${this.props.stage} stage`,
179
+ responseHeadersPolicyName: `${this.id}-${props.type}-response`,
180
+ securityHeadersBehavior: {
181
+ ...props.securityHeadersBehavior,
182
+ strictTransportSecurity: {
183
+ ...props.securityHeadersBehavior?.strictTransportSecurity,
184
+ accessControlMaxAge: aws_cdk_lib_1.Duration.seconds(props.securityHeadersBehavior?.strictTransportSecurity?.accessControlMaxAgeInSeconds),
185
+ },
186
+ },
187
+ });
188
+ }
189
+ createSiteOriginResponseHeadersPolicy() {
190
+ if (!this.props.siteOriginResponseHeadersPolicy)
191
+ return;
192
+ this.siteOriginResponseHeadersPolicy = this.createResponseHeaderPolicy(this.props.siteOriginResponseHeadersPolicy);
193
+ lodash_1.default.assign(this.props.siteDistribution.defaultBehavior, {
194
+ responseHeadersPolicy: this.siteOriginResponseHeadersPolicy,
195
+ });
196
+ }
197
+ createSiteOrigin() {
198
+ this.createSiteOriginResources();
199
+ this.siteOrigin = new aws_cloudfront_origins_1.HttpOrigin(aws_cdk_lib_1.Fn.select(2, aws_cdk_lib_1.Fn.split('/', this.siteLambdaUrl.url)), {
200
+ httpPort: 443,
201
+ originId: `${this.id}-server`,
202
+ protocolPolicy: aws_cloudfront_1.OriginProtocolPolicy.HTTPS_ONLY,
203
+ });
204
+ }
205
+ createSiteOriginResources() {
206
+ this.createSiteStaticAssetDeployment();
207
+ this.createSiteLambdaPolicy();
208
+ this.createSiteLambdaRole();
209
+ this.createSiteLambdaEnvironment();
210
+ this.createSiteLambdaLayers();
211
+ this.createSiteLambdaApplication();
212
+ this.createSiteLambda();
213
+ this.createSiteLambdaUrl();
214
+ }
215
+ createSiteStaticAssetDeployment() { }
216
+ createSiteLambdaPolicy() {
217
+ this.siteLambdaPolicy = new aws_iam_1.PolicyDocument({
218
+ statements: [
219
+ new aws_iam_1.PolicyStatement({
220
+ actions: ['lambda:InvokeFunctionUrl'],
221
+ effect: aws_iam_1.Effect.ALLOW,
222
+ resources: ['*'],
223
+ }),
224
+ ],
225
+ });
226
+ }
227
+ createSiteLambdaRole() {
228
+ this.siteLambdaRole = this.iamManager.createRoleForLambda(`${this.id}-role`, this, this.siteLambdaPolicy);
229
+ }
230
+ createSiteLambdaEnvironment() {
231
+ this.siteLambdaEnvironment = {
232
+ AWS_LAMBDA_EXEC_WRAPPER: this.props.siteExecWrapperPath ?? '/opt/bootstrap',
233
+ LOG_LEVEL: this.props.logLevel,
234
+ NODE_ENV: this.props.nodeEnv,
235
+ PORT: this.props.sitePort,
236
+ READINESS_CHECK_PATH: this.props.siteHealthEndpoint,
237
+ READINESS_CHECK_PORT: this.props.sitePort,
238
+ STAGE: this.props.stage,
239
+ TZ: this.props.timezone,
240
+ };
241
+ }
242
+ createSiteLambdaLayers() {
243
+ this.siteLambdaLayers = this.lambdaManager.createWebAdapterLayer(`${this.id}-web-adapter`, this);
244
+ }
245
+ createSiteLambdaApplication() { }
246
+ createSiteLambda() {
247
+ this.siteLambdaFunction = this.lambdaManager.createLambdaFunction(`${this.id}-lambda`, this, this.props.siteLambda, this.siteLambdaRole, this.siteLambdaLayers, this.siteLambdaApplication, this.props.siteLambda.handler, this.siteLambdaEnvironment);
248
+ }
249
+ createSiteLambdaUrl() {
250
+ const lambdaAlias = lodash_1.default.find(this.props.siteLambda.lambdaAliases, alias => alias.aliasName === constants_1.LAMBDA_ALIAS_NAME_CURRENT);
251
+ const lambdaFn = lambdaAlias
252
+ ? aws_lambda_1.Function.fromFunctionAttributes(this, `${this.id}-fn-alias`, {
253
+ functionArn: `${this.siteLambdaFunction.functionArn}:${lambdaAlias.aliasName}`,
254
+ sameEnvironment: true,
255
+ })
256
+ : this.siteLambdaFunction;
257
+ lambdaFn.node.addDependency(this.siteLambdaFunction);
258
+ this.siteLambdaUrl = lambdaFn.addFunctionUrl({
259
+ authType: aws_lambda_1.FunctionUrlAuthType.NONE,
260
+ });
261
+ this.siteLambdaUrl.node.addDependency(this.siteLambdaFunction);
262
+ this.siteLambdaUrl.node.addDependency(lambdaFn);
263
+ const principal = new aws_iam_1.AnyPrincipal();
264
+ principal.addToPolicy(new aws_iam_1.PolicyStatement({
265
+ actions: ['lambda:InvokeFunctionUrl'],
266
+ conditions: { StringEquals: { 'lambda:FunctionUrlAuthType': aws_lambda_1.FunctionUrlAuthType.NONE } },
267
+ effect: aws_iam_1.Effect.ALLOW,
268
+ resources: ['*'],
269
+ }));
270
+ lambdaFn.grantInvokeUrl({ grantPrincipal: principal });
271
+ this.addCfnOutput(`${this.id}-url`, this.siteLambdaUrl.url);
272
+ }
273
+ /**
274
+ * @summary Method to create a site cloudfront function
275
+ */
276
+ createSiteCloudfrontFunction() {
277
+ if (this.props.siteCloudfrontFunctionProps) {
278
+ this.siteCloudfrontFunction = this.cloudFrontManager.createCloudfrontFunction(`${this.id}-function`, this, this.props.siteCloudfrontFunctionProps);
279
+ }
280
+ }
281
+ /**
282
+ * @summary Method to create a site cloudfront function associations
283
+ */
284
+ resolveSiteFunctionAssociations() {
285
+ if (this.props.siteCloudfrontFunctionProps) {
286
+ this.siteFunctionAssociations = [
287
+ {
288
+ eventType: aws_cloudfront_1.FunctionEventType.VIEWER_REQUEST,
289
+ function: this.siteCloudfrontFunction,
290
+ },
291
+ ];
292
+ }
293
+ }
294
+ /**
295
+ * Method to create Site distribution
296
+ */
297
+ createDistribution() {
298
+ this.siteDistribution = this.cloudFrontManager.createDistributionWithHttpOrigin(`${this.id}-distribution`, this, this.props.siteDistribution, this.siteOrigin, this.siteDomainNames, this.siteLogBucket, this.siteCertificate, this.siteFunctionAssociations, this.props.siteDistribution.defaultBehavior.responseHeadersPolicy);
299
+ this.siteDistribution.node.addDependency(this.siteLambdaFunction);
300
+ this.siteDistribution.node.addDependency(this.siteLambdaUrl);
301
+ }
302
+ /**
303
+ * Method to create Route53 records for distribution
304
+ */
305
+ createNetworkMappings() {
306
+ this.route53Manager.createCloudFrontTargetARecord(`${this.id}-a-record`, this, this.siteDistribution, this.siteHostedZone, this.props.siteRecordName, this.props.skipStageForARecords);
307
+ }
308
+ /**
309
+ * Method to invalidation the cloudfront distribution cache after a deployment
310
+ */
311
+ invalidateDistributionCache() {
312
+ if (this.props.siteCacheInvalidationDockerFilePath) {
313
+ this.cloudFrontManager.invalidateCache(`${this.id}-cache-invalidation`, this, this.props.siteCacheInvalidationDockerFilePath, this.siteDistribution.distributionId);
314
+ }
315
+ }
316
+ }
317
+ exports.SiteWithLambdaBackend = SiteWithLambdaBackend;
@@ -0,0 +1,45 @@
1
+ import { CachePolicyProps, OriginRequestPolicyProps, ResponseHeadersPolicyProps, ResponseHeadersStrictTransportSecurity, ResponseSecurityHeadersBehavior } from 'aws-cdk-lib/aws-cloudfront';
2
+ import { CommonStackProps } from '../../common';
3
+ import { AcmProps, CloudfrontFunctionProps, DistributionProps, LambdaProps, LogProps, S3BucketProps } from '../../services';
4
+ import { SiteWithLambdaBackendResponseHeaderPolicyType } from './constants';
5
+ /**
6
+ */
7
+ export interface SiteWithLambdaBackendProps extends CommonStackProps {
8
+ logLevel: string;
9
+ nodeEnv: string;
10
+ siteCacheInvalidationDockerFilePath?: string;
11
+ siteCertificate: AcmProps;
12
+ siteCloudfrontFunctionProps?: CloudfrontFunctionProps;
13
+ siteDistribution: DistributionProps;
14
+ siteExecWrapperPath: string;
15
+ siteFunctionFilePath?: string;
16
+ siteHealthEndpoint: string;
17
+ siteLambda: LambdaProps;
18
+ siteLog: LogProps;
19
+ siteLogBucket: S3BucketProps;
20
+ sitePort: string;
21
+ siteCachePolicy?: SiteWithLambdaBackendCachePolicyProps;
22
+ siteOriginRequestPolicy: OriginRequestPolicyProps;
23
+ siteOriginResponseHeadersPolicy: SiteWithLambdaBackendResponseHeadersPolicyProps;
24
+ siteRecordName?: string;
25
+ siteRegionalCertificate: AcmProps;
26
+ siteSubDomain: string;
27
+ timezone: string;
28
+ useExistingHostedZone: boolean;
29
+ useExistingVpc: boolean;
30
+ }
31
+ export interface SiteWithLambdaBackendResponseHeadersStrictTransportSecurity extends ResponseHeadersStrictTransportSecurity {
32
+ accessControlMaxAgeInSeconds: number;
33
+ }
34
+ export interface SiteWithLambdaBackendSecurityHeadersBehavior extends ResponseSecurityHeadersBehavior {
35
+ strictTransportSecurity: SiteWithLambdaBackendResponseHeadersStrictTransportSecurity;
36
+ }
37
+ export interface SiteWithLambdaBackendResponseHeadersPolicyProps extends ResponseHeadersPolicyProps {
38
+ securityHeadersBehavior: SiteWithLambdaBackendSecurityHeadersBehavior;
39
+ type: SiteWithLambdaBackendResponseHeaderPolicyType;
40
+ }
41
+ export interface SiteWithLambdaBackendCachePolicyProps extends CachePolicyProps {
42
+ defaultTtlInSeconds: number;
43
+ minTtlInSeconds: number;
44
+ maxTtlInSeconds: number;
45
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -51,6 +51,7 @@ class ApiManager {
51
51
  },
52
52
  failOnWarnings: props.failOnWarnings || false,
53
53
  handler: lambdaFunction,
54
+ minCompressionSize: props.minCompressionSizeInBytes ? aws_cdk_lib_1.Size.bytes(props.minCompressionSizeInBytes) : undefined,
54
55
  proxy: props.proxy ?? true,
55
56
  restApiName: `${props.restApiName}-${scope.props.stage}`,
56
57
  });
@@ -4,4 +4,5 @@ import { TagProps } from '../../types';
4
4
  */
5
5
  export interface LambdaRestApiProps extends LambdaRestApigProps {
6
6
  tags?: TagProps[];
7
+ minCompressionSizeInBytes?: number;
7
8
  }
@@ -29,6 +29,7 @@ export declare class LambdaManager {
29
29
  * @param architectures
30
30
  */
31
31
  createLambdaLayer(id: string, scope: CommonConstruct, code: AssetCode, architectures?: Architecture[]): LayerVersion;
32
+ createWebAdapterLayer(id: string, scope: CommonConstruct): ILayerVersion[];
32
33
  /**
33
34
  * @summary Method to create a lambda function (nodejs)
34
35
  * @param id scoped id of the resource
@@ -48,6 +48,12 @@ class LambdaManager {
48
48
  (0, utils_1.createCfnOutput)(`${id}-lambdaLayerArn`, scope, lambdaLayer.layerVersionArn);
49
49
  return lambdaLayer;
50
50
  }
51
+ createWebAdapterLayer(id, scope) {
52
+ return [
53
+ aws_lambda_1.LayerVersion.fromLayerVersionArn(scope, `${id}-${aws_lambda_1.Architecture.X86_64}`, `arn:aws:lambda:${scope.props.region}:753240598075:layer:LambdaAdapterLayerX86:17`),
54
+ aws_lambda_1.LayerVersion.fromLayerVersionArn(scope, `${id}-${aws_lambda_1.Architecture.ARM_64}`, `arn:aws:lambda:${scope.props.region}:753240598075:layer:LambdaAdapterLayerArm64:17`),
55
+ ];
56
+ }
51
57
  /**
52
58
  * @summary Method to create a lambda function (nodejs)
53
59
  * @param id scoped id of the resource
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gradientedge/cdk-utils",
3
- "version": "8.126.0",
3
+ "version": "8.128.0",
4
4
  "description": "Utilities for AWS CDK provisioning",
5
5
  "main": "dist/index.js",
6
6
  "engines": {
@@ -46,15 +46,15 @@
46
46
  }
47
47
  },
48
48
  "dependencies": {
49
- "@aws-sdk/client-secrets-manager": "^3.418.0",
50
- "@aws-sdk/credential-providers": "^3.418.0",
49
+ "@aws-sdk/client-secrets-manager": "^3.421.0",
50
+ "@aws-sdk/credential-providers": "^3.421.0",
51
51
  "@aws-sdk/types": "^3.418.0",
52
- "@cdktf/provider-azurerm": "^10.0.4",
52
+ "@cdktf/provider-azurerm": "^10.0.5",
53
53
  "@types/lodash": "^4.14.199",
54
- "@types/node": "^20.7.0",
54
+ "@types/node": "^20.8.0",
55
55
  "@types/uuid": "^9.0.4",
56
56
  "app-root-path": "^3.1.0",
57
- "aws-cdk-lib": "^2.97.1",
57
+ "aws-cdk-lib": "^2.99.1",
58
58
  "cdktf": "^0.18.0",
59
59
  "constructs": "^10.2.70",
60
60
  "lodash": "^4.17.21",
@@ -71,7 +71,7 @@
71
71
  "@types/jest": "^29.5.5",
72
72
  "@typescript-eslint/eslint-plugin": "^6.7.3",
73
73
  "@typescript-eslint/parser": "^6.7.3",
74
- "aws-cdk": "^2.97.1",
74
+ "aws-cdk": "^2.99.1",
75
75
  "better-docs": "^2.7.2",
76
76
  "codecov": "^3.8.3",
77
77
  "commitizen": "^4.3.0",
@@ -92,7 +92,7 @@
92
92
  "jsdoc-to-markdown": "^8.0.0",
93
93
  "prettier": "^3.0.3",
94
94
  "prettier-plugin-organize-imports": "^3.2.3",
95
- "rimraf": "^5.0.4",
95
+ "rimraf": "^5.0.5",
96
96
  "semantic-release": "^22.0.5",
97
97
  "taffydb": "^2.7.3",
98
98
  "ts-jest": "^29.1.1",
@@ -9,5 +9,6 @@ export * from './lambda-with-iam-access'
9
9
  export * from './rest-api-lambda'
10
10
  export * from './rest-api-lambda-with-cache'
11
11
  export * from './site-with-ecs-backend'
12
+ export * from './site-with-lambda-backend'
12
13
  export * from './static-asset-deployment'
13
14
  export * from './static-site'
@@ -0,0 +1,6 @@
1
+ export enum SiteWithLambdaBackendResponseHeaderPolicyType {
2
+ ORIGIN = 'origin',
3
+ STATIC = 'static',
4
+ }
5
+
6
+ export const LAMBDA_ALIAS_NAME_CURRENT = 'current'
@@ -0,0 +1,3 @@
1
+ export * from './constants'
2
+ export * from './main'
3
+ export * from './types'
@@ -0,0 +1,433 @@
1
+ import { Duration, Fn } from 'aws-cdk-lib'
2
+ import { ICertificate } from 'aws-cdk-lib/aws-certificatemanager'
3
+ import {
4
+ CachePolicy,
5
+ IFunction as CfIFunction,
6
+ Distribution,
7
+ FunctionAssociation,
8
+ FunctionEventType,
9
+ OriginProtocolPolicy,
10
+ OriginRequestPolicy,
11
+ ResponseHeadersPolicy,
12
+ } from 'aws-cdk-lib/aws-cloudfront'
13
+ import { HttpOrigin } from 'aws-cdk-lib/aws-cloudfront-origins'
14
+ import { AnyPrincipal, Effect, PolicyDocument, PolicyStatement, Role } from 'aws-cdk-lib/aws-iam'
15
+ import { AssetCode, Function, FunctionUrl, FunctionUrlAuthType, IFunction, ILayerVersion } from 'aws-cdk-lib/aws-lambda'
16
+ import { IHostedZone } from 'aws-cdk-lib/aws-route53'
17
+ import { IBucket } from 'aws-cdk-lib/aws-s3'
18
+ import { BucketDeployment } from 'aws-cdk-lib/aws-s3-deployment'
19
+ import { Construct } from 'constructs'
20
+ import _ from 'lodash'
21
+ import { CommonConstruct } from '../../common'
22
+ import { LAMBDA_ALIAS_NAME_CURRENT } from './constants'
23
+ import {
24
+ SiteWithLambdaBackendCachePolicyProps,
25
+ SiteWithLambdaBackendProps,
26
+ SiteWithLambdaBackendResponseHeadersPolicyProps,
27
+ } from './types'
28
+
29
+ /**
30
+ * @classdesc Provides a construct to create and deploy a site hosted with an clustered ECS/ELB backend
31
+ * @example
32
+ * import { SiteWithLambdaBackend, SiteWithLambdaBackendProps } '@gradientedge/cdk-utils'
33
+ * import { Construct } from 'constructs'
34
+ *
35
+ * class CustomConstruct extends SiteWithLambdaBackend {
36
+ * constructor(parent: Construct, id: string, props: SiteWithLambdaBackendProps) {
37
+ * super(parent, id, props)
38
+ * this.props = props
39
+ * this.id = id
40
+ * this.initResources()
41
+ * }
42
+ * }
43
+ */
44
+ export class SiteWithLambdaBackend extends CommonConstruct {
45
+ /* site properties */
46
+ props: SiteWithLambdaBackendProps
47
+ id: string
48
+
49
+ /* site resources */
50
+ siteHostedZone: IHostedZone
51
+ siteCertificate: ICertificate
52
+ siteRegionalCertificate: ICertificate
53
+ siteSecrets: any
54
+ siteLogBucket: IBucket
55
+ siteOrigin: HttpOrigin
56
+ siteDistribution: Distribution
57
+ siteInternalDomainName: string
58
+ siteExternalDomainName: string
59
+ siteDomainNames: string[]
60
+ siteCloudfrontFunction: CfIFunction
61
+ siteFunctionAssociations: FunctionAssociation[]
62
+ siteOriginRequestPolicy: OriginRequestPolicy
63
+ siteOriginResponseHeadersPolicy?: ResponseHeadersPolicy
64
+ siteCachePolicy: CachePolicy
65
+ siteStaticAssetDeployment: BucketDeployment
66
+ siteLambdaPolicy: PolicyDocument
67
+ siteLambdaRole: Role
68
+ siteLambdaEnvironment: any
69
+ siteLambdaLayers: ILayerVersion[]
70
+ siteLambdaApplication: AssetCode
71
+ siteLambdaFunction: IFunction
72
+ siteLambdaUrl: FunctionUrl
73
+
74
+ constructor(parent: Construct, id: string, props: SiteWithLambdaBackendProps) {
75
+ super(parent, id, props)
76
+ this.props = props
77
+ this.id = id
78
+ }
79
+
80
+ /**
81
+ * @summary Initialise and provision resources
82
+ */
83
+ protected initResources() {
84
+ this.resolveHostedZone()
85
+ this.resolveCertificate()
86
+ this.resolveSiteSecrets()
87
+ this.resolveSiteDomainNames()
88
+ this.createSiteLogBucket()
89
+ this.createSiteOriginCachePolicy()
90
+ this.createSiteOriginRequestPolicy()
91
+ this.createSiteOriginResponseHeadersPolicy()
92
+ this.createSiteOrigin()
93
+ this.createSiteCloudfrontFunction()
94
+ this.resolveSiteFunctionAssociations()
95
+ this.createDistribution()
96
+ this.createNetworkMappings()
97
+ this.invalidateDistributionCache()
98
+ }
99
+
100
+ /**
101
+ * @summary Method to resolve a hosted zone based on domain attributes
102
+ */
103
+ protected resolveHostedZone() {
104
+ this.siteHostedZone = this.route53Manager.withHostedZoneFromFullyQualifiedDomainName(
105
+ `${this.id}-hosted-zone`,
106
+ this,
107
+ this.props.useExistingHostedZone
108
+ )
109
+ }
110
+
111
+ /**
112
+ * @summary Method to resolve a certificate based on attributes
113
+ */
114
+ protected resolveCertificate() {
115
+ this.resolveGlobalCertificate()
116
+ this.resolveRegionalCertificate()
117
+ }
118
+
119
+ protected resolveGlobalCertificate() {
120
+ if (
121
+ this.props.siteCertificate.useExistingCertificate &&
122
+ this.props.siteCertificate.certificateSsmName &&
123
+ this.props.siteCertificate.certificateRegion
124
+ ) {
125
+ this.props.siteCertificate.certificateArn = this.ssmManager.readStringParameterFromRegion(
126
+ `${this.id}-certificate-parameter`,
127
+ this,
128
+ this.props.siteCertificate.certificateSsmName,
129
+ this.props.siteCertificate.certificateRegion
130
+ )
131
+ }
132
+ this.siteCertificate = this.acmManager.resolveCertificate(
133
+ `${this.id}-certificate`,
134
+ this,
135
+ this.props.siteCertificate
136
+ )
137
+ }
138
+
139
+ protected resolveRegionalCertificate() {
140
+ if (
141
+ this.props.siteRegionalCertificate.useExistingCertificate &&
142
+ this.props.siteRegionalCertificate.certificateSsmName &&
143
+ this.props.siteRegionalCertificate.certificateRegion
144
+ ) {
145
+ this.props.siteRegionalCertificate.certificateArn = this.ssmManager.readStringParameterFromRegion(
146
+ `${this.id}-regional-certificate-parameter`,
147
+ this,
148
+ this.props.siteRegionalCertificate.certificateSsmName,
149
+ this.props.siteRegionalCertificate.certificateRegion
150
+ )
151
+ }
152
+ this.siteRegionalCertificate = this.acmManager.resolveCertificate(
153
+ `${this.id}-regional-certificate`,
154
+ this,
155
+ this.props.siteRegionalCertificate,
156
+ this.siteHostedZone
157
+ )
158
+ }
159
+
160
+ /**
161
+ * @summary Method to resolve secrets from SecretsManager
162
+ * - To be implemented in the overriding method in the implementation class
163
+ */
164
+ protected resolveSiteSecrets() {}
165
+
166
+ /**
167
+ * @summary Method to resolve site domain names
168
+ */
169
+ protected resolveSiteDomainNames() {
170
+ /* the internal domain name used by ELB */
171
+ this.siteInternalDomainName =
172
+ this.isProductionStage() || this.props.skipStageForARecords
173
+ ? `${this.props.siteSubDomain}-internal.${this.fullyQualifiedDomainName}`
174
+ : `${this.props.siteSubDomain}-internal-${this.props.stage}.${this.fullyQualifiedDomainName}`
175
+
176
+ /* the external domain name exposed to CloudFront */
177
+ this.siteExternalDomainName =
178
+ this.isProductionStage() || this.props.skipStageForARecords
179
+ ? `${this.props.siteSubDomain}.${this.fullyQualifiedDomainName}`
180
+ : `${this.props.siteSubDomain}-${this.props.stage}.${this.fullyQualifiedDomainName}`
181
+
182
+ this.siteDomainNames = [this.siteExternalDomainName]
183
+ }
184
+
185
+ /**
186
+ * Method to create log bucket for site distribution
187
+ */
188
+ protected createSiteLogBucket() {
189
+ this.siteLogBucket = this.s3Manager.createS3Bucket(`${this.id}-site-logs`, this, this.props.siteLogBucket)
190
+ }
191
+
192
+ protected createSiteCachePolicy(id: string, siteCachePolicy: SiteWithLambdaBackendCachePolicyProps) {
193
+ return new CachePolicy(this, `${id}`, {
194
+ cachePolicyName: `${this.id}-${siteCachePolicy.cachePolicyName}`,
195
+ comment: `Policy for ${this.id}-distribution - ${this.props.stage} stage`,
196
+ cookieBehavior: siteCachePolicy.cookieBehavior,
197
+ enableAcceptEncodingBrotli: siteCachePolicy.enableAcceptEncodingBrotli,
198
+ enableAcceptEncodingGzip: siteCachePolicy.enableAcceptEncodingGzip,
199
+ headerBehavior: siteCachePolicy.headerBehavior,
200
+ maxTtl: Duration.seconds(siteCachePolicy.maxTtlInSeconds),
201
+ minTtl: Duration.seconds(siteCachePolicy.minTtlInSeconds),
202
+ queryStringBehavior: siteCachePolicy.queryStringBehavior,
203
+ })
204
+ }
205
+
206
+ protected createSiteOriginCachePolicy() {
207
+ if (!this.props.siteCachePolicy) return
208
+ this.siteCachePolicy = this.createSiteCachePolicy(`${this.id}-site-cache-policy`, this.props.siteCachePolicy)
209
+ _.assign(this.props.siteDistribution.defaultBehavior, {
210
+ cachePolicy: this.siteCachePolicy,
211
+ })
212
+ }
213
+
214
+ protected createSiteOriginRequestPolicy() {
215
+ if (!this.props.siteOriginRequestPolicy) return
216
+ this.siteOriginRequestPolicy = new OriginRequestPolicy(this, `${this.id}-sorp`, {
217
+ comment: `Request Policy for ${this.id}-distribution - ${this.props.stage} stage`,
218
+ cookieBehavior: this.props.siteOriginRequestPolicy.cookieBehavior,
219
+ headerBehavior: this.props.siteOriginRequestPolicy.headerBehavior,
220
+ originRequestPolicyName: `${this.id}-origin-request`,
221
+ queryStringBehavior: this.props.siteOriginRequestPolicy.queryStringBehavior,
222
+ })
223
+
224
+ _.assign(this.props.siteDistribution.defaultBehavior, {
225
+ originRequestPolicy: this.siteOriginRequestPolicy,
226
+ })
227
+ }
228
+
229
+ protected createResponseHeaderPolicy(props: SiteWithLambdaBackendResponseHeadersPolicyProps) {
230
+ if (!props) return undefined
231
+ return new ResponseHeadersPolicy(this, `${this.id}-${props.type}-srhp`, {
232
+ ...props,
233
+ comment: `Response Header Policy for ${props.type} for ${this.id}-distribution - ${this.props.stage} stage`,
234
+ responseHeadersPolicyName: `${this.id}-${props.type}-response`,
235
+ securityHeadersBehavior: {
236
+ ...props.securityHeadersBehavior,
237
+ strictTransportSecurity: {
238
+ ...props.securityHeadersBehavior?.strictTransportSecurity,
239
+ accessControlMaxAge: Duration.seconds(
240
+ props.securityHeadersBehavior?.strictTransportSecurity?.accessControlMaxAgeInSeconds
241
+ ),
242
+ },
243
+ },
244
+ })
245
+ }
246
+
247
+ protected createSiteOriginResponseHeadersPolicy() {
248
+ if (!this.props.siteOriginResponseHeadersPolicy) return
249
+ this.siteOriginResponseHeadersPolicy = this.createResponseHeaderPolicy(this.props.siteOriginResponseHeadersPolicy)
250
+ _.assign(this.props.siteDistribution.defaultBehavior, {
251
+ responseHeadersPolicy: this.siteOriginResponseHeadersPolicy,
252
+ })
253
+ }
254
+
255
+ protected createSiteOrigin() {
256
+ this.createSiteOriginResources()
257
+ this.siteOrigin = new HttpOrigin(Fn.select(2, Fn.split('/', this.siteLambdaUrl.url)), {
258
+ httpPort: 443,
259
+ originId: `${this.id}-server`,
260
+ protocolPolicy: OriginProtocolPolicy.HTTPS_ONLY,
261
+ })
262
+ }
263
+
264
+ protected createSiteOriginResources() {
265
+ this.createSiteStaticAssetDeployment()
266
+ this.createSiteLambdaPolicy()
267
+ this.createSiteLambdaRole()
268
+ this.createSiteLambdaEnvironment()
269
+ this.createSiteLambdaLayers()
270
+ this.createSiteLambdaApplication()
271
+ this.createSiteLambda()
272
+ this.createSiteLambdaUrl()
273
+ }
274
+
275
+ protected createSiteStaticAssetDeployment() {}
276
+
277
+ protected createSiteLambdaPolicy() {
278
+ this.siteLambdaPolicy = new PolicyDocument({
279
+ statements: [
280
+ new PolicyStatement({
281
+ actions: ['lambda:InvokeFunctionUrl'],
282
+ effect: Effect.ALLOW,
283
+ resources: ['*'],
284
+ }),
285
+ ],
286
+ })
287
+ }
288
+
289
+ protected createSiteLambdaRole() {
290
+ this.siteLambdaRole = this.iamManager.createRoleForLambda(`${this.id}-role`, this, this.siteLambdaPolicy)
291
+ }
292
+
293
+ protected createSiteLambdaEnvironment() {
294
+ this.siteLambdaEnvironment = {
295
+ AWS_LAMBDA_EXEC_WRAPPER: this.props.siteExecWrapperPath ?? '/opt/bootstrap',
296
+ LOG_LEVEL: this.props.logLevel,
297
+ NODE_ENV: this.props.nodeEnv,
298
+ PORT: this.props.sitePort,
299
+ READINESS_CHECK_PATH: this.props.siteHealthEndpoint,
300
+ READINESS_CHECK_PORT: this.props.sitePort,
301
+ STAGE: this.props.stage,
302
+ TZ: this.props.timezone,
303
+ }
304
+ }
305
+
306
+ protected createSiteLambdaLayers() {
307
+ this.siteLambdaLayers = this.lambdaManager.createWebAdapterLayer(`${this.id}-web-adapter`, this)
308
+ }
309
+
310
+ protected createSiteLambdaApplication() {}
311
+
312
+ protected createSiteLambda() {
313
+ this.siteLambdaFunction = this.lambdaManager.createLambdaFunction(
314
+ `${this.id}-lambda`,
315
+ this,
316
+ this.props.siteLambda,
317
+ this.siteLambdaRole,
318
+ this.siteLambdaLayers,
319
+ this.siteLambdaApplication,
320
+ this.props.siteLambda.handler,
321
+ this.siteLambdaEnvironment
322
+ )
323
+ }
324
+
325
+ protected createSiteLambdaUrl() {
326
+ const lambdaAlias = _.find(
327
+ this.props.siteLambda.lambdaAliases,
328
+ alias => alias.aliasName === LAMBDA_ALIAS_NAME_CURRENT
329
+ )
330
+
331
+ const lambdaFn = lambdaAlias
332
+ ? Function.fromFunctionAttributes(this, `${this.id}-fn-alias`, {
333
+ functionArn: `${this.siteLambdaFunction.functionArn}:${lambdaAlias.aliasName}`,
334
+ sameEnvironment: true,
335
+ })
336
+ : this.siteLambdaFunction
337
+ lambdaFn.node.addDependency(this.siteLambdaFunction)
338
+
339
+ this.siteLambdaUrl = lambdaFn.addFunctionUrl({
340
+ authType: FunctionUrlAuthType.NONE,
341
+ })
342
+ this.siteLambdaUrl.node.addDependency(this.siteLambdaFunction)
343
+ this.siteLambdaUrl.node.addDependency(lambdaFn)
344
+
345
+ const principal = new AnyPrincipal()
346
+ principal.addToPolicy(
347
+ new PolicyStatement({
348
+ actions: ['lambda:InvokeFunctionUrl'],
349
+ conditions: { StringEquals: { 'lambda:FunctionUrlAuthType': FunctionUrlAuthType.NONE } },
350
+ effect: Effect.ALLOW,
351
+ resources: ['*'],
352
+ })
353
+ )
354
+
355
+ lambdaFn.grantInvokeUrl({ grantPrincipal: principal })
356
+
357
+ this.addCfnOutput(`${this.id}-url`, this.siteLambdaUrl.url)
358
+ }
359
+
360
+ /**
361
+ * @summary Method to create a site cloudfront function
362
+ */
363
+ protected createSiteCloudfrontFunction() {
364
+ if (this.props.siteCloudfrontFunctionProps) {
365
+ this.siteCloudfrontFunction = this.cloudFrontManager.createCloudfrontFunction(
366
+ `${this.id}-function`,
367
+ this,
368
+ this.props.siteCloudfrontFunctionProps
369
+ )
370
+ }
371
+ }
372
+
373
+ /**
374
+ * @summary Method to create a site cloudfront function associations
375
+ */
376
+ protected resolveSiteFunctionAssociations() {
377
+ if (this.props.siteCloudfrontFunctionProps) {
378
+ this.siteFunctionAssociations = [
379
+ {
380
+ eventType: FunctionEventType.VIEWER_REQUEST,
381
+ function: this.siteCloudfrontFunction,
382
+ },
383
+ ]
384
+ }
385
+ }
386
+
387
+ /**
388
+ * Method to create Site distribution
389
+ */
390
+ protected createDistribution() {
391
+ this.siteDistribution = this.cloudFrontManager.createDistributionWithHttpOrigin(
392
+ `${this.id}-distribution`,
393
+ this,
394
+ this.props.siteDistribution,
395
+ this.siteOrigin,
396
+ this.siteDomainNames,
397
+ this.siteLogBucket,
398
+ this.siteCertificate,
399
+ this.siteFunctionAssociations,
400
+ this.props.siteDistribution.defaultBehavior.responseHeadersPolicy
401
+ )
402
+ this.siteDistribution.node.addDependency(this.siteLambdaFunction)
403
+ this.siteDistribution.node.addDependency(this.siteLambdaUrl)
404
+ }
405
+
406
+ /**
407
+ * Method to create Route53 records for distribution
408
+ */
409
+ protected createNetworkMappings() {
410
+ this.route53Manager.createCloudFrontTargetARecord(
411
+ `${this.id}-a-record`,
412
+ this,
413
+ this.siteDistribution,
414
+ this.siteHostedZone,
415
+ this.props.siteRecordName,
416
+ this.props.skipStageForARecords
417
+ )
418
+ }
419
+
420
+ /**
421
+ * Method to invalidation the cloudfront distribution cache after a deployment
422
+ */
423
+ protected invalidateDistributionCache() {
424
+ if (this.props.siteCacheInvalidationDockerFilePath) {
425
+ this.cloudFrontManager.invalidateCache(
426
+ `${this.id}-cache-invalidation`,
427
+ this,
428
+ this.props.siteCacheInvalidationDockerFilePath,
429
+ this.siteDistribution.distributionId
430
+ )
431
+ }
432
+ }
433
+ }
@@ -0,0 +1,64 @@
1
+ import {
2
+ CachePolicyProps,
3
+ OriginRequestPolicyProps,
4
+ ResponseHeadersPolicyProps,
5
+ ResponseHeadersStrictTransportSecurity,
6
+ ResponseSecurityHeadersBehavior,
7
+ } from 'aws-cdk-lib/aws-cloudfront'
8
+ import { CommonStackProps } from '../../common'
9
+ import {
10
+ AcmProps,
11
+ CloudfrontFunctionProps,
12
+ DistributionProps,
13
+ LambdaProps,
14
+ LogProps,
15
+ S3BucketProps,
16
+ } from '../../services'
17
+ import { SiteWithLambdaBackendResponseHeaderPolicyType } from './constants'
18
+
19
+ /**
20
+ */
21
+ export interface SiteWithLambdaBackendProps extends CommonStackProps {
22
+ logLevel: string
23
+ nodeEnv: string
24
+ siteCacheInvalidationDockerFilePath?: string
25
+ siteCertificate: AcmProps
26
+ siteCloudfrontFunctionProps?: CloudfrontFunctionProps
27
+ siteDistribution: DistributionProps
28
+ siteExecWrapperPath: string
29
+ siteFunctionFilePath?: string
30
+ siteHealthEndpoint: string
31
+ siteLambda: LambdaProps
32
+ siteLog: LogProps
33
+ siteLogBucket: S3BucketProps
34
+ sitePort: string
35
+ siteCachePolicy?: SiteWithLambdaBackendCachePolicyProps
36
+ siteOriginRequestPolicy: OriginRequestPolicyProps
37
+ siteOriginResponseHeadersPolicy: SiteWithLambdaBackendResponseHeadersPolicyProps
38
+ siteRecordName?: string
39
+ siteRegionalCertificate: AcmProps
40
+ siteSubDomain: string
41
+ timezone: string
42
+ useExistingHostedZone: boolean
43
+ useExistingVpc: boolean
44
+ }
45
+
46
+ export interface SiteWithLambdaBackendResponseHeadersStrictTransportSecurity
47
+ extends ResponseHeadersStrictTransportSecurity {
48
+ accessControlMaxAgeInSeconds: number
49
+ }
50
+
51
+ export interface SiteWithLambdaBackendSecurityHeadersBehavior extends ResponseSecurityHeadersBehavior {
52
+ strictTransportSecurity: SiteWithLambdaBackendResponseHeadersStrictTransportSecurity
53
+ }
54
+
55
+ export interface SiteWithLambdaBackendResponseHeadersPolicyProps extends ResponseHeadersPolicyProps {
56
+ securityHeadersBehavior: SiteWithLambdaBackendSecurityHeadersBehavior
57
+ type: SiteWithLambdaBackendResponseHeaderPolicyType
58
+ }
59
+
60
+ export interface SiteWithLambdaBackendCachePolicyProps extends CachePolicyProps {
61
+ defaultTtlInSeconds: number
62
+ minTtlInSeconds: number
63
+ maxTtlInSeconds: number
64
+ }
@@ -1,4 +1,4 @@
1
- import { Tags } from 'aws-cdk-lib'
1
+ import { Tags, Size } from 'aws-cdk-lib'
2
2
  import {
3
3
  Cors,
4
4
  Deployment,
@@ -60,6 +60,7 @@ export class ApiManager {
60
60
  },
61
61
  failOnWarnings: props.failOnWarnings || false,
62
62
  handler: lambdaFunction,
63
+ minCompressionSize: props.minCompressionSizeInBytes ? Size.bytes(props.minCompressionSizeInBytes) : undefined,
63
64
  proxy: props.proxy ?? true,
64
65
  restApiName: `${props.restApiName}-${scope.props.stage}`,
65
66
  })
@@ -5,4 +5,5 @@ import { TagProps } from '../../types'
5
5
  */
6
6
  export interface LambdaRestApiProps extends LambdaRestApigProps {
7
7
  tags?: TagProps[]
8
+ minCompressionSizeInBytes?: number
8
9
  }
@@ -60,6 +60,21 @@ export class LambdaManager {
60
60
  return lambdaLayer
61
61
  }
62
62
 
63
+ public createWebAdapterLayer(id: string, scope: CommonConstruct) {
64
+ return [
65
+ LayerVersion.fromLayerVersionArn(
66
+ scope,
67
+ `${id}-${Architecture.X86_64}`,
68
+ `arn:aws:lambda:${scope.props.region}:753240598075:layer:LambdaAdapterLayerX86:17`
69
+ ),
70
+ LayerVersion.fromLayerVersionArn(
71
+ scope,
72
+ `${id}-${Architecture.ARM_64}`,
73
+ `arn:aws:lambda:${scope.props.region}:753240598075:layer:LambdaAdapterLayerArm64:17`
74
+ ),
75
+ ]
76
+ }
77
+
63
78
  /**
64
79
  * @summary Method to create a lambda function (nodejs)
65
80
  * @param id scoped id of the resource