From 33d7764ca270b2edcf31cf72a21edc6242a69498 Mon Sep 17 00:00:00 2001 From: Martin Dimitrov Date: Sat, 4 Oct 2025 12:18:44 -0700 Subject: [PATCH] add hardcoded door "onboardingflag" for automated onboarding process --- packages/doorman-api/src/common/TTLHelper.ts | 10 +++++ .../src/functions/api/door/info.ts | 8 ++-- .../src/functions/api/door/status.ts | 6 ++- packages/doorman-api/src/index.ts | 4 +- packages/doorman-api/src/local/localDdb.ts | 22 ++++++++++- packages/doorman-api/src/schema/DoorConfig.ts | 5 ++- packages/doorman-api/src/schema/LockStatus.ts | 6 +-- .../doorman-api/tst/integ-staging.test.ts | 37 +++++++++++++++++++ 8 files changed, 85 insertions(+), 13 deletions(-) create mode 100644 packages/doorman-api/src/common/TTLHelper.ts diff --git a/packages/doorman-api/src/common/TTLHelper.ts b/packages/doorman-api/src/common/TTLHelper.ts new file mode 100644 index 0000000..74be8e9 --- /dev/null +++ b/packages/doorman-api/src/common/TTLHelper.ts @@ -0,0 +1,10 @@ +export function isTTLInFuture(item?: { TTL: number }) { + if (!item) { + return false; + } + + // ttl is a UTC ms time + const ttl = item.TTL || 0; + + return parseInt("" + ttl) > Date.now(); +} diff --git a/packages/doorman-api/src/functions/api/door/info.ts b/packages/doorman-api/src/functions/api/door/info.ts index ec0a025..34de996 100644 --- a/packages/doorman-api/src/functions/api/door/info.ts +++ b/packages/doorman-api/src/functions/api/door/info.ts @@ -18,8 +18,8 @@ export const InfoRequestSchema = z.object({ door: z.string().optional(), buzzer: z.string().optional(), }) -.partial() -.refine(data => data.buzzer || data.door, 'Buzzer or door must be provided'); + .partial() + .refine(data => data.buzzer || data.door, 'Buzzer or door must be provided'); export type InfoRequest = z.infer; export interface InfoRequestTwilio extends ServerlessEventObject { }; @@ -75,7 +75,7 @@ export const handler: ServerlessFunctionSignature readdirSync(path).map(file => path + "/" + file)).flat(); @@ -15,7 +15,7 @@ console.log("functions to build:", functionFiles); const bundledModules = ['dynabridge', 'zod_utilz']; const externalModules = Object.keys(require('../package.json').dependencies) - .filter(dep => !bundledModules.includes(dep)); + .filter(dep => !bundledModules.includes(dep)); console.log("Explicitly bundling dependencies", bundledModules); diff --git a/packages/doorman-api/src/local/localDdb.ts b/packages/doorman-api/src/local/localDdb.ts index 0315603..bdcdabd 100644 --- a/packages/doorman-api/src/local/localDdb.ts +++ b/packages/doorman-api/src/local/localDdb.ts @@ -1,7 +1,7 @@ import { CreateTableCommand, DynamoDBClient, KeyType, PutItemCommand, ScalarAttributeType } from "@aws-sdk/client-dynamodb"; import { DynamoDBDocument } from "@aws-sdk/lib-dynamodb"; import { DoorAlias, DoorAliasEntity, DoorAliasSchema, getDoorAliasID } from "../schema/DoorAlias"; -import { DoorConfig } from "../schema/DoorConfig"; +import { DoorConfig, getDoorConfigID, ONBOARDING_DOOR_NAME, ONBOARDING_DOOR_PIN } from "../schema/DoorConfig"; import DynamoDbLocal from "dynamodb-local"; import { sleep } from "bun"; @@ -76,6 +76,20 @@ const doorConfig: DoorConfig = { greeting: "test door", }; +// for onboarding mode +const onboardingDoorConfig: DoorConfig = { + PK: "door-" + ONBOARDING_DOOR_NAME, + SK: "config", + buzzer: "nil", + pressKey: "nil", + discordUsers: [], + fallbackNumbers: [], + pin: ONBOARDING_DOOR_PIN, + buzzerCode: "nil", + timeout: 15 * 60, + greeting: "INTERNAL USE - for onboarding new door", +}; + try { await document.put({ TableName: tableName, @@ -86,6 +100,12 @@ try { TableName: tableName, Item: doorAlias, }); + + await document.put({ + TableName: tableName, + Item: onboardingDoorConfig, + }); + } catch (e) { console.error("failed seeding table", e); } diff --git a/packages/doorman-api/src/schema/DoorConfig.ts b/packages/doorman-api/src/schema/DoorConfig.ts index 7a37030..7c3c385 100644 --- a/packages/doorman-api/src/schema/DoorConfig.ts +++ b/packages/doorman-api/src/schema/DoorConfig.ts @@ -5,6 +5,9 @@ import { randomUUID } from "crypto"; export const DOOR_CONFIG_SK = "config"; export const EDIT_DOOR_CONFIG_SK = "config-update"; +export const ONBOARDING_DOOR_NAME = "onboardingflag"; +export const ONBOARDING_DOOR_PIN = "1234"; + export const DoorConfigSchema = z.object({ // keys PK: z.string().startsWith("door-", "Invalid door key"), @@ -59,7 +62,7 @@ export const editDoorToDoorConfig = (updateDoor: EditDoorConfig): DoorConfig => }; delete newItem.approvalId; - + return newItem; } diff --git a/packages/doorman-api/src/schema/LockStatus.ts b/packages/doorman-api/src/schema/LockStatus.ts index 3c67733..6ab7b65 100644 --- a/packages/doorman-api/src/schema/LockStatus.ts +++ b/packages/doorman-api/src/schema/LockStatus.ts @@ -1,5 +1,6 @@ import { z } from "zod"; import { DynaBridgeEntity } from 'dynabridge'; +import { isTTLInFuture } from "../common/TTLHelper"; export const LOCK_STATUS_SK = "lock"; @@ -24,10 +25,7 @@ export const LockStatusEntity: DynaBridgeEntity = { }; export const isLockOpen = (lock?: LockStatus) => { - // ttl is a UTC ms time for how long it is unlocked - const ttl = lock?.TTL || 0; - - return parseInt("" + ttl) > Date.now(); + return isTTLInFuture(lock); }; export const createLockStatusWithTimeout = (door: string, timeoutSeconds: number, fingerprint: any): LockStatus => { diff --git a/packages/doorman-api/tst/integ-staging.test.ts b/packages/doorman-api/tst/integ-staging.test.ts index 5fcea7e..ba7f97c 100644 --- a/packages/doorman-api/tst/integ-staging.test.ts +++ b/packages/doorman-api/tst/integ-staging.test.ts @@ -2,6 +2,8 @@ import { describe, test, expect } from "bun:test"; import { buzzerUrl, buzzerNumber, baseUrl, doorName, key } from "./testCommon"; import { DoorStatus } from "../src/types/DoorStatus"; import { InfoResponseUI } from "../src/functions/api/door/info"; +import { ONBOARDING_DOOR_NAME, ONBOARDING_DOOR_PIN } from "../src/schema/DoorConfig"; +import { StatusResponse } from "../src/functions/api/door/status"; if (process.env.STAGE !== 'staging') { process.exit(0); @@ -33,3 +35,38 @@ describe("buzzer client works", () => { expect(infoResp.status).toBe(DoorStatus.CLOSED); }); }); + +// CAVEAT, this door is currently manually created, for the purposes of automated onboarding flow +describe("onboardingflag door should exist", () => { + test("onboarding door exists", async () => { + // door info + const authResp = await fetch(baseUrl + `/api/door/info?door=${ONBOARDING_DOOR_NAME}`); + expect(authResp.status).toBe(200); + + const door = (await authResp.json()) as InfoResponseUI; + expect(door.timeout).toBe(15 * 60); + + // this may be flaky, it would fail when a user is onboarding and we are deploying + expect(door.status).toBe(DoorStatus.CLOSED); + }); + + test("onboarding door does not close after status check", async () => { + let authResp = await fetch(baseUrl + `/api/door/auth?door=${ONBOARDING_DOOR_NAME}&key=${ONBOARDING_DOOR_PIN}`); + expect(authResp.status).toBe(200); + + let statusResp = await fetch(baseUrl + `/api/door/status?door=${ONBOARDING_DOOR_NAME}`).then(res => res.json()) as StatusResponse; + expect(statusResp.status).toBe(DoorStatus.OPEN); + + // open again + statusResp = await fetch(baseUrl + `/api/door/status?door=${ONBOARDING_DOOR_NAME}`).then(res => res.json()) as StatusResponse; + expect(statusResp.status).toBe(DoorStatus.OPEN); + + // close it + authResp = await fetch(baseUrl + `/api/door/auth?door=${ONBOARDING_DOOR_NAME}&key=${ONBOARDING_DOOR_PIN}`); + expect(authResp.status).toBe(200); + + // door should now be closed + const infoResp = await fetch(baseUrl + `/api/door/info?door=${ONBOARDING_DOOR_NAME}`).then(res => res.json()) as InfoResponseUI; + expect(infoResp.status).toBe(DoorStatus.CLOSED); + }) +});