update onboard route

This commit is contained in:
Martin Dimitrov 2025-06-07 16:55:29 -07:00
parent 1c87e93d2f
commit 8ca379b6db

View File

@ -8,29 +8,37 @@ import { createDynaBridgeClient } from "../../../utils/ddb";
import DiscordOauth2 from "discord-oauth2";
import { DoorAliasSchema, getDoorAliasID } from "../../../schema/DoorAlias";
import { Counter } from "prom-client";
import { z } from "zod";
import zu from "zod_utilz";
import { setResponseJson } from "../../../utils/responseUtils";
export interface OnboardRequest extends ServerlessEventObject<{}, UserAgentHeader> {
newConfig?: string;
export const OnboardRequestSchema = z.object({
newConfig: zu.stringToJSON().optional(),
code: z.string().optional(),
state: zu.stringToJSON().optional(),
}).partial()
.refine(data => data.newConfig || (data.code && data.state), 'newConfig or (code and state) must be specified');
// for oauth redirect
code?: string;
state?: string;
}
export type OnboardRequest = z.infer<typeof OnboardRequestSchema>;
export interface DiscordOnboardingState {
name: string;
id: string;
apiRedirect: string;
}
export interface OnboardRequestTwilio extends ServerlessEventObject<OnboardRequest, UserAgentHeader> { };
export const DiscordOnboardingStateSchema = z.object({
name: z.string(),
id: z.string(),
apiRedirect: z.string(),
});
export type DiscordOnboardingState = z.infer<typeof DiscordOnboardingStateSchema>;
const ONBOARDING_SCOPE = ['identify', 'email', 'guilds.join'];
export const handler: ServerlessFunctionSignature<TwilioContext, OnboardRequest> = withMetrics('onboard', async (context, event, callback, metricsRegistry) => {
export const handler: ServerlessFunctionSignature<TwilioContext, OnboardRequestTwilio> = withMetrics('onboard', async (context, event, callback, metricsRegistry) => {
const response = new Twilio.Response();
response.appendHeader('Content-Type', 'application/json');
registerMetrics(metricsRegistry);
const req = OnboardRequestSchema.parse(event);
const db = createDynaBridgeClient(context);
// return oauth link
@ -41,23 +49,13 @@ export const handler: ServerlessFunctionSignature<TwilioContext, OnboardRequest>
});
// create door config route
if (event.code && event.state) {
const code = event.code;
let discordState: DiscordOnboardingState;
try {
discordState = JSON.parse(event.state);
} catch (e) {
response.setStatusCode(400);
response.setBody({ err: "invalid state" });
return callback(null, response);
}
if (req.code && req.state) {
const code = req.code;
let discordState: DiscordOnboardingState = DiscordOnboardingStateSchema.parse(req.state);
const config = await db.entities.onboardDoorConfig.findById(getOnboardDoorId(discordState.name));
if (!config || config.nonce !== discordState.id) {
response.setStatusCode(404)
.setBody({ err: "approval not found" });
setResponseJson(response, 404, { err: "approval not found" });
return callback(null, response);
}
@ -73,8 +71,7 @@ export const handler: ServerlessFunctionSignature<TwilioContext, OnboardRequest>
});
} catch (err) {
console.log("something went wrong with discord authorization");
response.setStatusCode(401);
response.setBody({ err });
setResponseJson(response, 401, { err });
return callback(null, response);
}
@ -84,9 +81,7 @@ export const handler: ServerlessFunctionSignature<TwilioContext, OnboardRequest>
console.log(profileId);
if (!profileId) {
response.setStatusCode(404);
response.setBody({ err: "profile not found" });
setResponseJson(response, 404, { err: "profile not found" });
return callback(null, response);
}
@ -144,37 +139,26 @@ export const handler: ServerlessFunctionSignature<TwilioContext, OnboardRequest>
// and all must succeed or none succeed
try {
await db.transaction([createDoorAlias, createDoorConfig, deleteOnboardingConfig]);
response.setStatusCode(200);
response.setBody({ redirect: context.DOORMAN_URL + `?door=${config.name}` });
setResponseJson(response, 200, { redirect: context.DOORMAN_URL + `?door=${config.name}` });
} catch (e) {
getMetricFromRegistry<Counter>(metricsRegistry, OnboardMetrics.TRANSACTION_CONFLICT).inc(1);
console.error(e);
response.setStatusCode(409);
response.setBody({ err: "something went wrong during onboarding" });
setResponseJson(response, 409, { err: "something went wrong during onboarding" });
}
return callback(null, response);
}
let newConfig = event.newConfig;
let newConfig = req.newConfig;
if (!newConfig) {
response.setStatusCode(400);
setResponseJson(response, 400, { err: "missing newConfig" });
return callback(null, response);
}
let newConfigParsed: OnboardDoorReq;
console.log("parsing config");
try {
newConfigParsed = OnboardDoorReqSchema.parse(JSON.parse(newConfig));
} catch (err) {
response.setStatusCode(400);
response.setBody({ err });
return callback(null, response);
}
let newConfigParsed: OnboardDoorReq = OnboardDoorReqSchema.parse(newConfig);
const newConfigObj = createOnboardDoorConfig(newConfigParsed);
// check if this name or buzzer is already registered?
@ -182,8 +166,7 @@ export const handler: ServerlessFunctionSignature<TwilioContext, OnboardRequest>
if (existingAlias) {
getMetricFromRegistry<Counter>(metricsRegistry, OnboardMetrics.BUZZER_CONFLICT).inc({ buzzer: newConfigObj.buzzer }, 1);
response.setStatusCode(409);
response.setBody({ err: `A buzzer is already registered with the number ${newConfigObj.buzzer}` });
setResponseJson(response, 409, { err: `A buzzer is already registered with the number ${newConfigObj.buzzer}` });
return callback(null, response);
}
@ -191,8 +174,7 @@ export const handler: ServerlessFunctionSignature<TwilioContext, OnboardRequest>
if (existingConfig) {
getMetricFromRegistry<Counter>(metricsRegistry, OnboardMetrics.NAME_CONFLICT).inc({ name: newConfigObj.name }, 1);
response.setStatusCode(409);
response.setBody({ err: `A buzzer is already registered with the name ${newConfigObj.name}` });
setResponseJson(response, 409, { err: `A buzzer is already registered with the name ${newConfigObj.name}` });
return callback(null, response);
}
@ -202,11 +184,11 @@ export const handler: ServerlessFunctionSignature<TwilioContext, OnboardRequest>
// save to DB, this is a middle step until discord auth comes in
await db.entities.onboardDoorConfig.save(newConfigObj);
const discordState: DiscordOnboardingState = {
const discordState: DiscordOnboardingState = DiscordOnboardingStateSchema.parse({
name: newConfigObj.name,
id: newConfigObj.nonce,
apiRedirect: '/api/door/onboard',
}
});
const redirect = oauth.generateAuthUrl({
scope: ONBOARDING_SCOPE,
@ -214,7 +196,6 @@ export const handler: ServerlessFunctionSignature<TwilioContext, OnboardRequest>
state: JSON.stringify(discordState),
});
response.setStatusCode(200);
response.setBody({ redirect });
setResponseJson(response, 200, { redirect });
return callback(null, response);
});