mirror of
https://github.com/Sea-Haven-Industries/seahaven-door-unlock-api.git
synced 2026-09-30 02:33:11 +00:00
Add door unlock API — Yealink DSS key triggers LenelS2 Elements TemporaryUnlock via API Gateway + Lambda
This commit is contained in:
commit
12eb0ff9be
9 changed files with 1520 additions and 0 deletions
5
.gitignore
vendored
Normal file
5
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
node_modules/
|
||||
cdk.out/
|
||||
*.js
|
||||
*.d.ts
|
||||
*.js.map
|
||||
9
bin/app.ts
Normal file
9
bin/app.ts
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
#!/usr/bin/env node
|
||||
import "source-map-support/register";
|
||||
import * as cdk from "aws-cdk-lib";
|
||||
import { DoorUnlockStack } from "../lib/door-unlock-stack";
|
||||
|
||||
const app = new cdk.App();
|
||||
new DoorUnlockStack(app, "SeaHavenDoorUnlockStack", {
|
||||
env: { account: "328440206208", region: "us-east-1" },
|
||||
});
|
||||
22
cdk.json
Normal file
22
cdk.json
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
{
|
||||
"app": "npx ts-node bin/app.ts",
|
||||
"watch": {
|
||||
"include": ["**"],
|
||||
"exclude": [
|
||||
"README.md",
|
||||
"cdk*.json",
|
||||
"**/*.d.ts",
|
||||
"**/*.js",
|
||||
"tsconfig.json",
|
||||
"package*.json",
|
||||
"node_modules",
|
||||
"test",
|
||||
"cdk.out"
|
||||
]
|
||||
},
|
||||
"context": {
|
||||
"@aws-cdk/aws-lambda:recognizeLayerVersion": true,
|
||||
"@aws-cdk/core:checkSecretUsage": true,
|
||||
"@aws-cdk/core:target-partitions": ["aws"]
|
||||
}
|
||||
}
|
||||
237
door-unlock-plan.md
Normal file
237
door-unlock-plan.md
Normal file
|
|
@ -0,0 +1,237 @@
|
|||
# Sea Haven — Desk Phone Door Unlock via LenelS2 Elements
|
||||
|
||||
## Project Plan for Claude Code
|
||||
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
Build a lightweight AWS middleware that allows a Yealink T58A desk phone to unlock the front door controlled by LenelS2 Elements. The user presses a dedicated button (DSS key) on the Yealink, which fires an HTTPS GET request to an API Gateway endpoint. A Lambda function receives the request, authenticates to the LenelS2 Elements API, and sends a door unlock command.
|
||||
|
||||
**Flow:** `Yealink T58A (DSS key press) → HTTPS GET → API Gateway → Lambda → Elements API → Door Unlocks`
|
||||
|
||||
---
|
||||
|
||||
## Architecture
|
||||
|
||||
```
|
||||
┌──────────────┐ HTTPS GET ┌──────────────────┐ HTTPS POST ┌─────────────────────┐
|
||||
│ Yealink T58A │ ───────────────► │ API Gateway │ ──────────────► │ LenelS2 Elements │
|
||||
│ DSS Key: │ ?token=xxxxx │ + Lambda │ api-key header │ api.elementssecure │
|
||||
│ "Unlock Door"│ │ (us-east-1) │ │ .com │
|
||||
└──────────────┘ └──────────────────┘ └─────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌──────────────┐
|
||||
│ Secrets Mgr │
|
||||
│ or SSM Param │
|
||||
│ (API keys) │
|
||||
└──────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Components to Build
|
||||
|
||||
### 1. AWS CDK Stack (TypeScript — matches existing LedgerFlow stack)
|
||||
|
||||
**Stack name:** `SeaHavenDoorUnlockStack`
|
||||
**Region:** `us-east-1` (matches existing infra)
|
||||
|
||||
#### Resources:
|
||||
- **API Gateway (HTTP API)** — a single GET endpoint at `/unlock`
|
||||
- **Lambda function (Node.js 20.x)** — handles the request
|
||||
- **SSM Parameter Store or Secrets Manager** — stores the Elements API key and the shared bearer token for Yealink auth
|
||||
- **Optional: Route 53 custom domain** — e.g., `doorunlock.seahaven.com` using the existing wildcard ACM cert and hosted zone
|
||||
|
||||
#### API Gateway Configuration:
|
||||
- HTTP API (not REST API — simpler, cheaper)
|
||||
- Single route: `GET /unlock`
|
||||
- No CORS needed (Yealink sends a plain GET, not a browser request)
|
||||
- Throttling: 5 requests/second burst, 2 requests/second sustained (matches Elements rate limit of 2 req/sec)
|
||||
|
||||
### 2. Lambda Function Logic
|
||||
|
||||
```
|
||||
// Pseudocode for the Lambda handler
|
||||
|
||||
1. Extract query parameter `token` from the request
|
||||
2. Validate `token` against stored shared secret (from SSM/Secrets Manager)
|
||||
- If invalid → return 403
|
||||
3. Retrieve Elements API key from SSM/Secrets Manager
|
||||
4. Identify which door/reader to unlock (hardcoded or passed as param)
|
||||
5. Call the Elements API to execute the door unlock command
|
||||
- Endpoint: https://api.elementssecure.com/...
|
||||
- Header: api-key: <elements-api-key>
|
||||
- Method: POST (or whatever the Elements API docs specify for door commands)
|
||||
6. Return 200 with a simple success message (or error details)
|
||||
```
|
||||
|
||||
#### Key Design Decisions:
|
||||
- **Auth from the Yealink:** Use a simple shared secret as a query parameter (`?token=<long-random-string>`). The Yealink DSS URL key supports specifying a full URL including query params. HTTPS ensures the token is encrypted in transit.
|
||||
- **Idempotency:** The door unlock is inherently idempotent (unlocking an already-unlocking door is harmless), so no special handling needed.
|
||||
- **Logging:** Log every unlock attempt to CloudWatch with timestamp, source IP, and success/failure for audit trail.
|
||||
- **Rate limiting:** API Gateway throttling prevents abuse. Optionally add a cooldown (e.g., ignore requests within 5 seconds of the last successful unlock).
|
||||
|
||||
### 3. Yealink T58A Configuration
|
||||
|
||||
This is manual config done in the Yealink web UI — not code. Documenting here for completeness.
|
||||
|
||||
1. Log into the Yealink web interface at `http://<phone-ip>`
|
||||
2. Navigate to **DSSKey → Line Key** (or **Programmable Key**)
|
||||
3. Configure an available key:
|
||||
- **Type:** URL
|
||||
- **Label:** Unlock Door
|
||||
- **Value:** `https://doorunlock.seahaven.com/unlock?token=<shared-secret>`
|
||||
4. Click **Confirm** to save
|
||||
|
||||
**Security note:** Under **Features → General Information**, the Yealink has an "Action URI" trusted IP list. This is for *incoming* action URIs (remote control of the phone). It's not relevant here since the phone is the one *initiating* the HTTP GET. No additional Yealink security config needed for outbound requests.
|
||||
|
||||
---
|
||||
|
||||
## Prerequisites — Adam Needs to Do Before Claude Code
|
||||
|
||||
### From LenelS2 Elements:
|
||||
|
||||
1. **Enable the Elements API** — Go to the Elements Marketplace and request/enable API access if not already done
|
||||
2. **Generate an API key** — From the Marketplace, create an API key. This key will be passed as an `api-key` header in requests to `api.elementssecure.com`
|
||||
3. **Identify the door/reader ID** — In the Elements portal, find the device ID for the front door reader that needs to be unlocked. You'll need this for the API call.
|
||||
4. **Review the API docs** — Go to `https://api.elementssecure.com/docs` and look for:
|
||||
- A door unlock or device command endpoint (likely under a `/doors` or `/devices` or `/commands` path)
|
||||
- The exact HTTP method, URL path, and request body format for a momentary unlock
|
||||
- Note the exact endpoint path and required parameters
|
||||
5. **Test the API manually** — Use Postman or curl to confirm you can unlock the door via the API before building the Lambda. Example:
|
||||
```
|
||||
curl -X POST https://api.elementssecure.com/v1/doors/<door-id>/unlock \
|
||||
-H "api-key: <your-api-key>" \
|
||||
-H "Content-Type: application/json"
|
||||
```
|
||||
(The exact path/method will come from the API docs — this is just an example)
|
||||
|
||||
### From AWS (should mostly be in place already):
|
||||
|
||||
6. **Confirm the seahaven.com Route 53 hosted zone ID** — for custom domain setup
|
||||
7. **Confirm the wildcard ACM certificate ARN** — `*.seahaven.com` cert in us-east-1
|
||||
8. **Generate a shared secret token** — a long random string (32+ characters) that will be embedded in the Yealink URL and validated by the Lambda. Generate with: `openssl rand -hex 32`
|
||||
|
||||
---
|
||||
|
||||
## Information to Provide to Claude Code
|
||||
|
||||
When starting the Claude Code session, provide the following:
|
||||
|
||||
```
|
||||
Project: Sea Haven Door Unlock Middleware
|
||||
Stack: AWS CDK (TypeScript), Lambda (Node.js 20.x)
|
||||
Region: us-east-1
|
||||
|
||||
Build an AWS CDK stack called SeaHavenDoorUnlockStack that creates:
|
||||
|
||||
1. An HTTP API Gateway with a single GET /unlock route
|
||||
2. A Lambda function (Node.js 20.x) that:
|
||||
- Validates a `token` query parameter against a secret stored in SSM Parameter Store
|
||||
- On valid token, calls the LenelS2 Elements API to unlock the front door
|
||||
- Elements API base URL: https://api.elementssecure.com
|
||||
- Elements API auth: pass `api-key` header with the stored API key
|
||||
- Elements API endpoint for door unlock: [FILL IN from API docs]
|
||||
- Door/reader ID: [FILL IN from Elements portal]
|
||||
- Logs all attempts to CloudWatch
|
||||
- Returns 200 on success, 403 on bad token, 502 on Elements API failure
|
||||
3. SSM Parameter Store parameters:
|
||||
- /seahaven/door-unlock/elements-api-key (SecureString)
|
||||
- /seahaven/door-unlock/auth-token (SecureString)
|
||||
- /seahaven/door-unlock/door-id (String)
|
||||
4. Custom domain: doorunlock.seahaven.com
|
||||
- Route 53 hosted zone: [FILL IN hosted zone ID]
|
||||
- ACM certificate: [FILL IN wildcard cert ARN]
|
||||
5. API Gateway throttling: 5 burst, 2 sustained requests/sec
|
||||
|
||||
After deployment, the Yealink phone will call:
|
||||
GET https://doorunlock.seahaven.com/unlock?token=<auth-token-value>
|
||||
|
||||
Additional requirements:
|
||||
- Add a 5-second cooldown between successful unlocks (store last unlock time in Lambda global scope or a simple DynamoDB TTL item)
|
||||
- Include proper error handling and structured JSON logging
|
||||
- No external npm dependencies beyond AWS SDK v3 (bundled with Lambda runtime)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Post-Deployment Steps
|
||||
|
||||
1. **Store the secrets in SSM:**
|
||||
```bash
|
||||
aws ssm put-parameter --name "/seahaven/door-unlock/elements-api-key" \
|
||||
--value "<your-elements-api-key>" --type SecureString --region us-east-1
|
||||
|
||||
aws ssm put-parameter --name "/seahaven/door-unlock/auth-token" \
|
||||
--value "<your-generated-shared-secret>" --type SecureString --region us-east-1
|
||||
|
||||
aws ssm put-parameter --name "/seahaven/door-unlock/door-id" \
|
||||
--value "<your-elements-door-id>" --type String --region us-east-1
|
||||
```
|
||||
|
||||
2. **Test the endpoint:**
|
||||
```bash
|
||||
# Should return 403
|
||||
curl https://doorunlock.seahaven.com/unlock?token=wrong-token
|
||||
|
||||
# Should unlock the door
|
||||
curl https://doorunlock.seahaven.com/unlock?token=<correct-token>
|
||||
```
|
||||
|
||||
3. **Configure the Yealink T58A** (see section above)
|
||||
|
||||
4. **Test end-to-end:** Press the DSS key on the Yealink and verify the door unlocks
|
||||
|
||||
---
|
||||
|
||||
## Security Considerations
|
||||
|
||||
- **HTTPS everywhere** — Yealink → API Gateway is TLS, Lambda → Elements API is TLS
|
||||
- **Shared secret in URL** — acceptable because HTTPS encrypts the full URL including query parameters. The token is never logged by API Gateway (use `$context.requestId` not `$context.path` in access logs). Only the Lambda sees the token for validation.
|
||||
- **Rate limiting** — API Gateway throttle + Lambda cooldown prevent brute force and accidental double-taps
|
||||
- **IP restriction (optional enhancement)** — if the Yealink phones have static IPs on the LAN and the office has a static public IP, you can add an API Gateway resource policy to restrict access to that IP only. This makes the shared token a second factor rather than the only factor.
|
||||
- **Audit trail** — every unlock attempt (success or failure) is logged to CloudWatch with timestamp and source IP
|
||||
- **API key rotation** — the Elements API key and shared token are in SSM Parameter Store, so rotation requires updating the SSM value and (for the shared token) the Yealink DSS key URL
|
||||
|
||||
---
|
||||
|
||||
## Estimated AWS Cost
|
||||
|
||||
- API Gateway HTTP API: $1.00 per million requests (negligible for door unlocks)
|
||||
- Lambda: free tier covers 1M requests/month (you'll use maybe 100)
|
||||
- SSM Parameter Store: free for standard parameters, $0.05/parameter/month for SecureString via advanced
|
||||
- Route 53: already have the hosted zone ($0.50/month existing)
|
||||
- **Total incremental cost: effectively $0/month**
|
||||
|
||||
---
|
||||
|
||||
## Files Claude Code Should Create
|
||||
|
||||
```
|
||||
seahaven-door-unlock/
|
||||
├── bin/
|
||||
│ └── app.ts # CDK app entry point
|
||||
├── lib/
|
||||
│ └── door-unlock-stack.ts # CDK stack definition
|
||||
├── lambda/
|
||||
│ └── unlock-handler.ts # Lambda function code
|
||||
├── cdk.json
|
||||
├── tsconfig.json
|
||||
├── package.json
|
||||
└── README.md
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Open Items (Requires Your Input)
|
||||
|
||||
| Item | What's Needed | Where to Find It |
|
||||
|------|--------------|-----------------|
|
||||
| Elements API door unlock endpoint | Exact path, method, and body for unlocking a door | https://api.elementssecure.com/docs |
|
||||
| Elements door/reader ID | The device ID for your front door | Elements portal → Devices |
|
||||
| Elements API key | Your API key for auth | Elements Marketplace |
|
||||
| Route 53 hosted zone ID | Your seahaven.com zone | AWS Console → Route 53 |
|
||||
| ACM wildcard cert ARN | Your *.seahaven.com cert | AWS Console → ACM (us-east-1) |
|
||||
| Shared secret token | Generate a random token | Run: `openssl rand -hex 32` |
|
||||
90
lambda/unlock-handler.ts
Normal file
90
lambda/unlock-handler.ts
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
import {
|
||||
SSMClient,
|
||||
GetParameterCommand,
|
||||
} from "@aws-sdk/client-ssm";
|
||||
|
||||
const ssm = new SSMClient({});
|
||||
|
||||
let cachedAuthToken: string | undefined;
|
||||
let cachedApiKey: string | undefined;
|
||||
let cachedDoorId: string | undefined;
|
||||
let lastUnlockTime = 0;
|
||||
|
||||
const COOLDOWN_MS = 5_000;
|
||||
|
||||
async function getParameter(name: string, decrypt: boolean): Promise<string> {
|
||||
const res = await ssm.send(
|
||||
new GetParameterCommand({ Name: name, WithDecryption: decrypt })
|
||||
);
|
||||
return res.Parameter!.Value!;
|
||||
}
|
||||
|
||||
async function loadSecrets() {
|
||||
const [authToken, apiKey, doorId] = await Promise.all([
|
||||
cachedAuthToken ?? getParameter(process.env.AUTH_TOKEN_PARAM!, true),
|
||||
cachedApiKey ?? getParameter(process.env.ELEMENTS_API_KEY_PARAM!, true),
|
||||
cachedDoorId ?? getParameter(process.env.DOOR_ID_PARAM!, false),
|
||||
]);
|
||||
cachedAuthToken = authToken;
|
||||
cachedApiKey = apiKey;
|
||||
cachedDoorId = doorId;
|
||||
return { authToken, apiKey, doorId };
|
||||
}
|
||||
|
||||
export async function handler(event: {
|
||||
queryStringParameters?: Record<string, string>;
|
||||
requestContext?: { http?: { sourceIp?: string } };
|
||||
}) {
|
||||
const sourceIp = event.requestContext?.http?.sourceIp ?? "unknown";
|
||||
const token = event.queryStringParameters?.token;
|
||||
|
||||
if (!token) {
|
||||
console.log(JSON.stringify({ action: "unlock_attempt", status: "rejected", reason: "missing_token", sourceIp }));
|
||||
return { statusCode: 403, body: JSON.stringify({ error: "Forbidden" }) };
|
||||
}
|
||||
|
||||
let secrets;
|
||||
try {
|
||||
secrets = await loadSecrets();
|
||||
} catch (err) {
|
||||
console.error(JSON.stringify({ action: "unlock_attempt", status: "error", reason: "ssm_failure", sourceIp, error: String(err) }));
|
||||
return { statusCode: 500, body: JSON.stringify({ error: "Internal error" }) };
|
||||
}
|
||||
|
||||
if (token !== secrets.authToken) {
|
||||
console.log(JSON.stringify({ action: "unlock_attempt", status: "rejected", reason: "invalid_token", sourceIp }));
|
||||
return { statusCode: 403, body: JSON.stringify({ error: "Forbidden" }) };
|
||||
}
|
||||
|
||||
const now = Date.now();
|
||||
if (now - lastUnlockTime < COOLDOWN_MS) {
|
||||
console.log(JSON.stringify({ action: "unlock_attempt", status: "cooldown", sourceIp }));
|
||||
return { statusCode: 429, body: JSON.stringify({ message: "Cooldown active, try again shortly" }) };
|
||||
}
|
||||
|
||||
const url = `https://api.elementssecure.com/v1/devices/${secrets.doorId}/commands/TemporaryUnlock/execute`;
|
||||
|
||||
try {
|
||||
const response = await fetch(url, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"api-key": secrets.apiKey,
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({}),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const body = await response.text();
|
||||
console.error(JSON.stringify({ action: "unlock_attempt", status: "elements_error", statusCode: response.status, body, sourceIp }));
|
||||
return { statusCode: 502, body: JSON.stringify({ error: "Door system error" }) };
|
||||
}
|
||||
|
||||
lastUnlockTime = now;
|
||||
console.log(JSON.stringify({ action: "unlock_attempt", status: "success", sourceIp }));
|
||||
return { statusCode: 200, body: JSON.stringify({ message: "Door unlocked" }) };
|
||||
} catch (err) {
|
||||
console.error(JSON.stringify({ action: "unlock_attempt", status: "error", reason: "elements_unreachable", sourceIp, error: String(err) }));
|
||||
return { statusCode: 502, body: JSON.stringify({ error: "Door system unreachable" }) };
|
||||
}
|
||||
}
|
||||
125
lib/door-unlock-stack.ts
Normal file
125
lib/door-unlock-stack.ts
Normal file
|
|
@ -0,0 +1,125 @@
|
|||
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: "SeaHavenDoorUnlock",
|
||||
});
|
||||
|
||||
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`,
|
||||
});
|
||||
}
|
||||
}
|
||||
984
package-lock.json
generated
Normal file
984
package-lock.json
generated
Normal file
|
|
@ -0,0 +1,984 @@
|
|||
{
|
||||
"name": "seahaven-door-unlock",
|
||||
"version": "1.0.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "seahaven-door-unlock",
|
||||
"version": "1.0.0",
|
||||
"dependencies": {
|
||||
"aws-cdk-lib": "^2.178.0",
|
||||
"constructs": "^10.0.0"
|
||||
},
|
||||
"bin": {
|
||||
"app": "bin/app.js"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^22.0.0",
|
||||
"aws-cdk": "^2.178.0",
|
||||
"esbuild": "^0.24.0",
|
||||
"typescript": "~5.7.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@aws-cdk/asset-awscli-v1": {
|
||||
"version": "2.2.273",
|
||||
"resolved": "https://registry.npmjs.org/@aws-cdk/asset-awscli-v1/-/asset-awscli-v1-2.2.273.tgz",
|
||||
"integrity": "sha512-X57HYUtHt9BQrlrzUNcMyRsDUCoakYNnY6qh5lNwRCHPtQoTfXmuISkfLk0AjLkcbS5lw1LLTQFiQhTDXfiTvg==",
|
||||
"license": "Apache-2.0"
|
||||
},
|
||||
"node_modules/@aws-cdk/asset-node-proxy-agent-v6": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@aws-cdk/asset-node-proxy-agent-v6/-/asset-node-proxy-agent-v6-2.1.1.tgz",
|
||||
"integrity": "sha512-We4bmHaowOPHr+IQR4/FyTGjRfjgBj4ICMjtqmJeBDWad3Q/6St12NT07leNtyuukv2qMhtSZJQorD8KpKTwRA==",
|
||||
"license": "Apache-2.0"
|
||||
},
|
||||
"node_modules/@aws-cdk/cloud-assembly-schema": {
|
||||
"version": "53.17.0",
|
||||
"resolved": "https://registry.npmjs.org/@aws-cdk/cloud-assembly-schema/-/cloud-assembly-schema-53.17.0.tgz",
|
||||
"integrity": "sha512-JB9L4JCbZeYbMJPs7OKV+7YaxKwk4i2tlI5PCSfCzk4DJvprhCO2TYcqbqo3Y1YWZyyY/SEPLWdorG7b88fUSA==",
|
||||
"bundleDependencies": [
|
||||
"jsonschema",
|
||||
"semver"
|
||||
],
|
||||
"license": "Apache-2.0",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"jsonschema": "~1.4.1",
|
||||
"semver": "^7.7.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@aws-cdk/cloud-assembly-schema/node_modules/jsonschema": {
|
||||
"version": "1.4.1",
|
||||
"inBundle": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@aws-cdk/cloud-assembly-schema/node_modules/semver": {
|
||||
"version": "7.7.4",
|
||||
"inBundle": true,
|
||||
"license": "ISC",
|
||||
"bin": {
|
||||
"semver": "bin/semver.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/aix-ppc64": {
|
||||
"version": "0.24.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.24.2.tgz",
|
||||
"integrity": "sha512-thpVCb/rhxE/BnMLQ7GReQLLN8q9qbHmI55F4489/ByVg2aQaQ6kbcLb6FHkocZzQhxc4gx0sCk0tJkKBFzDhA==",
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"aix"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/android-arm": {
|
||||
"version": "0.24.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.24.2.tgz",
|
||||
"integrity": "sha512-tmwl4hJkCfNHwFB3nBa8z1Uy3ypZpxqxfTQOcHX+xRByyYgunVbZ9MzUUfb0RxaHIMnbHagwAxuTL+tnNM+1/Q==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"android"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/android-arm64": {
|
||||
"version": "0.24.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.24.2.tgz",
|
||||
"integrity": "sha512-cNLgeqCqV8WxfcTIOeL4OAtSmL8JjcN6m09XIgro1Wi7cF4t/THaWEa7eL5CMoMBdjoHOTh/vwTO/o2TRXIyzg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"android"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/android-x64": {
|
||||
"version": "0.24.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.24.2.tgz",
|
||||
"integrity": "sha512-B6Q0YQDqMx9D7rvIcsXfmJfvUYLoP722bgfBlO5cGvNVb5V/+Y7nhBE3mHV9OpxBf4eAS2S68KZztiPaWq4XYw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"android"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/darwin-arm64": {
|
||||
"version": "0.24.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.24.2.tgz",
|
||||
"integrity": "sha512-kj3AnYWc+CekmZnS5IPu9D+HWtUI49hbnyqk0FLEJDbzCIQt7hg7ucF1SQAilhtYpIujfaHr6O0UHlzzSPdOeA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/darwin-x64": {
|
||||
"version": "0.24.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.24.2.tgz",
|
||||
"integrity": "sha512-WeSrmwwHaPkNR5H3yYfowhZcbriGqooyu3zI/3GGpF8AyUdsrrP0X6KumITGA9WOyiJavnGZUwPGvxvwfWPHIA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/freebsd-arm64": {
|
||||
"version": "0.24.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.24.2.tgz",
|
||||
"integrity": "sha512-UN8HXjtJ0k/Mj6a9+5u6+2eZ2ERD7Edt1Q9IZiB5UZAIdPnVKDoG7mdTVGhHJIeEml60JteamR3qhsr1r8gXvg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"freebsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/freebsd-x64": {
|
||||
"version": "0.24.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.24.2.tgz",
|
||||
"integrity": "sha512-TvW7wE/89PYW+IevEJXZ5sF6gJRDY/14hyIGFXdIucxCsbRmLUcjseQu1SyTko+2idmCw94TgyaEZi9HUSOe3Q==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"freebsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-arm": {
|
||||
"version": "0.24.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.24.2.tgz",
|
||||
"integrity": "sha512-n0WRM/gWIdU29J57hJyUdIsk0WarGd6To0s+Y+LwvlC55wt+GT/OgkwoXCXvIue1i1sSNWblHEig00GBWiJgfA==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-arm64": {
|
||||
"version": "0.24.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.24.2.tgz",
|
||||
"integrity": "sha512-7HnAD6074BW43YvvUmE/35Id9/NB7BeX5EoNkK9obndmZBUk8xmJJeU7DwmUeN7tkysslb2eSl6CTrYz6oEMQg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-ia32": {
|
||||
"version": "0.24.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.24.2.tgz",
|
||||
"integrity": "sha512-sfv0tGPQhcZOgTKO3oBE9xpHuUqguHvSo4jl+wjnKwFpapx+vUDcawbwPNuBIAYdRAvIDBfZVvXprIj3HA+Ugw==",
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-loong64": {
|
||||
"version": "0.24.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.24.2.tgz",
|
||||
"integrity": "sha512-CN9AZr8kEndGooS35ntToZLTQLHEjtVB5n7dl8ZcTZMonJ7CCfStrYhrzF97eAecqVbVJ7APOEe18RPI4KLhwQ==",
|
||||
"cpu": [
|
||||
"loong64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-mips64el": {
|
||||
"version": "0.24.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.24.2.tgz",
|
||||
"integrity": "sha512-iMkk7qr/wl3exJATwkISxI7kTcmHKE+BlymIAbHO8xanq/TjHaaVThFF6ipWzPHryoFsesNQJPE/3wFJw4+huw==",
|
||||
"cpu": [
|
||||
"mips64el"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-ppc64": {
|
||||
"version": "0.24.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.24.2.tgz",
|
||||
"integrity": "sha512-shsVrgCZ57Vr2L8mm39kO5PPIb+843FStGt7sGGoqiiWYconSxwTiuswC1VJZLCjNiMLAMh34jg4VSEQb+iEbw==",
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-riscv64": {
|
||||
"version": "0.24.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.24.2.tgz",
|
||||
"integrity": "sha512-4eSFWnU9Hhd68fW16GD0TINewo1L6dRrB+oLNNbYyMUAeOD2yCK5KXGK1GH4qD/kT+bTEXjsyTCiJGHPZ3eM9Q==",
|
||||
"cpu": [
|
||||
"riscv64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-s390x": {
|
||||
"version": "0.24.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.24.2.tgz",
|
||||
"integrity": "sha512-S0Bh0A53b0YHL2XEXC20bHLuGMOhFDO6GN4b3YjRLK//Ep3ql3erpNcPlEFed93hsQAjAQDNsvcK+hV90FubSw==",
|
||||
"cpu": [
|
||||
"s390x"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-x64": {
|
||||
"version": "0.24.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.24.2.tgz",
|
||||
"integrity": "sha512-8Qi4nQcCTbLnK9WoMjdC9NiTG6/E38RNICU6sUNqK0QFxCYgoARqVqxdFmWkdonVsvGqWhmm7MO0jyTqLqwj0Q==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/netbsd-arm64": {
|
||||
"version": "0.24.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.24.2.tgz",
|
||||
"integrity": "sha512-wuLK/VztRRpMt9zyHSazyCVdCXlpHkKm34WUyinD2lzK07FAHTq0KQvZZlXikNWkDGoT6x3TD51jKQ7gMVpopw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"netbsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/netbsd-x64": {
|
||||
"version": "0.24.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.24.2.tgz",
|
||||
"integrity": "sha512-VefFaQUc4FMmJuAxmIHgUmfNiLXY438XrL4GDNV1Y1H/RW3qow68xTwjZKfj/+Plp9NANmzbH5R40Meudu8mmw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"netbsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/openbsd-arm64": {
|
||||
"version": "0.24.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.24.2.tgz",
|
||||
"integrity": "sha512-YQbi46SBct6iKnszhSvdluqDmxCJA+Pu280Av9WICNwQmMxV7nLRHZfjQzwbPs3jeWnuAhE9Jy0NrnJ12Oz+0A==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"openbsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/openbsd-x64": {
|
||||
"version": "0.24.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.24.2.tgz",
|
||||
"integrity": "sha512-+iDS6zpNM6EnJyWv0bMGLWSWeXGN/HTaF/LXHXHwejGsVi+ooqDfMCCTerNFxEkM3wYVcExkeGXNqshc9iMaOA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"openbsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/sunos-x64": {
|
||||
"version": "0.24.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.24.2.tgz",
|
||||
"integrity": "sha512-hTdsW27jcktEvpwNHJU4ZwWFGkz2zRJUz8pvddmXPtXDzVKTTINmlmga3ZzwcuMpUvLw7JkLy9QLKyGpD2Yxig==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"sunos"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/win32-arm64": {
|
||||
"version": "0.24.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.24.2.tgz",
|
||||
"integrity": "sha512-LihEQ2BBKVFLOC9ZItT9iFprsE9tqjDjnbulhHoFxYQtQfai7qfluVODIYxt1PgdoyQkz23+01rzwNwYfutxUQ==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/win32-ia32": {
|
||||
"version": "0.24.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.24.2.tgz",
|
||||
"integrity": "sha512-q+iGUwfs8tncmFC9pcnD5IvRHAzmbwQ3GPS5/ceCyHdjXubwQWI12MKWSNSMYLJMq23/IUCvJMS76PDqXe1fxA==",
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/win32-x64": {
|
||||
"version": "0.24.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.24.2.tgz",
|
||||
"integrity": "sha512-7VTgWzgMGvup6aSqDPLiW5zHaxYJGTO4OokMjIlrCtf+VpEL+cXKtCvg723iguPYI5oaUNdS+/V7OU2gvXVWEg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "22.19.17",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.17.tgz",
|
||||
"integrity": "sha512-wGdMcf+vPYM6jikpS/qhg6WiqSV/OhG+jeeHT/KlVqxYfD40iYJf9/AE1uQxVWFvU7MipKRkRv8NSHiCGgPr8Q==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"undici-types": "~6.21.0"
|
||||
}
|
||||
},
|
||||
"node_modules/aws-cdk": {
|
||||
"version": "2.1118.4",
|
||||
"resolved": "https://registry.npmjs.org/aws-cdk/-/aws-cdk-2.1118.4.tgz",
|
||||
"integrity": "sha512-wJfRQdvb+FJ2cni059mYdmjhfwhMskP+PAB59BL9jhon+jYtjy8X3pbj3uzHgAOJwNhh6jGkP8xq36Cffccbbw==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"bin": {
|
||||
"cdk": "bin/cdk"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/aws-cdk-lib": {
|
||||
"version": "2.250.0",
|
||||
"resolved": "https://registry.npmjs.org/aws-cdk-lib/-/aws-cdk-lib-2.250.0.tgz",
|
||||
"integrity": "sha512-8U8/S9VcmKSc3MHZWiB7P0IecgXoohI8Ya3dgtZMgbzC4mB+MEQmsYBeNgm4vzGQdRos8HjQLnFX1IBlZh7jQA==",
|
||||
"bundleDependencies": [
|
||||
"@balena/dockerignore",
|
||||
"@aws-cdk/cloud-assembly-api",
|
||||
"case",
|
||||
"fs-extra",
|
||||
"ignore",
|
||||
"jsonschema",
|
||||
"minimatch",
|
||||
"punycode",
|
||||
"semver",
|
||||
"table",
|
||||
"yaml",
|
||||
"mime-types"
|
||||
],
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@aws-cdk/asset-awscli-v1": "2.2.273",
|
||||
"@aws-cdk/asset-node-proxy-agent-v6": "^2.1.1",
|
||||
"@aws-cdk/cloud-assembly-api": "^2.2.0",
|
||||
"@aws-cdk/cloud-assembly-schema": "^53.0.0",
|
||||
"@balena/dockerignore": "^1.0.2",
|
||||
"case": "1.6.3",
|
||||
"fs-extra": "^11.3.3",
|
||||
"ignore": "^5.3.2",
|
||||
"jsonschema": "^1.5.0",
|
||||
"mime-types": "^2.1.35",
|
||||
"minimatch": "^10.2.3",
|
||||
"punycode": "^2.3.1",
|
||||
"semver": "^7.7.4",
|
||||
"table": "^6.9.0",
|
||||
"yaml": "1.10.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 20.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"constructs": "^10.5.0"
|
||||
}
|
||||
},
|
||||
"node_modules/aws-cdk-lib/node_modules/@aws-cdk/cloud-assembly-api": {
|
||||
"version": "2.2.0",
|
||||
"bundleDependencies": [
|
||||
"jsonschema",
|
||||
"semver"
|
||||
],
|
||||
"inBundle": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"jsonschema": "~1.4.1",
|
||||
"semver": "^7.7.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 18.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@aws-cdk/cloud-assembly-schema": ">=53.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/aws-cdk-lib/node_modules/@aws-cdk/cloud-assembly-api/node_modules/jsonschema": {
|
||||
"version": "1.4.1",
|
||||
"inBundle": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/aws-cdk-lib/node_modules/@aws-cdk/cloud-assembly-api/node_modules/semver": {
|
||||
"version": "7.7.4",
|
||||
"inBundle": true,
|
||||
"license": "ISC",
|
||||
"bin": {
|
||||
"semver": "bin/semver.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/aws-cdk-lib/node_modules/@balena/dockerignore": {
|
||||
"version": "1.0.2",
|
||||
"inBundle": true,
|
||||
"license": "Apache-2.0"
|
||||
},
|
||||
"node_modules/aws-cdk-lib/node_modules/ajv": {
|
||||
"version": "8.18.0",
|
||||
"inBundle": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"fast-deep-equal": "^3.1.3",
|
||||
"fast-uri": "^3.0.1",
|
||||
"json-schema-traverse": "^1.0.0",
|
||||
"require-from-string": "^2.0.2"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/epoberezkin"
|
||||
}
|
||||
},
|
||||
"node_modules/aws-cdk-lib/node_modules/ansi-regex": {
|
||||
"version": "5.0.1",
|
||||
"inBundle": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/aws-cdk-lib/node_modules/ansi-styles": {
|
||||
"version": "4.3.0",
|
||||
"inBundle": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"color-convert": "^2.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/aws-cdk-lib/node_modules/astral-regex": {
|
||||
"version": "2.0.0",
|
||||
"inBundle": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/aws-cdk-lib/node_modules/balanced-match": {
|
||||
"version": "4.0.4",
|
||||
"inBundle": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": "18 || 20 || >=22"
|
||||
}
|
||||
},
|
||||
"node_modules/aws-cdk-lib/node_modules/brace-expansion": {
|
||||
"version": "5.0.5",
|
||||
"inBundle": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"balanced-match": "^4.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": "18 || 20 || >=22"
|
||||
}
|
||||
},
|
||||
"node_modules/aws-cdk-lib/node_modules/case": {
|
||||
"version": "1.6.3",
|
||||
"inBundle": true,
|
||||
"license": "(MIT OR GPL-3.0-or-later)",
|
||||
"engines": {
|
||||
"node": ">= 0.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/aws-cdk-lib/node_modules/color-convert": {
|
||||
"version": "2.0.1",
|
||||
"inBundle": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"color-name": "~1.1.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=7.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/aws-cdk-lib/node_modules/color-name": {
|
||||
"version": "1.1.4",
|
||||
"inBundle": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/aws-cdk-lib/node_modules/emoji-regex": {
|
||||
"version": "8.0.0",
|
||||
"inBundle": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/aws-cdk-lib/node_modules/fast-deep-equal": {
|
||||
"version": "3.1.3",
|
||||
"inBundle": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/aws-cdk-lib/node_modules/fast-uri": {
|
||||
"version": "3.1.0",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/fastify"
|
||||
},
|
||||
{
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/fastify"
|
||||
}
|
||||
],
|
||||
"inBundle": true,
|
||||
"license": "BSD-3-Clause"
|
||||
},
|
||||
"node_modules/aws-cdk-lib/node_modules/fs-extra": {
|
||||
"version": "11.3.3",
|
||||
"inBundle": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"graceful-fs": "^4.2.0",
|
||||
"jsonfile": "^6.0.1",
|
||||
"universalify": "^2.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.14"
|
||||
}
|
||||
},
|
||||
"node_modules/aws-cdk-lib/node_modules/graceful-fs": {
|
||||
"version": "4.2.11",
|
||||
"inBundle": true,
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/aws-cdk-lib/node_modules/ignore": {
|
||||
"version": "5.3.2",
|
||||
"inBundle": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 4"
|
||||
}
|
||||
},
|
||||
"node_modules/aws-cdk-lib/node_modules/is-fullwidth-code-point": {
|
||||
"version": "3.0.0",
|
||||
"inBundle": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/aws-cdk-lib/node_modules/json-schema-traverse": {
|
||||
"version": "1.0.0",
|
||||
"inBundle": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/aws-cdk-lib/node_modules/jsonfile": {
|
||||
"version": "6.2.0",
|
||||
"inBundle": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"universalify": "^2.0.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"graceful-fs": "^4.1.6"
|
||||
}
|
||||
},
|
||||
"node_modules/aws-cdk-lib/node_modules/jsonschema": {
|
||||
"version": "1.5.0",
|
||||
"inBundle": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/aws-cdk-lib/node_modules/lodash.truncate": {
|
||||
"version": "4.4.2",
|
||||
"inBundle": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/aws-cdk-lib/node_modules/mime-db": {
|
||||
"version": "1.52.0",
|
||||
"inBundle": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/aws-cdk-lib/node_modules/mime-types": {
|
||||
"version": "2.1.35",
|
||||
"inBundle": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"mime-db": "1.52.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/aws-cdk-lib/node_modules/minimatch": {
|
||||
"version": "10.2.5",
|
||||
"inBundle": true,
|
||||
"license": "BlueOak-1.0.0",
|
||||
"dependencies": {
|
||||
"brace-expansion": "^5.0.5"
|
||||
},
|
||||
"engines": {
|
||||
"node": "18 || 20 || >=22"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/aws-cdk-lib/node_modules/punycode": {
|
||||
"version": "2.3.1",
|
||||
"inBundle": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/aws-cdk-lib/node_modules/require-from-string": {
|
||||
"version": "2.0.2",
|
||||
"inBundle": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/aws-cdk-lib/node_modules/semver": {
|
||||
"version": "7.7.4",
|
||||
"inBundle": true,
|
||||
"license": "ISC",
|
||||
"bin": {
|
||||
"semver": "bin/semver.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/aws-cdk-lib/node_modules/slice-ansi": {
|
||||
"version": "4.0.0",
|
||||
"inBundle": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ansi-styles": "^4.0.0",
|
||||
"astral-regex": "^2.0.0",
|
||||
"is-fullwidth-code-point": "^3.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/chalk/slice-ansi?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/aws-cdk-lib/node_modules/string-width": {
|
||||
"version": "4.2.3",
|
||||
"inBundle": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"emoji-regex": "^8.0.0",
|
||||
"is-fullwidth-code-point": "^3.0.0",
|
||||
"strip-ansi": "^6.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/aws-cdk-lib/node_modules/strip-ansi": {
|
||||
"version": "6.0.1",
|
||||
"inBundle": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ansi-regex": "^5.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/aws-cdk-lib/node_modules/table": {
|
||||
"version": "6.9.0",
|
||||
"inBundle": true,
|
||||
"license": "BSD-3-Clause",
|
||||
"dependencies": {
|
||||
"ajv": "^8.0.1",
|
||||
"lodash.truncate": "^4.4.2",
|
||||
"slice-ansi": "^4.0.0",
|
||||
"string-width": "^4.2.3",
|
||||
"strip-ansi": "^6.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/aws-cdk-lib/node_modules/universalify": {
|
||||
"version": "2.0.1",
|
||||
"inBundle": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 10.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/aws-cdk-lib/node_modules/yaml": {
|
||||
"version": "1.10.3",
|
||||
"inBundle": true,
|
||||
"license": "ISC",
|
||||
"engines": {
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/constructs": {
|
||||
"version": "10.6.0",
|
||||
"resolved": "https://registry.npmjs.org/constructs/-/constructs-10.6.0.tgz",
|
||||
"integrity": "sha512-TxHOnBO5zMo/G76ykzGF/wMpEHu257TbWiIxP9K0Yv/+t70UzgBQiTqjkAsWOPC6jW91DzJI0+ehQV6xDRNBuQ==",
|
||||
"license": "Apache-2.0",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/esbuild": {
|
||||
"version": "0.24.2",
|
||||
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.24.2.tgz",
|
||||
"integrity": "sha512-+9egpBW8I3CD5XPe0n6BfT5fxLzxrlDzqydF3aviG+9ni1lDC/OvMHcxqEFV0+LANZG5R1bFMWfUrjVsdwxJvA==",
|
||||
"dev": true,
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"esbuild": "bin/esbuild"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@esbuild/aix-ppc64": "0.24.2",
|
||||
"@esbuild/android-arm": "0.24.2",
|
||||
"@esbuild/android-arm64": "0.24.2",
|
||||
"@esbuild/android-x64": "0.24.2",
|
||||
"@esbuild/darwin-arm64": "0.24.2",
|
||||
"@esbuild/darwin-x64": "0.24.2",
|
||||
"@esbuild/freebsd-arm64": "0.24.2",
|
||||
"@esbuild/freebsd-x64": "0.24.2",
|
||||
"@esbuild/linux-arm": "0.24.2",
|
||||
"@esbuild/linux-arm64": "0.24.2",
|
||||
"@esbuild/linux-ia32": "0.24.2",
|
||||
"@esbuild/linux-loong64": "0.24.2",
|
||||
"@esbuild/linux-mips64el": "0.24.2",
|
||||
"@esbuild/linux-ppc64": "0.24.2",
|
||||
"@esbuild/linux-riscv64": "0.24.2",
|
||||
"@esbuild/linux-s390x": "0.24.2",
|
||||
"@esbuild/linux-x64": "0.24.2",
|
||||
"@esbuild/netbsd-arm64": "0.24.2",
|
||||
"@esbuild/netbsd-x64": "0.24.2",
|
||||
"@esbuild/openbsd-arm64": "0.24.2",
|
||||
"@esbuild/openbsd-x64": "0.24.2",
|
||||
"@esbuild/sunos-x64": "0.24.2",
|
||||
"@esbuild/win32-arm64": "0.24.2",
|
||||
"@esbuild/win32-ia32": "0.24.2",
|
||||
"@esbuild/win32-x64": "0.24.2"
|
||||
}
|
||||
},
|
||||
"node_modules/typescript": {
|
||||
"version": "5.7.3",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.3.tgz",
|
||||
"integrity": "sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"bin": {
|
||||
"tsc": "bin/tsc",
|
||||
"tsserver": "bin/tsserver"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.17"
|
||||
}
|
||||
},
|
||||
"node_modules/undici-types": {
|
||||
"version": "6.21.0",
|
||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
|
||||
"integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
}
|
||||
}
|
||||
}
|
||||
24
package.json
Normal file
24
package.json
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
{
|
||||
"name": "seahaven-door-unlock",
|
||||
"version": "1.0.0",
|
||||
"bin": {
|
||||
"app": "bin/app.js"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "tsc",
|
||||
"cdk": "cdk",
|
||||
"synth": "cdk synth",
|
||||
"deploy": "cdk deploy",
|
||||
"diff": "cdk diff"
|
||||
},
|
||||
"devDependencies": {
|
||||
"aws-cdk": "^2.178.0",
|
||||
"typescript": "~5.7.0",
|
||||
"@types/node": "^22.0.0",
|
||||
"esbuild": "^0.24.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"aws-cdk-lib": "^2.178.0",
|
||||
"constructs": "^10.0.0"
|
||||
}
|
||||
}
|
||||
24
tsconfig.json
Normal file
24
tsconfig.json
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2022",
|
||||
"module": "commonjs",
|
||||
"lib": ["ES2022"],
|
||||
"declaration": true,
|
||||
"strict": true,
|
||||
"noImplicitAny": true,
|
||||
"strictNullChecks": true,
|
||||
"noImplicitReturns": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"inlineSourceMap": true,
|
||||
"inlineSources": true,
|
||||
"experimentalDecorators": true,
|
||||
"strictPropertyInitialization": false,
|
||||
"outDir": "./cdk.out",
|
||||
"rootDir": ".",
|
||||
"skipLibCheck": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"resolveJsonModule": true,
|
||||
"esModuleInterop": true
|
||||
},
|
||||
"exclude": ["node_modules", "cdk.out"]
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue