chore: implement notification module
This commit is contained in:
@@ -17,6 +17,7 @@ import { AuthModule } from "./modules/auth/auth.module";
|
|||||||
import { ContactUsModule } from "./modules/contact-us/contact-us.module";
|
import { ContactUsModule } from "./modules/contact-us/contact-us.module";
|
||||||
import { CriticismModule } from "./modules/criticisms/criticisms.module";
|
import { CriticismModule } from "./modules/criticisms/criticisms.module";
|
||||||
import { DanakServicesModule } from "./modules/danak-services/danak-services.module";
|
import { DanakServicesModule } from "./modules/danak-services/danak-services.module";
|
||||||
|
import { NotificationModule } from "./modules/notifications/notifications.module";
|
||||||
import { PaymentsModule } from "./modules/payments/payments.module";
|
import { PaymentsModule } from "./modules/payments/payments.module";
|
||||||
import { SettingModule } from "./modules/settings/settings.module";
|
import { SettingModule } from "./modules/settings/settings.module";
|
||||||
import { TicketsModule } from "./modules/tickets/tickets.module";
|
import { TicketsModule } from "./modules/tickets/tickets.module";
|
||||||
@@ -44,6 +45,7 @@ import { WalletsModule } from "./modules/wallets/wallets.module";
|
|||||||
ContactUsModule,
|
ContactUsModule,
|
||||||
WalletsModule,
|
WalletsModule,
|
||||||
PaymentsModule,
|
PaymentsModule,
|
||||||
|
NotificationModule,
|
||||||
],
|
],
|
||||||
controllers: [],
|
controllers: [],
|
||||||
providers: [],
|
providers: [],
|
||||||
|
|||||||
@@ -245,3 +245,17 @@ export const enum ContactUsMessage {
|
|||||||
TITLE_STRING = "عنوان باید یک رشته باشد",
|
TITLE_STRING = "عنوان باید یک رشته باشد",
|
||||||
NOT_FOUND = "پیامی با این ایدی یافت نشد",
|
NOT_FOUND = "پیامی با این ایدی یافت نشد",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const enum NotificationMessage {
|
||||||
|
NOT_FOUNT = "اعلان مورد نظر یافت نشد",
|
||||||
|
TITLE_IS_REQUIRED = "عنوان الزامی است",
|
||||||
|
TITLE_STRING = "عنوان باید یک رشته متنی باشد",
|
||||||
|
MESSAGE_IS_REQUIRED = "پیام الزامی است",
|
||||||
|
MESSAGE_STRING = "پیام باید یک رشته متنی باشد",
|
||||||
|
USERID_IS_REQUIRED = "ایدی کاربر الزامی است",
|
||||||
|
USERID_IS_UUID = "شناسه کاربر باید یک UUID معتبر باشد",
|
||||||
|
USERID_IS_STRING = "شناسه کاربر باید یک رشته متنی باشد",
|
||||||
|
LOGIN = "لاگین",
|
||||||
|
LOGIN_MESSAGE = "در تاریخ [DATE] یک لاگین به حساب کاربری شما صورت گرفت",
|
||||||
|
NOTIFICATION_NOT_FOUND_OR_ACCESS_DENIED = "اعلان یافت نشد یا دسترسی غیرمجاز است",
|
||||||
|
}
|
||||||
|
|||||||
@@ -9,9 +9,10 @@ import { UtilsModule } from "../utils/utils.module";
|
|||||||
import { AuthService } from "./providers/auth.service";
|
import { AuthService } from "./providers/auth.service";
|
||||||
import { TokensService } from "./providers/tokens.service";
|
import { TokensService } from "./providers/tokens.service";
|
||||||
import { JwtStrategy } from "./strategies/jwt.strategy";
|
import { JwtStrategy } from "./strategies/jwt.strategy";
|
||||||
|
import { NotificationModule } from "../notifications/notifications.module";
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [UtilsModule, UsersModule, PassportModule, JwtModule.registerAsync(jwtConfig())],
|
imports: [UtilsModule, UsersModule, PassportModule, JwtModule.registerAsync(jwtConfig()), NotificationModule],
|
||||||
controllers: [AuthController],
|
controllers: [AuthController],
|
||||||
providers: [AuthService, TokensService, JwtStrategy],
|
providers: [AuthService, TokensService, JwtStrategy],
|
||||||
exports: [AuthService],
|
exports: [AuthService],
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { DataSource } from "typeorm";
|
|||||||
|
|
||||||
import { TokensService } from "./tokens.service";
|
import { TokensService } from "./tokens.service";
|
||||||
import { AuthMessage, UserMessage } from "../../../common/enums/message.enum";
|
import { AuthMessage, UserMessage } from "../../../common/enums/message.enum";
|
||||||
|
import { NotificationsService } from "../../notifications/providers/notifications.service";
|
||||||
import { RoleEnum } from "../../users/enums/role.enum";
|
import { RoleEnum } from "../../users/enums/role.enum";
|
||||||
import { UsersService } from "../../users/providers/users.service";
|
import { UsersService } from "../../users/providers/users.service";
|
||||||
import { OTPService } from "../../utils/providers/otp.service";
|
import { OTPService } from "../../utils/providers/otp.service";
|
||||||
@@ -22,6 +23,7 @@ export class AuthService {
|
|||||||
private readonly otpService: OTPService,
|
private readonly otpService: OTPService,
|
||||||
private readonly tokensService: TokensService,
|
private readonly tokensService: TokensService,
|
||||||
private readonly dataSource: DataSource,
|
private readonly dataSource: DataSource,
|
||||||
|
private readonly notificationService: NotificationsService,
|
||||||
) {}
|
) {}
|
||||||
//****************** */
|
//****************** */
|
||||||
//****************** */
|
//****************** */
|
||||||
@@ -156,6 +158,9 @@ export class AuthService {
|
|||||||
const user = await this.checkUserLoginCredentialWithPhone(phone, code);
|
const user = await this.checkUserLoginCredentialWithPhone(phone, code);
|
||||||
|
|
||||||
const tokens = this.tokensService.generateAccessAndRefreshToken({ sub: user.id, role: user.role.name });
|
const tokens = this.tokensService.generateAccessAndRefreshToken({ sub: user.id, role: user.role.name });
|
||||||
|
|
||||||
|
const { notification } = await this.notificationService.createLoginNotification(user.id);
|
||||||
|
console.log(notification);
|
||||||
return {
|
return {
|
||||||
message: AuthMessage.LOGIN_SUCCESS,
|
message: AuthMessage.LOGIN_SUCCESS,
|
||||||
...tokens,
|
...tokens,
|
||||||
|
|||||||
@@ -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 { Column, Entity, ManyToOne } from "typeorm";
|
||||||
// import { BaseEntity } from "../../../common/entities/base.entity";
|
|
||||||
|
|
||||||
// @Entity()
|
import { BaseEntity } from "../../../common/entities/base.entity";
|
||||||
// export class Notification extends BaseEntity {
|
import { User } from "../../users/entities/user.entity";
|
||||||
// @Column({ type: "" })
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,6 +7,7 @@ import { BaseEntity } from "../../../common/entities/base.entity";
|
|||||||
import { UserAnnouncement } from "../../announcements/entities/user-announcement.entity";
|
import { UserAnnouncement } from "../../announcements/entities/user-announcement.entity";
|
||||||
import { Criticism } from "../../criticisms/entities/criticism.entity";
|
import { Criticism } from "../../criticisms/entities/criticism.entity";
|
||||||
import { DanakService } from "../../danak-services/entities/danak-service.entity";
|
import { DanakService } from "../../danak-services/entities/danak-service.entity";
|
||||||
|
import { Notification } from "../../notifications/entities/notification.entity";
|
||||||
import { Payment } from "../../payments/entities/payment.entity";
|
import { Payment } from "../../payments/entities/payment.entity";
|
||||||
import { UserSetting } from "../../settings/entities/user-setting.entity";
|
import { UserSetting } from "../../settings/entities/user-setting.entity";
|
||||||
import { TicketMessage } from "../../tickets/entities/ticket-message.entity";
|
import { TicketMessage } from "../../tickets/entities/ticket-message.entity";
|
||||||
@@ -64,9 +65,13 @@ export class User extends BaseEntity {
|
|||||||
|
|
||||||
@OneToMany(() => UserSetting, (settings) => settings.user, { nullable: false })
|
@OneToMany(() => UserSetting, (settings) => settings.user, { nullable: false })
|
||||||
settings: UserSetting;
|
settings: UserSetting;
|
||||||
|
|
||||||
@OneToOne(() => Wallet, (wallet) => wallet.user)
|
@OneToOne(() => Wallet, (wallet) => wallet.user)
|
||||||
wallet: Wallet;
|
wallet: Wallet;
|
||||||
|
|
||||||
@OneToMany(() => Payment, (payment) => payment.user)
|
@OneToMany(() => Payment, (payment) => payment.user)
|
||||||
payments: Payment[];
|
payments: Payment[];
|
||||||
|
|
||||||
|
@OneToMany(() => Notification, (notification) => notification.recipient)
|
||||||
|
notifications: Notification[];
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user