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