@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.
- package/app/api-destined-function/node_modules/.bin/rimraf +4 -4
- package/app/api-destined-function/package.json +1 -1
- package/dist/src/lib/aws/construct/index.d.ts +1 -0
- package/dist/src/lib/aws/construct/index.js +1 -0
- package/dist/src/lib/aws/construct/site-with-lambda-backend/constants.d.ts +5 -0
- package/dist/src/lib/aws/construct/site-with-lambda-backend/constants.js +9 -0
- package/dist/src/lib/aws/construct/site-with-lambda-backend/index.d.ts +3 -0
- package/dist/src/lib/aws/construct/site-with-lambda-backend/index.js +19 -0
- package/dist/src/lib/aws/construct/site-with-lambda-backend/main.d.ts +116 -0
- package/dist/src/lib/aws/construct/site-with-lambda-backend/main.js +317 -0
- package/dist/src/lib/aws/construct/site-with-lambda-backend/types.d.ts +45 -0
- package/dist/src/lib/aws/construct/site-with-lambda-backend/types.js +2 -0
- package/dist/src/lib/aws/services/cloudwatch/main.js +10 -10
- package/dist/src/lib/aws/services/lambda/main.d.ts +4 -2
- package/dist/src/lib/aws/services/lambda/main.js +9 -1
- package/package.json +16 -16
- package/src/lib/aws/construct/index.ts +1 -0
- package/src/lib/aws/construct/site-with-lambda-backend/constants.ts +6 -0
- package/src/lib/aws/construct/site-with-lambda-backend/index.ts +3 -0
- package/src/lib/aws/construct/site-with-lambda-backend/main.ts +433 -0
- package/src/lib/aws/construct/site-with-lambda-backend/types.ts +64 -0
- package/src/lib/aws/services/cloudwatch/main.ts +10 -10
- 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.
|
|
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.
|
|
50
|
-
"@aws-sdk/credential-providers": "^3.
|
|
51
|
-
"@aws-sdk/types": "^3.
|
|
52
|
-
"@cdktf/provider-azurerm": "^10.0.
|
|
53
|
-
"@types/lodash": "^4.14.
|
|
54
|
-
"@types/node": "^20.
|
|
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.
|
|
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.
|
|
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.
|
|
73
|
-
"@typescript-eslint/parser": "^6.7.
|
|
74
|
-
"aws-cdk": "^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.
|
|
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.
|
|
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.
|
|
96
|
-
"semantic-release": "^22.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,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
|
+
}
|