@mstuercke/pulumi-modules 0.0.6 → 0.0.8
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/dist/auth0/Auth0.js +81 -0
- package/dist/auth0/index.js +5 -0
- package/dist/domain/getDomain.js +14 -0
- package/dist/domain/index.js +5 -0
- package/dist/iam/SimpleIamRolePolicy.js +115 -0
- package/dist/iam/SimpleIamRolePolicyConfig.js +1 -0
- package/dist/iam/index.js +6 -0
- package/dist/index.js +29 -0
- package/dist/lambda/NodeLambdaFunction.js +71 -0
- package/dist/lambda/WithInputs.js +1 -0
- package/dist/lambda/index.js +5 -0
- package/dist/mongodb/MongoDB.js +51 -0
- package/dist/mongodb/MongoDBCustomInstance.js +32 -0
- package/dist/mongodb/MongoDBDefaultInstance.js +19 -0
- package/dist/mongodb/MongoDBUser.js +28 -0
- package/dist/mongodb/index.js +11 -0
- package/dist/rest/RestApi.js +129 -0
- package/dist/rest/index.js +5 -0
- package/dist/s3/PublicS3Bucket.js +48 -0
- package/dist/s3/index.js +5 -0
- package/dist/website/StaticWebsite.js +129 -0
- package/dist/website/index.js +5 -0
- package/dist/website/readFilesRecursive.js +23 -0
- package/dist/websocket/WebsocketApi.js +130 -0
- package/dist/websocket/index.js +5 -0
- package/package.json +2 -2
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { Resource } from '@pulumi/pulumi';
|
|
2
|
+
import { Client, ClientCredentials, Connection, ConnectionClient, Provider, Tenant, User } from '@pulumi/auth0';
|
|
3
|
+
export class Auth0 extends Resource {
|
|
4
|
+
domain;
|
|
5
|
+
clientId;
|
|
6
|
+
constructor(id, args, opts) {
|
|
7
|
+
super('mstuercke:auth0:Auth0', id, false, undefined, opts);
|
|
8
|
+
const { projectId, environmentName, tenantDisplayName, clientDisplayName, domain, m2mClientId, allowedUrls, allowedLoginCallbackUrls, allowedLogoutCallbackUrls, nativeAppId, defaultUsers = [], disableSignup = false, } = args;
|
|
9
|
+
new Provider('provider', {}, { parent: this });
|
|
10
|
+
new Tenant('tenant', {
|
|
11
|
+
friendlyName: tenantDisplayName,
|
|
12
|
+
flags: {
|
|
13
|
+
enableClientConnections: false,
|
|
14
|
+
},
|
|
15
|
+
}, { parent: this });
|
|
16
|
+
const nativeAppCallbackUrls = nativeAppId
|
|
17
|
+
? [
|
|
18
|
+
`${nativeAppId}.auth0://${domain}/ios/${nativeAppId}/callback`,
|
|
19
|
+
`${nativeAppId}.auth0://${domain}/android/${nativeAppId}/callback`,
|
|
20
|
+
]
|
|
21
|
+
: [];
|
|
22
|
+
const client = new Client('client', {
|
|
23
|
+
name: clientDisplayName,
|
|
24
|
+
appType: 'spa',
|
|
25
|
+
callbacks: [...(allowedLoginCallbackUrls || allowedUrls), ...nativeAppCallbackUrls],
|
|
26
|
+
webOrigins: allowedUrls,
|
|
27
|
+
allowedLogoutUrls: [...(allowedLogoutCallbackUrls || allowedUrls), ...nativeAppCallbackUrls],
|
|
28
|
+
jwtConfiguration: {
|
|
29
|
+
alg: 'RS256',
|
|
30
|
+
},
|
|
31
|
+
addons: {},
|
|
32
|
+
}, { parent: this });
|
|
33
|
+
const connection = new Connection('password-authentication', {
|
|
34
|
+
name: `${projectId}-${environmentName}`,
|
|
35
|
+
strategy: 'auth0',
|
|
36
|
+
options: {
|
|
37
|
+
disableSignup: disableSignup,
|
|
38
|
+
bruteForceProtection: true,
|
|
39
|
+
},
|
|
40
|
+
}, { parent: this });
|
|
41
|
+
const m2mClientConnection = new ConnectionClient('password-authentication-m2m-client', {
|
|
42
|
+
clientId: m2mClientId,
|
|
43
|
+
connectionId: connection.id,
|
|
44
|
+
}, { parent: client });
|
|
45
|
+
const clientConnection = new ConnectionClient('password-authentication-client', {
|
|
46
|
+
clientId: client.id,
|
|
47
|
+
connectionId: connection.id,
|
|
48
|
+
}, { parent: client });
|
|
49
|
+
// TODO: Implement disabling of google-oauth2 (If possible, also disable "Username-Password-Authentication")
|
|
50
|
+
// // disable google-oauth2 on all clients
|
|
51
|
+
// const googleOauth2Connection = output(getConnection({name: 'google-oauth2'}))
|
|
52
|
+
// new ConnectionClients(
|
|
53
|
+
// 'google-oauth2-clien',
|
|
54
|
+
// {
|
|
55
|
+
// connectionId: googleOauth2Connection.id,
|
|
56
|
+
// enabledClients: [],
|
|
57
|
+
// },
|
|
58
|
+
// {parent: client, dependsOn: [client]},
|
|
59
|
+
// )
|
|
60
|
+
new ClientCredentials('credentials', {
|
|
61
|
+
clientId: client.id,
|
|
62
|
+
authenticationMethod: 'none',
|
|
63
|
+
}, { parent: this });
|
|
64
|
+
for (const defaultUser of defaultUsers) {
|
|
65
|
+
const { name, email, emailVerified, password, appMetadata } = defaultUser;
|
|
66
|
+
new User(`default-user-${defaultUser.email}`, {
|
|
67
|
+
connectionName: connection.name,
|
|
68
|
+
name: name,
|
|
69
|
+
email: email,
|
|
70
|
+
emailVerified: emailVerified,
|
|
71
|
+
password: password,
|
|
72
|
+
appMetadata: appMetadata ? JSON.stringify(appMetadata) : undefined,
|
|
73
|
+
}, {
|
|
74
|
+
dependsOn: [m2mClientConnection, clientConnection],
|
|
75
|
+
parent: connection,
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
this.domain = domain;
|
|
79
|
+
this.clientId = client.id;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { acm, route53 } from '@pulumi/aws';
|
|
2
|
+
export const getDomain = async (options) => {
|
|
3
|
+
const { usEast1Provider, name } = options;
|
|
4
|
+
const zone = await route53.getZone({ name });
|
|
5
|
+
const euCentral1Certificate = await acm.getCertificate({ domain: name });
|
|
6
|
+
const usEast1Certificate = await acm.getCertificate({ domain: name }, { provider: usEast1Provider });
|
|
7
|
+
return {
|
|
8
|
+
name: zone.name,
|
|
9
|
+
zoneId: zone.zoneId,
|
|
10
|
+
nameServers: zone.nameServers,
|
|
11
|
+
usEast1CertificateArn: usEast1Certificate.arn,
|
|
12
|
+
euCentral1CertificateArn: euCentral1Certificate.arn,
|
|
13
|
+
};
|
|
14
|
+
};
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { iam } from '@pulumi/aws';
|
|
2
|
+
import { output } from '@pulumi/pulumi';
|
|
3
|
+
export class SimpleIamRolePolicy extends iam.RolePolicy {
|
|
4
|
+
constructor(iamRoleId, config, opts) {
|
|
5
|
+
switch (config.type) {
|
|
6
|
+
case 'custom':
|
|
7
|
+
super(config.name, mapIamRolePolicyConfig(iamRoleId, config.name, config.statements), opts);
|
|
8
|
+
break;
|
|
9
|
+
case 'dynamodb':
|
|
10
|
+
if (config.level !== 'full-access')
|
|
11
|
+
throw mapError(config);
|
|
12
|
+
super(config.name, mapIamRolePolicyConfig(iamRoleId, config.name, [
|
|
13
|
+
{
|
|
14
|
+
Action: ['dynamodb:*'],
|
|
15
|
+
Effect: 'Allow',
|
|
16
|
+
Resource: output(config.tableArn).apply((tableArn) => [`${tableArn}`, `${tableArn}/*`]),
|
|
17
|
+
},
|
|
18
|
+
]), opts);
|
|
19
|
+
break;
|
|
20
|
+
case 'timestream':
|
|
21
|
+
if (config.level !== 'full-access')
|
|
22
|
+
throw mapError(config);
|
|
23
|
+
super(config.name, mapIamRolePolicyConfig(iamRoleId, config.name, [
|
|
24
|
+
{
|
|
25
|
+
Action: ['timestream:*'],
|
|
26
|
+
Effect: 'Allow',
|
|
27
|
+
Resource: [config.dbArn, config.tableArn],
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
Action: ['timestream:DescribeEndpoin'],
|
|
31
|
+
Effect: 'Allow',
|
|
32
|
+
Resource: ['*'],
|
|
33
|
+
},
|
|
34
|
+
]), opts);
|
|
35
|
+
break;
|
|
36
|
+
case 's3':
|
|
37
|
+
if (config.level !== 'full-access')
|
|
38
|
+
throw mapError(config);
|
|
39
|
+
super(config.name, mapIamRolePolicyConfig(iamRoleId, config.name, [
|
|
40
|
+
{
|
|
41
|
+
Action: ['s3:*'],
|
|
42
|
+
Effect: 'Allow',
|
|
43
|
+
Resource: output(config.bucketArn).apply((bucketArn) => [`${bucketArn}`, `${bucketArn}/*`]),
|
|
44
|
+
},
|
|
45
|
+
]), opts);
|
|
46
|
+
break;
|
|
47
|
+
case 'cognito':
|
|
48
|
+
if (config.level !== 'full-access')
|
|
49
|
+
throw mapError(config);
|
|
50
|
+
super(config.name, mapIamRolePolicyConfig(iamRoleId, config.name, [
|
|
51
|
+
{
|
|
52
|
+
Action: ['cognito-idp:*'],
|
|
53
|
+
Effect: 'Allow',
|
|
54
|
+
Resource: [config.userPoolArn],
|
|
55
|
+
},
|
|
56
|
+
]), opts);
|
|
57
|
+
break;
|
|
58
|
+
case 'websocket':
|
|
59
|
+
if (config.level !== 'full-access')
|
|
60
|
+
throw mapError(config);
|
|
61
|
+
super(config.name, mapIamRolePolicyConfig(iamRoleId, config.name, [
|
|
62
|
+
{
|
|
63
|
+
Effect: 'Allow',
|
|
64
|
+
Action: 'execute-api:*',
|
|
65
|
+
Resource: config.apiArn,
|
|
66
|
+
},
|
|
67
|
+
]), opts);
|
|
68
|
+
break;
|
|
69
|
+
case 'sns':
|
|
70
|
+
if (config.level !== 'publish-message')
|
|
71
|
+
throw mapError(config);
|
|
72
|
+
super(config.name, mapIamRolePolicyConfig(iamRoleId, config.name, [
|
|
73
|
+
{
|
|
74
|
+
Effect: 'Allow',
|
|
75
|
+
Action: 'sns:Publish',
|
|
76
|
+
Resource: config.snsTopicArn,
|
|
77
|
+
},
|
|
78
|
+
]), opts);
|
|
79
|
+
break;
|
|
80
|
+
case 'execute-lambda':
|
|
81
|
+
super(config.name, mapIamRolePolicyConfig(iamRoleId, config.name, [
|
|
82
|
+
{
|
|
83
|
+
Effect: 'Allow',
|
|
84
|
+
Action: 'lambda:InvokeFunction',
|
|
85
|
+
Resource: config.lambdaArn,
|
|
86
|
+
},
|
|
87
|
+
]), opts);
|
|
88
|
+
break;
|
|
89
|
+
case 'event-bridge':
|
|
90
|
+
super(config.name, mapIamRolePolicyConfig(iamRoleId, config.name, [
|
|
91
|
+
{
|
|
92
|
+
Effect: 'Allow',
|
|
93
|
+
Action: ['events:DescribeRule', 'events:DisableRule', 'events:EnableRule'],
|
|
94
|
+
Resource: config.ruleArn,
|
|
95
|
+
},
|
|
96
|
+
]), opts);
|
|
97
|
+
break;
|
|
98
|
+
default:
|
|
99
|
+
throw mapError(config);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
function mapIamRolePolicyConfig(iamRoleId, name, statements) {
|
|
104
|
+
return {
|
|
105
|
+
role: iamRoleId,
|
|
106
|
+
name: name,
|
|
107
|
+
policy: {
|
|
108
|
+
Version: '2012-10-17',
|
|
109
|
+
Statement: statements,
|
|
110
|
+
},
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
function mapError(config) {
|
|
114
|
+
return new Error(`Unable to create policy with config: ${JSON.stringify(config)}`);
|
|
115
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { auth0 } from './auth0';
|
|
2
|
+
import { domain } from './domain';
|
|
3
|
+
import { iam } from './iam';
|
|
4
|
+
import { rest } from './rest';
|
|
5
|
+
import { lambda } from './lambda';
|
|
6
|
+
import { mongodb } from './mongodb';
|
|
7
|
+
import { s3 } from './s3';
|
|
8
|
+
import { website } from './website';
|
|
9
|
+
import { websocket } from './websocket';
|
|
10
|
+
export * from './auth0';
|
|
11
|
+
export * from './domain';
|
|
12
|
+
export * from './iam';
|
|
13
|
+
export * from './lambda';
|
|
14
|
+
export * from './mongodb';
|
|
15
|
+
export * from './rest';
|
|
16
|
+
export * from './s3';
|
|
17
|
+
export * from './website';
|
|
18
|
+
export * from './websocket';
|
|
19
|
+
export const mstuercke = {
|
|
20
|
+
auth0,
|
|
21
|
+
domain,
|
|
22
|
+
iam,
|
|
23
|
+
lambda,
|
|
24
|
+
mongodb,
|
|
25
|
+
rest,
|
|
26
|
+
s3,
|
|
27
|
+
website,
|
|
28
|
+
websocket: websocket,
|
|
29
|
+
};
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { iam, lambda } from '@pulumi/aws';
|
|
2
|
+
import { getFile } from '@pulumi/archive';
|
|
3
|
+
import { asset, Resource } from '@pulumi/pulumi';
|
|
4
|
+
import { SimpleIamRolePolicy } from '../iam';
|
|
5
|
+
export class NodeLambdaFunction extends Resource {
|
|
6
|
+
name;
|
|
7
|
+
arn;
|
|
8
|
+
invokeArn;
|
|
9
|
+
constructor(args, opts) {
|
|
10
|
+
super('mstuercke:lambda:NodeLambdaFunction', args.name, false, undefined, opts);
|
|
11
|
+
const { name, additionalIamRolePolicies = [], environmentVariables = undefined, runtime = 'nodejs22.x', functionHandler = 'lambdaFunction.lambdaHandler', memorySize = 128, timeoutSeconds = 30, projectId, } = args;
|
|
12
|
+
const role = new iam.Role(name, {
|
|
13
|
+
name: `${name}-lambda`,
|
|
14
|
+
assumeRolePolicy: JSON.stringify({
|
|
15
|
+
Version: '2012-10-17',
|
|
16
|
+
Statement: [
|
|
17
|
+
{
|
|
18
|
+
Action: 'sts:AssumeRole',
|
|
19
|
+
Principal: { Service: 'lambda.amazonaws.com' },
|
|
20
|
+
Effect: 'Allow',
|
|
21
|
+
},
|
|
22
|
+
],
|
|
23
|
+
}),
|
|
24
|
+
tags: {
|
|
25
|
+
project: projectId,
|
|
26
|
+
},
|
|
27
|
+
}, { parent: this });
|
|
28
|
+
role.id.apply((roleId) => {
|
|
29
|
+
new SimpleIamRolePolicy(roleId, {
|
|
30
|
+
type: 'custom',
|
|
31
|
+
name: 'logging',
|
|
32
|
+
statements: [
|
|
33
|
+
{
|
|
34
|
+
Effect: 'Allow',
|
|
35
|
+
Action: ['logs:CreateLogGroup', 'logs:CreateLogStream', 'logs:PutLogEven'],
|
|
36
|
+
Resource: '*',
|
|
37
|
+
},
|
|
38
|
+
],
|
|
39
|
+
}, { parent: this });
|
|
40
|
+
for (const config of additionalIamRolePolicies) {
|
|
41
|
+
new SimpleIamRolePolicy(roleId, config, { parent: this });
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
const lambdaZip = getFile({
|
|
45
|
+
type: 'zip',
|
|
46
|
+
sourceDir: args.lambdaPath,
|
|
47
|
+
outputPath: `dist/lambda/${name}.zip`,
|
|
48
|
+
});
|
|
49
|
+
const lambdaFunction = new lambda.Function(name, {
|
|
50
|
+
name: name,
|
|
51
|
+
handler: functionHandler,
|
|
52
|
+
runtime: runtime,
|
|
53
|
+
memorySize: memorySize,
|
|
54
|
+
timeout: timeoutSeconds,
|
|
55
|
+
code: new asset.FileArchive(lambdaZip.then((lambdaZip) => lambdaZip.outputPath)),
|
|
56
|
+
sourceCodeHash: lambdaZip.then((lambdaZip) => lambdaZip.outputBase64sha256),
|
|
57
|
+
role: role.arn,
|
|
58
|
+
environment: {
|
|
59
|
+
variables: {
|
|
60
|
+
...environmentVariables,
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
tags: {
|
|
64
|
+
project: projectId,
|
|
65
|
+
},
|
|
66
|
+
}, { parent: this });
|
|
67
|
+
this.name = name;
|
|
68
|
+
this.arn = lambdaFunction.arn;
|
|
69
|
+
this.invokeArn = lambdaFunction.invokeArn;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { Resource } from '@pulumi/pulumi';
|
|
2
|
+
import { RandomPassword } from '@pulumi/random';
|
|
3
|
+
import { DatabaseUser, Project, ProjectIpAccessList, Provider, ServerlessInstance } from '@pulumi/mongodbatlas';
|
|
4
|
+
export class MongoDB extends Resource {
|
|
5
|
+
connectionString;
|
|
6
|
+
databaseName;
|
|
7
|
+
username;
|
|
8
|
+
password;
|
|
9
|
+
constructor(args, opts) {
|
|
10
|
+
const { organizationId, projectId, environmentName } = args;
|
|
11
|
+
super('mstuercke:mongodb:MongoDB', organizationId, false, undefined, opts);
|
|
12
|
+
new Provider('provider', {}, { parent: this });
|
|
13
|
+
const mongoProject = new Project('project', {
|
|
14
|
+
orgId: organizationId,
|
|
15
|
+
name: `${projectId}-${environmentName}`,
|
|
16
|
+
tags: {
|
|
17
|
+
project: projectId,
|
|
18
|
+
},
|
|
19
|
+
}, { parent: this });
|
|
20
|
+
new ProjectIpAccessList('network-access', {
|
|
21
|
+
projectId: mongoProject.id,
|
|
22
|
+
cidrBlock: '0.0.0.0/0',
|
|
23
|
+
}, { parent: mongoProject });
|
|
24
|
+
const mongoInstance = new ServerlessInstance('database', {
|
|
25
|
+
projectId: mongoProject.id,
|
|
26
|
+
name: `${projectId}-${environmentName}`,
|
|
27
|
+
providerSettingsBackingProviderName: 'AWS',
|
|
28
|
+
providerSettingsProviderName: 'SERVERLESS',
|
|
29
|
+
providerSettingsRegionName: 'EU_WEST_1', // Ireland
|
|
30
|
+
}, { parent: mongoProject });
|
|
31
|
+
const username = `${projectId}-${environmentName}`;
|
|
32
|
+
const password = new RandomPassword('password', {
|
|
33
|
+
length: 32,
|
|
34
|
+
}, { parent: mongoProject });
|
|
35
|
+
const databaseName = projectId;
|
|
36
|
+
new DatabaseUser('user', {
|
|
37
|
+
projectId: mongoProject.id,
|
|
38
|
+
username,
|
|
39
|
+
password: password.result,
|
|
40
|
+
authDatabaseName: 'admin',
|
|
41
|
+
roles: [
|
|
42
|
+
{ roleName: 'dbAdmin', databaseName },
|
|
43
|
+
{ roleName: 'readWrite', databaseName },
|
|
44
|
+
],
|
|
45
|
+
}, { parent: mongoProject });
|
|
46
|
+
this.connectionString = mongoInstance.connectionStringsStandardSrv;
|
|
47
|
+
this.databaseName = databaseName;
|
|
48
|
+
this.username = username;
|
|
49
|
+
this.password = password.result;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { Resource } from '@pulumi/pulumi';
|
|
2
|
+
import { Project, ProjectIpAccessList, ServerlessInstance } from '@pulumi/mongodbatlas';
|
|
3
|
+
export class MongoDBCustomInstance extends Resource {
|
|
4
|
+
mongoProjectId;
|
|
5
|
+
connectionString;
|
|
6
|
+
databaseName;
|
|
7
|
+
constructor(args, opts) {
|
|
8
|
+
const { organizationId, projectId, environmentName } = args;
|
|
9
|
+
super('mstuercke:mongodb:MongoDBCustomInstance', organizationId, false, undefined, opts);
|
|
10
|
+
const mongoProject = new Project('project', {
|
|
11
|
+
orgId: organizationId,
|
|
12
|
+
name: `${projectId}-${environmentName}`,
|
|
13
|
+
tags: {
|
|
14
|
+
project: projectId,
|
|
15
|
+
},
|
|
16
|
+
}, { parent: this });
|
|
17
|
+
new ProjectIpAccessList('network-access', {
|
|
18
|
+
projectId: mongoProject.id,
|
|
19
|
+
cidrBlock: '0.0.0.0/0',
|
|
20
|
+
}, { parent: this });
|
|
21
|
+
const mongoInstance = new ServerlessInstance('database', {
|
|
22
|
+
projectId: mongoProject.id,
|
|
23
|
+
name: `${projectId}-${environmentName}`,
|
|
24
|
+
providerSettingsBackingProviderName: 'AWS',
|
|
25
|
+
providerSettingsProviderName: 'SERVERLESS',
|
|
26
|
+
providerSettingsRegionName: 'EU_WEST_1', // Ireland
|
|
27
|
+
}, { parent: this });
|
|
28
|
+
this.mongoProjectId = mongoProject.id;
|
|
29
|
+
this.connectionString = mongoInstance.connectionStringsStandardSrv;
|
|
30
|
+
this.databaseName = `${projectId}-${environmentName}`;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { output, Resource } from '@pulumi/pulumi';
|
|
2
|
+
import { getProject, getServerlessInstance } from '@pulumi/mongodbatlas';
|
|
3
|
+
export class MongoDBDefaultInstance extends Resource {
|
|
4
|
+
mongoProjectId;
|
|
5
|
+
connectionString;
|
|
6
|
+
databaseName;
|
|
7
|
+
constructor(args, opts) {
|
|
8
|
+
const { organizationId, projectId, environmentName } = args;
|
|
9
|
+
super('mstuercke:mongodb:MongoDBDefaultInstance', organizationId, false, undefined, opts);
|
|
10
|
+
const mongoProject = output(getProject({ name: 'default' }));
|
|
11
|
+
const mongoInstance = mongoProject.id.apply((mongoProjectId) => output(getServerlessInstance({
|
|
12
|
+
projectId: mongoProjectId,
|
|
13
|
+
name: 'default',
|
|
14
|
+
})));
|
|
15
|
+
this.mongoProjectId = mongoProject.id;
|
|
16
|
+
this.connectionString = mongoInstance.connectionStringsStandardSrv;
|
|
17
|
+
this.databaseName = `${projectId}-${environmentName}`;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { Resource } from '@pulumi/pulumi';
|
|
2
|
+
import { RandomPassword } from '@pulumi/random';
|
|
3
|
+
import { DatabaseUser } from '@pulumi/mongodbatlas';
|
|
4
|
+
export class MongoDBUser extends Resource {
|
|
5
|
+
username;
|
|
6
|
+
password;
|
|
7
|
+
constructor(args, opts) {
|
|
8
|
+
const { mongoProjectId, databaseName, projectId, environmentName } = args;
|
|
9
|
+
const username = `${projectId}-${environmentName}`;
|
|
10
|
+
super('mstuercke:mongodb:MongoDBUser', username, false, undefined, opts);
|
|
11
|
+
const password = new RandomPassword('password', {
|
|
12
|
+
length: 32,
|
|
13
|
+
special: false,
|
|
14
|
+
}, { parent: this });
|
|
15
|
+
new DatabaseUser('user', {
|
|
16
|
+
projectId: mongoProjectId,
|
|
17
|
+
username,
|
|
18
|
+
password: password.result,
|
|
19
|
+
authDatabaseName: 'admin',
|
|
20
|
+
roles: [
|
|
21
|
+
{ roleName: 'dbAdmin', databaseName },
|
|
22
|
+
{ roleName: 'readWrite', databaseName },
|
|
23
|
+
],
|
|
24
|
+
}, { parent: this });
|
|
25
|
+
this.username = username;
|
|
26
|
+
this.password = password.result;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { MongoDBCustomInstance } from './MongoDBCustomInstance';
|
|
2
|
+
import { MongoDBDefaultInstance } from './MongoDBDefaultInstance';
|
|
3
|
+
import { MongoDBUser } from './MongoDBUser';
|
|
4
|
+
export * from './MongoDBCustomInstance';
|
|
5
|
+
export * from './MongoDBDefaultInstance';
|
|
6
|
+
export * from './MongoDBUser';
|
|
7
|
+
export const mongodb = {
|
|
8
|
+
MongoDBCustomInstance,
|
|
9
|
+
MongoDBDefaultInstance,
|
|
10
|
+
MongoDBUser,
|
|
11
|
+
};
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import * as aws from '@pulumi/aws';
|
|
2
|
+
import { apigateway, lambda } from '@pulumi/aws';
|
|
3
|
+
import { Record } from '@pulumi/aws/route53';
|
|
4
|
+
import { BasePathMapping, DomainName } from '@pulumi/aws/apigateway';
|
|
5
|
+
export class RestApi extends aws.apigateway.RestApi {
|
|
6
|
+
url;
|
|
7
|
+
constructor(id, args, opts) {
|
|
8
|
+
const { name, projectId, stageName = 'v1', domain } = args;
|
|
9
|
+
super(id, {
|
|
10
|
+
name,
|
|
11
|
+
tags: {
|
|
12
|
+
project: projectId,
|
|
13
|
+
},
|
|
14
|
+
}, opts);
|
|
15
|
+
const { lambdaFunction, timeoutSeconds = 29 } = args;
|
|
16
|
+
const proxyResource = new apigateway.Resource(`proxy`, {
|
|
17
|
+
restApi: this.id,
|
|
18
|
+
parentId: this.rootResourceId,
|
|
19
|
+
pathPart: '{proxy+}',
|
|
20
|
+
}, { parent: this });
|
|
21
|
+
const proxyMethod = new apigateway.Method(`proxy`, {
|
|
22
|
+
restApi: this.id,
|
|
23
|
+
resourceId: proxyResource.id,
|
|
24
|
+
httpMethod: 'ANY',
|
|
25
|
+
authorization: 'NONE',
|
|
26
|
+
}, { parent: proxyResource });
|
|
27
|
+
const lambdaIntegration = new apigateway.Integration(`proxy`, {
|
|
28
|
+
restApi: this.id,
|
|
29
|
+
resourceId: proxyResource.id,
|
|
30
|
+
httpMethod: proxyMethod.httpMethod,
|
|
31
|
+
integrationHttpMethod: 'POST',
|
|
32
|
+
type: 'AWS_PROXY',
|
|
33
|
+
uri: lambdaFunction.invokeArn,
|
|
34
|
+
timeoutMilliseconds: timeoutSeconds * 1000,
|
|
35
|
+
}, { parent: proxyMethod });
|
|
36
|
+
const proxyOptionsMethod = new apigateway.Method(`options`, {
|
|
37
|
+
restApi: this.id,
|
|
38
|
+
resourceId: proxyResource.id,
|
|
39
|
+
httpMethod: 'OPTIONS',
|
|
40
|
+
authorization: 'NONE',
|
|
41
|
+
}, { parent: proxyResource });
|
|
42
|
+
const lambdaIntegrationOptions = new apigateway.Integration(`options`, {
|
|
43
|
+
restApi: this.id,
|
|
44
|
+
resourceId: proxyResource.id,
|
|
45
|
+
httpMethod: proxyOptionsMethod.httpMethod,
|
|
46
|
+
integrationHttpMethod: 'POST',
|
|
47
|
+
type: 'AWS_PROXY',
|
|
48
|
+
uri: lambdaFunction.invokeArn,
|
|
49
|
+
timeoutMilliseconds: timeoutSeconds * 1000,
|
|
50
|
+
}, { parent: proxyOptionsMethod });
|
|
51
|
+
const proxyRootMethod = new apigateway.Method(`proxy-root`, {
|
|
52
|
+
restApi: this.id,
|
|
53
|
+
resourceId: this.rootResourceId,
|
|
54
|
+
httpMethod: 'ANY',
|
|
55
|
+
authorization: 'NONE',
|
|
56
|
+
}, { parent: this });
|
|
57
|
+
const lambdaRootIntegration = new apigateway.Integration(`proxy-root`, {
|
|
58
|
+
restApi: this.id,
|
|
59
|
+
resourceId: proxyRootMethod.resourceId,
|
|
60
|
+
httpMethod: proxyRootMethod.httpMethod,
|
|
61
|
+
integrationHttpMethod: 'POST',
|
|
62
|
+
type: 'AWS_PROXY',
|
|
63
|
+
uri: lambdaFunction.invokeArn,
|
|
64
|
+
timeoutMilliseconds: timeoutSeconds * 1000,
|
|
65
|
+
}, { parent: proxyRootMethod });
|
|
66
|
+
const proxyRootOptionsMethod = new apigateway.Method(`proxy-root-options`, {
|
|
67
|
+
restApi: this.id,
|
|
68
|
+
resourceId: this.rootResourceId,
|
|
69
|
+
httpMethod: 'OPTIONS',
|
|
70
|
+
authorization: 'NONE',
|
|
71
|
+
}, { parent: this });
|
|
72
|
+
const lambdaRootIntegrationOptions = new apigateway.Integration(`proxy-root-options`, {
|
|
73
|
+
restApi: this.id,
|
|
74
|
+
resourceId: proxyRootOptionsMethod.resourceId,
|
|
75
|
+
httpMethod: proxyRootOptionsMethod.httpMethod,
|
|
76
|
+
integrationHttpMethod: 'POST',
|
|
77
|
+
type: 'AWS_PROXY',
|
|
78
|
+
uri: lambdaFunction.invokeArn,
|
|
79
|
+
timeoutMilliseconds: timeoutSeconds * 1000,
|
|
80
|
+
}, { parent: proxyRootOptionsMethod });
|
|
81
|
+
new lambda.Permission(`rest-api`, {
|
|
82
|
+
statementId: 'AllowRestApiInvoke',
|
|
83
|
+
action: 'lambda:InvokeFunction',
|
|
84
|
+
function: lambdaFunction.name,
|
|
85
|
+
principal: 'apigateway.amazonaws.com',
|
|
86
|
+
sourceArn: this.executionArn.apply((executionArn) => `${executionArn}/*`),
|
|
87
|
+
}, { parent: lambdaFunction });
|
|
88
|
+
const deployment = new apigateway.Deployment(stageName, { restApi: this.id }, {
|
|
89
|
+
dependsOn: [
|
|
90
|
+
proxyMethod,
|
|
91
|
+
lambdaIntegration,
|
|
92
|
+
lambdaIntegrationOptions,
|
|
93
|
+
proxyRootMethod,
|
|
94
|
+
lambdaRootIntegration,
|
|
95
|
+
lambdaRootIntegrationOptions,
|
|
96
|
+
],
|
|
97
|
+
parent: this,
|
|
98
|
+
});
|
|
99
|
+
const stage = new apigateway.Stage(stageName, {
|
|
100
|
+
restApi: this.id,
|
|
101
|
+
stageName: stageName,
|
|
102
|
+
deployment: deployment.id,
|
|
103
|
+
tags: { project: projectId },
|
|
104
|
+
}, { parent: deployment });
|
|
105
|
+
const domainName = new DomainName('domain', {
|
|
106
|
+
domainName: domain.fullName,
|
|
107
|
+
certificateArn: domain.usEast1CertificateArn,
|
|
108
|
+
}, { parent: this });
|
|
109
|
+
new Record('redirect', {
|
|
110
|
+
zoneId: domain.zoneId,
|
|
111
|
+
name: domain.fullName,
|
|
112
|
+
type: 'A',
|
|
113
|
+
aliases: [
|
|
114
|
+
{
|
|
115
|
+
zoneId: domainName.cloudfrontZoneId,
|
|
116
|
+
name: domainName.cloudfrontDomainName,
|
|
117
|
+
evaluateTargetHealth: true,
|
|
118
|
+
},
|
|
119
|
+
],
|
|
120
|
+
allowOverwrite: true,
|
|
121
|
+
}, { parent: domainName });
|
|
122
|
+
new BasePathMapping('root', {
|
|
123
|
+
restApi: this.id,
|
|
124
|
+
stageName: stage.stageName,
|
|
125
|
+
domainName: domainName.domainName,
|
|
126
|
+
}, { parent: stage });
|
|
127
|
+
this.url = `https://${domain.fullName}/`;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { Bucket, BucketAclV2, BucketCorsConfigurationV2, BucketOwnershipControls, BucketPolicy, BucketPublicAccessBlock, } from '@pulumi/aws/s3';
|
|
2
|
+
export class PublicS3Bucket extends Bucket {
|
|
3
|
+
constructor(id, args, opts) {
|
|
4
|
+
const { name, allowPresignedPost = false, tags } = args;
|
|
5
|
+
super(id, { bucket: name, tags }, opts);
|
|
6
|
+
const accessBlock = new BucketPublicAccessBlock('public-access', {
|
|
7
|
+
bucket: this.id,
|
|
8
|
+
blockPublicAcls: false,
|
|
9
|
+
blockPublicPolicy: false,
|
|
10
|
+
ignorePublicAcls: false,
|
|
11
|
+
restrictPublicBuckets: false,
|
|
12
|
+
}, { parent: this });
|
|
13
|
+
const ownershipControls = new BucketOwnershipControls('ownership', {
|
|
14
|
+
bucket: this.id,
|
|
15
|
+
rule: { objectOwnership: 'BucketOwnerPreferred' },
|
|
16
|
+
}, { parent: this, dependsOn: [accessBlock] });
|
|
17
|
+
new BucketAclV2('acl', {
|
|
18
|
+
bucket: this.id,
|
|
19
|
+
acl: 'public-read',
|
|
20
|
+
}, { parent: this, dependsOn: [ownershipControls] });
|
|
21
|
+
new BucketPolicy('policy', {
|
|
22
|
+
bucket: this.id,
|
|
23
|
+
policy: this.arn.apply((arn) => JSON.stringify({
|
|
24
|
+
Version: '2012-10-17',
|
|
25
|
+
Statement: [
|
|
26
|
+
{
|
|
27
|
+
Sid: 'Allow Public Access',
|
|
28
|
+
Effect: 'Allow',
|
|
29
|
+
Principal: '*',
|
|
30
|
+
Action: 's3:GetObject',
|
|
31
|
+
Resource: `${arn}/*`,
|
|
32
|
+
},
|
|
33
|
+
],
|
|
34
|
+
})),
|
|
35
|
+
}, { parent: this, dependsOn: [accessBlock] });
|
|
36
|
+
new BucketCorsConfigurationV2('allowAll', {
|
|
37
|
+
bucket: this.bucket,
|
|
38
|
+
corsRules: [
|
|
39
|
+
{
|
|
40
|
+
maxAgeSeconds: 3000,
|
|
41
|
+
allowedMethods: allowPresignedPost ? ['GET', 'POST'] : ['GET'],
|
|
42
|
+
allowedHeaders: ['*'],
|
|
43
|
+
allowedOrigins: ['*'],
|
|
44
|
+
},
|
|
45
|
+
],
|
|
46
|
+
}, { parent: this });
|
|
47
|
+
}
|
|
48
|
+
}
|
package/dist/s3/index.js
ADDED
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { Resource } from '@pulumi/pulumi';
|
|
2
|
+
import { 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.js';
|
|
7
|
+
export class StaticWebsite extends Resource {
|
|
8
|
+
url;
|
|
9
|
+
bucket;
|
|
10
|
+
constructor(id, args, opts) {
|
|
11
|
+
super('mstuercke:website:StaticWebsite', id, false, undefined, opts);
|
|
12
|
+
const { projectId, environmentName, filesPath, customDomain } = args;
|
|
13
|
+
const bucket = new s3.PublicS3Bucket('bucket', {
|
|
14
|
+
name: `${projectId}-web-${environmentName}`,
|
|
15
|
+
allowPresignedPost: false,
|
|
16
|
+
tags: {
|
|
17
|
+
project: projectId,
|
|
18
|
+
},
|
|
19
|
+
}, { parent: this });
|
|
20
|
+
for (const file of readFilesRecursive(filesPath)) {
|
|
21
|
+
new BucketObject(file.relativePath, {
|
|
22
|
+
key: file.relativePath,
|
|
23
|
+
bucket: bucket.id,
|
|
24
|
+
source: file.absolutePath,
|
|
25
|
+
contentType: file.mimeType,
|
|
26
|
+
etag: file.md5,
|
|
27
|
+
tags: {
|
|
28
|
+
project: projectId,
|
|
29
|
+
},
|
|
30
|
+
}, { parent: bucket });
|
|
31
|
+
}
|
|
32
|
+
const cloudfront = new Distribution(`website`, {
|
|
33
|
+
enabled: true,
|
|
34
|
+
comment: `${projectId} (${environmentName})`,
|
|
35
|
+
defaultRootObject: 'index.html',
|
|
36
|
+
origins: [
|
|
37
|
+
{
|
|
38
|
+
domainName: bucket.bucketRegionalDomainName,
|
|
39
|
+
originId: bucket.id,
|
|
40
|
+
},
|
|
41
|
+
],
|
|
42
|
+
defaultCacheBehavior: {
|
|
43
|
+
allowedMethods: ['GET', 'HEAD', 'OPTIONS'],
|
|
44
|
+
cachedMethods: ['GET', 'HEAD'],
|
|
45
|
+
targetOriginId: bucket.id,
|
|
46
|
+
viewerProtocolPolicy: 'redirect-to-https',
|
|
47
|
+
forwardedValues: {
|
|
48
|
+
queryString: false,
|
|
49
|
+
cookies: {
|
|
50
|
+
forward: 'none',
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
orderedCacheBehaviors: [
|
|
55
|
+
{
|
|
56
|
+
pathPattern: 'index.html',
|
|
57
|
+
allowedMethods: ['GET', 'HEAD', 'OPTIONS'],
|
|
58
|
+
cachedMethods: ['GET', 'HEAD'],
|
|
59
|
+
targetOriginId: bucket.id,
|
|
60
|
+
viewerProtocolPolicy: 'redirect-to-https',
|
|
61
|
+
defaultTtl: 0,
|
|
62
|
+
maxTtl: 0,
|
|
63
|
+
forwardedValues: {
|
|
64
|
+
queryString: false,
|
|
65
|
+
cookies: {
|
|
66
|
+
forward: 'none',
|
|
67
|
+
},
|
|
68
|
+
},
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
pathPattern: 'config.json',
|
|
72
|
+
allowedMethods: ['GET', 'HEAD', 'OPTIONS'],
|
|
73
|
+
cachedMethods: ['GET', 'HEAD'],
|
|
74
|
+
targetOriginId: bucket.id,
|
|
75
|
+
viewerProtocolPolicy: 'redirect-to-https',
|
|
76
|
+
defaultTtl: 0,
|
|
77
|
+
maxTtl: 0,
|
|
78
|
+
forwardedValues: {
|
|
79
|
+
queryString: false,
|
|
80
|
+
cookies: {
|
|
81
|
+
forward: 'none',
|
|
82
|
+
},
|
|
83
|
+
},
|
|
84
|
+
},
|
|
85
|
+
],
|
|
86
|
+
customErrorResponses: [
|
|
87
|
+
{
|
|
88
|
+
errorCode: 404,
|
|
89
|
+
responsePagePath: '/index.html',
|
|
90
|
+
responseCode: 200,
|
|
91
|
+
errorCachingMinTtl: 0,
|
|
92
|
+
},
|
|
93
|
+
],
|
|
94
|
+
restrictions: {
|
|
95
|
+
geoRestriction: {
|
|
96
|
+
restrictionType: 'none',
|
|
97
|
+
},
|
|
98
|
+
},
|
|
99
|
+
viewerCertificate: customDomain
|
|
100
|
+
? {
|
|
101
|
+
acmCertificateArn: customDomain?.usEast1CertificateArn,
|
|
102
|
+
sslSupportMethod: 'sni-only',
|
|
103
|
+
minimumProtocolVersion: 'TLSv1.2_2019',
|
|
104
|
+
}
|
|
105
|
+
: { cloudfrontDefaultCertificate: true },
|
|
106
|
+
aliases: customDomain ? [customDomain?.fullName] : undefined,
|
|
107
|
+
tags: {
|
|
108
|
+
project: projectId,
|
|
109
|
+
},
|
|
110
|
+
}, { parent: this });
|
|
111
|
+
if (customDomain) {
|
|
112
|
+
new Record('redirect', {
|
|
113
|
+
zoneId: customDomain?.zoneId,
|
|
114
|
+
name: customDomain?.fullName,
|
|
115
|
+
type: 'A',
|
|
116
|
+
allowOverwrite: true,
|
|
117
|
+
aliases: [
|
|
118
|
+
{
|
|
119
|
+
zoneId: cloudfront.hostedZoneId,
|
|
120
|
+
name: cloudfront.domainName,
|
|
121
|
+
evaluateTargetHealth: false,
|
|
122
|
+
},
|
|
123
|
+
],
|
|
124
|
+
}, { parent: this });
|
|
125
|
+
}
|
|
126
|
+
this.url = `https://${customDomain?.fullName || cloudfront.domainName}`;
|
|
127
|
+
this.bucket = bucket;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { createHash } from 'crypto';
|
|
2
|
+
import fs from 'fs';
|
|
3
|
+
import mime from 'mime';
|
|
4
|
+
import { globSync } from 'glob';
|
|
5
|
+
export function readFilesRecursive(dir) {
|
|
6
|
+
const files = globSync(`${dir}/**/*`, { nodir: true });
|
|
7
|
+
return files.reduce((previousValue, fullPath) => {
|
|
8
|
+
const content = fs.readFileSync(fullPath, 'base64');
|
|
9
|
+
const md5 = createHash('md5').update(content, 'base64').digest('hex');
|
|
10
|
+
const mimeType = mime.getType(fullPath);
|
|
11
|
+
if (!mimeType)
|
|
12
|
+
throw `Cannot determine mimeType for ${fullPath}`;
|
|
13
|
+
return [
|
|
14
|
+
...previousValue,
|
|
15
|
+
{
|
|
16
|
+
absolutePath: fullPath,
|
|
17
|
+
relativePath: fullPath.replace(`${dir}/`, '').replace(dir, ''),
|
|
18
|
+
md5,
|
|
19
|
+
mimeType,
|
|
20
|
+
},
|
|
21
|
+
];
|
|
22
|
+
}, []);
|
|
23
|
+
}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { Resource } from '@pulumi/pulumi';
|
|
2
|
+
import { Api, ApiMapping, Deployment, DomainName, Integration, IntegrationResponse, Route, Stage, } from '@pulumi/aws/apigatewayv2';
|
|
3
|
+
import { Policy, Role } from '@pulumi/aws/iam';
|
|
4
|
+
import { Record } from '@pulumi/aws/route53';
|
|
5
|
+
export class WebsocketApi extends Resource {
|
|
6
|
+
id;
|
|
7
|
+
url;
|
|
8
|
+
constructor(id, args, opts) {
|
|
9
|
+
super('mstuercke:websocket:WebsocketApi', id, false, undefined, opts);
|
|
10
|
+
const { name, lambdaFunction, routeThrottling, stageName = 'v1', domain, projectId } = args;
|
|
11
|
+
const api = new Api('api', {
|
|
12
|
+
name,
|
|
13
|
+
protocolType: 'WEBSOCKET',
|
|
14
|
+
routeSelectionExpression: '\\$default',
|
|
15
|
+
tags: {
|
|
16
|
+
project: projectId,
|
|
17
|
+
},
|
|
18
|
+
}, { parent: this });
|
|
19
|
+
const executeLambdaPolicy = new Policy('execute-lambda', {
|
|
20
|
+
name: `${name}-execute-lambda`,
|
|
21
|
+
path: '/',
|
|
22
|
+
policy: {
|
|
23
|
+
Version: '2012-10-17',
|
|
24
|
+
Statement: [
|
|
25
|
+
{
|
|
26
|
+
Action: 'lambda:InvokeFunction',
|
|
27
|
+
Effect: 'Allow',
|
|
28
|
+
Resource: lambdaFunction.arn,
|
|
29
|
+
},
|
|
30
|
+
],
|
|
31
|
+
},
|
|
32
|
+
tags: {
|
|
33
|
+
project: projectId,
|
|
34
|
+
},
|
|
35
|
+
}, { parent: api });
|
|
36
|
+
const wsApiRole = new Role('role', {
|
|
37
|
+
name: `${name}-role`,
|
|
38
|
+
assumeRolePolicy: JSON.stringify({
|
|
39
|
+
Version: '2012-10-17',
|
|
40
|
+
Statement: [
|
|
41
|
+
{
|
|
42
|
+
Effect: 'Allow',
|
|
43
|
+
Action: 'sts:AssumeRole',
|
|
44
|
+
Principal: { Service: 'apigateway.amazonaws.com' },
|
|
45
|
+
},
|
|
46
|
+
],
|
|
47
|
+
}),
|
|
48
|
+
tags: {
|
|
49
|
+
project: projectId,
|
|
50
|
+
},
|
|
51
|
+
managedPolicyArns: [executeLambdaPolicy.arn],
|
|
52
|
+
}, { parent: api });
|
|
53
|
+
const wsLambdaIntegration = new Integration('lambda-integration', {
|
|
54
|
+
apiId: api.id,
|
|
55
|
+
integrationType: 'AWS_PROXY',
|
|
56
|
+
integrationUri: lambdaFunction.invokeArn,
|
|
57
|
+
credentialsArn: wsApiRole.arn,
|
|
58
|
+
contentHandlingStrategy: 'CONVERT_TO_TEXT',
|
|
59
|
+
passthroughBehavior: 'WHEN_NO_MATCH',
|
|
60
|
+
}, { parent: this });
|
|
61
|
+
new IntegrationResponse('lambda-integration-response', {
|
|
62
|
+
apiId: api.id,
|
|
63
|
+
integrationId: wsLambdaIntegration.id,
|
|
64
|
+
integrationResponseKey: '/200/',
|
|
65
|
+
}, { parent: wsLambdaIntegration });
|
|
66
|
+
const routes = [];
|
|
67
|
+
const routeKeys = ['$connect', '$default', '$disconnect'];
|
|
68
|
+
for (const routeKey of routeKeys) {
|
|
69
|
+
const route = new Route(`${routeKey}-route`, {
|
|
70
|
+
apiId: api.id,
|
|
71
|
+
routeKey: routeKey,
|
|
72
|
+
target: wsLambdaIntegration.id.apply((id) => `integrations/${id}`),
|
|
73
|
+
authorizationType: 'NONE',
|
|
74
|
+
}, { parent: wsLambdaIntegration });
|
|
75
|
+
routes.push(route);
|
|
76
|
+
}
|
|
77
|
+
const deployment = new Deployment('deployment', {
|
|
78
|
+
apiId: api.id,
|
|
79
|
+
}, {
|
|
80
|
+
dependsOn: [wsLambdaIntegration, ...routes],
|
|
81
|
+
parent: api,
|
|
82
|
+
});
|
|
83
|
+
const stage = new Stage('stage', {
|
|
84
|
+
apiId: api.id,
|
|
85
|
+
name: stageName,
|
|
86
|
+
deploymentId: deployment.id,
|
|
87
|
+
defaultRouteSettings: {
|
|
88
|
+
throttlingRateLimit: routeThrottling?.rateLimit ?? 100,
|
|
89
|
+
throttlingBurstLimit: routeThrottling?.burstLimit ?? 100,
|
|
90
|
+
},
|
|
91
|
+
tags: {
|
|
92
|
+
project: projectId,
|
|
93
|
+
},
|
|
94
|
+
}, { parent: deployment });
|
|
95
|
+
const domainName = new DomainName('domain', {
|
|
96
|
+
domainName: domain.fullName,
|
|
97
|
+
domainNameConfiguration: {
|
|
98
|
+
certificateArn: domain.euCentral1CertificateArn,
|
|
99
|
+
endpointType: 'REGIONAL',
|
|
100
|
+
securityPolicy: 'TLS_1_2',
|
|
101
|
+
},
|
|
102
|
+
}, { parent: api });
|
|
103
|
+
new Record('redirect', {
|
|
104
|
+
zoneId: domain.zoneId,
|
|
105
|
+
name: domain.fullName,
|
|
106
|
+
type: 'A',
|
|
107
|
+
aliases: [
|
|
108
|
+
{
|
|
109
|
+
zoneId: domainName.domainNameConfiguration.hostedZoneId,
|
|
110
|
+
name: domainName.domainNameConfiguration.targetDomainName,
|
|
111
|
+
evaluateTargetHealth: true,
|
|
112
|
+
},
|
|
113
|
+
],
|
|
114
|
+
allowOverwrite: true,
|
|
115
|
+
}, { parent: api });
|
|
116
|
+
new ApiMapping('root', {
|
|
117
|
+
apiId: api.id,
|
|
118
|
+
domainName: domainName.id,
|
|
119
|
+
stage: stage.name,
|
|
120
|
+
}, { parent: api });
|
|
121
|
+
new ApiMapping('stage', {
|
|
122
|
+
apiId: api.id,
|
|
123
|
+
domainName: domainName.id,
|
|
124
|
+
stage: stage.name,
|
|
125
|
+
apiMappingKey: stage.name,
|
|
126
|
+
}, { parent: api });
|
|
127
|
+
this.id = api.id;
|
|
128
|
+
this.url = `wss://${domain.fullName}/`;
|
|
129
|
+
}
|
|
130
|
+
}
|