import * as cdk from "aws-cdk-lib"; import * as lambda from "aws-cdk-lib/aws-lambda"; import * as apigwv2 from "aws-cdk-lib/aws-apigatewayv2"; import * as integrations from "aws-cdk-lib/aws-apigatewayv2-integrations"; import * as ssm from "aws-cdk-lib/aws-ssm"; import * as route53 from "aws-cdk-lib/aws-route53"; import * as route53Targets from "aws-cdk-lib/aws-route53-targets"; import * as acm from "aws-cdk-lib/aws-certificatemanager"; import * as logs from "aws-cdk-lib/aws-logs"; import { Construct } from "constructs"; import * as path from "path"; export class DoorUnlockStack extends cdk.Stack { constructor(scope: Construct, id: string, props?: cdk.StackProps) { super(scope, id, props); const elementsApiKeyParam = ssm.StringParameter.fromSecureStringParameterAttributes( this, "ElementsApiKey", { parameterName: "/seahaven/door-unlock/elements-api-key" } ); const authTokenParam = ssm.StringParameter.fromSecureStringParameterAttributes( this, "AuthToken", { parameterName: "/seahaven/door-unlock/auth-token" } ); const doorIdParam = ssm.StringParameter.fromStringParameterName( this, "DoorId", "/seahaven/door-unlock/door-id" ); const unlockHandler = new lambda.Function(this, "UnlockHandler", { runtime: lambda.Runtime.NODEJS_20_X, handler: "unlock-handler.handler", code: lambda.Code.fromAsset(path.join(__dirname, "../lambda"), { bundling: { image: lambda.Runtime.NODEJS_20_X.bundlingImage, local: { tryBundle(outputDir: string) { const { execSync } = require("child_process"); execSync( `esbuild ${path.join(__dirname, "../lambda/unlock-handler.ts")} --bundle --platform=node --target=node20 --outfile=${path.join(outputDir, "unlock-handler.js")} --external:@aws-sdk/*` ); return true; }, }, }, }), environment: { ELEMENTS_API_KEY_PARAM: "/seahaven/door-unlock/elements-api-key", AUTH_TOKEN_PARAM: "/seahaven/door-unlock/auth-token", DOOR_ID_PARAM: "/seahaven/door-unlock/door-id", }, timeout: cdk.Duration.seconds(10), memorySize: 128, logRetention: logs.RetentionDays.THREE_MONTHS, }); elementsApiKeyParam.grantRead(unlockHandler); authTokenParam.grantRead(unlockHandler); doorIdParam.grantRead(unlockHandler); const httpApi = new apigwv2.HttpApi(this, "DoorUnlockApi", { apiName: "door-unlock-api", }); const defaultStage = httpApi.defaultStage!.node.defaultChild as apigwv2.CfnStage; defaultStage.addPropertyOverride("DefaultRouteSettings", { ThrottlingBurstLimit: 5, ThrottlingRateLimit: 2, }); httpApi.addRoutes({ path: "/unlock", methods: [apigwv2.HttpMethod.GET], integration: new integrations.HttpLambdaIntegration( "UnlockIntegration", unlockHandler ), }); const hostedZone = route53.HostedZone.fromHostedZoneAttributes( this, "SeaHavenZone", { hostedZoneId: "Z06652411XKH89KTZD3XA", zoneName: "seahaven.com", } ); const certificate = acm.Certificate.fromCertificateArn( this, "WildcardCert", "arn:aws:acm:us-east-1:328440206208:certificate/a66c0994-90d4-410a-a1d9-5595c2a3fae3" ); const domainName = new apigwv2.DomainName(this, "DoorUnlockDomain", { domainName: "doorunlock.seahaven.com", certificate, }); new apigwv2.ApiMapping(this, "DoorUnlockMapping", { api: httpApi, domainName, }); new route53.ARecord(this, "DoorUnlockARecord", { zone: hostedZone, recordName: "doorunlock", target: route53.RecordTarget.fromAlias( new route53Targets.ApiGatewayv2DomainProperties( domainName.regionalDomainName, domainName.regionalHostedZoneId ) ), }); new cdk.CfnOutput(this, "ApiUrl", { value: `https://doorunlock.seahaven.com/unlock`, }); } }