mirror of
https://github.com/Sea-Haven-Industries/seahaven-door-unlock-api.git
synced 2026-09-30 07:03:12 +00:00
Add a new Lambda handler that toggles Elements lockdown profiles (Bohemia and Ronkonkoma) via the Elements API, with status verification before and after each toggle. Returns Yealink XML to control linekey LEDs (green=inactive, red=locked down). Also brings both Lambda handlers into compliance with system standards: Node 22.x runtime, arm64 architecture, 60-day log retention, and kebab-case function names.
179 lines
5.8 KiB
TypeScript
179 lines
5.8 KiB
TypeScript
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", {
|
|
functionName: "door-unlock-api-unlock",
|
|
runtime: lambda.Runtime.NODEJS_22_X,
|
|
architecture: lambda.Architecture.ARM_64,
|
|
handler: "unlock-handler.handler",
|
|
code: lambda.Code.fromAsset(path.join(__dirname, "../lambda/unlock"), {
|
|
bundling: {
|
|
image: lambda.Runtime.NODEJS_22_X.bundlingImage,
|
|
local: {
|
|
tryBundle(outputDir: string) {
|
|
const { execSync } = require("child_process");
|
|
execSync(
|
|
`esbuild ${path.join(__dirname, "../lambda/unlock/unlock-handler.ts")} --bundle --platform=node --target=node22 --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.TWO_MONTHS,
|
|
});
|
|
|
|
const lockdownHandler = new lambda.Function(this, "LockdownHandler", {
|
|
functionName: "door-unlock-api-lockdown",
|
|
runtime: lambda.Runtime.NODEJS_22_X,
|
|
architecture: lambda.Architecture.ARM_64,
|
|
handler: "lockdown-handler.handler",
|
|
code: lambda.Code.fromAsset(path.join(__dirname, "../lambda/lockdown"), {
|
|
bundling: {
|
|
image: lambda.Runtime.NODEJS_22_X.bundlingImage,
|
|
local: {
|
|
tryBundle(outputDir: string) {
|
|
const { execSync } = require("child_process");
|
|
execSync(
|
|
`esbuild ${path.join(__dirname, "../lambda/lockdown/lockdown-handler.ts")} --bundle --platform=node --target=node22 --outfile=${path.join(outputDir, "lockdown-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",
|
|
},
|
|
timeout: cdk.Duration.seconds(15),
|
|
memorySize: 128,
|
|
logRetention: logs.RetentionDays.TWO_MONTHS,
|
|
});
|
|
|
|
elementsApiKeyParam.grantRead(unlockHandler);
|
|
authTokenParam.grantRead(unlockHandler);
|
|
doorIdParam.grantRead(unlockHandler);
|
|
|
|
elementsApiKeyParam.grantRead(lockdownHandler);
|
|
authTokenParam.grantRead(lockdownHandler);
|
|
|
|
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 lockdownIntegration = new integrations.HttpLambdaIntegration(
|
|
"LockdownIntegration",
|
|
lockdownHandler
|
|
);
|
|
|
|
httpApi.addRoutes({
|
|
path: "/lockdown",
|
|
methods: [apigwv2.HttpMethod.GET],
|
|
integration: lockdownIntegration,
|
|
});
|
|
|
|
httpApi.addRoutes({
|
|
path: "/lockdown/status",
|
|
methods: [apigwv2.HttpMethod.GET],
|
|
integration: lockdownIntegration,
|
|
});
|
|
|
|
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`,
|
|
});
|
|
|
|
new cdk.CfnOutput(this, "LockdownApiUrl", {
|
|
value: `https://doorunlock.seahaven.com/lockdown`,
|
|
});
|
|
}
|
|
}
|