carlin 1.26.9 → 1.27.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.
@@ -2,43 +2,45 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.getBucketTemplate = void 0;
4
4
  const config_1 = require("./config");
5
- const getBucketTemplate = () => ({
6
- AWSTemplateFormatVersion: '2010-09-09',
7
- Resources: {
8
- [config_1.BASE_STACK_BUCKET_LOGICAL_NAME]: {
9
- Type: 'AWS::S3::Bucket',
10
- DeletionPolicy: 'Retain',
11
- Properties: {
12
- LifecycleConfiguration: {
13
- Rules: [
14
- {
15
- ExpirationInDays: 1,
16
- Prefix: config_1.BASE_STACK_BUCKET_TEMPLATES_FOLDER,
17
- Status: 'Enabled',
18
- },
19
- {
20
- NoncurrentVersionExpirationInDays: 3,
21
- Status: 'Enabled',
22
- },
23
- ],
24
- },
25
- /**
26
- * This is necessary because if we update Lambda code without change
27
- * CloudFormation template, the Lambda will not be updated.
28
- */
29
- VersioningConfiguration: {
30
- Status: 'Enabled',
5
+ const getBucketTemplate = () => {
6
+ return {
7
+ AWSTemplateFormatVersion: '2010-09-09',
8
+ Resources: {
9
+ [config_1.BASE_STACK_BUCKET_LOGICAL_NAME]: {
10
+ Type: 'AWS::S3::Bucket',
11
+ DeletionPolicy: 'Retain',
12
+ Properties: {
13
+ LifecycleConfiguration: {
14
+ Rules: [
15
+ {
16
+ ExpirationInDays: 1,
17
+ Prefix: config_1.BASE_STACK_BUCKET_TEMPLATES_FOLDER,
18
+ Status: 'Enabled',
19
+ },
20
+ {
21
+ NoncurrentVersionExpirationInDays: 3,
22
+ Status: 'Enabled',
23
+ },
24
+ ],
25
+ },
26
+ /**
27
+ * This is necessary because if we update Lambda code without change
28
+ * CloudFormation template, the Lambda will not be updated.
29
+ */
30
+ VersioningConfiguration: {
31
+ Status: 'Enabled',
32
+ },
31
33
  },
32
34
  },
33
35
  },
34
- },
35
- Outputs: {
36
- [config_1.BASE_STACK_BUCKET_LOGICAL_NAME]: {
37
- Value: { Ref: config_1.BASE_STACK_BUCKET_LOGICAL_NAME },
38
- Export: {
39
- Name: config_1.BASE_STACK_BUCKET_NAME_EXPORTED_NAME,
36
+ Outputs: {
37
+ [config_1.BASE_STACK_BUCKET_LOGICAL_NAME]: {
38
+ Value: { Ref: config_1.BASE_STACK_BUCKET_LOGICAL_NAME },
39
+ Export: {
40
+ Name: config_1.BASE_STACK_BUCKET_NAME_EXPORTED_NAME,
41
+ },
40
42
  },
41
43
  },
42
- },
43
- });
44
+ };
45
+ };
44
46
  exports.getBucketTemplate = getBucketTemplate;
package/dist/deploy/s3.js CHANGED
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.deleteS3Directory = exports.emptyS3Directory = exports.uploadDirectoryToS3 = exports.getAllFilesInsideADirectory = exports.uploadFileToS3 = exports.getBucketKeyUrl = exports.s3 = void 0;
6
+ exports.deleteS3Directory = exports.emptyS3Directory = exports.uploadDirectoryToS3 = exports.copyRoot404To404Index = exports.getAllFilesInsideADirectory = exports.uploadFileToS3 = exports.getBucketKeyUrl = exports.s3 = void 0;
7
7
  /* eslint-disable no-restricted-syntax */
8
8
  /* eslint-disable no-await-in-loop */
9
9
  const aws_sdk_1 = require("aws-sdk");
@@ -62,6 +62,41 @@ const getAllFilesInsideADirectory = async ({ directory, }) => {
62
62
  return allFiles;
63
63
  };
64
64
  exports.getAllFilesInsideADirectory = getAllFilesInsideADirectory;
65
+ /**
66
+ * Docusaurus 2 has a 404.html file in the root of the build folder. This
67
+ * function copies it to 404/index.html so that it can be served by S3 and
68
+ * CloudFront.
69
+ */
70
+ const copyRoot404To404Index = async ({ bucket }) => {
71
+ try {
72
+ const root404Exists = await exports.s3
73
+ .headObject({
74
+ Bucket: bucket,
75
+ Key: '404.html',
76
+ })
77
+ .promise()
78
+ .catch(() => {
79
+ /**
80
+ * If the file does not exist, return false.
81
+ */
82
+ return false;
83
+ });
84
+ if (root404Exists) {
85
+ await exports.s3
86
+ .copyObject({
87
+ Bucket: bucket,
88
+ CopySource: `${bucket}/404.html`,
89
+ Key: '404/index.html',
90
+ })
91
+ .promise();
92
+ }
93
+ }
94
+ catch (err) {
95
+ npmlog_1.default.error(logPrefix, `Cannot copy 404.html to 404/index.html`);
96
+ throw err;
97
+ }
98
+ };
99
+ exports.copyRoot404To404Index = copyRoot404To404Index;
65
100
  const uploadDirectoryToS3 = async ({ bucket, bucketKey = '', directory, }) => {
66
101
  npmlog_1.default.info(logPrefix, `Uploading directory ${directory}/ to ${bucket}/${bucketKey}...`);
67
102
  const allFiles = await (0, exports.getAllFilesInsideADirectory)({ directory });
@@ -29,14 +29,23 @@ const removeOldVersions = async ({ bucket }) => {
29
29
  const { CommonPrefixes = [] } = await s3_1.s3
30
30
  .listObjectsV2({ Bucket: bucket, Delimiter: '/' })
31
31
  .promise();
32
- const versions = CommonPrefixes === null || CommonPrefixes === void 0 ? void 0 : CommonPrefixes.map(({ Prefix }) => Prefix === null || Prefix === void 0 ? void 0 : Prefix.replace('/', '')).filter((version) => semver_1.default.valid(version)).sort((a, b) => (semver_1.default.gt(a, b) ? -1 : 1));
32
+ const versions = CommonPrefixes === null || CommonPrefixes === void 0 ? void 0 : CommonPrefixes.map(({ Prefix }) => {
33
+ return Prefix === null || Prefix === void 0 ? void 0 : Prefix.replace('/', '');
34
+ }).filter((version) => {
35
+ return semver_1.default.valid(version);
36
+ }).sort((a, b) => {
37
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
38
+ return semver_1.default.gt(a, b) ? -1 : 1;
39
+ });
33
40
  /**
34
41
  * Keep the 3 most recent versions.
35
42
  */
36
43
  versions.shift();
37
44
  versions.shift();
38
45
  versions.shift();
39
- await Promise.all(versions.map((version) => (0, s3_1.deleteS3Directory)({ bucket, directory: `${version}` })));
46
+ await Promise.all(versions.map((version) => {
47
+ return (0, s3_1.deleteS3Directory)({ bucket, directory: `${version}` });
48
+ }));
40
49
  }
41
50
  catch (error) {
42
51
  npmlog_1.default.info(logPrefix, `Cannot remove older versions from "${bucket}" bucket.`);
@@ -1,13 +1,13 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.getStaticAppTemplate = exports.ROUTE_53_RECORD_SET_GROUP_LOGICAL_ID = exports.CLOUDFRONT_DISTRIBUTION_LOGICAL_ID = void 0;
3
+ exports.getStaticAppTemplate = exports.ERROR_DOCUMENT = exports.ROUTE_53_RECORD_SET_GROUP_LOGICAL_ID = exports.CLOUDFRONT_ORIGIN_ACCESS_CONTROL_LOGICAL_ID = exports.CLOUDFRONT_DISTRIBUTION_LOGICAL_ID = exports.STATIC_APP_BUCKET_LOGICAL_ID = void 0;
4
4
  const utils_1 = require("../../utils");
5
- const getOriginShieldRegion_1 = require("./getOriginShieldRegion");
6
5
  const PACKAGE_VERSION = (0, utils_1.getPackageVersion)();
7
- const STATIC_APP_BUCKET_LOGICAL_ID = 'StaticBucket';
8
- const CLOUDFRONT_DISTRIBUTION_ID = 'CloudFrontDistributionId';
6
+ exports.STATIC_APP_BUCKET_LOGICAL_ID = 'StaticBucket';
9
7
  exports.CLOUDFRONT_DISTRIBUTION_LOGICAL_ID = 'CloudFrontDistribution';
8
+ exports.CLOUDFRONT_ORIGIN_ACCESS_CONTROL_LOGICAL_ID = 'OriginAccessControl';
10
9
  exports.ROUTE_53_RECORD_SET_GROUP_LOGICAL_ID = 'Route53RecordSetGroup';
10
+ exports.ERROR_DOCUMENT = '404/index.html';
11
11
  /**
12
12
  * Name: Managed-CachingDisabled
13
13
  * ID: 4135ea2d-6df8-44a3-9df3-4b5a84be39ad
@@ -26,11 +26,11 @@ const ORIGIN_REQUEST_POLICY_ID = '88a5eaf4-2fd4-4709-b370-b4c650ea3fcf';
26
26
  * https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/using-managed-response-headers-policies.html
27
27
  */
28
28
  const ORIGIN_RESPONSE_POLICY_ID = 'eaab4381-ed33-4a86-88ca-d9558dc6cd63';
29
- const getBaseTemplate = ({ spa, }) => {
29
+ const getBucketStaticWebsiteTemplate = ({ spa, }) => {
30
30
  return {
31
31
  AWSTemplateFormatVersion: '2010-09-09',
32
32
  Resources: {
33
- [STATIC_APP_BUCKET_LOGICAL_ID]: {
33
+ [exports.STATIC_APP_BUCKET_LOGICAL_ID]: {
34
34
  Type: 'AWS::S3::Bucket',
35
35
  Properties: {
36
36
  CorsConfiguration: {
@@ -44,16 +44,19 @@ const getBaseTemplate = ({ spa, }) => {
44
44
  },
45
45
  ],
46
46
  },
47
+ PublicAccessBlockConfiguration: {
48
+ BlockPublicPolicy: false,
49
+ },
47
50
  WebsiteConfiguration: {
48
51
  IndexDocument: `index.html`,
49
- ErrorDocument: spa ? 'index.html' : '404/index.html',
52
+ ErrorDocument: spa ? 'index.html' : exports.ERROR_DOCUMENT,
50
53
  },
51
54
  },
52
55
  },
53
- [`${STATIC_APP_BUCKET_LOGICAL_ID}S3BucketPolicy`]: {
56
+ [`${exports.STATIC_APP_BUCKET_LOGICAL_ID}S3BucketPolicy`]: {
54
57
  Type: 'AWS::S3::BucketPolicy',
55
58
  Properties: {
56
- Bucket: { Ref: STATIC_APP_BUCKET_LOGICAL_ID },
59
+ Bucket: { Ref: exports.STATIC_APP_BUCKET_LOGICAL_ID },
57
60
  PolicyDocument: {
58
61
  Statement: [
59
62
  {
@@ -65,7 +68,7 @@ const getBaseTemplate = ({ spa, }) => {
65
68
  '',
66
69
  [
67
70
  'arn:aws:s3:::',
68
- { Ref: STATIC_APP_BUCKET_LOGICAL_ID },
71
+ { Ref: exports.STATIC_APP_BUCKET_LOGICAL_ID },
69
72
  '/*',
70
73
  ],
71
74
  ],
@@ -80,14 +83,74 @@ const getBaseTemplate = ({ spa, }) => {
80
83
  BucketWebsiteURL: {
81
84
  Description: 'Bucket static app website URL',
82
85
  Value: {
83
- 'Fn::GetAtt': [STATIC_APP_BUCKET_LOGICAL_ID, 'WebsiteURL'],
86
+ 'Fn::GetAtt': [exports.STATIC_APP_BUCKET_LOGICAL_ID, 'WebsiteURL'],
84
87
  },
85
88
  },
86
89
  },
87
90
  };
88
91
  };
89
- const getCloudFrontTemplate = ({ acm, aliases = [], cloudfront, spa, hostedZoneName, region, }) => {
90
- const template = { ...getBaseTemplate({ cloudfront, spa }) };
92
+ const getCloudFrontTemplate = ({ acm, aliases = [], spa, hostedZoneName, }) => {
93
+ const template = {
94
+ AWSTemplateFormatVersion: '2010-09-09',
95
+ Resources: {
96
+ [exports.STATIC_APP_BUCKET_LOGICAL_ID]: {
97
+ Type: 'AWS::S3::Bucket',
98
+ Properties: {
99
+ PublicAccessBlockConfiguration: {
100
+ BlockPublicPolicy: false,
101
+ },
102
+ },
103
+ },
104
+ [`${exports.STATIC_APP_BUCKET_LOGICAL_ID}S3BucketPolicy`]: {
105
+ Type: 'AWS::S3::BucketPolicy',
106
+ Properties: {
107
+ Bucket: { Ref: exports.STATIC_APP_BUCKET_LOGICAL_ID },
108
+ PolicyDocument: {
109
+ Statement: [
110
+ /**
111
+ * https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/private-content-restricting-access-to-s3.html
112
+ */
113
+ {
114
+ Sid: 'AllowCloudFrontServicePrincipalReadOnly',
115
+ Effect: 'Allow',
116
+ Principal: {
117
+ Service: 'cloudfront.amazonaws.com',
118
+ },
119
+ Action: 's3:GetObject',
120
+ Resource: {
121
+ 'Fn::Join': [
122
+ '',
123
+ [
124
+ 'arn:aws:s3:::',
125
+ { Ref: exports.STATIC_APP_BUCKET_LOGICAL_ID },
126
+ '/*',
127
+ ],
128
+ ],
129
+ },
130
+ Condition: {
131
+ StringEquals: {
132
+ 'AWS:SourceArn':
133
+ // 'arn:aws:cloudfront::<AWS account ID>:distribution/<CloudFront distribution ID>',
134
+ {
135
+ 'Fn::Join': [
136
+ '',
137
+ [
138
+ 'arn:aws:cloudfront::',
139
+ { Ref: 'AWS::AccountId' },
140
+ ':distribution/',
141
+ { Ref: exports.CLOUDFRONT_DISTRIBUTION_LOGICAL_ID },
142
+ ],
143
+ ],
144
+ },
145
+ },
146
+ },
147
+ },
148
+ ],
149
+ },
150
+ },
151
+ },
152
+ },
153
+ };
91
154
  const cloudFrontResources = {
92
155
  [exports.CLOUDFRONT_DISTRIBUTION_LOGICAL_ID]: {
93
156
  Type: 'AWS::CloudFront::Distribution',
@@ -112,7 +175,7 @@ const getCloudFrontTemplate = ({ acm, aliases = [], cloudfront, spa, hostedZoneN
112
175
  ErrorCachingMinTTL: 0,
113
176
  ErrorCode: errorCode,
114
177
  ResponseCode: 404,
115
- ResponsePagePath: '/404',
178
+ ResponsePagePath: '/' + exports.ERROR_DOCUMENT,
116
179
  };
117
180
  }),
118
181
  DefaultCacheBehavior: {
@@ -130,51 +193,54 @@ const getCloudFrontTemplate = ({ acm, aliases = [], cloudfront, spa, hostedZoneN
130
193
  */
131
194
  CachePolicyId: CACHE_POLICY_ID,
132
195
  ResponseHeadersPolicyId: ORIGIN_RESPONSE_POLICY_ID,
133
- TargetOriginId: { Ref: STATIC_APP_BUCKET_LOGICAL_ID },
196
+ TargetOriginId: { Ref: exports.STATIC_APP_BUCKET_LOGICAL_ID },
134
197
  ViewerProtocolPolicy: 'redirect-to-https',
135
198
  },
136
- DefaultRootObject: spa ? 'index.html' : undefined,
199
+ DefaultRootObject: 'index.html',
137
200
  Enabled: true,
138
201
  HttpVersion: 'http2',
139
202
  Origins: [
140
203
  {
141
- CustomOriginConfig: {
142
- OriginProtocolPolicy: 'http-only',
143
- },
144
- /**
145
- * https://github.com/aws/aws-cdk/issues/1882#issuecomment-629141467
146
- */
147
204
  DomainName: {
148
- 'Fn::Select': [
149
- 1,
150
- {
151
- 'Fn::Split': [
152
- '//',
153
- {
154
- 'Fn::GetAtt': [
155
- STATIC_APP_BUCKET_LOGICAL_ID,
156
- 'WebsiteURL',
157
- ],
158
- },
159
- ],
160
- },
205
+ 'Fn::GetAtt': [exports.STATIC_APP_BUCKET_LOGICAL_ID, 'DomainName'],
206
+ },
207
+ Id: { Ref: exports.STATIC_APP_BUCKET_LOGICAL_ID },
208
+ OriginAccessControlId: {
209
+ 'Fn::GetAtt': [
210
+ exports.CLOUDFRONT_ORIGIN_ACCESS_CONTROL_LOGICAL_ID,
211
+ 'Id',
161
212
  ],
162
213
  },
163
- Id: { Ref: STATIC_APP_BUCKET_LOGICAL_ID },
164
214
  /**
165
- * https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/origin-shield.html#choose-origin-shield-region
215
+ * Note: As of September 2022, an empty OriginAccessIdentity must be specified in S3OriginConfig.
166
216
  */
167
- ...(region && {
168
- OriginShield: {
169
- Enabled: true,
170
- OriginShieldRegion: (0, getOriginShieldRegion_1.getOriginShieldRegion)(region),
171
- },
172
- }),
217
+ S3OriginConfig: {
218
+ OriginAccessIdentity: '',
219
+ },
173
220
  },
174
221
  ],
175
222
  },
176
223
  },
177
224
  },
225
+ [exports.CLOUDFRONT_ORIGIN_ACCESS_CONTROL_LOGICAL_ID]: {
226
+ Type: 'AWS::CloudFront::OriginAccessControl',
227
+ Properties: {
228
+ OriginAccessControlConfig: {
229
+ Description: {
230
+ 'Fn::Sub': [
231
+ 'Default Origin Access Control for ${Project} project.',
232
+ { Project: { Ref: 'Project' } },
233
+ ],
234
+ },
235
+ Name: {
236
+ Ref: 'AWS::StackName',
237
+ },
238
+ OriginAccessControlOriginType: 's3',
239
+ SigningBehavior: 'always',
240
+ SigningProtocol: 'sigv4',
241
+ },
242
+ },
243
+ },
178
244
  };
179
245
  if (acm) {
180
246
  const acmRegex = /^arn:aws:acm:[-a-z0-9]+:\d{12}:certificate\/[-a-z0-9]+$/;
@@ -251,12 +317,14 @@ const getCloudFrontTemplate = ({ acm, aliases = [], cloudfront, spa, hostedZoneN
251
317
  /**
252
318
  * Add aliases output to template.
253
319
  */
254
- const aliasesOutput = (aliases || []).reduce((acc, alias, index) => ({
255
- ...acc,
256
- [`Alias${index}URL`]: {
257
- Value: `https://${alias}`,
258
- },
259
- }), {});
320
+ const aliasesOutput = (aliases || []).reduce((acc, alias, index) => {
321
+ return {
322
+ ...acc,
323
+ [`Alias${index}URL`]: {
324
+ Value: `https://${alias}`,
325
+ },
326
+ };
327
+ }, {});
260
328
  /**
261
329
  * Add CloudFront Distribution ID and CloudFront URL to template.
262
330
  */
@@ -276,7 +344,7 @@ const getCloudFrontTemplate = ({ acm, aliases = [], cloudfront, spa, hostedZoneN
276
344
  ],
277
345
  },
278
346
  },
279
- [CLOUDFRONT_DISTRIBUTION_ID]: {
347
+ CloudFrontDistributionId: {
280
348
  Value: {
281
349
  Ref: exports.CLOUDFRONT_DISTRIBUTION_LOGICAL_ID,
282
350
  },
@@ -298,6 +366,6 @@ const getStaticAppTemplate = ({ acm, aliases, cloudfront, spa, hostedZoneName, r
298
366
  region,
299
367
  });
300
368
  }
301
- return getBaseTemplate({ cloudfront, spa });
369
+ return getBucketStaticWebsiteTemplate({ spa });
302
370
  };
303
371
  exports.getStaticAppTemplate = getStaticAppTemplate;
@@ -1,8 +1,8 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.uploadBuiltAppToS3 = void 0;
4
- const findDefaultBuildFolder_1 = require("./findDefaultBuildFolder");
5
4
  const s3_1 = require("../s3");
5
+ const findDefaultBuildFolder_1 = require("./findDefaultBuildFolder");
6
6
  const uploadBuiltAppToS3 = async ({ buildFolder: directory, bucket, }) => {
7
7
  /**
8
8
  * Only empty directory if the number of the files inside $directory.
@@ -20,6 +20,7 @@ const uploadBuiltAppToS3 = async ({ buildFolder: directory, bucket, }) => {
20
20
  if (defaultDirectory) {
21
21
  await (0, s3_1.emptyS3Directory)({ bucket });
22
22
  await (0, s3_1.uploadDirectoryToS3)({ bucket, directory: defaultDirectory });
23
+ await (0, s3_1.copyRoot404To404Index)({ bucket });
23
24
  return;
24
25
  }
25
26
  throw new Error(`build-folder option wasn't provided and files weren't found in ${findDefaultBuildFolder_1.defaultBuildFolders.join(', ')} directories.`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "carlin",
3
- "version": "1.26.9",
3
+ "version": "1.27.1",
4
4
  "description": "",
5
5
  "license": "GPL-3.0",
6
6
  "author": "Pedro Arantes <arantespp@gmail.com> (https://twitter.com/arantespp)",
@@ -71,5 +71,5 @@
71
71
  "publishConfig": {
72
72
  "access": "public"
73
73
  },
74
- "gitHead": "f30779a5ea50d60cf3099e5fde712e1ef7b7882c"
74
+ "gitHead": "3d6ffa89bf6f83619ac12a3688e8250e47c1a8c6"
75
75
  }
@@ -1,30 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.getOriginShieldRegion = void 0;
4
- /**
5
- * https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/origin-shield.html#choose-origin-shield-region
6
- * @param region
7
- */
8
- const getOriginShieldRegion = (region) => {
9
- switch (region) {
10
- case 'us-west-1':
11
- return 'us-west-2';
12
- case 'af-south-1':
13
- return 'eu-west-1';
14
- case 'ap-east-1':
15
- return 'ap-southeast-1';
16
- case 'ca-central-1':
17
- return 'us-east-1';
18
- case 'eu-south-1':
19
- return 'eu-central-1';
20
- case 'eu-west-3':
21
- return 'eu-west-2';
22
- case 'eu-north-1':
23
- return 'eu-west-2';
24
- case 'me-south-1':
25
- return 'ap-south-1';
26
- default:
27
- return region;
28
- }
29
- };
30
- exports.getOriginShieldRegion = getOriginShieldRegion;