chore: implement notification module

This commit is contained in:
Matin
2025-02-08 11:56:56 +03:30
parent e216937dd9
commit 786ea465ff
12 changed files with 205 additions and 7 deletions
@@ -0,0 +1,18 @@
import { IsNotEmpty, IsString, IsUUID } from "class-validator";
import { NotificationMessage } from "../../../common/enums/message.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("4", { message: NotificationMessage.USERID_IS_UUID })
recipientId: string;
}
@@ -1,7 +1,27 @@
// import { Column, Entity } from "typeorm";
// import { BaseEntity } from "../../../common/entities/base.entity";
import { Column, Entity, ManyToOne } from "typeorm";
// @Entity()
// export class Notification extends BaseEntity {
// @Column({ type: "" })
// }
import { BaseEntity } from "../../../common/entities/base.entity";
import { User } from "../../users/entities/user.entity";
import { NotificationType } from "../enums/notification-type.enum";
@Entity()
export class Notification extends BaseEntity {
@Column({ type: "varchar", length: 250 })
title: string;
@Column({ type: "text" })
message: string;
@Column({
type: "enum",
enum: NotificationType,
default: NotificationType.LOGIN,
})
type: NotificationType;
@Column({ type: "boolean", default: false })
isRead: boolean;
@ManyToOne(() => User, (user) => user.notifications, { onDelete: "CASCADE", eager: true })
recipient: User;
}
@@ -0,0 +1,5 @@
export enum NotificationType {
LOGIN = "LOGIN",
PASSWORD_CAHNGED = "PASSWORD_CHANGED",
WALLET_CHARGED = "WALLER_CHARGED",
}
@@ -0,0 +1,34 @@
import { Controller, Get, Param, Patch, Query } from "@nestjs/common";
import { ApiOperation, ApiTags } from "@nestjs/swagger";
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 { PaginationDto } from "../../common/DTO/pagination.dto";
@Controller("notifications")
@ApiTags("Notifications")
export class NotificationController {
constructor(private readonly notificationService: NotificationsService) {}
//********************* */
@ApiOperation({ summary: "all notifications by user" })
@Pagination()
@AuthGuards()
@Get()
getAllNotifications(@UserDec("id") userId: string, @Query() paginationDto: PaginationDto) {
console.log(userId);
return this.notificationService.getAllNotifications(paginationDto, userId);
}
//********************* */
@ApiOperation({ summary: "mark user notification as read" })
@AuthGuards()
@Patch(":id/read")
markAsRead(@Param("id") id: string, @UserDec("id") userId: string) {
return this.notificationService.markAsRead(id, userId);
}
}
@@ -0,0 +1,17 @@
import { Module } from "@nestjs/common";
import { TypeOrmModule } from "@nestjs/typeorm";
import { Notification } from "./entities/notification.entity";
import { NotificationController } from "./notifications.controller";
import { NotificationsService } from "./providers/notifications.service";
import { NotificationRepository } from "./repositories/notifications.repository";
import { User } from "../users/entities/user.entity";
import { UsersModule } from "../users/users.module";
@Module({
imports: [TypeOrmModule.forFeature([Notification, User]), UsersModule],
providers: [NotificationRepository, NotificationsService],
controllers: [NotificationController],
exports: [NotificationRepository, NotificationsService],
})
export class NotificationModule {}
@@ -0,0 +1,65 @@
import { BadRequestException, Injectable, NotFoundException } from "@nestjs/common";
import { PaginationDto } from "../../../common/DTO/pagination.dto";
import { NotificationMessage, UserMessage } from "../../../common/enums/message.enum";
import { UsersService } from "../../users/providers/users.service";
import { PaginationUtils } from "../../utils/providers/pagination.utils";
import { CreateNotificationDto } from "../DTO/create-notification.dto";
import { NotificationRepository } from "../repositories/notifications.repository";
@Injectable()
export class NotificationsService {
constructor(
private readonly notificationRepository: NotificationRepository,
private readonly usersService: UsersService,
) {}
//************************ */
async createNotification(createNotificationDto: CreateNotificationDto) {
const { user } = await this.usersService.findOneById(createNotificationDto.recipientId);
if (!user) throw new BadRequestException(UserMessage.USER_NOT_FOUND);
const notification = this.notificationRepository.create({
...createNotificationDto,
recipient: user,
});
await this.notificationRepository.save(notification);
return { notification };
}
//************************ */
async markAsRead(notificationId: string, userId: string) {
const notification = await this.notificationRepository.findOne({
where: { id: notificationId, recipient: { id: userId } },
});
if (!notification) throw new NotFoundException(NotificationMessage.NOTIFICATION_NOT_FOUND_OR_ACCESS_DENIED);
notification.isRead = true;
return this.notificationRepository.save(notification);
}
//************************ */
async createLoginNotification(recipientId: string) {
const message = NotificationMessage.LOGIN_MESSAGE.replace("[DATE]", new Date().toLocaleString("fa-IR"));
return this.createNotification({ title: NotificationMessage.LOGIN, message, recipientId });
}
//************************ */
async getAllNotifications(paginationDto: PaginationDto, recipientId: string) {
const { skip, limit } = PaginationUtils(paginationDto);
const [notifications, count] = await this.notificationRepository
.createQueryBuilder("notification")
.where("notification.recipientId = :recipientId", { recipientId })
.orderBy("notification.createdAt", "DESC")
.skip(skip)
.take(limit)
.getManyAndCount();
return { notifications, count, paginate: true };
}
}
@@ -0,0 +1,12 @@
import { Injectable } from "@nestjs/common";
import { InjectRepository } from "@nestjs/typeorm";
import { Repository } from "typeorm";
import { Notification } from "../entities/notification.entity";
@Injectable()
export class NotificationRepository extends Repository<Notification> {
constructor(@InjectRepository(Notification) notificationRepository: Repository<Notification>) {
super(notificationRepository.target, notificationRepository.manager, notificationRepository.queryRunner);
}
}