@studion/infra-code-blocks 0.1.8 → 0.2.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.
@@ -0,0 +1,277 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.NuxtSSR = void 0;
4
+ const pulumi = require("@pulumi/pulumi");
5
+ const aws = require("@pulumi/aws");
6
+ const random = require("@pulumi/random");
7
+ const constants_1 = require("../constants");
8
+ const acm_certificate_1 = require("./acm-certificate");
9
+ const ecs_service_1 = require("./ecs-service");
10
+ const defaults = {
11
+ healthCheckPath: '/',
12
+ };
13
+ class NuxtSSR extends pulumi.ComponentResource {
14
+ constructor(name, args, opts = {}) {
15
+ super('studion:NuxtSSR', name, args, opts);
16
+ const { vpcId, domain, hostedZoneId, tags } = args;
17
+ const hasCustomDomain = domain && hostedZoneId;
18
+ if (domain && !hostedZoneId) {
19
+ throw new Error('NuxtSSR:hostedZoneId must be provided when the domain is specified');
20
+ }
21
+ this.name = name;
22
+ if (hasCustomDomain) {
23
+ this.certificate = this.createTlsCertificate({ domain, hostedZoneId });
24
+ }
25
+ this.customCFHeader = this.createCustomCFHeader();
26
+ const { lb, lbTargetGroup, lbHttpListener, lbSecurityGroup } = this.createLoadBalancer(args);
27
+ this.lb = lb;
28
+ this.lbTargetGroup = lbTargetGroup;
29
+ this.lbHttpListener = lbHttpListener;
30
+ this.lbSecurityGroup = lbSecurityGroup;
31
+ this.serviceSecurityGroup = this.createSecurityGroup(vpcId);
32
+ this.service = this.createEcsService(args);
33
+ this.cloudfront = this.createCloudfrontDistribution({ domain, tags });
34
+ if (hasCustomDomain) {
35
+ this.createDnsRecord({ domain, hostedZoneId });
36
+ }
37
+ this.registerOutputs();
38
+ }
39
+ createTlsCertificate({ domain, hostedZoneId, }) {
40
+ const certificate = new acm_certificate_1.AcmCertificate(`${domain}-acm-certificate`, {
41
+ domain,
42
+ hostedZoneId,
43
+ }, { parent: this });
44
+ return certificate;
45
+ }
46
+ createCustomCFHeader() {
47
+ const headerNameOpts = {
48
+ length: 4,
49
+ special: false,
50
+ numeric: false,
51
+ lower: false,
52
+ upper: true,
53
+ };
54
+ const headerNameSegment1 = new random.RandomString(`${this.name}-cf-header-name-segment1`, headerNameOpts, { parent: this });
55
+ const headerNameSegment2 = new random.RandomString(`${this.name}-cf-header-name-segment2`, headerNameOpts, { parent: this });
56
+ const headerValue = new random.RandomString(`${this.name}-cf-header-value`, {
57
+ length: 36,
58
+ special: false,
59
+ numeric: true,
60
+ lower: true,
61
+ upper: true,
62
+ }, { parent: this });
63
+ const headerName = pulumi
64
+ .all([headerNameSegment1.result, headerNameSegment2.result])
65
+ .apply(([segment1, segment2]) => {
66
+ return `X-${segment1}-${segment2}`;
67
+ });
68
+ return { name: headerName, value: headerValue.result };
69
+ }
70
+ createLoadBalancer({ vpcId, publicSubnetIds, port, healthCheckPath, }) {
71
+ const lbSecurityGroup = new aws.ec2.SecurityGroup(`${this.name}-lb-security-group`, {
72
+ vpcId,
73
+ ingress: [
74
+ {
75
+ protocol: 'tcp',
76
+ fromPort: 80,
77
+ toPort: 80,
78
+ cidrBlocks: ['0.0.0.0/0'],
79
+ },
80
+ ],
81
+ egress: [
82
+ {
83
+ fromPort: 0,
84
+ toPort: 0,
85
+ protocol: '-1',
86
+ cidrBlocks: ['0.0.0.0/0'],
87
+ },
88
+ ],
89
+ tags: constants_1.commonTags,
90
+ }, { parent: this });
91
+ const lb = new aws.lb.LoadBalancer(`${this.name}-lb`, {
92
+ namePrefix: 'lb-',
93
+ loadBalancerType: 'application',
94
+ subnets: publicSubnetIds,
95
+ securityGroups: [lbSecurityGroup.id],
96
+ internal: false,
97
+ ipAddressType: 'ipv4',
98
+ tags: Object.assign(Object.assign({}, constants_1.commonTags), { Name: `${this.name}-lb` }),
99
+ }, { parent: this });
100
+ const lbTargetGroup = new aws.lb.TargetGroup(`${this.name}-lb-tg`, {
101
+ namePrefix: 'lb-tg-',
102
+ port,
103
+ protocol: 'HTTP',
104
+ targetType: 'ip',
105
+ vpcId,
106
+ healthCheck: {
107
+ healthyThreshold: 3,
108
+ unhealthyThreshold: 2,
109
+ interval: 60,
110
+ timeout: 5,
111
+ path: healthCheckPath || defaults.healthCheckPath,
112
+ },
113
+ tags: Object.assign(Object.assign({}, constants_1.commonTags), { Name: `${this.name}-lb-target-group` }),
114
+ }, { parent: this, dependsOn: [this.lb] });
115
+ const lbHttpListener = new aws.lb.Listener(`${this.name}-lb-listener-80`, {
116
+ loadBalancerArn: lb.arn,
117
+ port: 80,
118
+ defaultActions: [
119
+ {
120
+ type: 'fixed-response',
121
+ fixedResponse: {
122
+ statusCode: '403',
123
+ messageBody: 'Not Allowed',
124
+ contentType: 'text/plain',
125
+ },
126
+ },
127
+ ],
128
+ tags: constants_1.commonTags,
129
+ }, { parent: this });
130
+ const lbHttpListenerRule = new aws.lb.ListenerRule(`${this.name}-lb-listener-rule`, {
131
+ listenerArn: lbHttpListener.arn,
132
+ priority: 1,
133
+ actions: [
134
+ {
135
+ type: 'forward',
136
+ targetGroupArn: lbTargetGroup.arn,
137
+ },
138
+ ],
139
+ conditions: [
140
+ {
141
+ httpHeader: {
142
+ httpHeaderName: this.customCFHeader.name,
143
+ values: [this.customCFHeader.value],
144
+ },
145
+ },
146
+ ],
147
+ }, { parent: this });
148
+ return {
149
+ lb,
150
+ lbTargetGroup,
151
+ lbHttpListener,
152
+ lbSecurityGroup,
153
+ };
154
+ }
155
+ createSecurityGroup(vpcId) {
156
+ const securityGroup = new aws.ec2.SecurityGroup(`${this.name}-security-group`, {
157
+ vpcId,
158
+ ingress: [
159
+ {
160
+ fromPort: 0,
161
+ toPort: 0,
162
+ protocol: '-1',
163
+ securityGroups: [this.lbSecurityGroup.id],
164
+ },
165
+ ],
166
+ egress: [
167
+ {
168
+ fromPort: 0,
169
+ toPort: 0,
170
+ protocol: '-1',
171
+ cidrBlocks: ['0.0.0.0/0'],
172
+ },
173
+ ],
174
+ tags: constants_1.commonTags,
175
+ }, { parent: this });
176
+ return securityGroup;
177
+ }
178
+ createEcsService(args) {
179
+ const service = new ecs_service_1.EcsService(this.name, Object.assign(Object.assign({}, args), { enableServiceAutoDiscovery: false, lbTargetGroupArn: this.lbTargetGroup.arn, assignPublicIp: true, subnetIds: args.publicSubnetIds, securityGroup: this.serviceSecurityGroup }), {
180
+ parent: this,
181
+ dependsOn: [this.lb, this.lbTargetGroup, this.lbHttpListener],
182
+ });
183
+ return service;
184
+ }
185
+ createCloudfrontDistribution({ domain, tags, }) {
186
+ const cachePolicy = new aws.cloudfront.CachePolicy(`${this.name}-cf-cache-policy`, {
187
+ comment: 'This cache policy is managed by Pulumi, changing its values will impact multiple services.',
188
+ defaultTtl: 0,
189
+ maxTtl: 31536000,
190
+ minTtl: 0,
191
+ parametersInCacheKeyAndForwardedToOrigin: {
192
+ cookiesConfig: {
193
+ cookieBehavior: 'none',
194
+ },
195
+ headersConfig: {
196
+ headerBehavior: 'none',
197
+ },
198
+ queryStringsConfig: {
199
+ queryStringBehavior: 'all',
200
+ },
201
+ },
202
+ }, { parent: this });
203
+ const originRequestPolicyId = aws.cloudfront
204
+ .getOriginRequestPolicyOutput({
205
+ name: 'Managed-AllViewer',
206
+ })
207
+ .apply(policy => policy.id);
208
+ const responseHeadersPolicyId = aws.cloudfront
209
+ .getResponseHeadersPolicyOutput({
210
+ name: 'Managed-SecurityHeadersPolicy',
211
+ })
212
+ .apply(policy => policy.id);
213
+ const cloudfront = new aws.cloudfront.Distribution(`${this.name}-cloudfront`, Object.assign(Object.assign({ enabled: true }, (domain && { aliases: [domain] })), { isIpv6Enabled: true, waitForDeployment: true, httpVersion: 'http2and3', viewerCertificate: Object.assign({}, (this.certificate
214
+ ? {
215
+ acmCertificateArn: this.certificate.certificate.arn,
216
+ sslSupportMethod: 'sni-only',
217
+ minimumProtocolVersion: 'TLSv1.2_2021',
218
+ }
219
+ : {
220
+ cloudfrontDefaultCertificate: true,
221
+ })), origins: [
222
+ {
223
+ originId: this.lb.arn,
224
+ domainName: this.lb.dnsName,
225
+ connectionAttempts: 3,
226
+ connectionTimeout: 10,
227
+ customOriginConfig: {
228
+ originProtocolPolicy: 'http-only',
229
+ httpPort: 80,
230
+ httpsPort: 443,
231
+ originSslProtocols: ['SSLv3'],
232
+ },
233
+ customHeaders: [
234
+ { name: 'X-Forwarded-Port', value: '443' },
235
+ { name: 'X-Forwarded-Ssl', value: 'on' },
236
+ this.customCFHeader,
237
+ ],
238
+ },
239
+ ], defaultCacheBehavior: {
240
+ targetOriginId: this.lb.arn,
241
+ viewerProtocolPolicy: 'redirect-to-https',
242
+ allowedMethods: [
243
+ 'GET',
244
+ 'HEAD',
245
+ 'OPTIONS',
246
+ 'PUT',
247
+ 'POST',
248
+ 'PATCH',
249
+ 'DELETE',
250
+ ],
251
+ cachedMethods: ['GET', 'HEAD'],
252
+ compress: true,
253
+ cachePolicyId: cachePolicy.id,
254
+ originRequestPolicyId,
255
+ responseHeadersPolicyId,
256
+ }, priceClass: 'PriceClass_100', restrictions: {
257
+ geoRestriction: { restrictionType: 'none' },
258
+ }, tags: Object.assign(Object.assign({}, constants_1.commonTags), tags) }), { parent: this });
259
+ return cloudfront;
260
+ }
261
+ createDnsRecord({ domain, hostedZoneId, }) {
262
+ const cdnAliasRecord = new aws.route53.Record(`${this.name}-cdn-route53-record`, {
263
+ type: 'A',
264
+ name: domain,
265
+ zoneId: hostedZoneId,
266
+ aliases: [
267
+ {
268
+ name: this.cloudfront.domainName,
269
+ zoneId: this.cloudfront.hostedZoneId,
270
+ evaluateTargetHealth: true,
271
+ },
272
+ ],
273
+ }, { parent: this });
274
+ return cdnAliasRecord;
275
+ }
276
+ }
277
+ exports.NuxtSSR = NuxtSSR;
@@ -4,10 +4,13 @@ import * as awsx from '@pulumi/awsx';
4
4
  import * as upstash from '@upstash/pulumi';
5
5
  import { Database, DatabaseArgs } from './database';
6
6
  import { WebServer, WebServerArgs } from './web-server';
7
+ import { Mongo, MongoArgs } from './mongo';
7
8
  import { Redis, RedisArgs } from './redis';
8
9
  import { StaticSite, StaticSiteArgs } from './static-site';
9
10
  import { Ec2SSMConnect } from './ec2-ssm-connect';
10
- export type Service = Database | Redis | StaticSite | WebServer;
11
+ import { EcsService, EcsServiceArgs } from './ecs-service';
12
+ import { NuxtSSR, NuxtSSRArgs } from './nuxt-ssr';
13
+ export type Service = Database | Redis | StaticSite | WebServer | NuxtSSR | Mongo | EcsService;
11
14
  export type Services = Record<string, Service>;
12
15
  type ServiceArgs = {
13
16
  /**
@@ -15,33 +18,44 @@ type ServiceArgs = {
15
18
  */
16
19
  serviceName: string;
17
20
  };
18
- export type DatabaseService = {
21
+ export type DatabaseServiceOptions = {
19
22
  type: 'DATABASE';
20
- } & ServiceArgs & Omit<DatabaseArgs, 'vpc'>;
21
- export type RedisService = {
23
+ } & ServiceArgs & Omit<DatabaseArgs, 'vpcId' | 'vpcCidrBlock' | 'isolatedSubnetIds'>;
24
+ export type RedisServiceOptions = {
22
25
  type: 'REDIS';
23
- } & ServiceArgs & Pick<RedisArgs, 'dbName' | 'region'>;
24
- export type StaticSiteService = {
26
+ } & ServiceArgs & RedisArgs;
27
+ export type StaticSiteServiceOptions = {
25
28
  type: 'STATIC_SITE';
26
- } & ServiceArgs & Omit<StaticSiteArgs, 'hostedZoneId'>;
27
- export type WebServerService = {
29
+ } & ServiceArgs & StaticSiteArgs;
30
+ export type WebServerServiceOptions = {
28
31
  type: 'WEB_SERVER';
29
32
  environment?: aws.ecs.KeyValuePair[] | ((services: Services) => aws.ecs.KeyValuePair[]);
30
33
  secrets?: aws.ecs.Secret[] | ((services: Services) => aws.ecs.Secret[]);
31
- } & ServiceArgs & Omit<WebServerArgs, 'cluster' | 'vpc' | 'hostedZoneId' | 'environment' | 'secrets'>;
34
+ } & ServiceArgs & Omit<WebServerArgs, 'cluster' | 'vpcId' | 'vpcCidrBlock' | 'publicSubnetIds' | 'environment' | 'secrets'>;
35
+ export type NuxtSSRServiceOptions = {
36
+ type: 'NUXT_SSR';
37
+ environment?: aws.ecs.KeyValuePair[] | ((services: Services) => aws.ecs.KeyValuePair[]);
38
+ secrets?: aws.ecs.Secret[] | ((services: Services) => aws.ecs.Secret[]);
39
+ } & ServiceArgs & Omit<NuxtSSRArgs, 'cluster' | 'vpcId' | 'vpcCidrBlock' | 'publicSubnetIds' | 'environment' | 'secrets'>;
40
+ export type MongoServiceOptions = {
41
+ type: 'MONGO';
42
+ } & ServiceArgs & Omit<MongoArgs, 'cluster' | 'vpcId' | 'vpcCidrBlock' | 'privateSubnetIds' | 'environment' | 'secrets'>;
43
+ export type EcsServiceOptions = {
44
+ type: 'ECS_SERVICE';
45
+ environment?: aws.ecs.KeyValuePair[] | ((services: Services) => aws.ecs.KeyValuePair[]);
46
+ secrets?: aws.ecs.Secret[] | ((services: Services) => aws.ecs.Secret[]);
47
+ } & ServiceArgs & Omit<EcsServiceArgs, 'cluster' | 'vpcId' | 'vpcCidrBlock' | 'subnetIds' | 'environment' | 'secrets'>;
32
48
  export type ProjectArgs = {
33
- services: (DatabaseService | RedisService | StaticSiteService | WebServerService)[];
34
- hostedZoneId?: pulumi.Input<string>;
49
+ services: (DatabaseServiceOptions | RedisServiceOptions | StaticSiteServiceOptions | WebServerServiceOptions | NuxtSSRServiceOptions | MongoServiceOptions | EcsServiceOptions)[];
35
50
  enableSSMConnect?: pulumi.Input<boolean>;
36
51
  };
37
- export declare class MissingHostedZoneId extends Error {
38
- constructor(serviceType: string);
52
+ export declare class MissingEcsCluster extends Error {
53
+ constructor();
39
54
  }
40
55
  export declare class Project extends pulumi.ComponentResource {
41
56
  name: string;
42
57
  vpc: awsx.ec2.Vpc;
43
58
  cluster?: aws.ecs.Cluster;
44
- hostedZoneId?: pulumi.Input<string>;
45
59
  upstashProvider?: upstash.Provider;
46
60
  ec2SSMConnect?: Ec2SSMConnect;
47
61
  services: Services;
@@ -49,10 +63,13 @@ export declare class Project extends pulumi.ComponentResource {
49
63
  private createVpc;
50
64
  private createServices;
51
65
  private createRedisPrerequisites;
52
- private createWebServerPrerequisites;
66
+ private createEcsCluster;
53
67
  private createDatabaseService;
54
68
  private createRedisService;
55
69
  private createStaticSiteService;
56
70
  private createWebServerService;
71
+ private createNuxtSSRService;
72
+ private createMongoService;
73
+ private createEcsService;
57
74
  }
58
75
  export {};
@@ -11,37 +11,39 @@ var __rest = (this && this.__rest) || function (s, e) {
11
11
  return t;
12
12
  };
13
13
  Object.defineProperty(exports, "__esModule", { value: true });
14
- exports.Project = exports.MissingHostedZoneId = void 0;
14
+ exports.Project = exports.MissingEcsCluster = void 0;
15
15
  const pulumi = require("@pulumi/pulumi");
16
16
  const aws = require("@pulumi/aws");
17
17
  const awsx = require("@pulumi/awsx");
18
18
  const upstash = require("@upstash/pulumi");
19
19
  const database_1 = require("./database");
20
20
  const web_server_1 = require("./web-server");
21
+ const mongo_1 = require("./mongo");
21
22
  const redis_1 = require("./redis");
22
23
  const static_site_1 = require("./static-site");
23
24
  const ec2_ssm_connect_1 = require("./ec2-ssm-connect");
24
25
  const constants_1 = require("../constants");
25
- class MissingHostedZoneId extends Error {
26
- constructor(serviceType) {
27
- super(`Project::hostedZoneId argument must be provided
28
- in order to create ${serviceType} service`);
26
+ const ecs_service_1 = require("./ecs-service");
27
+ const nuxt_ssr_1 = require("./nuxt-ssr");
28
+ class MissingEcsCluster extends Error {
29
+ constructor() {
30
+ super('Ecs Cluster does not exist');
29
31
  this.name = this.constructor.name;
30
32
  }
31
33
  }
32
- exports.MissingHostedZoneId = MissingHostedZoneId;
34
+ exports.MissingEcsCluster = MissingEcsCluster;
33
35
  class Project extends pulumi.ComponentResource {
34
36
  constructor(name, args, opts = {}) {
35
37
  super('studion:Project', name, {}, opts);
36
38
  this.services = {};
37
- const { services, hostedZoneId } = args;
38
39
  this.name = name;
39
- this.hostedZoneId = hostedZoneId;
40
40
  this.vpc = this.createVpc();
41
- this.createServices(services);
41
+ this.createServices(args.services);
42
42
  if (args.enableSSMConnect) {
43
43
  this.ec2SSMConnect = new ec2_ssm_connect_1.Ec2SSMConnect(`${name}-ssm-connect`, {
44
- vpc: this.vpc,
44
+ vpcId: this.vpc.vpcId,
45
+ privateSubnetId: this.vpc.privateSubnetIds.apply(ids => ids[0]),
46
+ vpcCidrBlock: this.vpc.vpc.cidrBlock,
45
47
  });
46
48
  }
47
49
  this.registerOutputs();
@@ -62,11 +64,14 @@ class Project extends pulumi.ComponentResource {
62
64
  }
63
65
  createServices(services) {
64
66
  const hasRedisService = services.some(it => it.type === 'REDIS');
65
- const hasWebServerService = services.some(it => it.type === 'WEB_SERVER');
67
+ const shouldCreateEcsCluster = services.some(it => it.type === 'WEB_SERVER' ||
68
+ it.type === 'NUXT_SSR' ||
69
+ it.type === 'MONGO' ||
70
+ it.type === 'ECS_SERVICE') && !this.cluster;
66
71
  if (hasRedisService)
67
72
  this.createRedisPrerequisites();
68
- if (hasWebServerService)
69
- this.createWebServerPrerequisites();
73
+ if (shouldCreateEcsCluster)
74
+ this.createEcsCluster();
70
75
  services.forEach(it => {
71
76
  if (it.type === 'DATABASE')
72
77
  this.createDatabaseService(it);
@@ -76,6 +81,12 @@ class Project extends pulumi.ComponentResource {
76
81
  this.createStaticSiteService(it);
77
82
  if (it.type === 'WEB_SERVER')
78
83
  this.createWebServerService(it);
84
+ if (it.type === 'NUXT_SSR')
85
+ this.createNuxtSSRService(it);
86
+ if (it.type === 'MONGO')
87
+ this.createMongoService(it);
88
+ if (it.type === 'ECS_SERVICE')
89
+ this.createEcsService(it);
79
90
  });
80
91
  }
81
92
  createRedisPrerequisites() {
@@ -85,7 +96,7 @@ class Project extends pulumi.ComponentResource {
85
96
  apiKey: upstashConfig.requireSecret('apiKey'),
86
97
  });
87
98
  }
88
- createWebServerPrerequisites() {
99
+ createEcsCluster() {
89
100
  const stack = pulumi.getStack();
90
101
  this.cluster = new aws.ecs.Cluster(`${this.name}-cluster`, {
91
102
  name: `${this.name}-${stack}`,
@@ -94,7 +105,7 @@ class Project extends pulumi.ComponentResource {
94
105
  }
95
106
  createDatabaseService(options) {
96
107
  const { serviceName, type } = options, databaseOptions = __rest(options, ["serviceName", "type"]);
97
- const service = new database_1.Database(serviceName, Object.assign(Object.assign({}, databaseOptions), { vpc: this.vpc }), { parent: this });
108
+ const service = new database_1.Database(serviceName, Object.assign(Object.assign({}, databaseOptions), { vpcId: this.vpc.vpcId, isolatedSubnetIds: this.vpc.isolatedSubnetIds, vpcCidrBlock: this.vpc.vpc.cidrBlock }), { parent: this });
98
109
  this.services[serviceName] = service;
99
110
  }
100
111
  createRedisService(options) {
@@ -109,22 +120,51 @@ class Project extends pulumi.ComponentResource {
109
120
  }
110
121
  createStaticSiteService(options) {
111
122
  const { serviceName } = options, staticSiteOptions = __rest(options, ["serviceName"]);
112
- if (!this.hostedZoneId)
113
- throw new MissingHostedZoneId(options.type);
114
- const service = new static_site_1.StaticSite(serviceName, Object.assign(Object.assign({}, staticSiteOptions), { hostedZoneId: this.hostedZoneId }), { parent: this });
123
+ const service = new static_site_1.StaticSite(serviceName, staticSiteOptions, {
124
+ parent: this,
125
+ });
115
126
  this.services[serviceName] = service;
116
127
  }
117
128
  createWebServerService(options) {
118
129
  if (!this.cluster)
119
- return;
120
- if (!this.hostedZoneId)
121
- throw new MissingHostedZoneId(options.type);
130
+ throw new MissingEcsCluster();
131
+ const { serviceName, environment, secrets } = options, ecsOptions = __rest(options, ["serviceName", "environment", "secrets"]);
132
+ const parsedEnv = typeof environment === 'function'
133
+ ? environment(this.services)
134
+ : environment;
135
+ const parsedSecrets = typeof secrets === 'function' ? secrets(this.services) : secrets;
136
+ const service = new web_server_1.WebServer(serviceName, Object.assign(Object.assign({}, ecsOptions), { cluster: this.cluster, vpcId: this.vpc.vpcId, vpcCidrBlock: this.vpc.vpc.cidrBlock, publicSubnetIds: this.vpc.publicSubnetIds, environment: parsedEnv, secrets: parsedSecrets }), { parent: this });
137
+ this.services[options.serviceName] = service;
138
+ }
139
+ createNuxtSSRService(options) {
140
+ if (!this.cluster)
141
+ throw new MissingEcsCluster();
142
+ const { serviceName, environment, secrets } = options, ecsOptions = __rest(options, ["serviceName", "environment", "secrets"]);
143
+ const parsedEnv = typeof environment === 'function'
144
+ ? environment(this.services)
145
+ : environment;
146
+ const parsedSecrets = typeof secrets === 'function' ? secrets(this.services) : secrets;
147
+ const service = new nuxt_ssr_1.NuxtSSR(serviceName, Object.assign(Object.assign({}, ecsOptions), { cluster: this.cluster, vpcId: this.vpc.vpcId, vpcCidrBlock: this.vpc.vpc.cidrBlock, publicSubnetIds: this.vpc.publicSubnetIds, environment: parsedEnv, secrets: parsedSecrets }), { parent: this });
148
+ this.services[options.serviceName] = service;
149
+ }
150
+ createMongoService(options) {
151
+ if (!this.cluster)
152
+ throw new MissingEcsCluster();
153
+ const { serviceName } = options, mongoOptions = __rest(options, ["serviceName"]);
154
+ const service = new mongo_1.Mongo(serviceName, Object.assign(Object.assign({}, mongoOptions), { cluster: this.cluster, vpcId: this.vpc.vpcId, vpcCidrBlock: this.vpc.vpc.cidrBlock, privateSubnetIds: this.vpc.privateSubnetIds }), { parent: this });
155
+ this.services[options.serviceName] = service;
156
+ }
157
+ createEcsService(options) {
158
+ if (!this.cluster)
159
+ throw new MissingEcsCluster();
122
160
  const { serviceName, environment, secrets } = options, ecsOptions = __rest(options, ["serviceName", "environment", "secrets"]);
123
161
  const parsedEnv = typeof environment === 'function'
124
162
  ? environment(this.services)
125
163
  : environment;
126
164
  const parsedSecrets = typeof secrets === 'function' ? secrets(this.services) : secrets;
127
- const service = new web_server_1.WebServer(serviceName, Object.assign(Object.assign({}, ecsOptions), { cluster: this.cluster, vpc: this.vpc, hostedZoneId: this.hostedZoneId, environment: parsedEnv, secrets: parsedSecrets }), { parent: this });
165
+ const service = new ecs_service_1.EcsService(serviceName, Object.assign(Object.assign({}, ecsOptions), { cluster: this.cluster, vpcId: this.vpc.vpcId, vpcCidrBlock: this.vpc.vpc.cidrBlock, subnetIds: ecsOptions.assignPublicIp
166
+ ? this.vpc.publicSubnetIds
167
+ : this.vpc.privateSubnetIds, environment: parsedEnv, secrets: parsedSecrets }), { parent: this });
128
168
  this.services[options.serviceName] = service;
129
169
  }
130
170
  }
@@ -6,11 +6,11 @@ export type StaticSiteArgs = {
6
6
  * The domain which will be used to access the static site.
7
7
  * The domain or subdomain must belong to the provided hostedZone.
8
8
  */
9
- domain: pulumi.Input<string>;
9
+ domain?: pulumi.Input<string>;
10
10
  /**
11
11
  * The ID of the hosted zone.
12
12
  */
13
- hostedZoneId: pulumi.Input<string>;
13
+ hostedZoneId?: pulumi.Input<string>;
14
14
  /**
15
15
  * A map of tags to assign to the resource.
16
16
  */
@@ -20,7 +20,7 @@ export type StaticSiteArgs = {
20
20
  };
21
21
  export declare class StaticSite extends pulumi.ComponentResource {
22
22
  name: string;
23
- certificate: AcmCertificate;
23
+ certificate?: AcmCertificate;
24
24
  bucket: aws.s3.Bucket;
25
25
  cloudfront: aws.cloudfront.Distribution;
26
26
  constructor(name: string, args: StaticSiteArgs, opts?: pulumi.ComponentResourceOptions);
@@ -10,10 +10,18 @@ class StaticSite extends pulumi.ComponentResource {
10
10
  super('studion:StaticSite', name, {}, opts);
11
11
  this.name = name;
12
12
  const { domain, hostedZoneId, tags } = args;
13
- this.certificate = this.createTlsCertificate({ domain, hostedZoneId });
13
+ const hasCustomDomain = domain && hostedZoneId;
14
+ if (domain && !hostedZoneId) {
15
+ throw new Error('StaticSite:hostedZoneId must be provided when the domain is specified');
16
+ }
17
+ if (hasCustomDomain) {
18
+ this.certificate = this.createTlsCertificate({ domain, hostedZoneId });
19
+ }
14
20
  this.bucket = this.createPublicBucket({ tags });
15
21
  this.cloudfront = this.createCloudfrontDistribution({ domain, tags });
16
- this.createDnsRecord({ domain, hostedZoneId });
22
+ if (hasCustomDomain) {
23
+ this.createDnsRecord({ domain, hostedZoneId });
24
+ }
17
25
  this.registerOutputs();
18
26
  }
19
27
  createTlsCertificate({ domain, hostedZoneId, }) {
@@ -59,19 +67,15 @@ class StaticSite extends pulumi.ComponentResource {
59
67
  return bucket;
60
68
  }
61
69
  createCloudfrontDistribution({ domain, tags, }) {
62
- const cloudfront = new aws.cloudfront.Distribution(`${this.name}-cloudfront`, {
63
- enabled: true,
64
- defaultRootObject: 'index.html',
65
- aliases: [domain],
66
- isIpv6Enabled: true,
67
- waitForDeployment: true,
68
- httpVersion: 'http2and3',
69
- viewerCertificate: {
70
- acmCertificateArn: this.certificate.certificate.arn,
71
- sslSupportMethod: 'sni-only',
72
- minimumProtocolVersion: 'TLSv1.2_2021',
73
- },
74
- origins: [
70
+ const cloudfront = new aws.cloudfront.Distribution(`${this.name}-cloudfront`, Object.assign(Object.assign({ enabled: true, defaultRootObject: 'index.html' }, (domain && { aliases: [domain] })), { isIpv6Enabled: true, waitForDeployment: true, httpVersion: 'http2and3', viewerCertificate: Object.assign({}, (this.certificate
71
+ ? {
72
+ acmCertificateArn: this.certificate.certificate.arn,
73
+ sslSupportMethod: 'sni-only',
74
+ minimumProtocolVersion: 'TLSv1.2_2021',
75
+ }
76
+ : {
77
+ cloudfrontDefaultCertificate: true,
78
+ })), origins: [
75
79
  {
76
80
  originId: this.bucket.arn,
77
81
  domainName: this.bucket.websiteEndpoint,
@@ -84,8 +88,7 @@ class StaticSite extends pulumi.ComponentResource {
84
88
  originSslProtocols: ['TLSv1.2'],
85
89
  },
86
90
  },
87
- ],
88
- defaultCacheBehavior: {
91
+ ], defaultCacheBehavior: {
89
92
  targetOriginId: this.bucket.arn,
90
93
  viewerProtocolPolicy: 'redirect-to-https',
91
94
  allowedMethods: ['GET', 'HEAD', 'OPTIONS'],
@@ -98,13 +101,9 @@ class StaticSite extends pulumi.ComponentResource {
98
101
  cookies: { forward: 'none' },
99
102
  queryString: false,
100
103
  },
101
- },
102
- priceClass: 'PriceClass_100',
103
- restrictions: {
104
+ }, priceClass: 'PriceClass_100', restrictions: {
104
105
  geoRestriction: { restrictionType: 'none' },
105
- },
106
- tags: Object.assign(Object.assign({}, constants_1.commonTags), tags),
107
- }, { parent: this });
106
+ }, tags: Object.assign(Object.assign({}, constants_1.commonTags), tags) }), { parent: this });
108
107
  return cloudfront;
109
108
  }
110
109
  createDnsRecord({ domain, hostedZoneId, }) {