add validation to edit route
This commit is contained in:
parent
0979d03a27
commit
231ea8149d
@ -17,13 +17,15 @@
|
||||
"discord-oauth2": "^2.12.1",
|
||||
"discord.js": "^14.19.3",
|
||||
"dynabridge": "^0.3.8",
|
||||
"is-deep-subset": "^0.1.1",
|
||||
"prom-client": "^15.1.3",
|
||||
"promise.timeout": "^1.2.0",
|
||||
"twilio": "^3.84.1",
|
||||
"winston": "^3.17.0",
|
||||
"winston-loki": "^6.1.3",
|
||||
"zod": "^3.25.42",
|
||||
"zod-validation-error": "^3.4.1"
|
||||
"zod-validation-error": "^3.4.1",
|
||||
"zod_utilz": "^0.8.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"twilio-run": "^3.5.4",
|
||||
|
||||
@ -5,33 +5,42 @@
|
||||
|
||||
import { ServerlessEventObject, ServerlessFunctionSignature } from "@twilio-labs/serverless-runtime-types/types";
|
||||
import { TwilioContext } from "../../../types/TwilioContext";
|
||||
import { shouldBlockRequest, UserAgentHeader } from "../../../utils/blockUserAgent";
|
||||
import { UserAgentHeader } from "../../../utils/blockUserAgent";
|
||||
import { createDynaBridgeClient } from "../../../utils/ddb";
|
||||
import { sendMessageToUser } from "../../../utils/discord";
|
||||
import { createEditDoorConfig, EditDoorConfigReq, editDoorToDoorConfig, getDoorConfigID, getEditDoorConfigID } from "../../../schema/DoorConfig";
|
||||
import { createEditDoorConfig, EditDoorConfigReqSchema, editDoorToDoorConfig, getDoorConfigID, getEditDoorConfigID } from "../../../schema/DoorConfig";
|
||||
import { z } from "zod";
|
||||
import { withMetrics } from "../../../common/DoormanHandler";
|
||||
import { setResponseJson } from "../../../utils/responseUtils";
|
||||
|
||||
export interface EditRequest extends ServerlessEventObject<{}, UserAgentHeader> {
|
||||
door?: string;
|
||||
approvalId?: string;
|
||||
newConfig?: string;
|
||||
}
|
||||
import { zu } from "zod_utilz";
|
||||
|
||||
export const handler: ServerlessFunctionSignature<TwilioContext, EditRequest> = async function(context, event, callback) {
|
||||
// @ts-ignore
|
||||
import isDeepSubset from "is-deep-subset";
|
||||
|
||||
export const EditRequestSchema = z.object({
|
||||
door: z.string(),
|
||||
approvalId: z.string().optional(),
|
||||
newConfig: zu.stringToJSON().optional(),
|
||||
});
|
||||
|
||||
export type EditRequest = z.infer<typeof EditRequestSchema>;
|
||||
|
||||
export interface EditRequestTwilio extends ServerlessEventObject<EditRequest, UserAgentHeader> { }
|
||||
|
||||
export const handler: ServerlessFunctionSignature<TwilioContext, EditRequestTwilio> = withMetrics("edit", async (context, event, callback, metricsRegistry) => {
|
||||
const response = new Twilio.Response();
|
||||
|
||||
if (shouldBlockRequest(event)) {
|
||||
response.setStatusCode(200);
|
||||
return callback(null, response);
|
||||
}
|
||||
const req = EditRequestSchema.parse(event);
|
||||
|
||||
let door = event.door;
|
||||
let approvalId = event.approvalId;
|
||||
let newConfigString = event.newConfig;
|
||||
let door = req.door;
|
||||
let approvalId = req.approvalId;
|
||||
let newConfigRaw = req.newConfig;
|
||||
|
||||
const db = createDynaBridgeClient(context);
|
||||
|
||||
// approve path
|
||||
if (door && approvalId) {
|
||||
if (approvalId) {
|
||||
const newConfig = await db.entities.editDoorConfig.findById(getEditDoorConfigID(door));
|
||||
|
||||
if (!newConfig || newConfig.approvalId !== approvalId) {
|
||||
@ -39,7 +48,7 @@ export const handler: ServerlessFunctionSignature<TwilioContext, EditRequest> =
|
||||
return callback(null, response);
|
||||
}
|
||||
|
||||
db.entities.doorConfig.save(editDoorToDoorConfig(newConfig));
|
||||
await db.entities.doorConfig.save(editDoorToDoorConfig(newConfig));
|
||||
|
||||
// send update to discord users
|
||||
const updateMessage = `Configuration change \`${approvalId}\` was approved @ Door "${door}"`;
|
||||
@ -60,16 +69,21 @@ export const handler: ServerlessFunctionSignature<TwilioContext, EditRequest> =
|
||||
return callback(null, response);
|
||||
}
|
||||
|
||||
if (!door || !newConfigString) {
|
||||
response.setStatusCode(400);
|
||||
if (!newConfigRaw) {
|
||||
setResponseJson(response, 400, {
|
||||
err: "Missing new config",
|
||||
});
|
||||
return callback(null, response);
|
||||
}
|
||||
|
||||
const newConfig: EditDoorConfigReq = JSON.parse(newConfigString);
|
||||
const newConfig = EditDoorConfigReqSchema.parse(newConfigRaw);
|
||||
|
||||
const config = await db.entities.doorConfig.findById(getDoorConfigID(door));
|
||||
|
||||
if (!config) {
|
||||
response.setStatusCode(404);
|
||||
setResponseJson(response, 404, {
|
||||
err: `Door not found ${door}`,
|
||||
});
|
||||
return callback(null, response);
|
||||
}
|
||||
|
||||
@ -78,14 +92,24 @@ export const handler: ServerlessFunctionSignature<TwilioContext, EditRequest> =
|
||||
newConfig.pin = config.pin;
|
||||
}
|
||||
|
||||
// if nothing changed, we should throw since this is a pointless change
|
||||
if (isDeepSubset(config, newConfig)) {
|
||||
setResponseJson(response, 400, {
|
||||
err: "Nothing changed in the new config",
|
||||
});
|
||||
|
||||
return callback(null, response);
|
||||
}
|
||||
|
||||
const editDoorConfig = createEditDoorConfig(door, newConfig);
|
||||
await db.entities.editDoorConfig.save(editDoorConfig);
|
||||
|
||||
// newConfig.discordUsers = undefined;
|
||||
// newConfig.fallbackNumbers = undefined;
|
||||
// newConfig.status = undefined;
|
||||
const params: EditRequest = {
|
||||
door,
|
||||
approvalId: editDoorConfig.approvalId,
|
||||
};
|
||||
|
||||
const approvalUrl = `https://doorman.chromart.cc/api/door/edit?door=${door}&approvalId=${editDoorConfig.approvalId as string}`;
|
||||
const approvalUrl = `https://doorman.chromart.cc/api/door/edit?` + (new URLSearchParams(params as any)).toString();
|
||||
console.log(approvalUrl);
|
||||
|
||||
// send update to discord users
|
||||
@ -101,14 +125,13 @@ export const handler: ServerlessFunctionSignature<TwilioContext, EditRequest> =
|
||||
|
||||
await Promise.all(discordPromises);
|
||||
|
||||
response
|
||||
.setStatusCode(200)
|
||||
.appendHeader('Content-Type', 'application/json')
|
||||
.setBody({ msg: "Created Configuration change" });
|
||||
setResponseJson(response, 200, {
|
||||
msg: 'Created configuration change',
|
||||
});
|
||||
|
||||
// destroy the internal client after
|
||||
// @ts-ignore
|
||||
db.ddbClient.destroy();
|
||||
|
||||
return callback(null, response);
|
||||
};
|
||||
});
|
||||
|
||||
@ -12,7 +12,7 @@ const imports = functionFiles.forEach(file => require('./' + path.relative('src'
|
||||
|
||||
console.log("functions to build:", functionFiles);
|
||||
|
||||
const bundledModules = ['dynabridge'];
|
||||
const bundledModules = ['dynabridge', 'zod_utilz'];
|
||||
|
||||
const externalModules = Object.keys(require('../package.json').dependencies)
|
||||
.filter(dep => !bundledModules.includes(dep));
|
||||
|
||||
@ -14,7 +14,7 @@ export const DoorConfigSchema = z.object({
|
||||
buzzerCode: z.string(),
|
||||
discordUsers: z.array(z.string()),
|
||||
fallbackNumbers: z.array(z.string()),
|
||||
pin: z.string(),
|
||||
pin: z.string().default(""),
|
||||
pressKey: z.string(),
|
||||
greeting: z.string().optional(),
|
||||
timeout: z.number(),
|
||||
@ -39,7 +39,8 @@ export const getEditDoorConfigID = (doorName: string): string[] => {
|
||||
export type DoorConfig = z.infer<typeof DoorConfigSchema>;
|
||||
export type EditDoorConfig = z.infer<typeof EditDoorConfigSchema>;
|
||||
|
||||
export type EditDoorConfigReq = Omit<EditDoorConfig, "PK" | "SK" | "approvalId">;
|
||||
export const EditDoorConfigReqSchema = EditDoorConfigSchema.omit({ "PK": true, "SK": true, "approvalId": true });
|
||||
export type EditDoorConfigReq = z.infer<typeof EditDoorConfigReqSchema>;
|
||||
|
||||
export const DoorConfigEntity: DynaBridgeEntity<DoorConfig> = {
|
||||
tableName: "doorman",
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user