validate call webhook data
This commit is contained in:
parent
aac662f702
commit
d452422355
@ -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;
|
|
||||||
|
Loading…
Reference in New Issue
Block a user