chore: add all notification sewrvice to the queue
This commit is contained in:
@@ -0,0 +1,10 @@
|
||||
export const NOTIFICATION = Object.freeze({
|
||||
QUEUE_NAME: "notification",
|
||||
SEND_NOTIFICATION_JOB_NAME: "sendNotification",
|
||||
|
||||
SEND_NOTIFICATION_JOB_DELAY: 1000, // delay after 1 second
|
||||
SEND_NOTIFICATION_JOB_PRIORITY: 1, // high priority
|
||||
SEND_NOTIFICATION_JOB_ATTEMPTS: 3, // retry 3 times
|
||||
SEND_NOTIFICATION_JOB_BACKOFF: 5 * 1000, // retry after 5 seconds
|
||||
SEND_NOTIFICATION_JOB_TIMEOUT: 10000, // timeout after 10 seconds
|
||||
});
|
||||
@@ -1,17 +1,45 @@
|
||||
import { BullModule } from "@nestjs/bullmq";
|
||||
import { Module } from "@nestjs/common";
|
||||
import { ConfigModule } from "@nestjs/config";
|
||||
import { TypeOrmModule } from "@nestjs/typeorm";
|
||||
|
||||
import { NOTIFICATION } from "./constants";
|
||||
import { LoggerModule } from "../logger/logger.module";
|
||||
import { SettingModule } from "../settings/settings.module";
|
||||
import { UtilsModule } from "../utils/utils.module";
|
||||
import { Notification } from "./entities/notification.entity";
|
||||
import { NotificationController } from "./notifications.controller";
|
||||
import { NotificationsService } from "./providers/notifications.service";
|
||||
import { NotificationProcessor } from "./queue/notification.processor";
|
||||
import { NotificationQueue } from "./queue/notification.queue";
|
||||
import { NotificationRepository } from "./repositories/notifications.repository";
|
||||
import { SettingModule } from "../settings/settings.module";
|
||||
import { UtilsModule } from "../utils/utils.module";
|
||||
import { NotificationSetting } from "../settings/entities/notification-setting.entity";
|
||||
import { EmailService } from "../utils/providers/email.service";
|
||||
import { SmsService } from "../utils/providers/sms.service";
|
||||
|
||||
@Module({
|
||||
imports: [TypeOrmModule.forFeature([Notification]), UtilsModule, SettingModule],
|
||||
providers: [NotificationRepository, NotificationsService],
|
||||
imports: [
|
||||
ConfigModule,
|
||||
LoggerModule,
|
||||
TypeOrmModule.forFeature([Notification, NotificationSetting]),
|
||||
BullModule.registerQueue({
|
||||
name: NOTIFICATION.QUEUE_NAME,
|
||||
defaultJobOptions: {
|
||||
attempts: NOTIFICATION.SEND_NOTIFICATION_JOB_ATTEMPTS,
|
||||
backoff: {
|
||||
type: "exponential",
|
||||
delay: NOTIFICATION.SEND_NOTIFICATION_JOB_BACKOFF,
|
||||
},
|
||||
removeOnComplete: true,
|
||||
removeOnFail: false,
|
||||
delay: NOTIFICATION.SEND_NOTIFICATION_JOB_DELAY,
|
||||
},
|
||||
}),
|
||||
UtilsModule,
|
||||
SettingModule,
|
||||
],
|
||||
providers: [NotificationRepository, NotificationsService, NotificationQueue, NotificationProcessor, SmsService, EmailService],
|
||||
controllers: [NotificationController],
|
||||
exports: [NotificationRepository, NotificationsService],
|
||||
exports: [NotificationRepository, NotificationsService, NotificationQueue],
|
||||
})
|
||||
export class NotificationModule {}
|
||||
|
||||
@@ -0,0 +1,173 @@
|
||||
import { Processor } from "@nestjs/bullmq";
|
||||
import { Logger } from "@nestjs/common";
|
||||
import { Job } from "bullmq";
|
||||
import { DataSource } from "typeorm";
|
||||
|
||||
import { WorkerProcessor } from "../../../common/queues/worker.processor";
|
||||
import { LoggerService } from "../../logger/logger.service";
|
||||
import { NotifType } from "../../settings/enums/notif-settings.enum";
|
||||
import { NOTIFICATION } from "../constants";
|
||||
import {
|
||||
IAnnouncementNotificationData,
|
||||
IBlogCommentNotificationData,
|
||||
IInvoiceNotificationData,
|
||||
INewCriticismNotificationData,
|
||||
INewCustomerNotificationData,
|
||||
INewSubscriptionNotificationData,
|
||||
INewTicketNotificationData,
|
||||
IPaymentNotificationData,
|
||||
IServiceReviewNotificationData,
|
||||
ISubscriptionNotificationData,
|
||||
ITicketNotificationData,
|
||||
IWalletNotificationData,
|
||||
} from "../interfaces/ISendNotificationData";
|
||||
import { NotificationsService } from "../providers/notifications.service";
|
||||
|
||||
type NotificationJobData = {
|
||||
type: NotifType;
|
||||
recipientId: string;
|
||||
data:
|
||||
| IBlogCommentNotificationData
|
||||
| IServiceReviewNotificationData
|
||||
| INewCustomerNotificationData
|
||||
| INewSubscriptionNotificationData
|
||||
| ITicketNotificationData
|
||||
| INewCriticismNotificationData
|
||||
| IInvoiceNotificationData
|
||||
| IAnnouncementNotificationData
|
||||
| IWalletNotificationData
|
||||
| ISubscriptionNotificationData
|
||||
| IPaymentNotificationData
|
||||
| INewTicketNotificationData;
|
||||
};
|
||||
|
||||
@Processor(NOTIFICATION.QUEUE_NAME, { concurrency: 5 })
|
||||
export class NotificationProcessor extends WorkerProcessor {
|
||||
protected readonly logger = new Logger(NotificationProcessor.name);
|
||||
|
||||
constructor(
|
||||
private readonly notificationsService: NotificationsService,
|
||||
private readonly dataSource: DataSource,
|
||||
protected readonly loggerService: LoggerService,
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
||||
async process(job: Job<NotificationJobData>, token?: string) {
|
||||
this.logger.log(`Processing notification job: ${job.id} ${token ? `with token: ${token}` : ""}`);
|
||||
|
||||
const queryRunner = this.dataSource.createQueryRunner();
|
||||
|
||||
try {
|
||||
await queryRunner.connect();
|
||||
await queryRunner.startTransaction();
|
||||
|
||||
const { type, recipientId, data } = job.data;
|
||||
|
||||
switch (type) {
|
||||
// Admin Notifications
|
||||
case NotifType.NEW_BLOG_COMMENT:
|
||||
await this.notificationsService.createNewBlogCommentNotification(recipientId, data as IBlogCommentNotificationData, queryRunner);
|
||||
break;
|
||||
case NotifType.NEW_SERVICE_REVIEW:
|
||||
await this.notificationsService.createNewServiceReviewNotification(
|
||||
recipientId,
|
||||
data as IServiceReviewNotificationData,
|
||||
queryRunner,
|
||||
);
|
||||
break;
|
||||
case NotifType.NEW_CUSTOMER:
|
||||
await this.notificationsService.createNewCustomerNotification(recipientId, data as INewCustomerNotificationData, queryRunner);
|
||||
break;
|
||||
case NotifType.NEW_SUBSCRIPTION:
|
||||
await this.notificationsService.createNewSubscriptionNotification(
|
||||
recipientId,
|
||||
data as INewSubscriptionNotificationData,
|
||||
queryRunner,
|
||||
);
|
||||
break;
|
||||
case NotifType.NEW_CRITICISM:
|
||||
await this.notificationsService.createNewCriticismNotification(recipientId, data as INewCriticismNotificationData, queryRunner);
|
||||
break;
|
||||
case NotifType.NEW_TICKET:
|
||||
await this.notificationsService.createNewTicketGlobalNotification(recipientId, data as INewTicketNotificationData, queryRunner);
|
||||
break;
|
||||
|
||||
// User Notifications
|
||||
case NotifType.USER_LOGIN:
|
||||
await this.notificationsService.createLoginNotification(recipientId, data);
|
||||
break;
|
||||
case NotifType.ANNOUNCEMENT:
|
||||
await this.notificationsService.createAnnouncementNotification(recipientId, data as IAnnouncementNotificationData, queryRunner);
|
||||
break;
|
||||
|
||||
// Wallet Notifications
|
||||
case NotifType.WALLET_CHARGE:
|
||||
await this.notificationsService.createWalletChargeNotification(recipientId, data as IWalletNotificationData, queryRunner);
|
||||
break;
|
||||
case NotifType.WALLET_DEDUCTION:
|
||||
await this.notificationsService.createWalletDeductionNotification(recipientId, data as IWalletNotificationData, queryRunner);
|
||||
break;
|
||||
|
||||
// Ticket Notifications
|
||||
case NotifType.ANSWER_TICKET:
|
||||
await this.notificationsService.createAnswerTicketNotification(recipientId, data as ITicketNotificationData, queryRunner);
|
||||
break;
|
||||
case NotifType.CREATE_TICKET:
|
||||
await this.notificationsService.createTicketNotification(recipientId, data as ITicketNotificationData, queryRunner);
|
||||
break;
|
||||
case NotifType.ASSIGN_TICKET:
|
||||
await this.notificationsService.createAssignTicketNotificationForAdmin(recipientId, data as ITicketNotificationData, queryRunner);
|
||||
break;
|
||||
|
||||
// Invoice Notifications
|
||||
case NotifType.CREATE_INVOICE:
|
||||
await this.notificationsService.createInvoiceCreationNotification(recipientId, data as IInvoiceNotificationData, queryRunner);
|
||||
break;
|
||||
case NotifType.BILL_INVOICE_REMINDER:
|
||||
await this.notificationsService.createBillInvoiceReminderNotification(recipientId, data as IInvoiceNotificationData, queryRunner);
|
||||
break;
|
||||
case NotifType.BILL_INVOICE:
|
||||
await this.notificationsService.createBillInvoiceNotification(recipientId, data as IInvoiceNotificationData, queryRunner);
|
||||
break;
|
||||
case NotifType.APPROVE_INVOICE:
|
||||
await this.notificationsService.createApprovedInvoiceNotification(recipientId, data as IInvoiceNotificationData, queryRunner);
|
||||
break;
|
||||
case NotifType.INVOICE_OVERDUE:
|
||||
await this.notificationsService.createInvoiceOverdueNotification(recipientId, data as IInvoiceNotificationData, queryRunner);
|
||||
break;
|
||||
case NotifType.RECURRING_INVOICE:
|
||||
await this.notificationsService.createRecurringInvoiceNotification(recipientId, data as IInvoiceNotificationData, queryRunner);
|
||||
break;
|
||||
|
||||
// Service Notifications
|
||||
case NotifType.BLOCK_SERVICE:
|
||||
await this.notificationsService.createBlockServiceNotification(recipientId, data as ISubscriptionNotificationData, queryRunner);
|
||||
break;
|
||||
|
||||
// Payment Notifications
|
||||
case NotifType.PAYMENT_REMINDER:
|
||||
await this.notificationsService.createPaymentReminderNotification(recipientId, data as IPaymentNotificationData, queryRunner);
|
||||
break;
|
||||
case NotifType.PAYMENT_CANCELLATION:
|
||||
await this.notificationsService.createPaymentCancellationNotification(recipientId, data as IPaymentNotificationData, queryRunner);
|
||||
break;
|
||||
|
||||
default:
|
||||
this.logger.warn(`Unknown notification type: ${type}`);
|
||||
}
|
||||
|
||||
await queryRunner.commitTransaction();
|
||||
return true;
|
||||
} catch (error) {
|
||||
this.logger.error(
|
||||
`Failed to process notification: ${error instanceof Error ? error.message : "Unknown error"}`,
|
||||
error instanceof Error ? error.stack : undefined,
|
||||
);
|
||||
await queryRunner.rollbackTransaction();
|
||||
throw error;
|
||||
} finally {
|
||||
await queryRunner.release();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
import { InjectQueue } from "@nestjs/bullmq";
|
||||
import { Injectable } from "@nestjs/common";
|
||||
import { Queue } from "bullmq";
|
||||
|
||||
import { NotifType } from "../../settings/enums/notif-settings.enum";
|
||||
import { NOTIFICATION } from "../constants";
|
||||
import {
|
||||
IAnnouncementNotificationData,
|
||||
IBaseNotificationData,
|
||||
IBlogCommentNotificationData,
|
||||
IInvoiceNotificationData,
|
||||
INewCriticismNotificationData,
|
||||
INewCustomerNotificationData,
|
||||
INewSubscriptionNotificationData,
|
||||
INewTicketNotificationData,
|
||||
IPaymentNotificationData,
|
||||
IServiceReviewNotificationData,
|
||||
ISubscriptionNotificationData,
|
||||
ITicketNotificationData,
|
||||
IWalletNotificationData,
|
||||
} from "../interfaces/ISendNotificationData";
|
||||
|
||||
@Injectable()
|
||||
export class NotificationQueue {
|
||||
constructor(@InjectQueue(NOTIFICATION.QUEUE_NAME) private readonly notificationsQueue: Queue) {}
|
||||
|
||||
async addNotificationJob(type: NotifType, recipientId: string, data: IBaseNotificationData) {
|
||||
await this.notificationsQueue.add(
|
||||
NOTIFICATION.SEND_NOTIFICATION_JOB_NAME,
|
||||
{
|
||||
type,
|
||||
recipientId,
|
||||
data,
|
||||
},
|
||||
{
|
||||
delay: NOTIFICATION.SEND_NOTIFICATION_JOB_DELAY,
|
||||
attempts: NOTIFICATION.SEND_NOTIFICATION_JOB_ATTEMPTS,
|
||||
backoff: {
|
||||
type: "exponential",
|
||||
delay: NOTIFICATION.SEND_NOTIFICATION_JOB_BACKOFF,
|
||||
},
|
||||
priority: NOTIFICATION.SEND_NOTIFICATION_JOB_PRIORITY,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
// Admin notification methods
|
||||
async addNewBlogCommentNotification(recipientId: string, data: IBlogCommentNotificationData) {
|
||||
await this.addNotificationJob(NotifType.NEW_BLOG_COMMENT, recipientId, data);
|
||||
}
|
||||
|
||||
async addNewServiceReviewNotification(recipientId: string, data: IServiceReviewNotificationData) {
|
||||
await this.addNotificationJob(NotifType.NEW_SERVICE_REVIEW, recipientId, data);
|
||||
}
|
||||
|
||||
async addNewCustomerNotification(recipientId: string, data: INewCustomerNotificationData) {
|
||||
await this.addNotificationJob(NotifType.NEW_CUSTOMER, recipientId, data);
|
||||
}
|
||||
|
||||
async addNewSubscriptionNotification(recipientId: string, data: INewSubscriptionNotificationData) {
|
||||
await this.addNotificationJob(NotifType.NEW_SUBSCRIPTION, recipientId, data);
|
||||
}
|
||||
|
||||
async addNewTicketGlobalNotification(recipientId: string, data: INewTicketNotificationData) {
|
||||
await this.addNotificationJob(NotifType.NEW_TICKET, recipientId, data);
|
||||
}
|
||||
|
||||
async addNewCriticismNotification(recipientId: string, data: INewCriticismNotificationData) {
|
||||
await this.addNotificationJob(NotifType.NEW_CRITICISM, recipientId, data);
|
||||
}
|
||||
|
||||
// User notification methods
|
||||
async addLoginNotification(recipientId: string, data: IBaseNotificationData) {
|
||||
await this.addNotificationJob(NotifType.USER_LOGIN, recipientId, data);
|
||||
}
|
||||
|
||||
async addInvoiceCreationNotification(recipientId: string, data: IInvoiceNotificationData) {
|
||||
await this.addNotificationJob(NotifType.CREATE_INVOICE, recipientId, data);
|
||||
}
|
||||
|
||||
async addAnnouncementNotification(recipientId: string, data: IAnnouncementNotificationData) {
|
||||
await this.addNotificationJob(NotifType.ANNOUNCEMENT, recipientId, data);
|
||||
}
|
||||
|
||||
async addWalletChargeNotification(recipientId: string, data: IWalletNotificationData) {
|
||||
await this.addNotificationJob(NotifType.WALLET_CHARGE, recipientId, data);
|
||||
}
|
||||
|
||||
async addWalletDeductionNotification(recipientId: string, data: IWalletNotificationData) {
|
||||
await this.addNotificationJob(NotifType.WALLET_DEDUCTION, recipientId, data);
|
||||
}
|
||||
|
||||
async addAnswerTicketNotification(recipientId: string, data: ITicketNotificationData) {
|
||||
await this.addNotificationJob(NotifType.ANSWER_TICKET, recipientId, data);
|
||||
}
|
||||
|
||||
async addTicketNotification(recipientId: string, data: ITicketNotificationData) {
|
||||
await this.addNotificationJob(NotifType.CREATE_TICKET, recipientId, data);
|
||||
}
|
||||
|
||||
async addAssignTicketNotificationForAdmin(recipientId: string, data: ITicketNotificationData) {
|
||||
await this.addNotificationJob(NotifType.ASSIGN_TICKET, recipientId, data);
|
||||
}
|
||||
|
||||
async addBillInvoiceReminderNotification(recipientId: string, data: IInvoiceNotificationData) {
|
||||
await this.addNotificationJob(NotifType.BILL_INVOICE_REMINDER, recipientId, data);
|
||||
}
|
||||
|
||||
async addBillInvoiceNotification(recipientId: string, data: IInvoiceNotificationData) {
|
||||
await this.addNotificationJob(NotifType.BILL_INVOICE, recipientId, data);
|
||||
}
|
||||
|
||||
async addApprovedInvoiceNotification(recipientId: string, data: IInvoiceNotificationData) {
|
||||
await this.addNotificationJob(NotifType.APPROVE_INVOICE, recipientId, data);
|
||||
}
|
||||
|
||||
async addInvoiceOverdueNotification(recipientId: string, data: IInvoiceNotificationData) {
|
||||
await this.addNotificationJob(NotifType.INVOICE_OVERDUE, recipientId, data);
|
||||
}
|
||||
|
||||
async addRecurringInvoiceNotification(recipientId: string, data: IInvoiceNotificationData) {
|
||||
await this.addNotificationJob(NotifType.RECURRING_INVOICE, recipientId, data);
|
||||
}
|
||||
|
||||
async addBlockServiceNotification(recipientId: string, data: ISubscriptionNotificationData) {
|
||||
await this.addNotificationJob(NotifType.BLOCK_SERVICE, recipientId, data);
|
||||
}
|
||||
|
||||
async addPaymentReminderNotification(recipientId: string, data: IPaymentNotificationData) {
|
||||
await this.addNotificationJob(NotifType.PAYMENT_REMINDER, recipientId, data);
|
||||
}
|
||||
|
||||
async addPaymentCancellationNotification(recipientId: string, data: IPaymentNotificationData) {
|
||||
await this.addNotificationJob(NotifType.PAYMENT_CANCELLATION, recipientId, data);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user