diff --git a/src/app.module.ts b/src/app.module.ts index 4a10051..a85d5e7 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -6,10 +6,33 @@ import { OtpService } from './otp.service'; import { SmsService } from './sms.service'; import { MailService } from './mail.service'; import { AppRedisModule } from './redis.provider'; +import { SmsProvider } from './sms/sms.provider'; +import { AfricasTalkingSmsProvider } from './sms/africastalking.provider'; +import { LeopardSmsProvider } from './sms/leopard.provider'; + @Module({ imports: [AppRedisModule], controllers: [AuthController], - providers: [OtpService,SmsService,MailService], + providers: [ + OtpService, + SmsService, + MailService, + AfricasTalkingSmsProvider, + LeopardSmsProvider, + { + provide: SmsProvider, + useFactory: ( + atProvider: AfricasTalkingSmsProvider, + leopardProvider: LeopardSmsProvider, + ) => { + return process.env.SMS_PROVIDER === 'leopard' + ? leopardProvider + : atProvider; + }, + inject: [AfricasTalkingSmsProvider, LeopardSmsProvider], + }, + ], + }) export class AppModule {} diff --git a/src/sms.service.ts b/src/sms.service.ts index 9f19577..da999c6 100644 --- a/src/sms.service.ts +++ b/src/sms.service.ts @@ -1,22 +1,15 @@ // sms.service.ts import { Injectable } from '@nestjs/common'; -import AfricasTalking from 'africastalking'; +import { SmsProvider } from './sms/sms.provider'; @Injectable() export class SmsService { - private client; - - constructor() { - this.client = AfricasTalking({ - apiKey: process.env.AT_API_KEY, - username: process.env.AT_USERNAME - }); - } + constructor(private smsProvider: SmsProvider) {} async sendOtp(phone: string, otp: string) { - await this.client.SMS.send({ - to: [phone], - message: `Your verification code is ${otp}. Expires in 5 minutes.` - }); + await this.smsProvider.send( + phone, + `Your verification code is ${otp}. Expires in 5 minutes.` + ); } } diff --git a/src/sms/africastalking.provider.ts b/src/sms/africastalking.provider.ts new file mode 100644 index 0000000..c93c07c --- /dev/null +++ b/src/sms/africastalking.provider.ts @@ -0,0 +1,22 @@ +import { Injectable } from '@nestjs/common'; +import { SmsProvider } from './sms.provider'; +import AfricasTalking from 'africastalking'; + +@Injectable() +export class AfricasTalkingSmsProvider implements SmsProvider { + private client; + + constructor() { + this.client = AfricasTalking({ + apiKey: process.env.AT_API_KEY, + username: process.env.AT_USERNAME, + }); + } + + async send(to: string, message: string): Promise { + return this.client.SMS.send({ + to: [to], + message: message, + }); + } +} diff --git a/src/sms/leopard.provider.ts b/src/sms/leopard.provider.ts new file mode 100644 index 0000000..9ae7bc7 --- /dev/null +++ b/src/sms/leopard.provider.ts @@ -0,0 +1,42 @@ +import { Injectable, Logger } from '@nestjs/common'; +import { SmsProvider } from './sms.provider'; + +@Injectable() +export class LeopardSmsProvider implements SmsProvider { + private readonly logger = new Logger(LeopardSmsProvider.name); + + async send(to: string, message: string): Promise { + const url = 'https://api.smsleopard.com/v1/sms/send'; + const auth = Buffer.from(`${process.env.LEOPARD_API_KEY}:${process.env.LEOPARD_API_SECRET}`).toString('base64'); + + try { + const response = await fetch(url, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'Authorization': `Basic ${auth}` + }, + body: JSON.stringify({ + source: process.env.LEOPARD_SENDER_ID || 'SMS_Leopard', + message: message, + destination: [ + { + number: to + } + ] + }) + }); + + if (!response.ok) { + const errorText = await response.text(); + this.logger.error(`SMS Leopard failed: ${response.status} ${response.statusText} - ${errorText}`); + throw new Error(`SMS Leopard failed: ${response.statusText}`); + } + + return await response.json(); + } catch (error) { + this.logger.error('Error sending SMS via Leopard', error); + throw error; + } + } +} diff --git a/src/sms/sms.provider.ts b/src/sms/sms.provider.ts new file mode 100644 index 0000000..2719c82 --- /dev/null +++ b/src/sms/sms.provider.ts @@ -0,0 +1,6 @@ +import { Injectable } from '@nestjs/common'; + +@Injectable() +export abstract class SmsProvider { + abstract send(to: string, message: string): Promise; +}