use twilio api key/secret instead of authToken

This commit is contained in:
m5r 2021-08-08 12:28:19 +08:00
parent 835fcaffcc
commit 6f672bfeb2
11 changed files with 55 additions and 53 deletions

View File

@ -1,8 +1,8 @@
import { Queue } from "quirrel/blitz"; import { Queue } from "quirrel/blitz";
import twilio from "twilio";
import db from "../../../../db"; import db from "../../../../db";
import insertMessagesQueue from "./insert-messages"; import insertMessagesQueue from "./insert-messages";
import getTwilioClient from "../../../../integrations/twilio";
type Payload = { type Payload = {
organizationId: string; organizationId: string;
@ -19,13 +19,10 @@ const fetchMessagesQueue = Queue<Payload>("api/queue/fetch-messages", async ({ o
} }
const organization = phoneNumber.organization; const organization = phoneNumber.organization;
if (!organization.twilioAccountSid || !organization.twilioAuthToken) { const twilioClient = getTwilioClient(organization);
return;
}
const [sent, received] = await Promise.all([ const [sent, received] = await Promise.all([
twilio(organization.twilioAccountSid, organization.twilioAuthToken).messages.list({ from: phoneNumber.number }), twilioClient.messages.list({ from: phoneNumber.number }),
twilio(organization.twilioAccountSid, organization.twilioAuthToken).messages.list({ to: phoneNumber.number }), twilioClient.messages.list({ to: phoneNumber.number }),
]); ]);
const messagesSent = sent.filter((message) => message.direction.startsWith("outbound")); const messagesSent = sent.filter((message) => message.direction.startsWith("outbound"));
const messagesReceived = received.filter((message) => message.direction === "inbound"); const messagesReceived = received.filter((message) => message.direction === "inbound");

View File

@ -1,10 +1,10 @@
import { Queue } from "quirrel/blitz"; import { Queue } from "quirrel/blitz";
import type { MessageInstance } from "twilio/lib/rest/api/v2010/account/message"; import type { MessageInstance } from "twilio/lib/rest/api/v2010/account/message";
import twilio from "twilio";
import db, { Direction, MessageStatus } from "../../../../db"; import db, { Direction, MessageStatus } from "../../../../db";
import { encrypt } from "../../../../db/_encryption"; import { encrypt } from "../../../../db/_encryption";
import notifyIncomingMessageQueue from "./notify-incoming-message"; import notifyIncomingMessageQueue from "./notify-incoming-message";
import getTwilioClient from "../../../../integrations/twilio";
type Payload = { type Payload = {
organizationId: string; organizationId: string;
@ -18,13 +18,12 @@ const insertIncomingMessageQueue = Queue<Payload>(
const organization = await db.organization.findFirst({ const organization = await db.organization.findFirst({
where: { id: organizationId }, where: { id: organizationId },
}); });
if (!organization || !organization.twilioAccountSid || !organization.twilioAuthToken) { if (!organization) {
return; return;
} }
const message = await twilio(organization.twilioAccountSid, organization.twilioAuthToken) const twilioClient = getTwilioClient(organization);
.messages.get(messageSid) const message = await twilioClient.messages.get(messageSid).fetch();
.fetch();
await db.message.create({ await db.message.create({
data: { data: {
organizationId, organizationId,

View File

@ -1,11 +1,11 @@
import { getConfig } from "blitz"; import { getConfig } from "blitz";
import { Queue } from "quirrel/blitz"; import { Queue } from "quirrel/blitz";
import type { MessageInstance } from "twilio/lib/rest/api/v2010/account/message"; import type { MessageInstance } from "twilio/lib/rest/api/v2010/account/message";
import twilio from "twilio";
import webpush, { PushSubscription, WebPushError } from "web-push"; import webpush, { PushSubscription, WebPushError } from "web-push";
import db from "../../../../db"; import db from "../../../../db";
import appLogger from "../../../../integrations/logger"; import appLogger from "../../../../integrations/logger";
import getTwilioClient from "../../../../integrations/twilio";
const { serverRuntimeConfig, publicRuntimeConfig } = getConfig(); const { serverRuntimeConfig, publicRuntimeConfig } = getConfig();
const logger = appLogger.child({ queue: "notify-incoming-message" }); const logger = appLogger.child({ queue: "notify-incoming-message" });
@ -28,15 +28,12 @@ const notifyIncomingMessageQueue = Queue<Payload>(
const organization = await db.organization.findFirst({ const organization = await db.organization.findFirst({
where: { id: organizationId }, where: { id: organizationId },
}); });
if (!organization || !organization.twilioAccountSid || !organization.twilioAuthToken) { const twilioClient = getTwilioClient(organization);
return; const message = await twilioClient.messages.get(messageSid).fetch();
}
const message = await twilio(organization.twilioAccountSid, organization.twilioAuthToken)
.messages.get(messageSid)
.fetch();
const notification = { message: `${message.from} - ${message.body}` }; 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( await Promise.all(
subscriptions.map(async (subscription) => { subscriptions.map(async (subscription) => {
const webPushSubscription: PushSubscription = { const webPushSubscription: PushSubscription = {

View File

@ -1,7 +1,7 @@
import { Queue } from "quirrel/blitz"; import { Queue } from "quirrel/blitz";
import twilio from "twilio";
import db, { MessageStatus } from "../../../../db"; import db, { MessageStatus } from "../../../../db";
import getTwilioClient from "../../../../integrations/twilio";
type Payload = { type Payload = {
id: string; id: string;
@ -19,12 +19,13 @@ const sendMessageQueue = Queue<Payload>(
include: { phoneNumbers: true }, include: { phoneNumbers: true },
}); });
const phoneNumber = organization?.phoneNumbers.find((phoneNumber) => phoneNumber.id === phoneNumberId); const phoneNumber = organization?.phoneNumbers.find((phoneNumber) => phoneNumber.id === phoneNumberId);
if (!organization || !organization.twilioAccountSid || !organization.twilioAuthToken || !phoneNumber) { if (!organization || !phoneNumber) {
return; return;
} }
const twilioClient = getTwilioClient(organization);
try { try {
const message = await twilio(organization.twilioAccountSid, organization.twilioAuthToken).messages.create({ const message = await twilioClient.messages.create({
body: content, body: content,
to, to,
from: phoneNumber.number, from: phoneNumber.number,

View File

@ -6,7 +6,6 @@ import type { ApiError } from "../../../api/_types";
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 notifyIncomingMessageQueue from "../queue/notify-incoming-message";
const logger = appLogger.child({ route: "/api/webhook/incoming-message" }); const logger = appLogger.child({ route: "/api/webhook/incoming-message" });
const { serverRuntimeConfig } = getConfig(); const { serverRuntimeConfig } = getConfig();

View File

@ -1,11 +1,11 @@
import { NotFoundError, resolver } from "blitz"; import { NotFoundError, resolver } from "blitz";
import { z } from "zod"; import { z } from "zod";
import twilio from "twilio";
import db, { Direction, MessageStatus } from "../../../db"; import db, { Direction, MessageStatus } from "../../../db";
import { encrypt } from "../../../db/_encryption"; import { encrypt } from "../../../db/_encryption";
import sendMessageQueue from "../../messages/api/queue/send-message"; import sendMessageQueue from "../../messages/api/queue/send-message";
import appLogger from "../../../integrations/logger"; import appLogger from "../../../integrations/logger";
import getTwilioClient from "../../../integrations/twilio";
const logger = appLogger.child({ mutation: "send-message" }); const logger = appLogger.child({ mutation: "send-message" });
@ -23,12 +23,10 @@ export default resolver.pipe(resolver.zod(Body), resolver.authorize(), async ({
if (!organization) { if (!organization) {
throw new NotFoundError(); throw new NotFoundError();
} }
if (!organization.twilioAccountSid || !organization.twilioAuthToken) {
return;
}
const twilioClient = getTwilioClient(organization);
try { try {
await twilio(organization.twilioAccountSid, organization.twilioAuthToken).lookups.v1.phoneNumbers(to).fetch(); await twilioClient.lookups.v1.phoneNumbers(to).fetch();
} catch (error) { } catch (error) {
logger.error(error); logger.error(error);
return; return;

View File

@ -1,12 +1,12 @@
import { resolver } from "blitz"; import { resolver } from "blitz";
import { z } from "zod"; import { z } from "zod";
import twilio from "twilio";
import db from "../../../db"; import db from "../../../db";
import getCurrentUser from "../../users/queries/get-current-user"; import getCurrentUser from "../../users/queries/get-current-user";
import fetchMessagesQueue from "../../messages/api/queue/fetch-messages"; import fetchMessagesQueue from "../../messages/api/queue/fetch-messages";
import fetchCallsQueue from "../../phone-calls/api/queue/fetch-calls"; import fetchCallsQueue from "../../phone-calls/api/queue/fetch-calls";
import setTwilioWebhooks from "../api/queue/set-twilio-webhooks"; import setTwilioWebhooks from "../api/queue/set-twilio-webhooks";
import getTwilioClient from "../../../integrations/twilio";
const Body = z.object({ const Body = z.object({
phoneNumberSid: z.string(), phoneNumberSid: z.string(),
@ -15,14 +15,12 @@ const Body = z.object({
export default resolver.pipe(resolver.zod(Body), resolver.authorize(), async ({ phoneNumberSid }, context) => { export default resolver.pipe(resolver.zod(Body), resolver.authorize(), async ({ phoneNumberSid }, context) => {
const user = await getCurrentUser(null, context); const user = await getCurrentUser(null, context);
const organization = user?.memberships[0]!.organization; const organization = user?.memberships[0]!.organization;
if (!user || !organization || !organization.twilioAccountSid || !organization.twilioAuthToken) { if (!user || !organization) {
return; return;
} }
const phoneNumbers = await twilio( const twilioClient = getTwilioClient(organization);
organization.twilioAccountSid, const phoneNumbers = await twilioClient.incomingPhoneNumbers.list();
organization.twilioAuthToken,
).incomingPhoneNumbers.list();
const phoneNumber = phoneNumbers.find((phoneNumber) => phoneNumber.sid === phoneNumberSid)!; const phoneNumber = phoneNumbers.find((phoneNumber) => phoneNumber.sid === phoneNumberSid)!;
const organizationId = organization.id; const organizationId = organization.id;
await db.phoneNumber.create({ await db.phoneNumber.create({

View File

@ -1,13 +1,13 @@
import type { BlitzPage, GetServerSideProps } from "blitz"; import type { BlitzPage, GetServerSideProps } from "blitz";
import { Routes, getSession, useRouter, useMutation } from "blitz"; import { Routes, getSession, useRouter, useMutation } from "blitz";
import { useEffect } from "react"; import { useEffect } from "react";
import twilio from "twilio";
import { useForm } from "react-hook-form"; import { useForm } from "react-hook-form";
import clsx from "clsx"; import clsx from "clsx";
import db from "../../../../db"; import db from "../../../../db";
import OnboardingLayout from "../../components/onboarding-layout"; import OnboardingLayout from "../../components/onboarding-layout";
import setPhoneNumber from "../../mutations/set-phone-number"; import setPhoneNumber from "../../mutations/set-phone-number";
import getTwilioClient from "../../../../integrations/twilio";
type PhoneNumber = { type PhoneNumber = {
phoneNumber: string; phoneNumber: string;
@ -130,10 +130,8 @@ export const getServerSideProps: GetServerSideProps<Props> = async ({ req, res }
}; };
} }
const incomingPhoneNumbers = await twilio( const twilioClient = getTwilioClient(organization);
organization.twilioAccountSid, const incomingPhoneNumbers = await twilioClient.incomingPhoneNumbers.list();
organization.twilioAuthToken,
).incomingPhoneNumbers.list();
const phoneNumbers = incomingPhoneNumbers.map(({ phoneNumber, sid }) => ({ phoneNumber, sid })); const phoneNumbers = incomingPhoneNumbers.map(({ phoneNumber, sid }) => ({ phoneNumber, sid }));
return { return {

View File

@ -1,8 +1,8 @@
import { Queue } from "quirrel/blitz"; import { Queue } from "quirrel/blitz";
import twilio from "twilio";
import db from "../../../../db"; import db from "../../../../db";
import insertCallsQueue from "./insert-calls"; import insertCallsQueue from "./insert-calls";
import getTwilioClient from "../../../../integrations/twilio";
type Payload = { type Payload = {
organizationId: string; organizationId: string;
@ -19,17 +19,10 @@ const fetchCallsQueue = Queue<Payload>("api/queue/fetch-calls", async ({ organiz
} }
const organization = phoneNumber.organization; const organization = phoneNumber.organization;
if (!organization.twilioAccountSid || !organization.twilioAuthToken) { const twilioClient = getTwilioClient(organization);
return;
}
const [callsSent, callsReceived] = await Promise.all([ const [callsSent, callsReceived] = await Promise.all([
twilio(organization.twilioAccountSid, organization.twilioAuthToken).calls.list({ twilioClient.calls.list({ from: phoneNumber.number }),
from: phoneNumber.number, twilioClient.calls.list({ to: phoneNumber.number }),
}),
twilio(organization.twilioAccountSid, organization.twilioAuthToken).calls.list({
to: phoneNumber.number,
}),
]); ]);
const calls = [...callsSent, ...callsReceived].sort((a, b) => a.dateCreated.getTime() - b.dateCreated.getTime()); const calls = [...callsSent, ...callsReceived].sort((a, b) => a.dateCreated.getTime() - b.dateCreated.getTime());

View File

@ -21,7 +21,8 @@ export default async function getCurrentUser(_ = null, { session }: Ctx) {
paddleCustomerId: true, paddleCustomerId: true,
paddleSubscriptionId: true, paddleSubscriptionId: true,
twilioAccountSid: true, twilioAccountSid: true,
twilioAuthToken: true, twilioApiKey: true,
twilioApiSecret: true,
twimlAppSid: true, twimlAppSid: true,
}, },
}, },

21
integrations/twilio.ts Normal file
View File

@ -0,0 +1,21 @@
import { NotFoundError } from "blitz";
import twilio from "twilio";
import type { Organization } from "db";
type MinimalOrganization = Pick<Organization, "twilioAccountSid" | "twilioApiKey" | "twilioApiSecret">;
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,
});
}