clean setTwilioWebhooks and twilio webhook verification

This commit is contained in:
m5r 2021-08-30 19:24:05 +08:00
parent e77e7d17e0
commit 6a2e76857b
5 changed files with 39 additions and 26 deletions

View File

@ -2,6 +2,7 @@ import { BlitzApiRequest, BlitzApiResponse } from "blitz";
import db from "db"; import db from "db";
import twilio from "twilio"; import twilio from "twilio";
import setTwilioWebhooks from "../onboarding/api/queue/set-twilio-webhooks";
export default async function ddd(req: BlitzApiRequest, res: BlitzApiResponse) { export default async function ddd(req: BlitzApiRequest, res: BlitzApiResponse) {
/*await Promise.all([ /*await Promise.all([
@ -49,7 +50,12 @@ export default async function ddd(req: BlitzApiRequest, res: BlitzApiResponse) {
console.log("messagesReceived", messagesReceived.sort((a, b) => a.dateCreated.getTime() - b.dateCreated.getTime())); console.log("messagesReceived", messagesReceived.sort((a, b) => a.dateCreated.getTime() - b.dateCreated.getTime()));
// console.log("messagesReceived", messagesReceived);*/ // console.log("messagesReceived", messagesReceived);*/
setTimeout(() => { /*setTwilioWebhooks.enqueue({
phoneNumberId: "PNb77c9690c394368bdbaf20ea6fe5e9fc",
organizationId: "95267d60-3d35-4c36-9905-8543ecb4f174",
});*/
// setTimeout(() => {
res.status(200).end(); res.status(200).end();
}, 1000 * 60 * 5); // }, 1000 * 60 * 5);
} }

View File

@ -5,6 +5,7 @@ import twilio from "twilio";
import appLogger from "../../../../integrations/logger"; import appLogger from "../../../../integrations/logger";
import db from "../../../../db"; import db from "../../../../db";
import insertIncomingMessageQueue from "../queue/insert-incoming-message"; import insertIncomingMessageQueue from "../queue/insert-incoming-message";
import { smsUrl } from "../../../../integrations/twilio";
type ApiError = { type ApiError = {
statusCode: number; statusCode: number;
@ -53,13 +54,12 @@ export default async function incomingMessageHandler(req: BlitzApiRequest, res:
return; return;
} }
const url = `https://${serverRuntimeConfig.app.baseUrl}/api/webhook/incoming-message`;
const phoneNumber = phoneNumbers.find((phoneNumber) => { const phoneNumber = phoneNumbers.find((phoneNumber) => {
// if multiple organizations have the same number // if multiple organizations have the same number
// find the organization currently using that phone number // find the organization currently using that phone number
// maybe we shouldn't let multiple organizations use the same phone number // maybe we shouldn't let multiple organizations use the same phone number
const authToken = phoneNumber.organization.twilioAuthToken ?? ""; const authToken = phoneNumber.organization.twilioAuthToken ?? "";
return twilio.validateRequest(authToken, twilioSignature, url, req.body); return twilio.validateRequest(authToken, twilioSignature, smsUrl, req.body);
}); });
if (!phoneNumber) { if (!phoneNumber) {
const statusCode = 400; const statusCode = 400;

View File

@ -4,7 +4,7 @@ import type twilio from "twilio";
import type { ApplicationInstance } from "twilio/lib/rest/api/v2010/account/application"; import type { ApplicationInstance } from "twilio/lib/rest/api/v2010/account/application";
import db from "../../../../db"; import db from "../../../../db";
import getTwilioClient from "../../../../integrations/twilio"; import getTwilioClient, { getTwiMLName, smsUrl, voiceUrl } from "../../../../integrations/twilio";
type Payload = { type Payload = {
organizationId: string; organizationId: string;
@ -45,7 +45,7 @@ async function getTwimlApplication(
): Promise<ApplicationInstance> { ): Promise<ApplicationInstance> {
try { try {
if (organizationTwimlAppSid) { if (organizationTwimlAppSid) {
return updateTwimlApplication(twilioClient, organizationTwimlAppSid); return await updateTwimlApplication(twilioClient, organizationTwimlAppSid);
} }
} catch { } catch {
// twiml app with sid `organizationTwimlAppSid` probably doesn't exist anymore // twiml app with sid `organizationTwimlAppSid` probably doesn't exist anymore
@ -59,33 +59,23 @@ async function getTwimlApplication(
return twilioClient.applications.create({ return twilioClient.applications.create({
friendlyName: getTwiMLName(), friendlyName: getTwiMLName(),
smsUrl: `https://${serverRuntimeConfig.app.baseUrl}/api/webhook/incoming-message`, smsUrl,
smsMethod: "POST", smsMethod: "POST",
voiceUrl: `https://${serverRuntimeConfig.app.baseUrl}/api/webhook/call`, voiceUrl,
voiceMethod: "POST", voiceMethod: "POST",
}); });
} }
async function updateTwimlApplication(twilioClient: twilio.Twilio, twimlAppSid: string) { async function updateTwimlApplication(twilioClient: twilio.Twilio, twimlAppSid: string) {
await twilioClient.applications.get(twimlAppSid).update({ await twilioClient.applications.get(twimlAppSid).update({
smsUrl: `https://${serverRuntimeConfig.app.baseUrl}/api/webhook/incoming-message`, friendlyName: getTwiMLName(),
smsUrl,
smsMethod: "POST", smsMethod: "POST",
voiceUrl: `https://${serverRuntimeConfig.app.baseUrl}/api/webhook/call`, voiceUrl,
voiceMethod: "POST", voiceMethod: "POST",
}); });
return twilioClient.applications.get(twimlAppSid).fetch(); return twilioClient.applications.get(twimlAppSid).fetch();
} }
function getTwiMLName() {
switch (serverRuntimeConfig.app.baseUrl) {
case "local.shellphone.app":
return "Shellphone LOCAL";
case "dev.shellphone.app":
return "Shellphone DEV";
case "www.shellphone.app":
return "Shellphone";
}
}
export default setTwilioWebhooks; export default setTwilioWebhooks;

View File

@ -5,6 +5,7 @@ import type { CallInstance } from "twilio/lib/rest/api/v2010/account/call";
import db, { CallStatus, Direction } from "../../../../db"; import db, { CallStatus, Direction } from "../../../../db";
import appLogger from "../../../../integrations/logger"; import appLogger from "../../../../integrations/logger";
import { voiceUrl } from "../../../../integrations/twilio";
const { serverRuntimeConfig } = getConfig(); const { serverRuntimeConfig } = getConfig();
const logger = appLogger.child({ route: "/api/webhook/call" }); const logger = appLogger.child({ route: "/api/webhook/call" });
@ -17,7 +18,6 @@ type ApiError = {
export default async function incomingCallHandler(req: BlitzApiRequest, res: BlitzApiResponse) { export default async function incomingCallHandler(req: BlitzApiRequest, res: BlitzApiResponse) {
console.log("req.body", req.body); console.log("req.body", req.body);
const url = `https://${serverRuntimeConfig.app.baseUrl}/api/webhook/call`;
const twilioSignature = req.headers["X-Twilio-Signature"] || req.headers["x-twilio-signature"]; const twilioSignature = req.headers["X-Twilio-Signature"] || req.headers["x-twilio-signature"];
if (!twilioSignature || Array.isArray(twilioSignature)) { if (!twilioSignature || Array.isArray(twilioSignature)) {
const statusCode = 400; const statusCode = 400;
@ -42,7 +42,7 @@ export default async function incomingCallHandler(req: BlitzApiRequest, res: Bli
if ( if (
!phoneNumber || !phoneNumber ||
!phoneNumber.organization.twilioAuthToken || !phoneNumber.organization.twilioAuthToken ||
!twilio.validateRequest(phoneNumber.organization.twilioAuthToken, twilioSignature, url, req.body) !twilio.validateRequest(phoneNumber.organization.twilioAuthToken, twilioSignature, voiceUrl, req.body)
) { ) {
const statusCode = 400; const statusCode = 400;
const apiError: ApiError = { const apiError: ApiError = {
@ -93,7 +93,7 @@ export default async function incomingCallHandler(req: BlitzApiRequest, res: Bli
// find the organization currently using that phone number // find the organization currently using that phone number
// maybe we shouldn't let multiple organizations use the same phone number // maybe we shouldn't let multiple organizations use the same phone number
const authToken = phoneNumber.organization.twilioAuthToken ?? ""; const authToken = phoneNumber.organization.twilioAuthToken ?? "";
return twilio.validateRequest(authToken, twilioSignature, url, req.body); return twilio.validateRequest(authToken, twilioSignature, voiceUrl, req.body);
}); });
if (!phoneNumber) { if (!phoneNumber) {
const statusCode = 400; const statusCode = 400;

View File

@ -1,4 +1,4 @@
import { NotFoundError } from "blitz"; import { getConfig, NotFoundError } from "blitz";
import twilio from "twilio"; import twilio from "twilio";
import type { Organization } from "db"; import type { Organization } from "db";
@ -19,3 +19,20 @@ export default function getTwilioClient(organization: MinimalOrganization | null
accountSid: organization.twilioAccountSid, accountSid: organization.twilioAccountSid,
}); });
} }
const { serverRuntimeConfig } = getConfig();
export const smsUrl = `https://${serverRuntimeConfig.app.baseUrl}/api/webhook/incoming-message`;
export const voiceUrl = `https://${serverRuntimeConfig.app.baseUrl}/api/webhook/call`;
export function getTwiMLName() {
switch (serverRuntimeConfig.app.baseUrl) {
case "local.shellphone.app":
return "Shellphone LOCAL";
case "dev.shellphone.app":
return "Shellphone DEV";
case "www.shellphone.app":
return "Shellphone";
}
}