chore: first

This commit is contained in:
Mahyargdz
2025-05-11 10:27:30 +03:30
commit 4e563c497d
102 changed files with 18602 additions and 0 deletions
+23
View File
@@ -0,0 +1,23 @@
import { IsEnum, IsNotEmpty, IsString, IsUUID } from "class-validator";
import { NotificationMessage } from "../../../common/enums/message.enum";
import { NotifType } from "../enums/notif-settings.enum";
export class CreateNotificationDto {
@IsNotEmpty({ message: NotificationMessage.TITLE_IS_REQUIRED })
@IsString({ message: NotificationMessage.TITLE_STRING })
title: string;
@IsNotEmpty({ message: NotificationMessage.MESSAGE_IS_REQUIRED })
@IsString({ message: NotificationMessage.MESSAGE_STRING })
message: string;
@IsNotEmpty({ message: NotificationMessage.USERID_IS_REQUIRED })
@IsString({ message: NotificationMessage.USERID_IS_STRING })
@IsUUID("7", { message: NotificationMessage.USERID_IS_UUID })
recipientId: string;
@IsNotEmpty()
@IsEnum(NotifType)
type: NotifType;
}
@@ -0,0 +1,13 @@
import { ApiPropertyOptional } from "@nestjs/swagger";
import { Type } from "class-transformer";
import { IsIn, IsOptional } from "class-validator";
import { PaginationDto } from "../../../common/DTO/pagination.dto";
export class SearchNotificationQueryDto extends PaginationDto {
@IsOptional()
@Type(() => Number)
@IsIn([0, 1])
@ApiPropertyOptional({ description: "Notification status", example: 1 })
isRead?: number;
}
@@ -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
});
+26
View File
@@ -0,0 +1,26 @@
import { Entity, EntityRepositoryType, Enum, ManyToOne, Opt, Property } from "@mikro-orm/core";
import { BaseEntity } from "../../../common/entities/base.entity";
import { User } from "../../users/entities/user.entity";
import { NotifType } from "../enums/notif-settings.enum";
import { NotificationRepository } from "../repositories/notifications.repository";
@Entity({ repository: () => NotificationRepository })
export class Notification extends BaseEntity {
@Property({ type: "varchar", length: 250, nullable: false })
title!: string;
@Property({ type: "text", nullable: false })
message!: string;
@Enum({ items: () => NotifType, nativeEnumName: "notif_type", nullable: false })
type: NotifType;
@Property({ type: "boolean", default: false })
isRead: boolean & Opt;
@ManyToOne(() => User, { deleteRule: "cascade" })
recipient!: User;
[EntityRepositoryType]?: NotificationRepository;
}
+37
View File
@@ -0,0 +1,37 @@
export enum NotifType {
// Account category
OTP = "OTP",
USER_LOGIN = "USER_LOGIN",
ANNOUNCEMENT = "ANNOUNCEMENT",
// Finance category
WALLET_CHARGE = "WALLET_CHARGE",
WALLET_DEDUCTION = "WALLET_DEDUCTION",
//Invoice
BILL_INVOICE_REMINDER = "BILL_INVOICE_REMINDER",
BILL_INVOICE = "BILL_INVOICE",
APPROVE_INVOICE = "APPROVE_INVOICE",
CREATE_INVOICE = "CREATE_INVOICE",
INVOICE_OVERDUE = "INVOICE_OVERDUE",
// Service category
CREATE_SERVICE = "CREATE_SERVICE",
UNBLOCK_SERVICE = "UNBLOCK_SERVICE",
BLOCK_SERVICE = "BLOCK_SERVICE",
// Support category
ANSWER_TICKET = "ANSWER_TICKET",
CREATE_TICKET = "CREATE_TICKET",
//
ASSIGN_TICKET = "ASSIGN_TICKET",
RECURRING_INVOICE = "RECURRING_INVOICE",
PAYMENT_REMINDER = "PAYMENT_REMINDER",
PAYMENT_CANCELLATION = "PAYMENT_CANCELLATION",
// // Admin category
NEW_CUSTOMER = "NEW_CUSTOMER",
NEW_TICKET = "NEW_TICKET",
NEW_CRITICISM = "NEW_CRITICISM",
}
@@ -0,0 +1,40 @@
export interface IBaseNotificationData {
userPhone: string;
userEmail?: string;
}
export interface IOtpNotificationData extends IBaseNotificationData {
otp: string;
}
export interface IInvoiceNotificationData extends IBaseNotificationData {
price: number;
items: string;
dueDate: Date;
createDate: Date;
invoiceId: string;
paidAt?: Date;
lateFee?: number;
}
export interface ITicketNotificationData extends IBaseNotificationData {
ticketId: string;
subject: string;
date: Date;
user?: string;
}
export interface IGenericNotificationData extends IBaseNotificationData {
message: string;
}
export interface IAnnouncementNotificationData extends IBaseNotificationData {
title: string;
description: string;
date: Date;
}
export interface IPaymentNotificationData extends IBaseNotificationData {
amount: number;
paymentId: string;
}
+41
View File
@@ -0,0 +1,41 @@
import { Controller, Get, Param, Patch, Query } from "@nestjs/common";
import { ApiOperation, ApiTags } from "@nestjs/swagger";
import { SearchNotificationQueryDto } from "./DTO/search-notification-query.dto";
import { NotificationsService } from "./providers/notifications.service";
import { AuthGuards } from "../../common/decorators/auth-guard.decorator";
import { Pagination } from "../../common/decorators/pagination.decorator";
import { UserDec } from "../../common/decorators/user.decorator";
import { ParamDto } from "../../common/DTO/param.dto";
@Controller("notifications")
@ApiTags("Notifications")
@AuthGuards()
export class NotificationController {
constructor(private readonly notificationService: NotificationsService) {}
//********************* */
@ApiOperation({ summary: "all notifications by user" })
@Pagination()
@Get()
getAllNotifications(@UserDec("id") userId: string, @Query() queryDto: SearchNotificationQueryDto) {
return this.notificationService.getAllNotifications(queryDto, userId);
}
//********************* */
@ApiOperation({ summary: "mark user notification as read" })
@Patch(":id/read")
markAsRead(@Param() paramDto: ParamDto, @UserDec("id") userId: string) {
return this.notificationService.markAsRead(paramDto.id, userId);
}
//********************* */
@ApiOperation({ summary: "mark user all notification as read" })
@Patch("read-all")
markAllAsRead(@UserDec("id") userId: string) {
return this.notificationService.markAllAsRead(userId);
}
}
+36
View File
@@ -0,0 +1,36 @@
import { MikroOrmModule } from "@mikro-orm/nestjs";
import { BullModule } from "@nestjs/bullmq";
import { Module } from "@nestjs/common";
import { ConfigModule } from "@nestjs/config";
import { NOTIFICATION } from "./constants";
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";
@Module({
imports: [
ConfigModule,
MikroOrmModule.forFeature([Notification]),
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,
],
providers: [NotificationsService, NotificationQueue, NotificationProcessor],
controllers: [NotificationController],
exports: [NotificationsService, NotificationQueue],
})
export class NotificationModule {}
@@ -0,0 +1,299 @@
import { EntityManager } from "@mikro-orm/postgresql";
import { BadRequestException, Injectable, Logger, NotFoundException } from "@nestjs/common";
import { CommonMessage, NotificationMessage } from "../../../common/enums/message.enum";
import { User } from "../../users/entities/user.entity";
import { EmailService } from "../../utils/providers/email.service";
import { dateFormat, numberFormat } from "../../utils/providers/international.utils";
import { SmsService } from "../../utils/providers/sms.service";
import { CreateNotificationDto } from "../DTO/create-notification.dto";
import { SearchNotificationQueryDto } from "../DTO/search-notification-query.dto";
import { Notification } from "../entities/notification.entity";
import { NotifType } from "../enums/notif-settings.enum";
import {
IAnnouncementNotificationData,
IBaseNotificationData,
IInvoiceNotificationData,
IOtpNotificationData,
IPaymentNotificationData,
ITicketNotificationData,
} from "../interfaces/ISendNotificationData";
import { NotificationRepository } from "../repositories/notifications.repository";
@Injectable()
export class NotificationsService {
private readonly logger = new Logger(NotificationsService.name);
constructor(
private readonly notificationRepository: NotificationRepository,
private readonly smsService: SmsService,
private readonly emailService: EmailService,
private readonly em: EntityManager,
) {}
//************************ */
async createNotification(createNotificationDto: CreateNotificationDto, em?: EntityManager) {
if (!em) {
const localEm = this.em.fork();
try {
await localEm.begin();
//
const notification = localEm.create(Notification, {
...createNotificationDto,
recipient: localEm.getReference(User, createNotificationDto.recipientId),
});
await localEm.persistAndFlush(notification);
await localEm.commit();
return notification;
//
} catch (error) {
this.logger.error("error in createNotification", error);
await localEm.rollback();
throw error;
//
}
//
} else {
//
const notification = em.create(Notification, {
...createNotificationDto,
recipient: em.getReference(User, createNotificationDto.recipientId),
});
//
await em.persistAndFlush(notification);
return notification;
}
}
//************************ */
async markAsRead(notificationId: string, userId: string) {
const notification = await this.notificationRepository.findOne({ id: notificationId, recipient: { id: userId } });
if (!notification) throw new NotFoundException(NotificationMessage.NOTIFICATION_NOT_FOUND_OR_ACCESS_DENIED);
notification.isRead = true;
await this.em.flush();
return notification;
}
//************************ */
async getAllNotifications(queryDto: SearchNotificationQueryDto, recipientId: string) {
return await this.notificationRepository.getAllNotifications(queryDto, recipientId);
}
//************************ */
async getNotificationById(id: string) {
const notification = await this.notificationRepository.findOne({ id });
if (!notification) throw new BadRequestException(NotificationMessage.NOTIFICATION_NOT_FOUND_OR_ACCESS_DENIED);
return { notification };
}
//************************ */
async countUserNotifications(userId: string) {
const notificationCount = await this.notificationRepository.count({ recipient: { id: userId }, isRead: false });
return notificationCount;
}
//************************ */
async markAllAsRead(userId: string) {
await this.em.nativeUpdate(Notification, { recipient: { id: userId } }, { isRead: true });
return {
message: CommonMessage.UPDATE_SUCCESS,
};
}
//************************ */
async createOtpNotification(data: IOtpNotificationData) {
//sent notification based on the userSettings
return await this.smsService.sendSmsVerifyCode(data.userPhone, data.otp);
}
async createLoginNotification(recipientId: string, data: IBaseNotificationData) {
const loginDate = new Date().toLocaleString("fa-IR");
const message = NotificationMessage.LOGIN_MESSAGE.replace("[loginDate]", loginDate);
//sent notification based on the userSettings
await this.smsService.sendLoginSms(data.userPhone, loginDate);
// if (data.userEmail) await this.emailService.sendLoginEmail(data);
return this.createNotification({ title: NotificationMessage.LOGIN, type: NotifType.USER_LOGIN, message, recipientId });
}
//************************ */
async createInvoiceCreationNotification(recipientId: string, data: IInvoiceNotificationData, em: EntityManager) {
const message = NotificationMessage.INVOICE_CREATION_MESSAGE.replace("[id]", data.invoiceId);
//sent notification based on the userSettings
await this.smsService.sendInvoiceCreationSms(
data.userPhone,
data.invoiceId,
data.price,
data.items,
dateFormat(data.createDate),
dateFormat(data.dueDate),
);
if (data.userEmail) await this.emailService.sendInvoiceEmail(data);
return this.createNotification(
{ title: NotificationMessage.INVOICE_CREATION, type: NotifType.CREATE_INVOICE, message, recipientId },
em,
);
}
//************************ */
async createAnnouncementNotification(recipientId: string, data: IAnnouncementNotificationData, em: EntityManager) {
const message = NotificationMessage.ANNOUNCEMENT_MESSAGE;
//sent notification based on the userSettings
await this.smsService.sendAnnouncementSms(data.userPhone, data.title, data.description, dateFormat(data.date));
if (data.userEmail) await this.emailService.sendAnnouncementEmail(data);
return this.createNotification({ title: NotificationMessage.ANNOUNCEMENT, type: NotifType.ANNOUNCEMENT, message, recipientId }, em);
}
// //************************ */
async createAnswerTicketNotification(recipientId: string, data: ITicketNotificationData, em: EntityManager) {
const message = NotificationMessage.ANSWER_TICKET_MESSAGE.replace("[ticketId]", data.ticketId);
//sent notification based on the userSettings
await this.smsService.sendAnswerTicketSms(data.userPhone, data.ticketId, data.subject);
if (data.userEmail) await this.emailService.sendTicketEmail(data);
return this.createNotification({ title: NotificationMessage.ANSWER_TICKET, type: NotifType.ANSWER_TICKET, message, recipientId }, em);
}
// //************************ */
async createTicketNotification(recipientId: string, data: ITicketNotificationData, em: EntityManager) {
const message = NotificationMessage.CREATE_TICKET_MESSAGE.replace("[ticketId]", data.ticketId);
//sent notification based on the userSettings
await this.smsService.sendCreateTicketSms(data.userPhone, data.ticketId, data.subject, dateFormat(data.date));
if (data.userEmail) await this.emailService.sendTicketEmail(data);
return this.createNotification({ title: NotificationMessage.CREATE_TICKET, type: NotifType.CREATE_TICKET, message, recipientId }, em);
}
//************************ */
async createAssignTicketNotificationForAdmin(recipientId: string, data: ITicketNotificationData, em: EntityManager) {
const message = NotificationMessage.ASSIGN_TICKET_MESSAGE.replace("[ticketId]", data.ticketId);
//sent notification based on the userSettings
await this.smsService.sendTicketAssignedToAdminSms(data.userPhone, data.ticketId, data.subject, data.user!);
if (data.userEmail) await this.emailService.sendTicketEmail(data);
return this.createNotification({ title: NotificationMessage.ASSIGN_TICKET, type: NotifType.ASSIGN_TICKET, message, recipientId }, em);
}
//************************ */
async createBillInvoiceReminderNotification(recipientId: string, data: IInvoiceNotificationData, em: EntityManager) {
const message = NotificationMessage.BILL_INVOICE_REMINDER_MESSAGE.replace("[invoiceNumber]", data.invoiceId);
//sent notification based on the userSettings
await this.smsService.sendInvoicePaymentReminderSms(data.userPhone, data.invoiceId, data.price, dateFormat(data.dueDate));
if (data.userEmail) await this.emailService.sendInvoiceEmail(data);
return this.createNotification(
{ title: NotificationMessage.BILL_INVOICE_REMINDER, type: NotifType.BILL_INVOICE_REMINDER, message, recipientId },
em,
);
}
//************************ */
async createBillInvoiceNotification(recipientId: string, data: IInvoiceNotificationData, em: EntityManager) {
const message = NotificationMessage.BILL_INVOICE_MESSAGE.replace("[invoiceNumber]", data.invoiceId);
//sent notification based on the userSettings
await this.smsService.sendInvoicePaidSms(data.userPhone, data.invoiceId, data.price, dateFormat(data.paidAt!));
if (data.userEmail) await this.emailService.sendInvoiceEmail(data);
return this.createNotification({ title: NotificationMessage.BILL_INVOICE, type: NotifType.BILL_INVOICE, message, recipientId }, em);
}
//************************ */
async createApprovedInvoiceNotification(recipientId: string, data: IInvoiceNotificationData, em: EntityManager) {
const message = NotificationMessage.APPROVED_INVOICE_MESSAGE.replace("[invoiceNumber]", data.invoiceId);
//sent notification based on the userSettings
await this.smsService.sendInvoiceApprovedSms(data.userPhone, data.invoiceId, data.price, dateFormat(data.dueDate));
if (data.userEmail) await this.emailService.sendInvoiceEmail(data);
return this.createNotification({ title: NotificationMessage.APPROVED_INVOICE, type: NotifType.BILL_INVOICE, message, recipientId }, em);
}
//************************ */
async createInvoiceOverdueNotification(recipientId: string, data: IInvoiceNotificationData, em: EntityManager) {
const message = NotificationMessage.INVOICE_OVERDUE_MESSAGE.replace("[invoiceNumber]", data.invoiceId).replace(
"[lateFee]",
numberFormat(data.lateFee || 0),
);
//sent notification based on the userSettings
await this.smsService.sendInvoiceOverdueSms(data.userPhone, data.invoiceId, data.price, data.lateFee || 0, dateFormat(data.dueDate));
if (data.userEmail) await this.emailService.sendInvoiceEmail(data);
return this.createNotification(
{ title: NotificationMessage.INVOICE_OVERDUE, type: NotifType.INVOICE_OVERDUE, message, recipientId },
em,
);
}
//************************ */
async createRecurringInvoiceNotification(recipientId: string, data: IInvoiceNotificationData, em: EntityManager) {
const message = NotificationMessage.RECURRING_INVOICE_MESSAGE.replace("[invoiceNumber]", data.invoiceId);
//sent notification based on the userSettings
await this.smsService.sendRecurringInvoiceDraftSms(data.userPhone, data.invoiceId, data.price);
if (data.userEmail) await this.emailService.sendInvoiceEmail(data);
return this.createNotification(
{ title: NotificationMessage.RECURRING_INVOICE, type: NotifType.RECURRING_INVOICE, message, recipientId },
em,
);
}
// //************************ */
async createPaymentReminderNotification(recipientId: string, data: IPaymentNotificationData, em: EntityManager) {
const message = NotificationMessage.PAYMENT_REMINDER_MESSAGE.replace("[amount]", numberFormat(data.amount));
await this.smsService.sendPaymentReminderSms(data.userPhone, numberFormat(data.amount));
if (data.userEmail) await this.emailService.sendPaymentReminderEmail(data);
return this.createNotification(
{
title: NotificationMessage.PAYMENT_REMINDER,
type: NotifType.PAYMENT_REMINDER,
message,
recipientId,
},
em,
);
}
// //************************ */
async createPaymentCancellationNotification(recipientId: string, data: IPaymentNotificationData, em: EntityManager) {
const message = NotificationMessage.PAYMENT_CANCELLATION_MESSAGE.replace("[amount]", numberFormat(data.amount));
await this.smsService.sendPaymentCancellationSms(data.userPhone, numberFormat(data.amount));
if (data.userEmail) await this.emailService.sendPaymentCancellationEmail(data);
return this.createNotification(
{
title: NotificationMessage.PAYMENT_CANCELLATION,
type: NotifType.PAYMENT_CANCELLATION,
message,
recipientId,
},
em,
);
}
//--------------------------------------
}
@@ -0,0 +1,129 @@
import { EntityManager } from "@mikro-orm/postgresql";
import { Processor } from "@nestjs/bullmq";
import { Logger } from "@nestjs/common";
import { Job } from "bullmq";
import { WorkerProcessor } from "../../../common/queues/worker.processor";
import { NOTIFICATION } from "../constants";
import { NotifType } from "../enums/notif-settings.enum";
import {
IAnnouncementNotificationData,
IInvoiceNotificationData,
IOtpNotificationData,
IPaymentNotificationData,
ITicketNotificationData,
} from "../interfaces/ISendNotificationData";
import { NotificationsService } from "../providers/notifications.service";
type NotificationJobData = {
type: NotifType;
recipientId: string;
data:
| ITicketNotificationData
| IInvoiceNotificationData
| IAnnouncementNotificationData
| IPaymentNotificationData
| IOtpNotificationData;
};
@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 em: EntityManager,
) {
super();
}
async process(job: Job<NotificationJobData>, token?: string) {
this.logger.log(`Processing notification job: ${job.id} ${token ? `with token: ${token}` : ""}`);
const entityManager = this.em.fork();
try {
await entityManager.begin();
const { type, recipientId, data } = job.data;
switch (type) {
// User Notifications
case NotifType.OTP:
await this.notificationsService.createOtpNotification(data as IOtpNotificationData);
break;
case NotifType.USER_LOGIN:
await this.notificationsService.createLoginNotification(recipientId, data);
break;
case NotifType.ANNOUNCEMENT:
await this.notificationsService.createAnnouncementNotification(recipientId, data as IAnnouncementNotificationData, entityManager);
break;
// Ticket Notifications
case NotifType.ANSWER_TICKET:
await this.notificationsService.createAnswerTicketNotification(recipientId, data as ITicketNotificationData, entityManager);
break;
case NotifType.CREATE_TICKET:
await this.notificationsService.createTicketNotification(recipientId, data as ITicketNotificationData, entityManager);
break;
case NotifType.ASSIGN_TICKET:
await this.notificationsService.createAssignTicketNotificationForAdmin(
recipientId,
data as ITicketNotificationData,
entityManager,
);
break;
// Invoice Notifications
case NotifType.CREATE_INVOICE:
await this.notificationsService.createInvoiceCreationNotification(recipientId, data as IInvoiceNotificationData, entityManager);
break;
case NotifType.BILL_INVOICE_REMINDER:
await this.notificationsService.createBillInvoiceReminderNotification(
recipientId,
data as IInvoiceNotificationData,
entityManager,
);
break;
case NotifType.BILL_INVOICE:
await this.notificationsService.createBillInvoiceNotification(recipientId, data as IInvoiceNotificationData, entityManager);
break;
case NotifType.APPROVE_INVOICE:
await this.notificationsService.createApprovedInvoiceNotification(recipientId, data as IInvoiceNotificationData, entityManager);
break;
case NotifType.INVOICE_OVERDUE:
await this.notificationsService.createInvoiceOverdueNotification(recipientId, data as IInvoiceNotificationData, entityManager);
break;
case NotifType.RECURRING_INVOICE:
await this.notificationsService.createRecurringInvoiceNotification(recipientId, data as IInvoiceNotificationData, entityManager);
break;
// Payment Notifications
case NotifType.PAYMENT_REMINDER:
await this.notificationsService.createPaymentReminderNotification(recipientId, data as IPaymentNotificationData, entityManager);
break;
case NotifType.PAYMENT_CANCELLATION:
await this.notificationsService.createPaymentCancellationNotification(
recipientId,
data as IPaymentNotificationData,
entityManager,
);
break;
default:
this.logger.warn(`Unknown notification type: ${type}`);
}
await entityManager.commit();
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 entityManager.rollback();
throw error;
}
}
}
@@ -0,0 +1,96 @@
import { InjectQueue } from "@nestjs/bullmq";
import { Injectable } from "@nestjs/common";
import { Queue } from "bullmq";
import { NOTIFICATION } from "../constants";
import { NotifType } from "../enums/notif-settings.enum";
import {
IAnnouncementNotificationData,
IBaseNotificationData,
IInvoiceNotificationData,
IOtpNotificationData,
IPaymentNotificationData,
ITicketNotificationData,
} 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,
},
);
}
async addOtpNotification(recipientId: string, data: IOtpNotificationData) {
await this.addNotificationJob(NotifType.OTP, 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 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 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);
}
}
@@ -0,0 +1,19 @@
import { EntityRepository } from "@mikro-orm/postgresql";
import { PaginationUtils } from "../../utils/providers/pagination.utils";
import { SearchNotificationQueryDto } from "../DTO/search-notification-query.dto";
import { Notification } from "../entities/notification.entity";
export class NotificationRepository extends EntityRepository<Notification> {
async getAllNotifications(queryDto: SearchNotificationQueryDto, recipientId: string) {
const { skip, limit } = PaginationUtils(queryDto);
const qb = this.qb("notification").where({ recipientId });
if (queryDto.isRead !== undefined) {
qb.andWhere({ isRead: queryDto.isRead === 1 });
}
return qb.orderBy({ createdAt: "DESC" }).offset(skip).limit(limit).getResultAndCount();
}
}