diff --git a/packages/doorman-api/src/functions/api/door/checkOtp.ts b/packages/doorman-api/src/functions/api/door/checkOtp.ts new file mode 100644 index 0000000..abce8cc --- /dev/null +++ b/packages/doorman-api/src/functions/api/door/checkOtp.ts @@ -0,0 +1,53 @@ +/** + * Try to get buzzer number for a given OTP + */ + +import { ServerlessEventObject, ServerlessFunctionSignature } from "@twilio-labs/serverless-runtime-types/types"; +import { TwilioContext } from "../../../types/TwilioContext"; +import { createDynaBridgeClient } from "../../../utils/ddb"; +import { withMetrics } from "../../../common/DoormanHandler"; +import { z } from "zod"; +import { UserAgentHeader } from "../../../utils/blockUserAgent"; +import { setResponseJson } from "../../../utils/responseUtils"; +import { getLogCallID } from "../../../schema/LogCall"; +import { isTTLInFuture } from "../../../common/TTLHelper"; + +export const CheckOtpRequestSchema = z.object({ + otp: z.string(), +}); + +export type CheckOtpRequest = z.infer; +export interface CheckOtpRequestTwilio extends ServerlessEventObject { }; + +export const CheckOtpResponseSchema = z.object({ + buzzer: z.string(), +}); + +export type CheckOtpResponse = z.infer; + +export const handler: ServerlessFunctionSignature = withMetrics("checkotp", async (context, event, callback, metricsRegistry) => { + const response = new Twilio.Response(); + const req = CheckOtpRequestSchema.parse(event); + + let otp = req.otp; + + const db = createDynaBridgeClient(context); + + const log = await db.entities.logCall.findById(getLogCallID(otp)); + + if (!log || !isTTLInFuture(log)) { + setResponseJson(response, 404, { + msg: "OTP expired or not found", + }) + } else { + setResponseJson(response, 200, { + buzzer: log.caller, + }); + } + + // destroy the internal client after + // @ts-ignore + db.ddbClient.destroy(); + + return callback(null, response); +}); diff --git a/packages/doorman-api/tst/integ-local.test.ts b/packages/doorman-api/tst/integ-local.test.ts index c72e5d0..2d2c017 100644 --- a/packages/doorman-api/tst/integ-local.test.ts +++ b/packages/doorman-api/tst/integ-local.test.ts @@ -5,6 +5,7 @@ import { StatusResponse } from "../src/functions/api/door/status"; import { sleep } from "bun"; import { ONBOARDING_DOOR_NAME, ONBOARDING_DOOR_PIN } from "../src/schema/DoorConfig"; import { LogCallResponse } from "../src/functions/api/door/logCall"; +import { CheckOtpResponse } from "../src/functions/api/door/checkOtp"; // these tests should only run locally if (process.env.STAGE === 'staging') { @@ -109,7 +110,15 @@ describe("call log path works", () => { expect(logCallRes.status).toBe(200); const otp = (await logCallRes.json() as LogCallResponse).otp - expect(otp.length).toBe(4) + expect(otp.length).toBe(4); + + const checkOtpRes = await fetch(baseUrl + `/api/door/checkOtp?otp=${otp}`); + + expect(checkOtpRes.status).toBe(200); + + // check OTP + const caller = (await checkOtpRes.json() as CheckOtpResponse).buzzer; + expect(caller).toBe(buzzerNumber); }); test("call log after door closed, should not return OTP", async () => {