mirror of
https://github.com/HamzaSha1/zod-backend.git
synced 2025-07-10 15:17:44 +00:00
feat: sms using twillio
This commit is contained in:
74
package-lock.json
generated
74
package-lock.json
generated
@ -40,6 +40,7 @@
|
|||||||
"moment": "^2.30.1",
|
"moment": "^2.30.1",
|
||||||
"nestjs-i18n": "^10.4.9",
|
"nestjs-i18n": "^10.4.9",
|
||||||
"nestjs-pino": "^4.1.0",
|
"nestjs-pino": "^4.1.0",
|
||||||
|
"nestjs-twilio": "^4.4.0",
|
||||||
"nodemailer": "^6.9.16",
|
"nodemailer": "^6.9.16",
|
||||||
"oci-common": "^2.99.0",
|
"oci-common": "^2.99.0",
|
||||||
"oci-sdk": "^2.99.0",
|
"oci-sdk": "^2.99.0",
|
||||||
@ -3808,7 +3809,6 @@
|
|||||||
"node_modules/axios": {
|
"node_modules/axios": {
|
||||||
"version": "1.7.7",
|
"version": "1.7.7",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"follow-redirects": "^1.15.6",
|
"follow-redirects": "^1.15.6",
|
||||||
"form-data": "^4.0.0",
|
"form-data": "^4.0.0",
|
||||||
@ -6350,7 +6350,6 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=4.0"
|
"node": ">=4.0"
|
||||||
},
|
},
|
||||||
@ -10036,6 +10035,19 @@
|
|||||||
"pino-http": "^6.4.0 || ^7.0.0 || ^8.0.0 || ^9.0.0 || ^10.0.0"
|
"pino-http": "^6.4.0 || ^7.0.0 || ^8.0.0 || ^9.0.0 || ^10.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/nestjs-twilio": {
|
||||||
|
"version": "4.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/nestjs-twilio/-/nestjs-twilio-4.4.0.tgz",
|
||||||
|
"integrity": "sha512-TtT+mgVaIsiGNX1J8zkjVhIBxJPsChfU8gfu6dbPyEtde9ewgb5sxhAreOE6STT5U95OiSAlFcqKoqlARCIFxA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"twilio": "^5.0.2"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@nestjs/common": ">=9.0.0",
|
||||||
|
"@nestjs/core": ">=9.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/nice-try": {
|
"node_modules/nice-try": {
|
||||||
"version": "1.0.5",
|
"version": "1.0.5",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
@ -15120,8 +15132,7 @@
|
|||||||
},
|
},
|
||||||
"node_modules/proxy-from-env": {
|
"node_modules/proxy-from-env": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"license": "MIT",
|
"license": "MIT"
|
||||||
"peer": true
|
|
||||||
},
|
},
|
||||||
"node_modules/pug": {
|
"node_modules/pug": {
|
||||||
"version": "3.0.3",
|
"version": "3.0.3",
|
||||||
@ -16020,6 +16031,12 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/scmp": {
|
||||||
|
"version": "2.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/scmp/-/scmp-2.1.0.tgz",
|
||||||
|
"integrity": "sha512-o/mRQGk9Rcer/jEEw/yw4mwo3EU/NvYvp577/Btqrym9Qy5/MdWGBqipbALgd2lrdWTJ5/gqDusxfnQBxOxT2Q==",
|
||||||
|
"license": "BSD-3-Clause"
|
||||||
|
},
|
||||||
"node_modules/secure-json-parse": {
|
"node_modules/secure-json-parse": {
|
||||||
"version": "2.7.0",
|
"version": "2.7.0",
|
||||||
"license": "BSD-3-Clause"
|
"license": "BSD-3-Clause"
|
||||||
@ -17032,6 +17049,46 @@
|
|||||||
"version": "0.14.5",
|
"version": "0.14.5",
|
||||||
"license": "Unlicense"
|
"license": "Unlicense"
|
||||||
},
|
},
|
||||||
|
"node_modules/twilio": {
|
||||||
|
"version": "5.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/twilio/-/twilio-5.4.0.tgz",
|
||||||
|
"integrity": "sha512-kEmxzdOLTzXzUEXIkBVwT1Itxlbp+rtGrQogNfPtSE3EjoEsxrxB/9tdMIEbrsioL8CzTk/+fiKNJekAyHxjuQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"axios": "^1.7.4",
|
||||||
|
"dayjs": "^1.11.9",
|
||||||
|
"https-proxy-agent": "^5.0.0",
|
||||||
|
"jsonwebtoken": "^9.0.2",
|
||||||
|
"qs": "^6.9.4",
|
||||||
|
"scmp": "^2.1.0",
|
||||||
|
"xmlbuilder": "^13.0.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/twilio/node_modules/jsonwebtoken": {
|
||||||
|
"version": "9.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz",
|
||||||
|
"integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"jws": "^3.2.2",
|
||||||
|
"lodash.includes": "^4.3.0",
|
||||||
|
"lodash.isboolean": "^3.0.3",
|
||||||
|
"lodash.isinteger": "^4.0.4",
|
||||||
|
"lodash.isnumber": "^3.0.3",
|
||||||
|
"lodash.isplainobject": "^4.0.6",
|
||||||
|
"lodash.isstring": "^4.0.1",
|
||||||
|
"lodash.once": "^4.0.0",
|
||||||
|
"ms": "^2.1.1",
|
||||||
|
"semver": "^7.5.4"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12",
|
||||||
|
"npm": ">=6"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/type-check": {
|
"node_modules/type-check": {
|
||||||
"version": "0.4.0",
|
"version": "0.4.0",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
@ -17800,6 +17857,15 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "ISC"
|
"license": "ISC"
|
||||||
},
|
},
|
||||||
|
"node_modules/xmlbuilder": {
|
||||||
|
"version": "13.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-13.0.2.tgz",
|
||||||
|
"integrity": "sha512-Eux0i2QdDYKbdbA6AM6xE4m6ZTZr4G4xF9kahI2ukSEMCzwce2eX9WlTI5J3s+NU7hpasFsr8hWIONae7LluAQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/xtend": {
|
"node_modules/xtend": {
|
||||||
"version": "4.0.2",
|
"version": "4.0.2",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
@ -57,6 +57,7 @@
|
|||||||
"moment": "^2.30.1",
|
"moment": "^2.30.1",
|
||||||
"nestjs-i18n": "^10.4.9",
|
"nestjs-i18n": "^10.4.9",
|
||||||
"nestjs-pino": "^4.1.0",
|
"nestjs-pino": "^4.1.0",
|
||||||
|
"nestjs-twilio": "^4.4.0",
|
||||||
"nodemailer": "^6.9.16",
|
"nodemailer": "^6.9.16",
|
||||||
"oci-common": "^2.99.0",
|
"oci-common": "^2.99.0",
|
||||||
"oci-sdk": "^2.99.0",
|
"oci-sdk": "^2.99.0",
|
||||||
|
@ -26,6 +26,7 @@ import { JuniorModule } from './junior/junior.module';
|
|||||||
import { MoneyRequestModule } from './money-request/money-request.module';
|
import { MoneyRequestModule } from './money-request/money-request.module';
|
||||||
import { SavingGoalsModule } from './saving-goals/saving-goals.module';
|
import { SavingGoalsModule } from './saving-goals/saving-goals.module';
|
||||||
import { TaskModule } from './task/task.module';
|
import { TaskModule } from './task/task.module';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
controllers: [],
|
controllers: [],
|
||||||
imports: [
|
imports: [
|
||||||
|
@ -1,15 +1,25 @@
|
|||||||
import { Module } from '@nestjs/common';
|
import { Module } from '@nestjs/common';
|
||||||
|
import { ConfigService } from '@nestjs/config';
|
||||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||||
|
import { TwilioModule } from 'nestjs-twilio';
|
||||||
import { AuthModule } from '~/auth/auth.module';
|
import { AuthModule } from '~/auth/auth.module';
|
||||||
|
import { buildTwilioOptions } from '~/core/module-options';
|
||||||
import { NotificationsController } from './controllers';
|
import { NotificationsController } from './controllers';
|
||||||
import { Notification } from './entities';
|
import { Notification } from './entities';
|
||||||
import { NotificationsRepository } from './repositories';
|
import { NotificationsRepository } from './repositories';
|
||||||
import { FirebaseService } from './services/firebase.service';
|
import { FirebaseService } from './services/firebase.service';
|
||||||
import { NotificationsService } from './services/notifications.service';
|
import { NotificationsService } from './services/notifications.service';
|
||||||
|
import { TwilioService } from './services/twilio.service';
|
||||||
@Module({
|
@Module({
|
||||||
imports: [TypeOrmModule.forFeature([Notification]), AuthModule],
|
imports: [
|
||||||
providers: [NotificationsService, FirebaseService, NotificationsRepository],
|
TypeOrmModule.forFeature([Notification]),
|
||||||
|
AuthModule,
|
||||||
|
TwilioModule.forRootAsync({
|
||||||
|
useFactory: buildTwilioOptions,
|
||||||
|
inject: [ConfigService],
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
providers: [NotificationsService, FirebaseService, NotificationsRepository, TwilioService],
|
||||||
exports: [NotificationsService],
|
exports: [NotificationsService],
|
||||||
controllers: [NotificationsController],
|
controllers: [NotificationsController],
|
||||||
})
|
})
|
||||||
|
@ -4,6 +4,7 @@ import { PageOptionsRequestDto } from '~/core/dtos';
|
|||||||
import { Notification } from '../entities';
|
import { Notification } from '../entities';
|
||||||
import { NotificationsRepository } from '../repositories';
|
import { NotificationsRepository } from '../repositories';
|
||||||
import { FirebaseService } from './firebase.service';
|
import { FirebaseService } from './firebase.service';
|
||||||
|
import { TwilioService } from './twilio.service';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class NotificationsService {
|
export class NotificationsService {
|
||||||
@ -11,6 +12,7 @@ export class NotificationsService {
|
|||||||
private readonly deviceService: DeviceService,
|
private readonly deviceService: DeviceService,
|
||||||
private readonly firebaseService: FirebaseService,
|
private readonly firebaseService: FirebaseService,
|
||||||
private readonly notificationRepository: NotificationsRepository,
|
private readonly notificationRepository: NotificationsRepository,
|
||||||
|
private readonly twilioService: TwilioService,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
async sendPushNotification(userId: string, title: string, body: string) {
|
async sendPushNotification(userId: string, title: string, body: string) {
|
||||||
@ -25,6 +27,10 @@ export class NotificationsService {
|
|||||||
return this.firebaseService.sendNotification(tokens, title, body);
|
return this.firebaseService.sendNotification(tokens, title, body);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async sendSMS(to: string, body: string) {
|
||||||
|
await this.twilioService.sendSMS(to, body);
|
||||||
|
}
|
||||||
|
|
||||||
async getNotifications(userId: string, pageOptionsDto: PageOptionsRequestDto) {
|
async getNotifications(userId: string, pageOptionsDto: PageOptionsRequestDto) {
|
||||||
const [[notifications, count], unreadCount] = await Promise.all([
|
const [[notifications, count], unreadCount] = await Promise.all([
|
||||||
this.notificationRepository.getNotifications(userId, pageOptionsDto),
|
this.notificationRepository.getNotifications(userId, pageOptionsDto),
|
||||||
|
21
src/common/modules/notification/services/twilio.service.ts
Normal file
21
src/common/modules/notification/services/twilio.service.ts
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
import { Injectable, Logger } from '@nestjs/common';
|
||||||
|
import { ConfigService } from '@nestjs/config';
|
||||||
|
import { TwilioService as TwilioApiService } from 'nestjs-twilio';
|
||||||
|
import { Environment } from '~/core/enums';
|
||||||
|
@Injectable()
|
||||||
|
export class TwilioService {
|
||||||
|
private logger = new Logger(TwilioService.name);
|
||||||
|
constructor(private readonly twilioService: TwilioApiService, private readonly configService: ConfigService) {}
|
||||||
|
private from = this.configService.getOrThrow('TWILIO_PHONE_NUMBER');
|
||||||
|
sendSMS(to: string, body: string) {
|
||||||
|
if (this.configService.get('NODE_ENV') === Environment.DEV) {
|
||||||
|
this.logger.log(`Skipping SMS sending in DEV environment. Message: ${body} to: ${to}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
return this.twilioService.client.messages.create({
|
||||||
|
body,
|
||||||
|
to,
|
||||||
|
from: this.from,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,5 @@
|
|||||||
export * from '././keyv-options';
|
export * from '././keyv-options';
|
||||||
export * from './config-options';
|
export * from './config-options';
|
||||||
export * from './logger-options';
|
export * from './logger-options';
|
||||||
|
export * from './twilio-options';
|
||||||
export * from './typeorm-options';
|
export * from './typeorm-options';
|
||||||
|
9
src/core/module-options/twilio-options.ts
Normal file
9
src/core/module-options/twilio-options.ts
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
import { ConfigService } from '@nestjs/config';
|
||||||
|
import { TwilioModuleOptions } from 'nestjs-twilio';
|
||||||
|
|
||||||
|
export function buildTwilioOptions(config: ConfigService): TwilioModuleOptions {
|
||||||
|
return {
|
||||||
|
accountSid: config.get('TWILIO_ACCOUNT_SID'),
|
||||||
|
authToken: config.get('TWILIO_AUTH_TOKEN'),
|
||||||
|
};
|
||||||
|
}
|
Reference in New Issue
Block a user