Skip to content

Commit 42b9cf2

Browse files
tidy up
1 parent 2ac0f41 commit 42b9cf2

File tree

9 files changed

+314
-295
lines changed

9 files changed

+314
-295
lines changed

README.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,23 @@ distribution:
5454
- https://my-bucket.s3.amazonaws.com
5555
```
5656
57+
#### Complex origin objects
58+
59+
You can extend your origins configuration by declaring them as objects. For example, to add cache behaviors:
60+
61+
```yml
62+
# serverless.yml
63+
64+
distribution:
65+
component: '@serverless/aws-cloudfront'
66+
inputs:
67+
origins:
68+
- url: https://my-assets.com
69+
pathPatterns:
70+
/static/images: # route any /static/images requests to https://my-assets.com
71+
ttl: 10
72+
```
73+
5774
### 4. Deploy
5875
5976
```console

__tests__/__snapshots__/origin-with-path-pattern.test.js.snap

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ Object {
2525
"Quantity": 2,
2626
},
2727
"Compress": true,
28-
"DefaultTTL": 0,
28+
"DefaultTTL": 10,
2929
"FieldLevelEncryptionId": "",
3030
"ForwardedValues": Object {
3131
"Cookies": Object {
@@ -45,7 +45,7 @@ Object {
4545
"Items": Array [],
4646
"Quantity": 0,
4747
},
48-
"MaxTTL": 0,
48+
"MaxTTL": 10,
4949
"MinTTL": 10,
5050
"PathPattern": "/some/path",
5151
"SmoothStreaming": false,
@@ -163,7 +163,7 @@ Object {
163163
"Quantity": 2,
164164
},
165165
"Compress": true,
166-
"DefaultTTL": 0,
166+
"DefaultTTL": 10,
167167
"FieldLevelEncryptionId": "",
168168
"ForwardedValues": Object {
169169
"Cookies": Object {
@@ -183,7 +183,7 @@ Object {
183183
"Items": Array [],
184184
"Quantity": 0,
185185
},
186-
"MaxTTL": 0,
186+
"MaxTTL": 10,
187187
"MinTTL": 10,
188188
"PathPattern": "/some/other/path",
189189
"SmoothStreaming": false,

lib/getCacheBehavior.js

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
module.exports = (pathPattern, pathPatternConfig, originId) => {
2+
const { ttl } = pathPatternConfig
3+
4+
return {
5+
ForwardedValues: {
6+
Cookies: {
7+
Forward: 'all'
8+
},
9+
QueryString: true,
10+
Headers: {
11+
Quantity: 0,
12+
Items: []
13+
},
14+
QueryStringCacheKeys: {
15+
Quantity: 0,
16+
Items: []
17+
}
18+
},
19+
MinTTL: ttl,
20+
PathPattern: pathPattern,
21+
TargetOriginId: originId,
22+
TrustedSigners: {
23+
Enabled: false,
24+
Quantity: 0
25+
},
26+
ViewerProtocolPolicy: 'https-only',
27+
AllowedMethods: {
28+
Quantity: 2,
29+
Items: ['GET', 'HEAD'],
30+
CachedMethods: {
31+
Items: ['GET', 'HEAD'],
32+
Quantity: 2
33+
}
34+
},
35+
Compress: true,
36+
SmoothStreaming: false,
37+
DefaultTTL: ttl,
38+
MaxTTL: ttl,
39+
FieldLevelEncryptionId: '',
40+
LambdaFunctionAssociations: {
41+
Quantity: 0,
42+
Items: []
43+
}
44+
}
45+
}

lib/getDefaultCacheBehavior.js

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
module.exports = (originId) => {
2+
return {
3+
TargetOriginId: originId,
4+
ForwardedValues: {
5+
QueryString: false,
6+
Cookies: {
7+
Forward: 'none'
8+
},
9+
Headers: {
10+
Quantity: 0,
11+
Items: []
12+
},
13+
QueryStringCacheKeys: {
14+
Quantity: 0,
15+
Items: []
16+
}
17+
},
18+
TrustedSigners: {
19+
Enabled: false,
20+
Quantity: 0,
21+
Items: []
22+
},
23+
ViewerProtocolPolicy: 'redirect-to-https',
24+
MinTTL: 0,
25+
AllowedMethods: {
26+
Quantity: 2,
27+
Items: ['HEAD', 'GET'],
28+
CachedMethods: {
29+
Quantity: 2,
30+
Items: ['HEAD', 'GET']
31+
}
32+
},
33+
SmoothStreaming: false,
34+
DefaultTTL: 86400,
35+
MaxTTL: 31536000,
36+
Compress: false,
37+
LambdaFunctionAssociations: {
38+
Quantity: 0,
39+
Items: []
40+
},
41+
FieldLevelEncryptionId: ''
42+
}
43+
}

lib/getOriginConfig.js

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
const url = require('url')
2+
3+
module.exports = (origin) => {
4+
const originUrl = typeof origin === 'string' ? origin : origin.url
5+
6+
const { hostname } = url.parse(originUrl)
7+
8+
const originConfig = {
9+
Id: hostname,
10+
DomainName: hostname,
11+
CustomHeaders: {
12+
Quantity: 0,
13+
Items: []
14+
},
15+
OriginPath: ''
16+
}
17+
18+
if (originUrl.includes('s3')) {
19+
const bucketName = hostname.split('.')[0]
20+
originConfig.Id = bucketName
21+
originConfig.DomainName = `${bucketName}.s3.amazonaws.com`
22+
originConfig.S3OriginConfig = {
23+
OriginAccessIdentity: ''
24+
}
25+
} else {
26+
originConfig.CustomOriginConfig = {
27+
HTTPPort: 80,
28+
HTTPSPort: 443,
29+
OriginProtocolPolicy: 'https-only',
30+
OriginSslProtocols: {
31+
Quantity: 1,
32+
Items: ['TLSv1.2']
33+
},
34+
OriginReadTimeout: 30,
35+
OriginKeepaliveTimeout: 5
36+
}
37+
}
38+
39+
return originConfig
40+
}

lib/index.js

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
const parseInputOrigins = require('./parseInputOrigins')
2+
const getDefaultCacheBehavior = require('./getDefaultCacheBehavior')
3+
4+
const createCloudFrontDistribution = async (cf, inputs) => {
5+
const params = {
6+
DistributionConfig: {
7+
CallerReference: String(Date.now()),
8+
Comment: '',
9+
Aliases: {
10+
Quantity: 0,
11+
Items: []
12+
},
13+
Origins: {
14+
Quantity: 0,
15+
Items: []
16+
},
17+
PriceClass: 'PriceClass_All',
18+
Enabled: inputs.enabled === false ? false : true,
19+
HttpVersion: 'http2'
20+
}
21+
}
22+
23+
const distributionConfig = params.DistributionConfig
24+
25+
const { Origins, CacheBehaviors } = parseInputOrigins(inputs.origins)
26+
27+
distributionConfig.Origins = Origins
28+
29+
// set first origin declared as the default cache behavior
30+
distributionConfig.DefaultCacheBehavior = getDefaultCacheBehavior(Origins.Items[0].Id)
31+
32+
if (CacheBehaviors) {
33+
distributionConfig.CacheBehaviors = CacheBehaviors
34+
}
35+
36+
const res = await cf.createDistribution(params).promise()
37+
38+
return {
39+
id: res.Distribution.Id,
40+
arn: res.Distribution.ARN,
41+
url: `https://${res.Distribution.DomainName}`
42+
}
43+
}
44+
45+
const updateCloudFrontDistribution = async (cf, distributionId, inputs) => {
46+
// Update logic is a bit weird...
47+
// https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/CloudFront.html#updateDistribution-property
48+
49+
// 1. we gotta get the config first...
50+
// todo what if id does not exist?
51+
const params = await cf.getDistributionConfig({ Id: distributionId }).promise()
52+
53+
// 2. then add this property
54+
params.IfMatch = params.ETag
55+
56+
// 3. then delete this property
57+
delete params.ETag
58+
59+
// 4. then set this property
60+
params.Id = distributionId
61+
62+
// 5. then make our changes
63+
64+
params.DistributionConfig.Enabled = inputs.enabled === false ? false : true
65+
66+
const { Origins, CacheBehaviors } = parseInputOrigins(inputs.origins)
67+
68+
params.DistributionConfig.DefaultCacheBehavior = getDefaultCacheBehavior(Origins.Items[0].Id)
69+
params.DistributionConfig.Origins = Origins
70+
71+
if (CacheBehaviors) {
72+
params.DistributionConfig.CacheBehaviors = CacheBehaviors
73+
}
74+
75+
// 6. then finally update!
76+
const res = await cf.updateDistribution(params).promise()
77+
78+
return {
79+
id: res.Distribution.Id,
80+
arn: res.Distribution.ARN,
81+
url: `https://${res.Distribution.DomainName}`
82+
}
83+
}
84+
85+
const disableCloudFrontDistribution = async (cf, distributionId) => {
86+
const params = await cf.getDistributionConfig({ Id: distributionId }).promise()
87+
88+
params.IfMatch = params.ETag
89+
90+
delete params.ETag
91+
92+
params.Id = distributionId
93+
94+
params.DistributionConfig.Enabled = false
95+
96+
const res = await cf.updateDistribution(params).promise()
97+
98+
return {
99+
id: res.Distribution.Id,
100+
arn: res.Distribution.ARN,
101+
url: `https://${res.Distribution.DomainName}`
102+
}
103+
}
104+
105+
const deleteCloudFrontDistribution = async (cf, distributionId) => {
106+
try {
107+
const res = await cf.getDistributionConfig({ Id: distributionId }).promise()
108+
109+
const params = { Id: distributionId, IfMatch: res.ETag }
110+
await cf.deleteDistribution(params).promise()
111+
} catch (e) {
112+
if (e.code === 'DistributionNotDisabled') {
113+
await disableCloudFrontDistribution(cf, distributionId)
114+
} else {
115+
throw e
116+
}
117+
}
118+
}
119+
120+
module.exports = {
121+
createCloudFrontDistribution,
122+
updateCloudFrontDistribution,
123+
deleteCloudFrontDistribution
124+
}

lib/parseInputOrigins.js

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
const getOriginConfig = require('./getOriginConfig')
2+
const getCacheBehavior = require('./getCacheBehavior')
3+
4+
module.exports = (origins) => {
5+
const distributionOrigins = {
6+
Quantity: 0,
7+
Items: []
8+
}
9+
let distributionCacheBehaviors
10+
11+
for (const origin of origins) {
12+
const originConfig = getOriginConfig(origin)
13+
14+
distributionOrigins.Quantity = distributionOrigins.Quantity + 1
15+
distributionOrigins.Items.push(originConfig)
16+
17+
if (typeof origin === 'object') {
18+
// add any cache behaviors
19+
for (const pathPattern in origin.pathPatterns) {
20+
const cacheBehavior = getCacheBehavior(
21+
pathPattern,
22+
origin.pathPatterns[pathPattern],
23+
originConfig.Id
24+
)
25+
26+
distributionCacheBehaviors = {
27+
Quantity: 0,
28+
Items: []
29+
}
30+
distributionCacheBehaviors.Quantity = distributionCacheBehaviors.Quantity + 1
31+
distributionCacheBehaviors.Items.push(cacheBehavior)
32+
}
33+
}
34+
}
35+
36+
return {
37+
Origins: distributionOrigins,
38+
CacheBehaviors: distributionCacheBehaviors
39+
}
40+
}

serverless.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ const {
55
createCloudFrontDistribution,
66
updateCloudFrontDistribution,
77
deleteCloudFrontDistribution
8-
} = require('./utils')
8+
} = require('./lib')
99

1010
/*
1111
* Website

0 commit comments

Comments
 (0)