From 391f1d48d0389a58af10b1c19fc927fdba4d814e Mon Sep 17 00:00:00 2001 From: BlankParticle Date: Mon, 26 May 2025 16:09:07 +0530 Subject: [PATCH] feat: seprate twilio service --- .env.example | 4 ++- apps/server/src/lib/auth.ts | 40 +++++++++--------------------- apps/server/src/lib/services.ts | 44 +++++++++++++++++++++++++++++++++ 3 files changed, 59 insertions(+), 29 deletions(-) diff --git a/.env.example b/.env.example index a7b68f603..af9fa2775 100644 --- a/.env.example +++ b/.env.example @@ -31,4 +31,6 @@ NODE_ENV="development" AUTUMN_SECRET_KEY= -PERPLEXITY_API_KEY= \ No newline at end of file +TWILIO_ACCOUNT_SID= +TWILIO_AUTH_TOKEN= +TWILIO_PHONE_NUMBER= \ No newline at end of file diff --git a/apps/server/src/lib/auth.ts b/apps/server/src/lib/auth.ts index fb60e20a3..76ab38fc5 100644 --- a/apps/server/src/lib/auth.ts +++ b/apps/server/src/lib/auth.ts @@ -12,10 +12,10 @@ import { defaultUserSettings } from '@zero/db/user_settings_default'; import { getBrowserTimezone, isValidTimezone } from './timezones'; import { drizzleAdapter } from 'better-auth/adapters/drizzle'; import { getSocialProviders } from './auth-providers'; +import { redis, resend, twilio } from './services'; import { getContext } from 'hono/context-storage'; import { getActiveDriver } from './driver/utils'; import { APIError } from 'better-auth/api'; -import { redis, resend } from './services'; import type { HonoContext } from '../ctx'; import { env } from 'cloudflare:workers'; import { createDriver } from './driver'; @@ -79,39 +79,23 @@ const connectionHandlerHook = async (account: Account) => { export const createAuth = () => { const c = getContext(); + const twilioClient = twilio(); return betterAuth({ plugins: [ phoneNumber({ sendOTP: async (data) => { - if (!env.TWILIO_ACCOUNT_SID || !env.TWILIO_AUTH_TOKEN || !env.TWILIO_PHONE_NUMBER) { - throw new APIError('INTERNAL_SERVER_ERROR', { - message: 'Twilio configuration missing', + await twilioClient.messages + .send( + data.phoneNumber, + `Your verification code is: ${data.code}, do not share it with anyone.`, + ) + .catch((error) => { + console.error('Failed to send OTP', error); + throw new APIError('INTERNAL_SERVER_ERROR', { + message: `Failed to send OTP, ${error.message}`, + }); }); - } - - const response = await fetch( - `https://api.twilio.com/2010-04-01/Accounts/${env.TWILIO_ACCOUNT_SID}/Messages.json`, - { - method: 'POST', - headers: { - 'Content-Type': 'application/x-www-form-urlencoded', - Authorization: `Basic ${btoa(`${env.TWILIO_ACCOUNT_SID}:${env.TWILIO_AUTH_TOKEN}`)}`, - }, - body: new URLSearchParams({ - To: data.phoneNumber, - From: env.TWILIO_PHONE_NUMBER, - Body: `Your verification code is: ${data.code}, do not share it with anyone.`, - }), - }, - ); - - if (!response.ok) { - const error = await response.text(); - throw new APIError('INTERNAL_SERVER_ERROR', { - message: `Failed to send OTP: ${error}`, - }); - } }, }), ], diff --git a/apps/server/src/lib/services.ts b/apps/server/src/lib/services.ts index b4e448fd5..7d6d3dc95 100644 --- a/apps/server/src/lib/services.ts +++ b/apps/server/src/lib/services.ts @@ -8,3 +8,47 @@ export const resend = () => : { emails: { send: async (...args: unknown[]) => console.log(args) } }; export const redis = () => new Redis({ url: env.REDIS_URL, token: env.REDIS_TOKEN }); + +export const twilio = (forceUseRealService = false) => { + if (env.NODE_ENV === 'development' && !forceUseRealService) { + return { + messages: { + send: async (to: string, body: string) => + console.log(`[TWILIO:MOCK] Sending message to ${to}: ${body}`), + }, + }; + } + + if (!env.TWILIO_ACCOUNT_SID || !env.TWILIO_AUTH_TOKEN || !env.TWILIO_PHONE_NUMBER) { + throw new Error('Twilio is not configured correctly'); + } + + const send = async (to: string, body: string) => { + const response = await fetch( + `https://api.twilio.com/2010-04-01/Accounts/${env.TWILIO_ACCOUNT_SID}/Messages.json`, + { + method: 'POST', + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + Authorization: `Basic ${btoa(`${env.TWILIO_ACCOUNT_SID}:${env.TWILIO_AUTH_TOKEN}`)}`, + }, + body: new URLSearchParams({ + To: to, + From: env.TWILIO_PHONE_NUMBER, + Body: body, + }), + }, + ); + + if (!response.ok) { + const error = await response.text(); + throw new Error(`Failed to send OTP: ${error}`); + } + }; + + return { + messages: { + send, + }, + }; +};