import { Construct } from 'constructs'; import kebabCase from 'lodash.kebabcase'; import { COMPANY_NAME } from '../../../src/constants'; import { Alb, LbListener, LbTargetGroup, LbListenerCertificate, } from '../generated/providers/aws/elb'; import { Route53Record, Route53Zone } from '../generated/providers/aws/route53'; import { SecurityGroup } from '../generated/providers/aws/vpc'; import { getId } from '../utils'; import Certificate from './Certificate'; import Network from './Network'; export type LoadBalancerProps = { network: Network; }; export default class LoadBalancer extends Construct { readonly securityGroup: SecurityGroup; readonly legacyDnsRecord: Route53Record; readonly dnsRecord: Route53Record; readonly legacyDomainName: string; readonly domainName: string; readonly targetGroup: LbTargetGroup; constructor(scope: Construct, id: string, props: LoadBalancerProps) { super(scope, id); const domainSuffix = process.env.NODE_ENV === 'production' ? '' : `-${process.env.NODE_ENV}`; // @NOTE: this is the subdomain that we use. If you would instead like to // use a root domain, change this. const legacyZoneDomainName = `${COMPANY_NAME}${domainSuffix}.createinc.co`; const zoneDomainName = process.env.NODE_ENV === 'production' ? 'spoken.io' : 'spoken-staging.com'; this.legacyDomainName = `api.${legacyZoneDomainName}`; this.domainName = `api.${zoneDomainName}`; this.securityGroup = new SecurityGroup(this, 'sg', { namePrefix: kebabCase(getId('api')), ingress: [ { fromPort: 80, toPort: 80, protocol: 'tcp', cidrBlocks: ['0.0.0.0/0'], // Allowing traffic in from all sources }, { fromPort: 443, toPort: 443, protocol: 'tcp', cidrBlocks: ['0.0.0.0/0'], // Allowing traffic in from all sources }, ], egress: [ { fromPort: 0, //Allowing any incoming port toPort: 0, // Allowing any outgoing port protocol: '-1', // Allowing any outgoing protocol cidrBlocks: ['0.0.0.0/0'], // Allowing traffic out to all IP addresses }, ], lifecycle: { createBeforeDestroy: true, }, }); const loadBalancer = new Alb(this, 'alb', { name: kebabCase(getId('api')), loadBalancerType: 'application', subnets: props.network.subnetIds, securityGroups: [this.securityGroup.id], }); this.targetGroup = new LbTargetGroup(this, 'tg', { namePrefix: kebabCase(getId('api')).slice(0, 6), port: 80, protocol: 'HTTP', targetType: 'ip', vpcId: props.network.id, healthCheck: { matcher: '200,301,302', path: '/', }, }); // listen to load balancer and send to target new LbListener(this, 'http_listener', { loadBalancerArn: loadBalancer.arn, port: 80, protocol: 'HTTP', defaultAction: [ { type: 'forward', targetGroupArn: this.targetGroup.arn, }, ], }); const dnsId = getId('dns'); const dnsZoneId = getId(dnsId, 'zone'); const legacyDnsZone = new Route53Zone(this, dnsZoneId, { name: legacyZoneDomainName, forceDestroy: true, }); const dnsZone = new Route53Zone(this, getId(dnsZoneId, '2'), { name: zoneDomainName, forceDestroy: true, }); const dnsRecordId = getId(dnsId, 'record'); this.dnsRecord = new Route53Record(this, getId(dnsRecordId, '2'), { zoneId: dnsZone.id, name: this.domainName, type: 'A', alias: [ { name: loadBalancer.dnsName, zoneId: loadBalancer.zoneId, evaluateTargetHealth: false, }, ], }); this.legacyDnsRecord = new Route53Record(this, dnsRecordId, { zoneId: legacyDnsZone.id, name: this.legacyDomainName, type: 'A', alias: [ { name: loadBalancer.dnsName, zoneId: loadBalancer.zoneId, evaluateTargetHealth: false, }, ], }); const legacyCertificate = new Certificate(this, getId('cert'), { domainName: this.legacyDomainName, zoneId: legacyDnsZone.id, }); const certificate = new Certificate(this, getId('cert', '2'), { domainName: this.domainName, zoneId: dnsZone.id, }); // listen to ssl load balancer and send to target const listener = new LbListener(this, 'https_listener', { loadBalancerArn: loadBalancer.arn, port: 443, protocol: 'HTTPS', sslPolicy: 'ELBSecurityPolicy-2016-08', certificateArn: legacyCertificate.arn, defaultAction: [ { type: 'forward', targetGroupArn: this.targetGroup.arn, }, ], }); new LbListenerCertificate(this, 'https_listener_2', { listenerArn: listener.arn, certificateArn: certificate.arn, }); } }