@gradientedge/cdk-utils 8.125.0 → 8.127.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 (23) hide show
  1. package/app/api-destined-function/node_modules/.bin/rimraf +4 -4
  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/cloudwatch/main.js +10 -10
  14. package/dist/src/lib/aws/services/lambda/main.d.ts +4 -2
  15. package/dist/src/lib/aws/services/lambda/main.js +9 -1
  16. package/package.json +16 -16
  17. package/src/lib/aws/construct/index.ts +1 -0
  18. package/src/lib/aws/construct/site-with-lambda-backend/constants.ts +6 -0
  19. package/src/lib/aws/construct/site-with-lambda-backend/index.ts +3 -0
  20. package/src/lib/aws/construct/site-with-lambda-backend/main.ts +433 -0
  21. package/src/lib/aws/construct/site-with-lambda-backend/types.ts +64 -0
  22. package/src/lib/aws/services/cloudwatch/main.ts +10 -10
  23. package/src/lib/aws/services/lambda/main.ts +18 -1
@@ -1,7 +1,7 @@
1
1
  import { ISecurityGroup, IVpc, SubnetSelection } from 'aws-cdk-lib/aws-ec2';
2
2
  import { IAccessPoint } from 'aws-cdk-lib/aws-efs';
3
3
  import { CfnRole, Role } from 'aws-cdk-lib/aws-iam';
4
- import { Alias, AssetCode, DockerImageCode, DockerImageFunction, Function, ILayerVersion, IVersion, LayerVersion } from 'aws-cdk-lib/aws-lambda';
4
+ import { Alias, Architecture, AssetCode, DockerImageCode, DockerImageFunction, Function, ILayerVersion, IVersion, LayerVersion } from 'aws-cdk-lib/aws-lambda';
5
5
  import { CommonConstruct } from '../../common';
6
6
  import { LambdaAliasProps, LambdaEdgeProps, LambdaProps } from './types';
7
7
  /**
@@ -26,8 +26,10 @@ export declare class LambdaManager {
26
26
  * @param id scoped id of the resource
27
27
  * @param scope scope in which this resource is defined
28
28
  * @param code
29
+ * @param architectures
29
30
  */
30
- createLambdaLayer(id: string, scope: CommonConstruct, code: AssetCode): LayerVersion;
31
+ createLambdaLayer(id: string, scope: CommonConstruct, code: AssetCode, architectures?: Architecture[]): LayerVersion;
32
+ createWebAdapterLayer(id: string, scope: CommonConstruct): ILayerVersion[];
31
33
  /**
32
34
  * @summary Method to create a lambda function (nodejs)
33
35
  * @param id scoped id of the resource
@@ -35,10 +35,12 @@ class LambdaManager {
35
35
  * @param id scoped id of the resource
36
36
  * @param scope scope in which this resource is defined
37
37
  * @param code
38
+ * @param architectures
38
39
  */
39
- createLambdaLayer(id, scope, code) {
40
+ createLambdaLayer(id, scope, code, architectures) {
40
41
  const lambdaLayer = new aws_lambda_1.LayerVersion(scope, `${id}`, {
41
42
  code: code,
43
+ compatibleArchitectures: architectures ?? [aws_lambda_1.Architecture.ARM_64],
42
44
  compatibleRuntimes: [scope.props.nodejsRuntime ?? common_1.CommonStack.NODEJS_RUNTIME],
43
45
  description: `${id}`,
44
46
  layerVersionName: `${id}-${scope.props.stage}`,
@@ -46,6 +48,12 @@ class LambdaManager {
46
48
  (0, utils_1.createCfnOutput)(`${id}-lambdaLayerArn`, scope, lambdaLayer.layerVersionArn);
47
49
  return lambdaLayer;
48
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
+ }
49
57
  /**
50
58
  * @summary Method to create a lambda function (nodejs)
51
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.125.0",
3
+ "version": "8.127.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.414.0",
50
- "@aws-sdk/credential-providers": "^3.414.0",
51
- "@aws-sdk/types": "^3.413.0",
52
- "@cdktf/provider-azurerm": "^10.0.3",
53
- "@types/lodash": "^4.14.198",
54
- "@types/node": "^20.6.2",
49
+ "@aws-sdk/client-secrets-manager": "^3.421.0",
50
+ "@aws-sdk/credential-providers": "^3.421.0",
51
+ "@aws-sdk/types": "^3.418.0",
52
+ "@cdktf/provider-azurerm": "^10.0.5",
53
+ "@types/lodash": "^4.14.199",
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.96.2",
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",
@@ -65,22 +65,22 @@
65
65
  "uuid": "^9.0.1"
66
66
  },
67
67
  "devDependencies": {
68
- "@babel/core": "^7.22.20",
68
+ "@babel/core": "^7.23.0",
69
69
  "@babel/eslint-parser": "^7.22.15",
70
70
  "@babel/plugin-proposal-class-properties": "^7.18.6",
71
71
  "@types/jest": "^29.5.5",
72
- "@typescript-eslint/eslint-plugin": "^6.7.0",
73
- "@typescript-eslint/parser": "^6.7.0",
74
- "aws-cdk": "^2.96.2",
72
+ "@typescript-eslint/eslint-plugin": "^6.7.3",
73
+ "@typescript-eslint/parser": "^6.7.3",
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",
78
78
  "docdash": "^2.0.2",
79
79
  "dotenv": "^16.3.1",
80
- "eslint": "^8.49.0",
80
+ "eslint": "^8.50.0",
81
81
  "eslint-config-prettier": "^9.0.0",
82
82
  "eslint-plugin-import": "^2.28.1",
83
- "eslint-plugin-jsdoc": "^46.8.1",
83
+ "eslint-plugin-jsdoc": "^46.8.2",
84
84
  "husky": "^8.0.3",
85
85
  "jest": "^29.7.0",
86
86
  "jest-extended": "^4.0.1",
@@ -92,8 +92,8 @@
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.1",
96
- "semantic-release": "^22.0.0",
95
+ "rimraf": "^5.0.5",
96
+ "semantic-release": "^22.0.5",
97
97
  "taffydb": "^2.7.3",
98
98
  "ts-jest": "^29.1.1",
99
99
  "ts-node": "^10.9.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
+ }