validate call webhook data

This commit is contained in:
m5r 2022-06-13 20:51:41 +02:00
parent aac662f702
commit d452422355

View File

@ -1,6 +1,6 @@
import { type ActionFunction } from "@remix-run/node"; import { type ActionFunction } from "@remix-run/node";
import { type CallInstance } from "twilio/lib/rest/api/v2010/account/call";
import { badRequest, serverError } from "remix-utils"; import { badRequest, serverError } from "remix-utils";
import { z } from "zod";
import { Direction, Prisma, SubscriptionStatus } from "@prisma/client"; import { Direction, Prisma, SubscriptionStatus } from "@prisma/client";
import logger from "~/utils/logger.server"; import logger from "~/utils/logger.server";
@ -8,6 +8,7 @@ import db from "~/utils/db.server";
import twilio from "twilio"; import twilio from "twilio";
import { voiceUrl, translateCallStatus } from "~/utils/twilio.server"; import { voiceUrl, translateCallStatus } from "~/utils/twilio.server";
import { decrypt } from "~/utils/encryption"; import { decrypt } from "~/utils/encryption";
import { validate } from "~/utils/validation.server";
export const action: ActionFunction = async ({ request }) => { export const action: ActionFunction = async ({ request }) => {
const twilioSignature = request.headers.get("X-Twilio-Signature") || request.headers.get("x-twilio-signature"); const twilioSignature = request.headers.get("X-Twilio-Signature") || request.headers.get("x-twilio-signature");
@ -15,9 +16,16 @@ export const action: ActionFunction = async ({ request }) => {
return badRequest("Invalid header X-Twilio-Signature"); return badRequest("Invalid header X-Twilio-Signature");
} }
const body: Body = Object.fromEntries(await request.formData()) as any; const formData = Object.fromEntries(await request.formData());
const isOutgoingCall = body.From.startsWith("client:"); const isOutgoingCall = formData.Caller?.toString().startsWith("client:");
if (isOutgoingCall) { if (isOutgoingCall) {
const validation = validate(validations.outgoing, formData);
if (validation.errors) {
logger.error(validation.errors);
return badRequest("");
}
const body = validation.data;
const recipient = body.To; const recipient = body.To;
const accountSid = body.From.slice("client:".length).split("__")[0]; const accountSid = body.From.slice("client:".length).split("__")[0];
@ -101,46 +109,57 @@ export const action: ActionFunction = async ({ request }) => {
} }
}; };
type OutgoingCallBody = { const CallStatus = z.union([
AccountSid: string; z.literal("busy"),
ApiVersion: string; z.literal("canceled"),
ApplicationSid: string; z.literal("completed"),
CallSid: string; z.literal("failed"),
CallStatus: CallInstance["status"]; z.literal("in-progress"),
Called: string; z.literal("no-answer"),
Caller: string; z.literal("queued"),
Direction: `outbound${string}`; z.literal("ringing"),
From: string; ]);
To: string;
};
type IncomingCallBody = { const validations = {
AccountSid: string; outgoing: z.object({
ApiVersion: string; AccountSid: z.string(),
ApplicationSid: string; ApiVersion: z.string(),
CallSid: string; ApplicationSid: z.string(),
CallStatus: CallInstance["status"]; CallSid: z.string(),
Called: string; CallStatus,
CalledCity: string; Called: z.string(),
CalledCountry: string; Caller: z.string(),
CalledState: string; // Direction: z.string().refine((direction) => direction.startsWith("outbound")),
CalledZip: string; Direction: z.string(),
Caller: string; From: z.string(),
CallerCity: string; To: z.string(),
CallerCountry: string; }),
CallerState: string; incoming: z.object({
CallerZip: string; AccountSid: z.string(),
Direction: "inbound"; ApiVersion: z.string(),
From: string; ApplicationSid: z.string(),
FromCity: string; CallSid: z.string(),
FromCountry: string; CallStatus,
FromState: string; Called: z.string(),
FromZip: string; CalledCity: z.string(),
To: string; CalledCountry: z.string(),
ToCity: string; CalledState: z.string(),
ToCountry: string; CalledZip: z.string(),
ToState: string; Caller: z.string(),
ToZip: string; CallerCity: z.string(),
CallerCountry: z.string(),
CallerState: z.string(),
CallerZip: z.string(),
Direction: z.literal("inbound"),
From: z.string(),
FromCity: z.string(),
FromCountry: z.string(),
FromState: z.string(),
FromZip: z.string(),
To: z.string(),
ToCity: z.string(),
ToCountry: z.string(),
ToState: z.string(),
ToZip: z.string(),
}),
}; };
type Body = OutgoingCallBody | IncomingCallBody;