@@ -12,6 +12,7 @@ const bucketUtils = require('./lib/bucketUtils');
12
12
const uploadDirectory = require ( './lib/upload' ) ;
13
13
const validateClient = require ( './lib/validate' ) ;
14
14
const invalidateCloudfrontDistribution = require ( './lib/cloudFront' ) ;
15
+ const { groupDomainsByHostedZone} = require ( './lib/route53' ) ;
15
16
16
17
class ServerlessFullstackPlugin {
17
18
constructor ( serverless , cliOptions ) {
@@ -256,8 +257,9 @@ class ServerlessFullstackPlugin {
256
257
filename : filename
257
258
} ) ;
258
259
259
- this . prepareResources ( resources ) ;
260
- return _ . merge ( baseResources , resources ) ;
260
+ return this . prepareResources ( resources ) . then ( ( ) => {
261
+ return _ . merge ( baseResources , resources ) ;
262
+ } ) ;
261
263
}
262
264
263
265
checkForApiGataway ( ) {
@@ -335,9 +337,11 @@ class ServerlessFullstackPlugin {
335
337
this . serverless . cli . consoleLog ( ` ${ apiDistributionDomain . OutputValue } (CNAME: ${ cnameDomain } )` ) ;
336
338
}
337
339
338
- prepareResources ( resources ) {
340
+ async prepareResources ( resources ) {
339
341
const distributionConfig = resources . Resources . ApiDistribution . Properties . DistributionConfig ;
340
342
343
+ await this . prepareRoute53 ( resources . Resources ) ;
344
+
341
345
this . prepareLogging ( distributionConfig ) ;
342
346
this . prepareDomain ( distributionConfig ) ;
343
347
this . preparePriceClass ( distributionConfig ) ;
@@ -353,6 +357,56 @@ class ServerlessFullstackPlugin {
353
357
354
358
}
355
359
360
+ async prepareRoute53 ( resources ) {
361
+ if ( this . options . domain ) {
362
+ const certificate = this . getConfig ( "certificate" , null ) ;
363
+ const distributionCertificate = resources . ApiDistribution . Properties . DistributionConfig . ViewerCertificate ;
364
+
365
+ if ( this . getConfig ( "route53" , false ) === true ) {
366
+ const filename = path . resolve ( __dirname , 'lib/resources/templates.yml' ) ;
367
+ const content = fs . readFileSync ( filename , 'utf-8' ) ;
368
+ const templates = yaml . safeLoad ( content , { filename} ) ;
369
+
370
+ const domains = Array . isArray ( this . options . domain ) ? this . options . domain : [ this . options . domain ] ;
371
+ const domainsByHostedZones = await groupDomainsByHostedZone ( this . serverless , domains ) ;
372
+ const domainsWithoutHostedZone = domainsByHostedZones
373
+ . filter ( ( hostedZone ) => ! hostedZone . Id )
374
+ . reduce ( ( acc , hostedZone ) => [ ...acc , ...hostedZone . domains ] , [ ] ) ;
375
+ const filteredDomainsByHostedZones = domainsByHostedZones
376
+ . filter ( hostedZone => ! ! hostedZone . Id && hostedZone . domains . length ) ;
377
+
378
+ if ( domainsWithoutHostedZone ?. length > 0 )
379
+ this . serverless . cli . log ( `No hosted zones found for ${ domainsWithoutHostedZone } , records pointing to`
380
+ + ` the cloudfront domain will have to be added manually.` , "Route53" , { color : "orange" , underline : true } ) ;
381
+
382
+ const aliasTemplate = templates . Route53AliasTemplate ;
383
+ const recordSetTemplate = aliasTemplate . Properties . RecordSets . pop ( ) ;
384
+ recordSetTemplate . AliasTarget . DNSName = { "Fn::GetAtt" : [ "ApiDistribution" , "DomainName" ] } ;
385
+
386
+ for ( const hostedZone of filteredDomainsByHostedZones ) {
387
+ const recordSets = hostedZone . domains . map ( domain => ( { ...recordSetTemplate , Name : domain } ) ) ;
388
+ const alias = { ...aliasTemplate , Properties : { ...aliasTemplate . Properties , RecordSets : recordSets , HostedZoneId : hostedZone . Id } } ;
389
+ resources [ "Route53AliasHZ" + hostedZone . Id ] = alias ;
390
+ }
391
+
392
+ // only create and override if not specified
393
+ if ( certificate === null ) {
394
+ const certTemplate = templates . CertTemplate ;
395
+ certTemplate . Properties . DomainName = domains [ 0 ] ;
396
+ if ( domains . length > 1 ) certTemplate . Properties . SubjectAlternativeNames = domains . slice ( 1 ) ;
397
+
398
+ const route53domainValidations = filteredDomainsByHostedZones . flatMap ( hz => hz . domains . map ( DomainName => ( { DomainName, HostedZoneId : hz . Id } ) ) ) ;
399
+ const manualValidations = domainsWithoutHostedZone . map ( DomainName => ( { DomainName, ValidationDomain : DomainName } ) ) ;
400
+ certTemplate . Properties . DomainValidationOptions = [ ...route53domainValidations , ...manualValidations ] ;
401
+
402
+ const certResourceName = "ApiDistributionCertificate" ;
403
+ resources [ certResourceName ] = certTemplate ;
404
+ distributionCertificate . AcmCertificateArn = { Ref : certResourceName } ;
405
+ }
406
+ }
407
+ }
408
+ }
409
+
356
410
prepareLogging ( distributionConfig ) {
357
411
const loggingBucket = this . getConfig ( 'logging.bucket' , null ) ;
358
412
@@ -428,7 +482,7 @@ class ServerlessFullstackPlugin {
428
482
if ( certificate !== null ) {
429
483
this . serverless . cli . log ( `Configuring SSL certificate...` ) ;
430
484
distributionConfig . ViewerCertificate . AcmCertificateArn = certificate ;
431
- } else {
485
+ } else if ( ! distributionConfig . ViewerCertificate . AcmCertificateArn ) {
432
486
delete distributionConfig . ViewerCertificate ;
433
487
}
434
488
}
0 commit comments