Skip to content

Commit d4e20a9

Browse files
authored
Merge pull request #1 from wangsha/pullrequests/refactoring-setup
allow multiple custom domains
2 parents ceeaaf8 + 486da52 commit d4e20a9

File tree

7 files changed

+9963
-186
lines changed

7 files changed

+9963
-186
lines changed

.github/workflows/main.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ jobs:
2121
with:
2222
node-version: 12
2323
- run: npm install
24+
- run: npm run test-unit
2425
- name: 'Automated Version Bump'
2526
uses: 'phips28/gh-action-bump-version@master'
2627
env:

README.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,12 @@ plugins:
2424

2525
custom:
2626
urlDomain:
27-
apiDomain: ${env:SUBDOMAIN}.yourdomain.com # change by your custom domain
27+
domains:
28+
- ${env:SUBDOMAIN}.yourdomain.com # custom domain 1
29+
- ${env:SUBDOMAIN}-alt.yourdomain.com # custom domain 2
2830
hostedZoneName: yourdomain.com. # your domain Route 53 hosted zone name
2931
certificateArn: 'arn:aws:acm:us-east-1:xxxxx:certificate/xxxxx' # need to be located at NVirgina
30-
32+
route53: false # disable route 53 integration
3133
functions:
3234
api:
3335
handler: wsgi_handler.handler

index.js

Lines changed: 73 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -2,29 +2,42 @@ const path = require('path');
22
const _ = require('lodash');
33
const yaml = require('js-yaml');
44
const fs = require('fs');
5+
const Mustache = require('mustache');
56

67
class ServerlessAWSFunctionURLCustomDomainPlugin {
78
constructor(serverless, options) {
89
this.serverless = serverless;
910
this.options = options;
1011

1112
this.hooks = {
12-
'before:package:createDeploymentArtifacts': this.createDeploymentArtifacts.bind(this),
13+
'before:package:finalize': this.createDeploymentArtifacts.bind(this),
1314
'aws:info:displayStackOutputs': this.printSummary.bind(this),
1415
};
1516
}
1617

1718
createDeploymentArtifacts() {
1819
const baseResources = this.serverless.service.provider.compiledCloudFormationTemplate;
1920

20-
const filename = path.resolve(__dirname, 'resources.yml');
21-
const content = fs.readFileSync(filename, 'utf-8');
22-
const resources = yaml.load(content, {
23-
filename,
24-
});
21+
var functionURLResourceName = null;
22+
for(var key in baseResources.Resources) {
23+
if (baseResources.Resources[key]['Type'] === "AWS::Lambda::Url") {
24+
functionURLResourceName = key
25+
}
26+
}
27+
28+
if (functionURLResourceName === null) {
29+
this.serverless.cli.consoleLog("no function url defined");
30+
return baseResources;
31+
}
32+
33+
const config = this.serverless.service.custom.urlDomain;
34+
config['lambdaFunctionUrl'] = functionURLResourceName
35+
36+
const resources = this.prepareResources(config);
2537

26-
this.prepareResources(resources);
2738
const combinedResouces = _.merge(baseResources, resources);
39+
console.log(JSON.stringify(combinedResouces));
40+
2841
return combinedResouces;
2942
}
3043

@@ -37,7 +50,7 @@ class ServerlessAWSFunctionURLCustomDomainPlugin {
3750
}
3851

3952
const { outputs } = awsInfo.gatheredData;
40-
const apiDistributionDomain = _.find(outputs, (output) => output.OutputKey === 'ApiCloudFrontDistributionDomain');
53+
const apiDistributionDomain = _.find(outputs, (output) => output.OutputKey === 'CloudFrontDistributionDomain');
4154

4255
if (!apiDistributionDomain || !apiDistributionDomain.OutputValue) {
4356
return;
@@ -46,39 +59,66 @@ class ServerlessAWSFunctionURLCustomDomainPlugin {
4659
const cnameDomain = this.getConfig('apiDomain', '-');
4760

4861
this.serverless.cli.consoleLog('CloudFront domain name');
49-
this.serverless.cli.consoleLog(` ${apiDistributionDomain.OutputValue} (CNAME: ${cnameDomain})`);
62+
this.serverless.cli.consoleLog(`${apiDistributionDomain.OutputValue} (CNAME: ${cnameDomain})`);
5063
}
5164

52-
prepareResources(resources) {
53-
const distributionConfig = resources.Resources.ApiCloudFrontDistribution.Properties.DistributionConfig;
54-
const apiRecordsConfig = resources.Resources.ApiRecordSetGroup.Properties;
55-
this.prepareDomain(distributionConfig, apiRecordsConfig);
56-
this.prepareAcmCertificateArn(distributionConfig);
57-
this.prepareHostedZoneName(apiRecordsConfig);
58-
}
59-
60-
prepareHostedZoneName(apiRecordsConfig) {
61-
const name = this.getConfig('hostedZoneName', null);
65+
prepareResources(config) {
6266

63-
apiRecordsConfig.HostedZoneName = name;
67+
const route53 = this.getConfig('route53', true);
68+
var resources = this.getCloudfrontResources(config);
69+
if (route53) {
70+
resources = _.merge(resources, this.getRoute53Resources(config));
71+
}
72+
return resources
6473
}
6574

66-
prepareAcmCertificateArn(distributionConfig) {
67-
const arn = this.getConfig('certificateArn', null);
68-
distributionConfig.ViewerCertificate.AcmCertificateArn = arn;
75+
getCloudfrontResources(config) {
76+
const filename = path.resolve(__dirname, 'resources.yml');
77+
const content = fs.readFileSync(filename, 'utf-8');
78+
const resources = yaml.load(content, {
79+
filename,
80+
});
81+
var output = Mustache.render(JSON.stringify(resources), config);
82+
output = JSON.parse(output)
83+
output['Resources']['CloudFrontDistribution']['Properties']['DistributionConfig']['Aliases'] = config['domains']
84+
return output;
6985
}
7086

71-
prepareDomain(distributionConfig, apiRecordsConfig) {
72-
const domain = this.getConfig('apiDomain', null);
73-
74-
if (domain !== null) {
75-
const domains = Array.isArray(domain) ? domain : [domain];
76-
distributionConfig.Aliases = domains;
77-
apiRecordsConfig.RecordSets[0].Name = domains[0];
78-
distributionConfig.Comment = `Api distribution for ${domains[0]}`;
79-
} else {
80-
delete distributionConfig.Aliases;
87+
getRoute53Resources(config) {
88+
const domains = this.getConfig('domains', null);
89+
const hostedZoneName = this.getConfig('hostedZoneName', null);
90+
91+
const template = JSON.stringify({
92+
"Type": "AWS::Route53::RecordSetGroup",
93+
"DeletionPolicy": "Delete",
94+
"DependsOn": [
95+
"CloudFrontDistribution"
96+
],
97+
"Properties": {
98+
"HostedZoneName": hostedZoneName,
99+
"RecordSets": [
100+
{
101+
"Name": "{{{ domain }}}",
102+
"Type": "A",
103+
"AliasTarget": {
104+
"HostedZoneId": "Z2FDTNDATAQYW2",
105+
"DNSName": {
106+
"Fn::GetAtt": [
107+
"CloudFrontDistribution",
108+
"DomainName"
109+
]
110+
}
111+
}
112+
}
113+
]
114+
}
115+
})
116+
var resources = {}
117+
for (var idx in domains) {
118+
var output = Mustache.render(template, {'domain': domains[idx]});
119+
resources[`Route53Record${idx}`] = JSON.parse(output);
81120
}
121+
return {'Resources': resources}
82122
}
83123

84124
getConfig(field, defaultValue) {

index.test.js

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
var assert = require('assert');
2+
describe('Array', function () {
3+
describe('#getCloudfrontResources()', function () {
4+
it('domains should be a list', function () {
5+
const ServerlessAWSFunctionURLCustomDomainPlugin = require('./index')
6+
config = {
7+
domains: ['sudomain1.yourdomain.com', 'subdomain2.yourdomain.com'],
8+
hostedZoneName: 'yourdomain.com.',
9+
certificateArn: 'arn:aws:acm:us-east-1:xxxxx:certificate/xxxxxxx',
10+
route53: true
11+
}
12+
var plugin = new ServerlessAWSFunctionURLCustomDomainPlugin();
13+
var resources = plugin.getCloudfrontResources(config);
14+
console.log(resources['Resources']['CloudFrontDistribution']['Properties']['DistributionConfig']['Comment'])
15+
assert(resources['Resources']['CloudFrontDistribution']['Properties']['DistributionConfig']['Aliases'] == config['domains']);
16+
17+
});
18+
});
19+
});

0 commit comments

Comments
 (0)