@mstuercke/pulumi-modules 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +33 -0
- package/src/auth0/Auth0.ts +162 -0
- package/src/auth0/index.ts +7 -0
- package/src/domain/getDomain.ts +32 -0
- package/src/domain/index.ts +7 -0
- package/src/iam/SimpleIamRolePolicy.ts +165 -0
- package/src/iam/SimpleIamRolePolicyConfig.ts +75 -0
- package/src/iam/index.ts +8 -0
- package/src/index.ts +31 -0
- package/src/lambda/NodeLambdaFunction.ts +120 -0
- package/src/lambda/WithInputs.ts +5 -0
- package/src/lambda/index.ts +7 -0
- package/src/mongodb/MongoDB.ts +86 -0
- package/src/mongodb/index.ts +9 -0
- package/src/rest/RestApi.ts +226 -0
- package/src/rest/index.ts +7 -0
- package/src/s3/PublicS3Bucket.ts +92 -0
- package/src/s3/index.ts +7 -0
- package/src/website/StaticWebsite.ts +159 -0
- package/src/website/index.ts +7 -0
- package/src/website/readFilesRecursive.ts +33 -0
- package/src/websocket/WebsocketApi.ts +219 -0
- package/src/websocket/index.ts +7 -0
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import {type CustomResourceOptions, type Output, Resource} from '@pulumi/pulumi'
|
|
2
|
+
import {RandomPassword} from '@pulumi/random'
|
|
3
|
+
import {DatabaseUser, Project, ProjectIpAccessList, Provider, ServerlessInstance} from '@pulumi/mongodbatlas'
|
|
4
|
+
|
|
5
|
+
export type MongoDBArgs = {
|
|
6
|
+
organizationId: string
|
|
7
|
+
projectId: string
|
|
8
|
+
environmentName: string
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export class MongoDB extends Resource {
|
|
12
|
+
readonly connectionString: Output<string>
|
|
13
|
+
readonly databaseName: string
|
|
14
|
+
readonly username: string
|
|
15
|
+
readonly password: Output<string>
|
|
16
|
+
|
|
17
|
+
constructor(args: MongoDBArgs, opts?: CustomResourceOptions) {
|
|
18
|
+
const {organizationId, projectId, environmentName} = args
|
|
19
|
+
super('mstuercke:mongodb:MongoDB', organizationId, false, undefined, opts)
|
|
20
|
+
|
|
21
|
+
new Provider('provider', {}, {parent: this})
|
|
22
|
+
|
|
23
|
+
const mongoProject = new Project(
|
|
24
|
+
'project',
|
|
25
|
+
{
|
|
26
|
+
orgId: organizationId,
|
|
27
|
+
name: `${projectId}-${environmentName}`,
|
|
28
|
+
tags: {
|
|
29
|
+
project: projectId,
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
{parent: this},
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
new ProjectIpAccessList(
|
|
36
|
+
'network-access',
|
|
37
|
+
{
|
|
38
|
+
projectId: mongoProject.id,
|
|
39
|
+
cidrBlock: '0.0.0.0/0',
|
|
40
|
+
},
|
|
41
|
+
{parent: mongoProject},
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
const mongoInstance = new ServerlessInstance(
|
|
45
|
+
'database',
|
|
46
|
+
{
|
|
47
|
+
projectId: mongoProject.id,
|
|
48
|
+
name: `${projectId}-${environmentName}`,
|
|
49
|
+
providerSettingsBackingProviderName: 'AWS',
|
|
50
|
+
providerSettingsProviderName: 'SERVERLESS',
|
|
51
|
+
providerSettingsRegionName: 'EU_WEST_1', // Ireland
|
|
52
|
+
},
|
|
53
|
+
{parent: mongoProject},
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
const username = `${projectId}-${environmentName}`
|
|
57
|
+
const password = new RandomPassword(
|
|
58
|
+
'password',
|
|
59
|
+
{
|
|
60
|
+
length: 32,
|
|
61
|
+
},
|
|
62
|
+
{parent: mongoProject},
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
const databaseName = projectId
|
|
66
|
+
new DatabaseUser(
|
|
67
|
+
'user',
|
|
68
|
+
{
|
|
69
|
+
projectId: mongoProject.id,
|
|
70
|
+
username,
|
|
71
|
+
password: password.result,
|
|
72
|
+
authDatabaseName: 'admin',
|
|
73
|
+
roles: [
|
|
74
|
+
{roleName: 'dbAdmin', databaseName},
|
|
75
|
+
{roleName: 'readWrite', databaseName},
|
|
76
|
+
],
|
|
77
|
+
},
|
|
78
|
+
{parent: mongoProject},
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
this.connectionString = mongoInstance.connectionStringsStandardSrv
|
|
82
|
+
this.databaseName = databaseName
|
|
83
|
+
this.username = username
|
|
84
|
+
this.password = password.result
|
|
85
|
+
}
|
|
86
|
+
}
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
import * as aws from '@pulumi/aws'
|
|
2
|
+
import {apigateway, lambda} from '@pulumi/aws'
|
|
3
|
+
import type {CustomResourceOptions, Input} from '@pulumi/pulumi'
|
|
4
|
+
import {Record} from '@pulumi/aws/route53'
|
|
5
|
+
import {BasePathMapping, DomainName} from '@pulumi/aws/apigateway'
|
|
6
|
+
import type {NodeLambdaFunction} from '../lambda'
|
|
7
|
+
|
|
8
|
+
export interface RestApiArgs {
|
|
9
|
+
name: string
|
|
10
|
+
projectId: string
|
|
11
|
+
lambdaFunction: NodeLambdaFunction
|
|
12
|
+
timeoutSeconds?: number
|
|
13
|
+
stageName?: string
|
|
14
|
+
domain: {
|
|
15
|
+
zoneId: Input<string>
|
|
16
|
+
fullName: Input<string>
|
|
17
|
+
usEast1CertificateArn: Input<string>
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export class RestApi extends aws.apigateway.RestApi {
|
|
22
|
+
readonly url: string
|
|
23
|
+
|
|
24
|
+
constructor(id: string, args: RestApiArgs, opts?: CustomResourceOptions) {
|
|
25
|
+
const {name, projectId, stageName = 'v1', domain} = args
|
|
26
|
+
super(
|
|
27
|
+
id,
|
|
28
|
+
{
|
|
29
|
+
name,
|
|
30
|
+
tags: {
|
|
31
|
+
project: projectId,
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
opts,
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
const {lambdaFunction, timeoutSeconds = 29} = args
|
|
38
|
+
|
|
39
|
+
const proxyResource = new apigateway.Resource(
|
|
40
|
+
`proxy`,
|
|
41
|
+
{
|
|
42
|
+
restApi: this.id,
|
|
43
|
+
parentId: this.rootResourceId,
|
|
44
|
+
pathPart: '{proxy+}',
|
|
45
|
+
},
|
|
46
|
+
{parent: this},
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
const proxyMethod = new apigateway.Method(
|
|
50
|
+
`proxy`,
|
|
51
|
+
{
|
|
52
|
+
restApi: this.id,
|
|
53
|
+
resourceId: proxyResource.id,
|
|
54
|
+
httpMethod: 'ANY',
|
|
55
|
+
authorization: 'NONE',
|
|
56
|
+
},
|
|
57
|
+
{parent: proxyResource},
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
const lambdaIntegration = new apigateway.Integration(
|
|
61
|
+
`proxy`,
|
|
62
|
+
{
|
|
63
|
+
restApi: this.id,
|
|
64
|
+
resourceId: proxyResource.id,
|
|
65
|
+
httpMethod: proxyMethod.httpMethod,
|
|
66
|
+
integrationHttpMethod: 'POST',
|
|
67
|
+
type: 'AWS_PROXY',
|
|
68
|
+
uri: lambdaFunction.invokeArn,
|
|
69
|
+
timeoutMilliseconds: timeoutSeconds * 1000,
|
|
70
|
+
},
|
|
71
|
+
{parent: proxyMethod},
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
const proxyOptionsMethod = new apigateway.Method(
|
|
75
|
+
`options`,
|
|
76
|
+
{
|
|
77
|
+
restApi: this.id,
|
|
78
|
+
resourceId: proxyResource.id,
|
|
79
|
+
httpMethod: 'OPTIONS',
|
|
80
|
+
authorization: 'NONE',
|
|
81
|
+
},
|
|
82
|
+
{parent: proxyResource},
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
const lambdaIntegrationOptions = new apigateway.Integration(
|
|
86
|
+
`options`,
|
|
87
|
+
{
|
|
88
|
+
restApi: this.id,
|
|
89
|
+
resourceId: proxyResource.id,
|
|
90
|
+
httpMethod: proxyOptionsMethod.httpMethod,
|
|
91
|
+
integrationHttpMethod: 'POST',
|
|
92
|
+
type: 'AWS_PROXY',
|
|
93
|
+
uri: lambdaFunction.invokeArn,
|
|
94
|
+
timeoutMilliseconds: timeoutSeconds * 1000,
|
|
95
|
+
},
|
|
96
|
+
{parent: proxyOptionsMethod},
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
const proxyRootMethod = new apigateway.Method(
|
|
100
|
+
`proxy-root`,
|
|
101
|
+
{
|
|
102
|
+
restApi: this.id,
|
|
103
|
+
resourceId: this.rootResourceId,
|
|
104
|
+
httpMethod: 'ANY',
|
|
105
|
+
authorization: 'NONE',
|
|
106
|
+
},
|
|
107
|
+
{parent: this},
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
const lambdaRootIntegration = new apigateway.Integration(
|
|
111
|
+
`proxy-root`,
|
|
112
|
+
{
|
|
113
|
+
restApi: this.id,
|
|
114
|
+
resourceId: proxyRootMethod.resourceId,
|
|
115
|
+
httpMethod: proxyRootMethod.httpMethod,
|
|
116
|
+
integrationHttpMethod: 'POST',
|
|
117
|
+
type: 'AWS_PROXY',
|
|
118
|
+
uri: lambdaFunction.invokeArn,
|
|
119
|
+
timeoutMilliseconds: timeoutSeconds * 1000,
|
|
120
|
+
},
|
|
121
|
+
{parent: proxyRootMethod},
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
const proxyRootOptionsMethod = new apigateway.Method(
|
|
125
|
+
`proxy-root-options`,
|
|
126
|
+
{
|
|
127
|
+
restApi: this.id,
|
|
128
|
+
resourceId: this.rootResourceId,
|
|
129
|
+
httpMethod: 'OPTIONS',
|
|
130
|
+
authorization: 'NONE',
|
|
131
|
+
},
|
|
132
|
+
{parent: this},
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
const lambdaRootIntegrationOptions = new apigateway.Integration(
|
|
136
|
+
`proxy-root-options`,
|
|
137
|
+
{
|
|
138
|
+
restApi: this.id,
|
|
139
|
+
resourceId: proxyRootOptionsMethod.resourceId,
|
|
140
|
+
httpMethod: proxyRootOptionsMethod.httpMethod,
|
|
141
|
+
integrationHttpMethod: 'POST',
|
|
142
|
+
type: 'AWS_PROXY',
|
|
143
|
+
uri: lambdaFunction.invokeArn,
|
|
144
|
+
timeoutMilliseconds: timeoutSeconds * 1000,
|
|
145
|
+
},
|
|
146
|
+
{parent: proxyRootOptionsMethod},
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
new lambda.Permission(
|
|
150
|
+
`rest-api`,
|
|
151
|
+
{
|
|
152
|
+
statementId: 'AllowRestApiInvoke',
|
|
153
|
+
action: 'lambda:InvokeFunction',
|
|
154
|
+
function: lambdaFunction.name,
|
|
155
|
+
principal: 'apigateway.amazonaws.com',
|
|
156
|
+
sourceArn: this.executionArn.apply((executionArn) => `${executionArn}/*`),
|
|
157
|
+
},
|
|
158
|
+
{parent: lambdaFunction},
|
|
159
|
+
)
|
|
160
|
+
|
|
161
|
+
const deployment = new apigateway.Deployment(
|
|
162
|
+
stageName,
|
|
163
|
+
{restApi: this.id},
|
|
164
|
+
{
|
|
165
|
+
dependsOn: [
|
|
166
|
+
proxyMethod,
|
|
167
|
+
lambdaIntegration,
|
|
168
|
+
lambdaIntegrationOptions,
|
|
169
|
+
proxyRootMethod,
|
|
170
|
+
lambdaRootIntegration,
|
|
171
|
+
lambdaRootIntegrationOptions,
|
|
172
|
+
],
|
|
173
|
+
parent: this,
|
|
174
|
+
},
|
|
175
|
+
)
|
|
176
|
+
|
|
177
|
+
const stage = new apigateway.Stage(
|
|
178
|
+
stageName,
|
|
179
|
+
{
|
|
180
|
+
restApi: this.id,
|
|
181
|
+
stageName: stageName,
|
|
182
|
+
deployment: deployment.id,
|
|
183
|
+
},
|
|
184
|
+
{parent: deployment},
|
|
185
|
+
)
|
|
186
|
+
|
|
187
|
+
const domainName = new DomainName(
|
|
188
|
+
'domain',
|
|
189
|
+
{
|
|
190
|
+
domainName: domain.fullName,
|
|
191
|
+
certificateArn: domain.usEast1CertificateArn,
|
|
192
|
+
},
|
|
193
|
+
{parent: this},
|
|
194
|
+
)
|
|
195
|
+
|
|
196
|
+
new Record(
|
|
197
|
+
'redirect',
|
|
198
|
+
{
|
|
199
|
+
zoneId: domain.zoneId,
|
|
200
|
+
name: domain.fullName,
|
|
201
|
+
type: 'A',
|
|
202
|
+
aliases: [
|
|
203
|
+
{
|
|
204
|
+
zoneId: domainName.cloudfrontZoneId,
|
|
205
|
+
name: domainName.cloudfrontDomainName,
|
|
206
|
+
evaluateTargetHealth: true,
|
|
207
|
+
},
|
|
208
|
+
],
|
|
209
|
+
allowOverwrite: true,
|
|
210
|
+
},
|
|
211
|
+
{parent: domainName},
|
|
212
|
+
)
|
|
213
|
+
|
|
214
|
+
new BasePathMapping(
|
|
215
|
+
'root',
|
|
216
|
+
{
|
|
217
|
+
restApi: this.id,
|
|
218
|
+
stageName: stage.stageName,
|
|
219
|
+
domainName: domainName.domainName,
|
|
220
|
+
},
|
|
221
|
+
{parent: stage},
|
|
222
|
+
)
|
|
223
|
+
|
|
224
|
+
this.url = `https://${domain.fullName}/`
|
|
225
|
+
}
|
|
226
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Bucket,
|
|
3
|
+
BucketAclV2,
|
|
4
|
+
type BucketArgs,
|
|
5
|
+
BucketCorsConfigurationV2,
|
|
6
|
+
BucketOwnershipControls,
|
|
7
|
+
BucketPolicy,
|
|
8
|
+
BucketPublicAccessBlock,
|
|
9
|
+
} from '@pulumi/aws/s3'
|
|
10
|
+
import type {CustomResourceOptions} from '@pulumi/pulumi'
|
|
11
|
+
|
|
12
|
+
type PublicS3BucketArgs = {
|
|
13
|
+
name: string
|
|
14
|
+
allowPresignedPost?: boolean
|
|
15
|
+
tags?: BucketArgs['tags']
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export class PublicS3Bucket extends Bucket {
|
|
19
|
+
constructor(id: string, args: PublicS3BucketArgs, opts?: CustomResourceOptions) {
|
|
20
|
+
const {name, allowPresignedPost = false, tags} = args
|
|
21
|
+
|
|
22
|
+
super(id, {bucket: name, tags}, opts)
|
|
23
|
+
|
|
24
|
+
const accessBlock = new BucketPublicAccessBlock(
|
|
25
|
+
'public-access',
|
|
26
|
+
{
|
|
27
|
+
bucket: this.id,
|
|
28
|
+
blockPublicAcls: false,
|
|
29
|
+
blockPublicPolicy: false,
|
|
30
|
+
ignorePublicAcls: false,
|
|
31
|
+
restrictPublicBuckets: false,
|
|
32
|
+
},
|
|
33
|
+
{parent: this},
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
const ownershipControls = new BucketOwnershipControls(
|
|
37
|
+
'ownership',
|
|
38
|
+
{
|
|
39
|
+
bucket: this.id,
|
|
40
|
+
rule: {objectOwnership: 'BucketOwnerPreferred'},
|
|
41
|
+
},
|
|
42
|
+
{parent: this, dependsOn: [accessBlock]},
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
new BucketAclV2(
|
|
46
|
+
'acl',
|
|
47
|
+
{
|
|
48
|
+
bucket: this.id,
|
|
49
|
+
acl: 'public-read',
|
|
50
|
+
},
|
|
51
|
+
{parent: this, dependsOn: [ownershipControls]},
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
new BucketPolicy(
|
|
55
|
+
'policy',
|
|
56
|
+
{
|
|
57
|
+
bucket: this.id,
|
|
58
|
+
policy: this.arn.apply((arn) =>
|
|
59
|
+
JSON.stringify({
|
|
60
|
+
Version: '2012-10-17',
|
|
61
|
+
Statement: [
|
|
62
|
+
{
|
|
63
|
+
Sid: 'Allow Public Access',
|
|
64
|
+
Effect: 'Allow',
|
|
65
|
+
Principal: '*',
|
|
66
|
+
Action: 's3:GetObject',
|
|
67
|
+
Resource: `${arn}/*`,
|
|
68
|
+
},
|
|
69
|
+
],
|
|
70
|
+
}),
|
|
71
|
+
),
|
|
72
|
+
},
|
|
73
|
+
{parent: this, dependsOn: [accessBlock]},
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
new BucketCorsConfigurationV2(
|
|
77
|
+
'allowAll',
|
|
78
|
+
{
|
|
79
|
+
bucket: this.bucket,
|
|
80
|
+
corsRules: [
|
|
81
|
+
{
|
|
82
|
+
maxAgeSeconds: 3000,
|
|
83
|
+
allowedMethods: allowPresignedPost ? ['GET', 'POST'] : ['GET'],
|
|
84
|
+
allowedHeaders: ['*'],
|
|
85
|
+
allowedOrigins: ['*'],
|
|
86
|
+
},
|
|
87
|
+
],
|
|
88
|
+
},
|
|
89
|
+
{parent: this},
|
|
90
|
+
)
|
|
91
|
+
}
|
|
92
|
+
}
|
package/src/s3/index.ts
ADDED
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import {type CustomResourceOptions, type Input, Resource} from '@pulumi/pulumi'
|
|
2
|
+
import {type PublicS3Bucket, s3} from '../s3'
|
|
3
|
+
import {BucketObject} from '@pulumi/aws/s3'
|
|
4
|
+
import {Distribution} from '@pulumi/aws/cloudfront'
|
|
5
|
+
import {Record} from '@pulumi/aws/route53'
|
|
6
|
+
import {readFilesRecursive} from './readFilesRecursive'
|
|
7
|
+
|
|
8
|
+
export interface StaticWebsiteArgs {
|
|
9
|
+
projectId: string
|
|
10
|
+
environmentName: string
|
|
11
|
+
filesPath: string
|
|
12
|
+
customDomain?: {
|
|
13
|
+
fullName: Input<string>
|
|
14
|
+
zoneId: Input<string>
|
|
15
|
+
usEast1CertificateArn: Input<string>
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export class StaticWebsite extends Resource {
|
|
20
|
+
public url: string
|
|
21
|
+
public bucket: PublicS3Bucket
|
|
22
|
+
|
|
23
|
+
constructor(id: string, args: StaticWebsiteArgs, opts?: CustomResourceOptions) {
|
|
24
|
+
super('mstuercke:website:StaticWebsite', id, false, undefined, opts)
|
|
25
|
+
|
|
26
|
+
const {projectId, environmentName, filesPath, customDomain} = args
|
|
27
|
+
|
|
28
|
+
const bucket = new s3.PublicS3Bucket(
|
|
29
|
+
'bucket',
|
|
30
|
+
{
|
|
31
|
+
name: `${projectId}-web-${environmentName}`,
|
|
32
|
+
allowPresignedPost: false,
|
|
33
|
+
tags: {
|
|
34
|
+
project: projectId,
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
{parent: this},
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
for (const file of readFilesRecursive(filesPath)) {
|
|
41
|
+
new BucketObject(
|
|
42
|
+
file.relativePath,
|
|
43
|
+
{
|
|
44
|
+
key: file.relativePath,
|
|
45
|
+
bucket: bucket.id,
|
|
46
|
+
source: file.absolutePath,
|
|
47
|
+
contentType: file.mimeType,
|
|
48
|
+
etag: file.md5,
|
|
49
|
+
tags: {
|
|
50
|
+
project: projectId,
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
{parent: bucket},
|
|
54
|
+
)
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const cloudfront = new Distribution(
|
|
58
|
+
`website`,
|
|
59
|
+
{
|
|
60
|
+
enabled: true,
|
|
61
|
+
comment: `${projectId} (${environmentName})`,
|
|
62
|
+
defaultRootObject: 'index.html',
|
|
63
|
+
|
|
64
|
+
origins: [
|
|
65
|
+
{
|
|
66
|
+
domainName: bucket.bucketRegionalDomainName,
|
|
67
|
+
originId: bucket.id,
|
|
68
|
+
},
|
|
69
|
+
],
|
|
70
|
+
|
|
71
|
+
defaultCacheBehavior: {
|
|
72
|
+
allowedMethods: ['GET', 'HEAD', 'OPTIONS'],
|
|
73
|
+
cachedMethods: ['GET', 'HEAD'],
|
|
74
|
+
targetOriginId: bucket.id,
|
|
75
|
+
viewerProtocolPolicy: 'redirect-to-https',
|
|
76
|
+
|
|
77
|
+
forwardedValues: {
|
|
78
|
+
queryString: false,
|
|
79
|
+
cookies: {
|
|
80
|
+
forward: 'none',
|
|
81
|
+
},
|
|
82
|
+
},
|
|
83
|
+
},
|
|
84
|
+
|
|
85
|
+
orderedCacheBehaviors: [
|
|
86
|
+
{
|
|
87
|
+
pathPattern: 'index.html',
|
|
88
|
+
allowedMethods: ['GET', 'HEAD', 'OPTIONS'],
|
|
89
|
+
cachedMethods: ['GET', 'HEAD'],
|
|
90
|
+
targetOriginId: bucket.id,
|
|
91
|
+
viewerProtocolPolicy: 'redirect-to-https',
|
|
92
|
+
defaultTtl: 0,
|
|
93
|
+
maxTtl: 0,
|
|
94
|
+
|
|
95
|
+
forwardedValues: {
|
|
96
|
+
queryString: false,
|
|
97
|
+
cookies: {
|
|
98
|
+
forward: 'none',
|
|
99
|
+
},
|
|
100
|
+
},
|
|
101
|
+
},
|
|
102
|
+
],
|
|
103
|
+
|
|
104
|
+
customErrorResponses: [
|
|
105
|
+
{
|
|
106
|
+
errorCode: 404,
|
|
107
|
+
responsePagePath: '/index.html',
|
|
108
|
+
responseCode: 200,
|
|
109
|
+
errorCachingMinTtl: 0,
|
|
110
|
+
},
|
|
111
|
+
],
|
|
112
|
+
|
|
113
|
+
restrictions: {
|
|
114
|
+
geoRestriction: {
|
|
115
|
+
restrictionType: 'none',
|
|
116
|
+
},
|
|
117
|
+
},
|
|
118
|
+
|
|
119
|
+
viewerCertificate: customDomain
|
|
120
|
+
? {
|
|
121
|
+
acmCertificateArn: customDomain?.usEast1CertificateArn,
|
|
122
|
+
sslSupportMethod: 'sni-only',
|
|
123
|
+
minimumProtocolVersion: 'TLSv1.2_2019',
|
|
124
|
+
}
|
|
125
|
+
: {cloudfrontDefaultCertificate: true},
|
|
126
|
+
|
|
127
|
+
aliases: customDomain ? [customDomain?.fullName] : undefined,
|
|
128
|
+
|
|
129
|
+
tags: {
|
|
130
|
+
project: projectId,
|
|
131
|
+
},
|
|
132
|
+
},
|
|
133
|
+
{parent: this},
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
if (customDomain) {
|
|
137
|
+
new Record(
|
|
138
|
+
'redirect',
|
|
139
|
+
{
|
|
140
|
+
zoneId: customDomain?.zoneId,
|
|
141
|
+
name: customDomain?.fullName,
|
|
142
|
+
type: 'A',
|
|
143
|
+
allowOverwrite: true,
|
|
144
|
+
aliases: [
|
|
145
|
+
{
|
|
146
|
+
zoneId: cloudfront.hostedZoneId,
|
|
147
|
+
name: cloudfront.domainName,
|
|
148
|
+
evaluateTargetHealth: false,
|
|
149
|
+
},
|
|
150
|
+
],
|
|
151
|
+
},
|
|
152
|
+
{parent: this},
|
|
153
|
+
)
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
this.url = `https://${customDomain?.fullName || cloudfront.domainName}`
|
|
157
|
+
this.bucket = bucket
|
|
158
|
+
}
|
|
159
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import {createHash} from 'crypto'
|
|
2
|
+
import * as fs from 'fs'
|
|
3
|
+
import * as mime from 'mime'
|
|
4
|
+
import {globSync} from 'glob'
|
|
5
|
+
|
|
6
|
+
interface File {
|
|
7
|
+
absolutePath: string
|
|
8
|
+
relativePath: string
|
|
9
|
+
md5: string
|
|
10
|
+
mimeType: string
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function readFilesRecursive(dir: string): File[] {
|
|
14
|
+
const files = globSync(`${dir}/**/*`, {nodir: true})
|
|
15
|
+
|
|
16
|
+
return files.reduce((previousValue, fullPath) => {
|
|
17
|
+
const content = fs.readFileSync(fullPath, 'base64')
|
|
18
|
+
const md5 = createHash('md5').update(content, 'base64').digest('hex')
|
|
19
|
+
|
|
20
|
+
const mimeType = mime.getType(fullPath)
|
|
21
|
+
if (!mimeType) throw `Cannot determine mimeType for ${fullPath}`
|
|
22
|
+
|
|
23
|
+
return [
|
|
24
|
+
...previousValue,
|
|
25
|
+
{
|
|
26
|
+
absolutePath: fullPath,
|
|
27
|
+
relativePath: fullPath.replace(`${dir}/`, '').replace(dir, ''),
|
|
28
|
+
md5,
|
|
29
|
+
mimeType,
|
|
30
|
+
},
|
|
31
|
+
]
|
|
32
|
+
}, [] as File[])
|
|
33
|
+
}
|