/
viktorphp
/
back-socket
Обзор
Документация
Войти
/
viktorphp
/
back-socket
Код
Запросы
0
Задачи
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
src/gateways/diary/diary.gateway.ts
133 строки
5 KB
Иванов Виктор Евгеньевич
[feature-diary]: добавить сохранение отредактированного OCR
12 апр 2026, 20:08
12 апр 2026, 20:08
1c5d24c
Код
Авторство
О чём код?
import { WebSocketGateway, WebSocketServer, OnGatewayConnection, OnGatewayDisconnect, ConnectedSocket, } from '@nestjs/websockets'; import { Server, Socket } from 'socket.io'; import { UsersService } from 'src/services/users/users.service'; /** * DiaryGateway — WebSocket-шлюз для событий дневника. * * Единственная задача этого шлюза — доставлять серверные push-уведомления * конкретному пользователю. Клиент не отправляет сюда никаких событий. * * Сейчас реализовано одно событие: * - ocrComplete: отправляется клиенту когда OCR-распознавание завершилось * * Аутентификация при подключении — такая же как в MessagesGateway: * извлекаем sessionId из cookies, проверяем в БД. */ @WebSocketGateway({ namespace: '/diary', // отдельный namespace, чтобы не смешивать с чатом cors: { origin: '*', }, }) export class DiaryGateway implements OnGatewayConnection, OnGatewayDisconnect { constructor(private readonly usersService: UsersService) {} @WebSocketServer() server: Server; /** * Карта userId → socketId для отслеживания подключённых пользователей. * Используется чтобы знать, куда отправить push-уведомление. */ private userSockets: Map<string, string> = new Map(); /** * Обрабатывает подключение клиента к namespace /diary. * Аутентифицирует пользователя через sessionId из cookies. */ async handleConnection(@ConnectedSocket() client: Socket) { try { const sessionId = this.extractSessionId(client); if (!sessionId) { client.emit('error', { message: 'Сессия не предоставлена' }); client.disconnect(true); return; } const user = await this.usersService.getUserBySessionId(sessionId); if (!user) { client.emit('error', { message: 'Сессия недействительна' }); client.disconnect(true); return; } // Сохраняем связь userId → socketId для дальнейшей адресной отправки this.userSockets.set(user.id, client.id); client.emit('connected', { status: 'success' }); } catch (error) { console.error('DiaryGateway: ошибка при подключении:', error); client.disconnect(true); } } /** * Обрабатывает отключение клиента — удаляем его из карты сокетов. */ handleDisconnect(@ConnectedSocket() client: Socket) { const userId = Array.from(this.userSockets.entries()).find( ([, socketId]) => socketId === client.id, )?.[0]; if (userId) { this.userSockets.delete(userId); } } /** * Отправляет результат OCR конкретному пользователю. * Вызывается из DiaryService когда фоновая задача распознавания завершилась. * * @param userId - ID пользователя, которому нужно отправить результат * @param payload - Данные результата OCR */ emitOcrComplete( userId: string, payload: { fileId: string; ocrStatus: 'done' | 'error'; ocrText?: string; ocrError?: string; }, ) { const socketId = this.userSockets.get(userId); if (socketId) { // Отправляем событие только конкретному пользователю по его socketId this.server.to(socketId).emit('ocrComplete', payload); } // Если пользователь не подключён — ничего не делаем. // Он увидит результат при следующем запросе GET /diary/entries/:id } /** * Вспомогательный метод: извлекает sessionId из cookies сокет-подключения. * Формат cookies: 'key1=value1; key2=value2' */ private extractSessionId(client: Socket): string | null { try { const authCookies: string = client.handshake.auth?.cookies; if (!authCookies) return null; const decoded = decodeURIComponent(authCookies); // Ищем sessionId среди всех cookie-значений const entry = decoded.split(',').find((el) => el.includes('sessionId')); if (!entry) return null; return entry.split(':')[1]?.replace(/["}]/g, '') ?? null; } catch { return null; } } }