@@ -2,29 +2,42 @@ const path = require('path');
2
2
const _ = require ( 'lodash' ) ;
3
3
const yaml = require ( 'js-yaml' ) ;
4
4
const fs = require ( 'fs' ) ;
5
+ const Mustache = require ( 'mustache' ) ;
5
6
6
7
class ServerlessAWSFunctionURLCustomDomainPlugin {
7
8
constructor ( serverless , options ) {
8
9
this . serverless = serverless ;
9
10
this . options = options ;
10
11
11
12
this . hooks = {
12
- 'before:package:createDeploymentArtifacts ' : this . createDeploymentArtifacts . bind ( this ) ,
13
+ 'before:package:finalize ' : this . createDeploymentArtifacts . bind ( this ) ,
13
14
'aws:info:displayStackOutputs' : this . printSummary . bind ( this ) ,
14
15
} ;
15
16
}
16
17
17
18
createDeploymentArtifacts ( ) {
18
19
const baseResources = this . serverless . service . provider . compiledCloudFormationTemplate ;
19
20
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 ) ;
25
37
26
- this . prepareResources ( resources ) ;
27
38
const combinedResouces = _ . merge ( baseResources , resources ) ;
39
+ console . log ( JSON . stringify ( combinedResouces ) ) ;
40
+
28
41
return combinedResouces ;
29
42
}
30
43
@@ -37,7 +50,7 @@ class ServerlessAWSFunctionURLCustomDomainPlugin {
37
50
}
38
51
39
52
const { outputs } = awsInfo . gatheredData ;
40
- const apiDistributionDomain = _ . find ( outputs , ( output ) => output . OutputKey === 'ApiCloudFrontDistributionDomain ' ) ;
53
+ const apiDistributionDomain = _ . find ( outputs , ( output ) => output . OutputKey === 'CloudFrontDistributionDomain ' ) ;
41
54
42
55
if ( ! apiDistributionDomain || ! apiDistributionDomain . OutputValue ) {
43
56
return ;
@@ -46,39 +59,66 @@ class ServerlessAWSFunctionURLCustomDomainPlugin {
46
59
const cnameDomain = this . getConfig ( 'apiDomain' , '-' ) ;
47
60
48
61
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 } )` ) ;
50
63
}
51
64
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 ) {
62
66
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
64
73
}
65
74
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 ;
69
85
}
70
86
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 ) ;
81
120
}
121
+ return { 'Resources' : resources }
82
122
}
83
123
84
124
getConfig ( field , defaultValue ) {
0 commit comments