From 2815c503ff40d5c2dd06eb4ce730be0c46b9a39d Mon Sep 17 00:00:00 2001 From: morteza-mortezai Date: Fri, 12 Dec 2025 23:35:26 +0330 Subject: [PATCH] update auth --- src/modules/auth/services/tokens.service.ts | 8 +- .../notifications/notifications.gateway.ts | 6 +- test-notifications-socket.html | 96 +++++++++++++++++-- 3 files changed, 96 insertions(+), 14 deletions(-) diff --git a/src/modules/auth/services/tokens.service.ts b/src/modules/auth/services/tokens.service.ts index eff89e0..69a0412 100755 --- a/src/modules/auth/services/tokens.service.ts +++ b/src/modules/auth/services/tokens.service.ts @@ -27,7 +27,6 @@ export class TokensService { slug: string, em?: EntityManager, ) { - const entityManager = em || this.em; const refreshExpire = this.configService.getOrThrow('REFRESH_TOKEN_EXPIRE'); const accessExpire = this.configService.getOrThrow('JWT_EXPIRATION_TIME'); @@ -39,7 +38,8 @@ export class TokensService { const refreshToken = this.generateRefreshToken(); const type = isAdmin ? RefreshTokenType.ADMIN : RefreshTokenType.USER; - await this.storeRefreshToken(ownerId, restaurantId, refreshToken, type, entityManager); + // Only pass em if it's a transaction manager (not this.em), otherwise pass undefined to trigger flush + await this.storeRefreshToken(ownerId, restaurantId, refreshToken, type, em); return { accessToken: { token: accessToken, expire: dayjs().add(accessExpire, 'seconds').valueOf() }, @@ -79,8 +79,8 @@ export class TokensService { // Within transaction, just persist (flush will be called by transaction) entityManager.persist(token); } else { - // Outside transaction, persist and flush - return entityManager.persistAndFlush(token); + // Outside transaction, persist and flush immediately + await entityManager.persistAndFlush(token); } } diff --git a/src/modules/notifications/notifications.gateway.ts b/src/modules/notifications/notifications.gateway.ts index 93671e5..d25ad90 100644 --- a/src/modules/notifications/notifications.gateway.ts +++ b/src/modules/notifications/notifications.gateway.ts @@ -9,8 +9,7 @@ import { } from '@nestjs/websockets'; import { Server, Socket } from 'socket.io'; import { Logger, UseGuards, Inject, forwardRef } from '@nestjs/common'; -// import { WsRestId } from './decorators/ws-rest-id.decorator'; -import { NotificationService } from './services/notification.service'; + import { NotificationService } from './services/notification.service'; import { WsAdminAuthGuard, type AuthenticatedSocket } from './guards/ws-admin-auth.guard'; import { ModuleRef } from '@nestjs/core'; import { ExecutionContext } from '@nestjs/common'; @@ -46,6 +45,7 @@ export class NotificationsGateway implements OnGatewayConnection, OnGatewayDisco this.logger.error( `Connection authentication failed: ${error instanceof Error ? error.message : 'Unknown error'}`, ); + void client.emit('error', { message: 'Authentication failed' }); client.disconnect(); } } @@ -273,7 +273,7 @@ export class NotificationsGateway implements OnGatewayConnection, OnGatewayDisco const canActivate = await guard.canActivate(context); if (!canActivate) { - throw new Error('Authentication failed'); + throw new Error('Authentication failed'); } } } diff --git a/test-notifications-socket.html b/test-notifications-socket.html index e010896..3353f51 100644 --- a/test-notifications-socket.html +++ b/test-notifications-socket.html @@ -249,6 +249,17 @@ font-size: 12px; color: #999; } + + @keyframes slideIn { + from { + opacity: 0; + transform: translateY(-10px); + } + to { + opacity: 1; + transform: translateY(0); + } + } @@ -370,9 +381,34 @@ addEvent('ERROR', `Connection error: ${error.message}`); }); + socket.on('joined', data => { + addEvent('SUCCESS', `Joined room: ${data.room || 'unknown'}`, data); + }); + + // Handle notifications event - can be either: + // 1. List response: { notifications: [...] } + // 2. Real-time notification: { subject: string, body: string } socket.on('notifications', data => { - addEvent('SUCCESS', `Received ${data.notifications?.length || 0} notifications`, data); - displayNotifications(data.notifications || []); + if (Array.isArray(data.notifications)) { + // List of notifications response + addEvent('SUCCESS', `Received ${data.notifications.length} notifications`, data); + displayNotifications(data.notifications); + } else if (data.subject || data.body) { + // Real-time single notification + addEvent('NOTIFICATION', 'New notification received', data); + addRealTimeNotification(data); + } else if (Array.isArray(data)) { + // Direct array response + addEvent('SUCCESS', `Received ${data.length} notifications`, data); + displayNotifications(data); + } else { + // Unknown format, try to extract notifications from various possible structures + addEvent('INFO', 'Received notifications data', data); + const notifications = data.notifications || data.data || (Array.isArray(data) ? data : [data]); + if (Array.isArray(notifications) && notifications.length > 0) { + displayNotifications(notifications); + } + } }); socket.on('error', data => { @@ -423,14 +459,28 @@ let html = ''; notifications.forEach(notification => { - const date = notification.createdAt ? new Date(notification.createdAt).toLocaleString() : 'N/A'; + // Handle both full notification objects and simple payloads + const title = notification.title || notification.subject || 'N/A'; + const content = notification.content || notification.body || 'No content'; + const id = notification.id || 'N/A'; + const date = notification.createdAt + ? new Date(notification.createdAt).toLocaleString() + : notification.created_at + ? new Date(notification.created_at).toLocaleString() + : 'N/A'; + + const userName = notification.user + ? `${notification.user.firstName || ''} ${notification.user.lastName || ''}`.trim() + : notification.userName || ''; + html += `
-
${notification.title || 'N/A'}
-
${notification.content || 'No content'}
+
${escapeHtml(title)}
+
${escapeHtml(content)}
- ID: ${notification.id} | Created: ${date} - ${notification.user ? ` | User: ${notification.user.firstName || ''} ${notification.user.lastName || ''}` : ''} + ID: ${id} | Created: ${date} + ${userName ? ` | User: ${escapeHtml(userName)}` : ''} + ${notification.admin ? ` | Admin: ${notification.admin.firstName || ''} ${notification.admin.lastName || ''}` : ''}
`; @@ -439,6 +489,37 @@ listDiv.innerHTML = html; } + function addRealTimeNotification(notification) { + const listDiv = document.getElementById('notificationsList'); + const title = notification.subject || notification.title || 'New Notification'; + const content = notification.body || notification.content || 'No content'; + const currentTime = new Date().toLocaleString(); + + const notificationHtml = ` +
+
${escapeHtml(title)}
+
${escapeHtml(content)}
+
+ Received: ${currentTime} +
+
+ `; + + // Add to the top of the list + if (listDiv.children.length === 1 && listDiv.children[0].textContent.includes('No notifications')) { + listDiv.innerHTML = notificationHtml; + } else { + listDiv.insertAdjacentHTML('afterbegin', notificationHtml); + } + } + + function escapeHtml(text) { + if (!text) return ''; + const div = document.createElement('div'); + div.textContent = text; + return div.innerHTML; + } + function updateStatus(status, message) { const statusEl = document.getElementById('connectionStatus'); statusEl.className = `status ${status}`; @@ -457,6 +538,7 @@ WARNING: '#dcdcaa', ERROR: '#f48771', NOTIFICATIONS_LIST: '#4fc1ff', + NOTIFICATION: '#9cdcfe', }; eventItem.innerHTML = `