* first implementation of verifying phone number * add UI + logic for verifying phone number flow * check if all phone numbers are verified before submit * add numberVerification pending * only send SMS to verified numbers * fix design for mobile view * check if phone number is verified before testing action * add back message service id check * add TWILIO_VERIFY_SID to .env.example * code clean up * add TWILIO_VERIFY_SID to README.md instructions * save new verified numbers * fix wrongly thrown error for new verified numbers * use false as default value for isVerificationPending paramater * add translations * add migration file * code clean up * don't throw error if phone number is not needed * Feedback * Shows error when Twillio isn't properly setup * Type fixes Co-authored-by: CarinaWolli <wollencarina@gmail.com> Co-authored-by: zomars <zomars@me.com> Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
75 lines
2.2 KiB
TypeScript
75 lines
2.2 KiB
TypeScript
import TwilioClient from "twilio";
|
|
|
|
declare global {
|
|
// eslint-disable-next-line no-var
|
|
var twilio: TwilioClient.Twilio | undefined;
|
|
}
|
|
|
|
export const twilio =
|
|
globalThis.twilio ||
|
|
(process.env.TWILIO_SID && process.env.TWILIO_TOKEN && process.env.TWILIO_MESSAGING_SID)
|
|
? TwilioClient(process.env.TWILIO_SID, process.env.TWILIO_TOKEN)
|
|
: undefined;
|
|
|
|
if (process.env.NODE_ENV !== "production") {
|
|
globalThis.twilio = twilio;
|
|
}
|
|
|
|
function assertTwilio(twilio: TwilioClient.Twilio | undefined): asserts twilio is TwilioClient.Twilio {
|
|
if (!twilio) throw new Error("Twilio credentials are missing from the .env file");
|
|
}
|
|
|
|
export const sendSMS = async (phoneNumber: string, body: string, sender: string) => {
|
|
assertTwilio(twilio);
|
|
const response = await twilio.messages.create({
|
|
body: body,
|
|
messagingServiceSid: process.env.TWILIO_MESSAGING_SID,
|
|
to: phoneNumber,
|
|
from: sender ? sender : process.env.TWILIO_PHONE_NUMBER,
|
|
});
|
|
|
|
return response;
|
|
};
|
|
|
|
export const scheduleSMS = async (phoneNumber: string, body: string, scheduledDate: Date, sender: string) => {
|
|
assertTwilio(twilio);
|
|
const response = await twilio.messages.create({
|
|
body: body,
|
|
messagingServiceSid: process.env.TWILIO_MESSAGING_SID,
|
|
to: phoneNumber,
|
|
scheduleType: "fixed",
|
|
sendAt: scheduledDate,
|
|
from: sender ? sender : process.env.TWILIO_PHONE_NUMBER,
|
|
});
|
|
|
|
return response;
|
|
};
|
|
|
|
export const cancelSMS = async (referenceId: string) => {
|
|
assertTwilio(twilio);
|
|
await twilio.messages(referenceId).update({ status: "canceled" });
|
|
};
|
|
|
|
export const sendVerificationCode = async (phoneNumber: string) => {
|
|
assertTwilio(twilio);
|
|
if (process.env.TWILIO_VERIFY_SID) {
|
|
await twilio.verify
|
|
.services(process.env.TWILIO_VERIFY_SID)
|
|
.verifications.create({ to: phoneNumber, channel: "sms" });
|
|
}
|
|
};
|
|
|
|
export const verifyNumber = async (phoneNumber: string, code: string) => {
|
|
assertTwilio(twilio);
|
|
if (process.env.TWILIO_VERIFY_SID) {
|
|
try {
|
|
const verification_check = await twilio.verify.v2
|
|
.services(process.env.TWILIO_VERIFY_SID)
|
|
.verificationChecks.create({ to: phoneNumber, code: code });
|
|
return verification_check.status;
|
|
} catch (e) {
|
|
return "failed";
|
|
}
|
|
}
|
|
};
|