This commit is contained in:
2025-12-12 22:42:58 +03:30
parent 10e13c7901
commit 0434329db6
5 changed files with 42 additions and 36 deletions
@@ -15,7 +15,7 @@ export enum NotifTitleEnum {
REVIEW_CREATED = 'review.created', REVIEW_CREATED = 'review.created',
ORDER_STATUS_CHANGED = 'order.status.changed', ORDER_STATUS_CHANGED = 'order.status.changed',
} }
export type recipientType = { userId: string; restaurantId: string } | { adminId: string; restaurantId: string }; export type recipientType = { userId: string } | { adminId: string };
export interface NotifRequestMessage { export interface NotifRequestMessage {
subject: NotifTitleEnum; subject: NotifTitleEnum;
@@ -37,6 +37,7 @@ export interface NotifRequest {
// timestamp: Date; // timestamp: Date;
// notifType: NotifTypeEnum; // notifType: NotifTypeEnum;
// channels: NotifChannelEnum[]; // channels: NotifChannelEnum[];
restaurantId: string;
recipients: recipientType[]; recipients: recipientType[];
message: NotifRequestMessage; message: NotifRequestMessage;
metadata: { metadata: {
@@ -18,8 +18,8 @@ import { WsAdminAuthGuard, type AuthenticatedSocket } from './guards/ws-admin-au
cors: { cors: {
origin: true, origin: true,
credentials: true, credentials: true,
connectionGuard: WsAdminAuthGuard,
}, },
// connectionGuard: WsAdminAuthGuard,
namespace: '/notifications', namespace: '/notifications',
transports: ['websocket', 'polling'], transports: ['websocket', 'polling'],
}) })
@@ -35,21 +35,23 @@ export class NotificationsGateway implements OnGatewayConnection, OnGatewayDisco
) {} ) {}
handleConnection(client: AuthenticatedSocket) { handleConnection(client: AuthenticatedSocket) {
console.log(client); this.logger.log(`Admin connected: ${client.adminId}`);
this.logger.log(`Client connected: ${client.id}`); this.handleJoinRoom(client);
} }
handleDisconnect(client: Socket) { handleDisconnect(client: Socket) {
this.logger.log(`Client disconnected: ${client.id}`); this.logger.log(`Client disconnected: ${client.id}`);
} }
sendAdminNotification(client: AuthenticatedSocket, event: string, data: any) { sendInAppNotification(repipient: { adminId: string; restaurantId: string }, event: string, data: any) {
const room = this.getRoom(client); this.logger.log(`Sending in app notification to admin: ${repipient.adminId}`);
const room = this.getRoom(repipient.adminId, repipient.restaurantId);
this.server.to(room).emit(event, data); this.server.to(room).emit(event, data);
this.logger.log(`In app notification sent to admin: ${repipient.adminId}`);
} }
private getRoom(client: AuthenticatedSocket) { private getRoom(adminId: string, restaurantId: string) {
return `restaurant:${client.restId}-admin:${client.adminId}`; return `restaurant:${restaurantId}-admin:${adminId}`;
} }
/** /**
* Join a room for a specific restaurant (admin/staff) * Join a room for a specific restaurant (admin/staff)
@@ -57,14 +59,12 @@ export class NotificationsGateway implements OnGatewayConnection, OnGatewayDisco
* Restaurant ID is extracted from the authenticated admin's token * Restaurant ID is extracted from the authenticated admin's token
* Room format: `restaurant:${restId}` * Room format: `restaurant:${restId}`
*/ */
// @UseGuards(WsAdminAuthGuard) handleJoinRoom(client: AuthenticatedSocket) {
// @SubscribeMessage('join:restaurant') const room = this.getRoom(client.adminId!, client.restId!);
// handleJoinRestaurant(@ConnectedSocket() client: AuthenticatedSocket, @WsRestId() restId: string) { void client.join(room);
// const room = `restaurant:${restId}`; this.logger.log(`Admin ${client.adminId} (Client ${client.id}) joined room: ${room}`);
// void client.join(room); void client.emit('joined', { room, message: 'Successfully Admin joined room' });
// this.logger.log(`Admin ${client.adminId} (Client ${client.id}) joined room: ${room}`); }
// void client.emit('joined', { room, message: 'Successfully joined restaurant room' });
// }
/** /**
* Join a room for a specific cookie/session (public user) * Join a room for a specific cookie/session (public user)
@@ -3,19 +3,12 @@ import { EntityManager } from '@mikro-orm/postgresql';
import { Notification } from '../entities/notification.entity'; import { Notification } from '../entities/notification.entity';
import { NotificationPreferenceService } from './notification-preference.service'; import { NotificationPreferenceService } from './notification-preference.service';
import { NotificationQueueService } from './notification-queue.service'; import { NotificationQueueService } from './notification-queue.service';
import { NotificationsGateway } from '../notifications.gateway';
import { NotificationQueueJob } from '../interfaces/notification-queue.interface'; import { NotificationQueueJob } from '../interfaces/notification-queue.interface';
import { Restaurant } from '../../restaurants/entities/restaurant.entity'; import { Restaurant } from '../../restaurants/entities/restaurant.entity';
import { User } from '../../users/entities/user.entity'; import { User } from '../../users/entities/user.entity';
import { NotifChannelEnum, NotifRequest, NotifTitleEnum } from '../interfaces/notification.interface'; import { NotifChannelEnum, NotifRequest, NotifTitleEnum } from '../interfaces/notification.interface';
export interface SendNotificationParams {
restaurantId: string;
userId?: string;
title: NotifTitleEnum;
content: string;
idempotencyKey?: string;
}
@Injectable() @Injectable()
export class NotificationService { export class NotificationService {
private readonly logger = new Logger(NotificationService.name); private readonly logger = new Logger(NotificationService.name);
@@ -24,22 +17,23 @@ export class NotificationService {
private readonly em: EntityManager, private readonly em: EntityManager,
private readonly preferenceService: NotificationPreferenceService, private readonly preferenceService: NotificationPreferenceService,
private readonly queueService: NotificationQueueService, private readonly queueService: NotificationQueueService,
private readonly notificationGateway: NotificationsGateway,
) {} ) {}
async sendNotification(params: NotifRequest): Promise<Notification[]> { async sendNotification(params: NotifRequest): Promise<Notification[]> {
const { recipients, message, metadata } = params; const { recipients, message, metadata, restaurantId } = params;
// create Database notifications // create Database notifications
const notifications = await this.createAdminBulkNotifications( const notifications = await this.createAdminBulkNotifications(
recipients.map(recipient => ({ recipients.map(recipient => ({
restaurantId: recipient.restaurantId, restaurantId,
title: message.subject, title: message.subject,
content: message.body, content: message.body,
adminId: 'adminId' in recipient ? recipient.adminId : null, adminId: 'adminId' in recipient ? recipient.adminId : null,
userId: 'userId' in recipient ? recipient.userId : null, userId: 'userId' in recipient ? recipient.userId : null,
})), })),
); );
const restaurantId = recipients[0].restaurantId;
// get admin prefrences // get admin prefrences
const preference = await this.preferenceService.findByRestaurantAndType(restaurantId, message.subject); const preference = await this.preferenceService.findByRestaurantAndType(restaurantId, message.subject);
@@ -48,6 +42,17 @@ export class NotificationService {
return notifications; return notifications;
} }
// send in app notification
if (preference?.channels?.includes(NotifChannelEnum.IN_APP)) {
recipients.forEach(recipient => {
if ('adminId' in recipient) {
this.notificationGateway.sendInAppNotification({ adminId: recipient.adminId, restaurantId }, 'notification', {
title: message.subject,
content: message.body,
});
}
});
}
// const job: NotificationQueueJob = { // const job: NotificationQueueJob = {
// restaurantId, // restaurantId,
// userId, // userId,
@@ -22,10 +22,10 @@ export class PagerListeners {
// get admnin os restuaraant that have pager permissuins // get admnin os restuaraant that have pager permissuins
const admins = await this.adminService.findAdminsWithPermission(event.restaurantId, Permission.MANAGE_PAGER); const admins = await this.adminService.findAdminsWithPermission(event.restaurantId, Permission.MANAGE_PAGER);
const recipients = admins.map(admin => ({ const recipients = admins.map(admin => ({
restaurantId: event.restaurantId,
adminId: admin.id, adminId: admin.id,
})); }));
await this.notificationService.sendNotification({ await this.notificationService.sendNotification({
restaurantId: event.restaurantId,
message: { message: {
subject: NotifTitleEnum.PAGER_CREATED, subject: NotifTitleEnum.PAGER_CREATED,
body: `Your pager has created successfully`, body: `Your pager has created successfully`,
+8 -8
View File
@@ -48,14 +48,14 @@ export class PagerService {
} }
} }
const cookieId = userCookieId || ulid().toString(); const cookieId = userCookieId || ulid().toString();
const existingPager = await this.em.findOne(Pager, { // const existingPager = await this.em.findOne(Pager, {
restaurant: { id: restaurant!.id }, // restaurant: { id: restaurant!.id },
status: PagerStatus.Pending, // status: PagerStatus.Pending,
cookieId, // cookieId,
}); // });
if (existingPager) { // if (existingPager) {
throw new BadRequestException('Pager already exists'); // throw new BadRequestException('Pager already exists');
} // }
const pager = this.em.create(Pager, { const pager = this.em.create(Pager, {
...createPagerDto, ...createPagerDto,