shellphone.app/app/messages/api/webhook/incoming-message.ts

118 lines
2.8 KiB
TypeScript
Raw Normal View History

2021-07-31 17:22:48 +00:00
import type { BlitzApiRequest, BlitzApiResponse } from "blitz";
import { getConfig } from "blitz";
import twilio from "twilio";
2021-07-31 14:33:18 +00:00
import type { ApiError } from "../../../api/_types";
import appLogger from "../../../../integrations/logger";
import db from "../../../../db";
import insertIncomingMessageQueue from "../queue/insert-incoming-message";
2021-07-31 14:33:18 +00:00
const logger = appLogger.child({ route: "/api/webhook/incoming-message" });
const { serverRuntimeConfig } = getConfig();
2021-07-31 14:33:18 +00:00
2021-07-31 17:22:48 +00:00
export default async function incomingMessageHandler(req: BlitzApiRequest, res: BlitzApiResponse) {
2021-07-31 14:33:18 +00:00
if (req.method !== "POST") {
const statusCode = 405;
2021-07-31 14:33:18 +00:00
const apiError: ApiError = {
statusCode,
errorMessage: `Method ${req.method} Not Allowed`,
};
logger.error(apiError);
2021-07-31 14:33:18 +00:00
res.setHeader("Allow", ["POST"]);
res.status(statusCode).send(apiError);
return;
2021-07-31 14:33:18 +00:00
}
const twilioSignature = req.headers["X-Twilio-Signature"] || req.headers["x-twilio-signature"];
2021-07-31 14:33:18 +00:00
if (!twilioSignature || Array.isArray(twilioSignature)) {
const statusCode = 400;
2021-07-31 14:33:18 +00:00
const apiError: ApiError = {
statusCode,
errorMessage: "Invalid header X-Twilio-Signature",
};
logger.error(apiError);
2021-07-31 14:33:18 +00:00
res.status(statusCode).send(apiError);
return;
2021-07-31 14:33:18 +00:00
}
const body: Body = req.body;
2021-07-31 14:33:18 +00:00
try {
const customerPhoneNumber = await db.phoneNumber.findFirst({
where: { phoneNumber: body.To },
});
2021-08-01 10:36:32 +00:00
if (!customerPhoneNumber) {
// phone number is not registered by any of our customer
2021-08-01 10:36:32 +00:00
res.status(200).end();
return;
}
2021-07-31 14:33:18 +00:00
const customer = await db.customer.findFirst({
2021-08-01 10:36:32 +00:00
where: { id: customerPhoneNumber.customerId },
});
2021-08-01 10:36:32 +00:00
if (!customer || !customer.authToken) {
res.status(200).end();
return;
}
const url = `https://${serverRuntimeConfig.app.baseUrl}/api/webhook/incoming-message`;
2021-08-01 14:03:49 +00:00
const isRequestValid = twilio.validateRequest(customer.authToken, twilioSignature, url, req.body);
2021-07-31 14:33:18 +00:00
if (!isRequestValid) {
const statusCode = 400;
2021-07-31 14:33:18 +00:00
const apiError: ApiError = {
statusCode,
errorMessage: "Invalid webhook",
};
logger.error(apiError);
2021-07-31 14:33:18 +00:00
res.status(statusCode).send(apiError);
return;
2021-07-31 14:33:18 +00:00
}
2021-08-01 10:36:32 +00:00
// TODO: send notification
const messageSid = body.MessageSid;
await insertIncomingMessageQueue.enqueue(
{
messageSid,
2021-08-01 10:36:32 +00:00
customerId: customer.id,
2021-07-31 14:33:18 +00:00
},
2021-08-01 12:04:04 +00:00
{ id: messageSid },
);
res.status(200).end();
2021-07-31 14:33:18 +00:00
} catch (error) {
const statusCode = error.statusCode ?? 500;
2021-07-31 14:33:18 +00:00
const apiError: ApiError = {
statusCode,
errorMessage: error.message,
};
logger.error(error);
2021-07-31 14:33:18 +00:00
res.status(statusCode).send(apiError);
2021-07-31 14:33:18 +00:00
}
}
2021-08-01 10:36:32 +00:00
type Body = {
ToCountry: string;
ToState: string;
SmsMessageSid: string;
NumMedia: string;
ToCity: string;
FromZip: string;
SmsSid: string;
FromState: string;
SmsStatus: string;
FromCity: string;
Body: string;
FromCountry: string;
To: string;
ToZip: string;
NumSegments: string;
MessageSid: string;
AccountSid: string;
From: string;
ApiVersion: string;
};