feat: seprate twilio service

This commit is contained in:
BlankParticle
2025-05-26 16:09:07 +05:30
parent a39dd10776
commit 391f1d48d0
3 changed files with 59 additions and 29 deletions

View File

@@ -31,4 +31,6 @@ NODE_ENV="development"
AUTUMN_SECRET_KEY=
PERPLEXITY_API_KEY=
TWILIO_ACCOUNT_SID=
TWILIO_AUTH_TOKEN=
TWILIO_PHONE_NUMBER=

View File

@@ -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<HonoContext>();
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}`,
});
}
},
}),
],

View File

@@ -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,
},
};
};