diff --git a/app/messages/api/queue/fetch-messages.ts b/app/messages/api/queue/fetch-messages.ts index f19a3c7..2aafe23 100644 --- a/app/messages/api/queue/fetch-messages.ts +++ b/app/messages/api/queue/fetch-messages.ts @@ -1,8 +1,8 @@ import { Queue } from "quirrel/blitz"; -import twilio from "twilio"; import db from "../../../../db"; import insertMessagesQueue from "./insert-messages"; +import getTwilioClient from "../../../../integrations/twilio"; type Payload = { organizationId: string; @@ -19,13 +19,10 @@ const fetchMessagesQueue = Queue("api/queue/fetch-messages", async ({ o } const organization = phoneNumber.organization; - if (!organization.twilioAccountSid || !organization.twilioAuthToken) { - return; - } - + const twilioClient = getTwilioClient(organization); const [sent, received] = await Promise.all([ - twilio(organization.twilioAccountSid, organization.twilioAuthToken).messages.list({ from: phoneNumber.number }), - twilio(organization.twilioAccountSid, organization.twilioAuthToken).messages.list({ to: phoneNumber.number }), + twilioClient.messages.list({ from: phoneNumber.number }), + twilioClient.messages.list({ to: phoneNumber.number }), ]); const messagesSent = sent.filter((message) => message.direction.startsWith("outbound")); const messagesReceived = received.filter((message) => message.direction === "inbound"); diff --git a/app/messages/api/queue/insert-incoming-message.ts b/app/messages/api/queue/insert-incoming-message.ts index f75bf1a..f90a65d 100644 --- a/app/messages/api/queue/insert-incoming-message.ts +++ b/app/messages/api/queue/insert-incoming-message.ts @@ -1,10 +1,10 @@ import { Queue } from "quirrel/blitz"; import type { MessageInstance } from "twilio/lib/rest/api/v2010/account/message"; -import twilio from "twilio"; import db, { Direction, MessageStatus } from "../../../../db"; import { encrypt } from "../../../../db/_encryption"; import notifyIncomingMessageQueue from "./notify-incoming-message"; +import getTwilioClient from "../../../../integrations/twilio"; type Payload = { organizationId: string; @@ -18,13 +18,12 @@ const insertIncomingMessageQueue = Queue( const organization = await db.organization.findFirst({ where: { id: organizationId }, }); - if (!organization || !organization.twilioAccountSid || !organization.twilioAuthToken) { + if (!organization) { return; } - const message = await twilio(organization.twilioAccountSid, organization.twilioAuthToken) - .messages.get(messageSid) - .fetch(); + const twilioClient = getTwilioClient(organization); + const message = await twilioClient.messages.get(messageSid).fetch(); await db.message.create({ data: { organizationId, diff --git a/app/messages/api/queue/notify-incoming-message.ts b/app/messages/api/queue/notify-incoming-message.ts index 830303a..99ccf6f 100644 --- a/app/messages/api/queue/notify-incoming-message.ts +++ b/app/messages/api/queue/notify-incoming-message.ts @@ -1,11 +1,11 @@ import { getConfig } from "blitz"; import { Queue } from "quirrel/blitz"; import type { MessageInstance } from "twilio/lib/rest/api/v2010/account/message"; -import twilio from "twilio"; import webpush, { PushSubscription, WebPushError } from "web-push"; import db from "../../../../db"; import appLogger from "../../../../integrations/logger"; +import getTwilioClient from "../../../../integrations/twilio"; const { serverRuntimeConfig, publicRuntimeConfig } = getConfig(); const logger = appLogger.child({ queue: "notify-incoming-message" }); @@ -28,15 +28,12 @@ const notifyIncomingMessageQueue = Queue( const organization = await db.organization.findFirst({ where: { id: organizationId }, }); - if (!organization || !organization.twilioAccountSid || !organization.twilioAuthToken) { - return; - } - - const message = await twilio(organization.twilioAccountSid, organization.twilioAuthToken) - .messages.get(messageSid) - .fetch(); + const twilioClient = getTwilioClient(organization); + const message = await twilioClient.messages.get(messageSid).fetch(); const notification = { message: `${message.from} - ${message.body}` }; - const subscriptions = await db.notificationSubscription.findMany({ where: { organizationId, phoneNumberId } }); + const subscriptions = await db.notificationSubscription.findMany({ + where: { organizationId, phoneNumberId }, + }); await Promise.all( subscriptions.map(async (subscription) => { const webPushSubscription: PushSubscription = { diff --git a/app/messages/api/queue/send-message.ts b/app/messages/api/queue/send-message.ts index 395cd64..fb6ba36 100644 --- a/app/messages/api/queue/send-message.ts +++ b/app/messages/api/queue/send-message.ts @@ -1,7 +1,7 @@ import { Queue } from "quirrel/blitz"; -import twilio from "twilio"; import db, { MessageStatus } from "../../../../db"; +import getTwilioClient from "../../../../integrations/twilio"; type Payload = { id: string; @@ -19,12 +19,13 @@ const sendMessageQueue = Queue( include: { phoneNumbers: true }, }); const phoneNumber = organization?.phoneNumbers.find((phoneNumber) => phoneNumber.id === phoneNumberId); - if (!organization || !organization.twilioAccountSid || !organization.twilioAuthToken || !phoneNumber) { + if (!organization || !phoneNumber) { return; } + const twilioClient = getTwilioClient(organization); try { - const message = await twilio(organization.twilioAccountSid, organization.twilioAuthToken).messages.create({ + const message = await twilioClient.messages.create({ body: content, to, from: phoneNumber.number, diff --git a/app/messages/api/webhook/incoming-message.ts b/app/messages/api/webhook/incoming-message.ts index 3629f4c..dcaf6dc 100644 --- a/app/messages/api/webhook/incoming-message.ts +++ b/app/messages/api/webhook/incoming-message.ts @@ -6,7 +6,6 @@ import type { ApiError } from "../../../api/_types"; import appLogger from "../../../../integrations/logger"; import db from "../../../../db"; import insertIncomingMessageQueue from "../queue/insert-incoming-message"; -import notifyIncomingMessageQueue from "../queue/notify-incoming-message"; const logger = appLogger.child({ route: "/api/webhook/incoming-message" }); const { serverRuntimeConfig } = getConfig(); diff --git a/app/messages/mutations/send-message.ts b/app/messages/mutations/send-message.ts index d3f1418..05ae4d7 100644 --- a/app/messages/mutations/send-message.ts +++ b/app/messages/mutations/send-message.ts @@ -1,11 +1,11 @@ import { NotFoundError, resolver } from "blitz"; import { z } from "zod"; -import twilio from "twilio"; import db, { Direction, MessageStatus } from "../../../db"; import { encrypt } from "../../../db/_encryption"; import sendMessageQueue from "../../messages/api/queue/send-message"; import appLogger from "../../../integrations/logger"; +import getTwilioClient from "../../../integrations/twilio"; const logger = appLogger.child({ mutation: "send-message" }); @@ -23,12 +23,10 @@ export default resolver.pipe(resolver.zod(Body), resolver.authorize(), async ({ if (!organization) { throw new NotFoundError(); } - if (!organization.twilioAccountSid || !organization.twilioAuthToken) { - return; - } + const twilioClient = getTwilioClient(organization); try { - await twilio(organization.twilioAccountSid, organization.twilioAuthToken).lookups.v1.phoneNumbers(to).fetch(); + await twilioClient.lookups.v1.phoneNumbers(to).fetch(); } catch (error) { logger.error(error); return; diff --git a/app/onboarding/mutations/set-phone-number.ts b/app/onboarding/mutations/set-phone-number.ts index 388f02e..2b2f1c6 100644 --- a/app/onboarding/mutations/set-phone-number.ts +++ b/app/onboarding/mutations/set-phone-number.ts @@ -1,12 +1,12 @@ import { resolver } from "blitz"; import { z } from "zod"; -import twilio from "twilio"; import db from "../../../db"; import getCurrentUser from "../../users/queries/get-current-user"; import fetchMessagesQueue from "../../messages/api/queue/fetch-messages"; import fetchCallsQueue from "../../phone-calls/api/queue/fetch-calls"; import setTwilioWebhooks from "../api/queue/set-twilio-webhooks"; +import getTwilioClient from "../../../integrations/twilio"; const Body = z.object({ phoneNumberSid: z.string(), @@ -15,14 +15,12 @@ const Body = z.object({ export default resolver.pipe(resolver.zod(Body), resolver.authorize(), async ({ phoneNumberSid }, context) => { const user = await getCurrentUser(null, context); const organization = user?.memberships[0]!.organization; - if (!user || !organization || !organization.twilioAccountSid || !organization.twilioAuthToken) { + if (!user || !organization) { return; } - const phoneNumbers = await twilio( - organization.twilioAccountSid, - organization.twilioAuthToken, - ).incomingPhoneNumbers.list(); + const twilioClient = getTwilioClient(organization); + const phoneNumbers = await twilioClient.incomingPhoneNumbers.list(); const phoneNumber = phoneNumbers.find((phoneNumber) => phoneNumber.sid === phoneNumberSid)!; const organizationId = organization.id; await db.phoneNumber.create({ diff --git a/app/onboarding/pages/welcome/step-three.tsx b/app/onboarding/pages/welcome/step-three.tsx index deacb34..a182715 100644 --- a/app/onboarding/pages/welcome/step-three.tsx +++ b/app/onboarding/pages/welcome/step-three.tsx @@ -1,13 +1,13 @@ import type { BlitzPage, GetServerSideProps } from "blitz"; import { Routes, getSession, useRouter, useMutation } from "blitz"; import { useEffect } from "react"; -import twilio from "twilio"; import { useForm } from "react-hook-form"; import clsx from "clsx"; import db from "../../../../db"; import OnboardingLayout from "../../components/onboarding-layout"; import setPhoneNumber from "../../mutations/set-phone-number"; +import getTwilioClient from "../../../../integrations/twilio"; type PhoneNumber = { phoneNumber: string; @@ -130,10 +130,8 @@ export const getServerSideProps: GetServerSideProps = async ({ req, res } }; } - const incomingPhoneNumbers = await twilio( - organization.twilioAccountSid, - organization.twilioAuthToken, - ).incomingPhoneNumbers.list(); + const twilioClient = getTwilioClient(organization); + const incomingPhoneNumbers = await twilioClient.incomingPhoneNumbers.list(); const phoneNumbers = incomingPhoneNumbers.map(({ phoneNumber, sid }) => ({ phoneNumber, sid })); return { diff --git a/app/phone-calls/api/queue/fetch-calls.ts b/app/phone-calls/api/queue/fetch-calls.ts index 09b02d7..ef0732c 100644 --- a/app/phone-calls/api/queue/fetch-calls.ts +++ b/app/phone-calls/api/queue/fetch-calls.ts @@ -1,8 +1,8 @@ import { Queue } from "quirrel/blitz"; -import twilio from "twilio"; import db from "../../../../db"; import insertCallsQueue from "./insert-calls"; +import getTwilioClient from "../../../../integrations/twilio"; type Payload = { organizationId: string; @@ -19,17 +19,10 @@ const fetchCallsQueue = Queue("api/queue/fetch-calls", async ({ organiz } const organization = phoneNumber.organization; - if (!organization.twilioAccountSid || !organization.twilioAuthToken) { - return; - } - + const twilioClient = getTwilioClient(organization); const [callsSent, callsReceived] = await Promise.all([ - twilio(organization.twilioAccountSid, organization.twilioAuthToken).calls.list({ - from: phoneNumber.number, - }), - twilio(organization.twilioAccountSid, organization.twilioAuthToken).calls.list({ - to: phoneNumber.number, - }), + twilioClient.calls.list({ from: phoneNumber.number }), + twilioClient.calls.list({ to: phoneNumber.number }), ]); const calls = [...callsSent, ...callsReceived].sort((a, b) => a.dateCreated.getTime() - b.dateCreated.getTime()); diff --git a/app/users/queries/get-current-user.ts b/app/users/queries/get-current-user.ts index fc5e851..7fb0cf4 100644 --- a/app/users/queries/get-current-user.ts +++ b/app/users/queries/get-current-user.ts @@ -21,7 +21,8 @@ export default async function getCurrentUser(_ = null, { session }: Ctx) { paddleCustomerId: true, paddleSubscriptionId: true, twilioAccountSid: true, - twilioAuthToken: true, + twilioApiKey: true, + twilioApiSecret: true, twimlAppSid: true, }, }, diff --git a/integrations/twilio.ts b/integrations/twilio.ts new file mode 100644 index 0000000..86f5440 --- /dev/null +++ b/integrations/twilio.ts @@ -0,0 +1,21 @@ +import { NotFoundError } from "blitz"; +import twilio from "twilio"; + +import type { Organization } from "db"; + +type MinimalOrganization = Pick; + +export default function getTwilioClient(organization: MinimalOrganization | null): twilio.Twilio { + if ( + !organization || + !organization.twilioAccountSid || + !organization.twilioApiKey || + !organization.twilioApiSecret + ) { + throw new NotFoundError(); + } + + return twilio(organization.twilioApiKey, organization.twilioApiSecret, { + accountSid: organization.twilioAccountSid, + }); +}