diff --git a/app/onboarding/api/queue/set-twilio-webhooks.ts b/app/onboarding/api/queue/set-twilio-webhooks.ts index 0e631e3..5308804 100644 --- a/app/onboarding/api/queue/set-twilio-webhooks.ts +++ b/app/onboarding/api/queue/set-twilio-webhooks.ts @@ -1,6 +1,7 @@ import { getConfig } from "blitz"; import { Queue } from "quirrel/blitz"; import twilio from "twilio"; +import type { ApplicationInstance } from "twilio/lib/rest/api/v2010/account/application"; import db from "../../../../db"; @@ -25,31 +26,65 @@ const setTwilioWebhooks = Queue("api/queue/set-twilio-webhooks", async return; } - const twimlApp = organization.twimlAppSid - ? await twilio(organization.twilioAccountSid, organization.twilioAuthToken) - .applications.get(organization.twimlAppSid) - .fetch() - : await twilio(organization.twilioAccountSid, organization.twilioAuthToken).applications.create({ - friendlyName: "Shellphone", - smsUrl: `https://${serverRuntimeConfig.app.baseUrl}/api/webhook/incoming-message`, - smsMethod: "POST", - voiceUrl: `https://${serverRuntimeConfig.app.baseUrl}/api/webhook/incoming-call`, - voiceMethod: "POST", - }); + const mainTwilioClient = twilio(organization.twilioAccountSid, organization.twilioAuthToken); + const [twimlApp, apiKey] = await Promise.all([ + getTwimlApplication(mainTwilioClient, organization.twimlAppSid), + mainTwilioClient.newKeys.create({ friendlyName: "Shellphone API key" }), + ]); const twimlAppSid = twimlApp.sid; await Promise.all([ db.organization.update({ where: { id: organizationId }, - data: { twimlAppSid }, + data: { + twimlAppSid, + twilioApiKey: apiKey.sid, + twilioApiSecret: apiKey.secret, + }, + }), + mainTwilioClient.incomingPhoneNumbers.get(phoneNumber.id).update({ + smsApplicationSid: twimlAppSid, + voiceApplicationSid: twimlAppSid, }), - twilio(organization.twilioAccountSid, organization.twilioAuthToken) - .incomingPhoneNumbers.get(phoneNumber.id) - .update({ - smsApplicationSid: twimlAppSid, - voiceApplicationSid: twimlAppSid, - }), ]); }); +async function getTwimlApplication( + twilioClient: twilio.Twilio, + organizationTwimlAppSid: string | null, +): Promise { + try { + if (organizationTwimlAppSid) { + return updateTwimlApplication(twilioClient, organizationTwimlAppSid); + } + } catch { + // twiml app with sid `organizationTwimlAppSid` probably doesn't exist anymore + } + + const twimlApps = await twilioClient.applications.list(); + const twimlApp = twimlApps.find((app) => app.friendlyName === "Shellphone"); + if (twimlApp) { + return updateTwimlApplication(twilioClient, twimlApp.sid); + } + + return twilioClient.applications.create({ + friendlyName: "Shellphone", + smsUrl: `https://${serverRuntimeConfig.app.baseUrl}/api/webhook/incoming-message`, + smsMethod: "POST", + voiceUrl: `https://${serverRuntimeConfig.app.baseUrl}/api/webhook/incoming-call`, + voiceMethod: "POST", + }); +} + +async function updateTwimlApplication(twilioClient: twilio.Twilio, twimlAppSid: string) { + await twilioClient.applications.get(twimlAppSid).update({ + smsUrl: `https://${serverRuntimeConfig.app.baseUrl}/api/webhook/incoming-message`, + smsMethod: "POST", + voiceUrl: `https://${serverRuntimeConfig.app.baseUrl}/api/webhook/incoming-call`, + voiceMethod: "POST", + }); + + return twilioClient.applications.get(twimlAppSid).fetch(); +} + export default setTwilioWebhooks; diff --git a/db/migrations/20210808034924_add_twilio_api_key/migration.sql b/db/migrations/20210808034924_add_twilio_api_key/migration.sql new file mode 100644 index 0000000..e8a78ba --- /dev/null +++ b/db/migrations/20210808034924_add_twilio_api_key/migration.sql @@ -0,0 +1,3 @@ +-- AlterTable +ALTER TABLE "Organization" ADD COLUMN "twilioApiKey" TEXT, +ADD COLUMN "twilioApiSecret" TEXT; diff --git a/db/schema.prisma b/db/schema.prisma index f2fc041..b04c773 100644 --- a/db/schema.prisma +++ b/db/schema.prisma @@ -22,6 +22,8 @@ model Organization { twilioAccountSid String? twilioAuthToken String? // TODO: encrypt it with encryptionKey + twilioApiKey String? + twilioApiSecret String? // TODO: encrypt it with encryptionKey twimlAppSid String? memberships Membership[]