1+ import { WebSocketGateway , WebSocketServer , SubscribeMessage , ConnectedSocket , MessageBody } from '@nestjs/websockets' ;
2+ import { Server , Socket } from 'socket.io' ;
3+ import { UseFilters , UseGuards } from '@nestjs/common' ;
4+ import { OriginLogger } from '@multiversx/sdk-nestjs-common' ;
5+
6+ import { QueryPagination } from 'src/common/entities/query.pagination' ;
7+ import { WsValidationPipe } from 'src/utils/ws-validation.pipe' ;
8+ import { WebsocketExceptionsFilter } from 'src/utils/ws-exceptions.filter' ;
9+ import { WsSubscriptionLimiterGuard } from 'src/utils/ws.subscription.limiter' ;
10+ import { RoomKeyGenerator } from './room.key.generator' ;
11+ import { EventsService } from 'src/endpoints/events/events.service' ;
12+ import { EventsCustomSubscribePayload } from 'src/endpoints/events/entities/events.custom.subscribe' ;
13+ import { EventsFilter } from 'src/endpoints/events/entities/events.filter' ;
14+ import { Events } from 'src/endpoints/events/entities/events' ;
15+
16+
17+ @UseFilters ( WebsocketExceptionsFilter )
18+ @WebSocketGateway ( { cors : { origin : '*' } , path : '/ws/subscription' } )
19+ export class EventsCustomGateway {
20+ private readonly logger = new OriginLogger ( EventsCustomGateway . name ) ;
21+
22+ // Prefix distinct pentru camerele de evenimente pentru a nu se suprapune cu txs
23+ static keyPrefix = 'custom-events-' ;
24+
25+ @WebSocketServer ( )
26+ server ! : Server ;
27+
28+ constructor (
29+ private readonly eventsService : EventsService ,
30+ ) { }
31+
32+ @UseGuards ( WsSubscriptionLimiterGuard )
33+ @SubscribeMessage ( 'subscribeCustomEvents' )
34+ async handleCustomSubscription (
35+ @ConnectedSocket ( ) client : Socket ,
36+ @MessageBody ( new WsValidationPipe ( ) ) payload : EventsCustomSubscribePayload
37+ ) {
38+ const filterIdentifier = RoomKeyGenerator . deterministicStringify ( payload ) ;
39+ const roomName = `${ EventsCustomGateway . keyPrefix } ${ filterIdentifier } ` ;
40+
41+ if ( ! client . rooms . has ( roomName ) ) {
42+ await client . join ( roomName ) ;
43+ }
44+
45+ return { status : 'success' } ;
46+ }
47+
48+ @SubscribeMessage ( 'unsubscribeCustomEvents' )
49+ async handleCustomUnsubscribe (
50+ @ConnectedSocket ( ) client : Socket ,
51+ @MessageBody ( new WsValidationPipe ( ) ) payload : EventsCustomSubscribePayload
52+ ) {
53+ const filterIdentifier = RoomKeyGenerator . deterministicStringify ( payload ) ;
54+ const roomName = `${ EventsCustomGateway . keyPrefix } ${ filterIdentifier } ` ;
55+
56+ if ( client . rooms . has ( roomName ) ) {
57+ await client . leave ( roomName ) ;
58+ }
59+
60+ return { status : 'unsubscribed' } ;
61+ }
62+
63+ async pushEventsForTimestampMs ( timestampMs : number ) : Promise < void > {
64+ try {
65+ const timestamp = timestampMs / 1000 ;
66+
67+ const allEvents = await this . eventsService . getEvents (
68+ new QueryPagination ( { size : 10000 } ) ,
69+ new EventsFilter ( { before : timestamp , after : timestamp } ) ,
70+ ) ;
71+
72+ const eventsFilteredForBroadcast : Map < string , Events [ ] > = new Map ( ) ;
73+
74+ for ( const event of allEvents ) {
75+ const roomKeys = RoomKeyGenerator . generate (
76+ EventsCustomGateway . keyPrefix ,
77+ event ,
78+ EventsCustomSubscribePayload ,
79+ ) ;
80+
81+ for ( const roomKey of roomKeys ) {
82+ if ( this . server . sockets . adapter . rooms . has ( roomKey ) ) {
83+ if ( ! eventsFilteredForBroadcast . has ( roomKey ) ) {
84+ eventsFilteredForBroadcast . set ( roomKey , [ ] ) ;
85+ }
86+ eventsFilteredForBroadcast . get ( roomKey ) ! . push ( event ) ;
87+ }
88+ }
89+ }
90+
91+ for ( const [ roomName ] of eventsFilteredForBroadcast ) {
92+ this . server . to ( roomName ) . emit ( "customEventUpdate" , {
93+ events : eventsFilteredForBroadcast . get ( roomName ) ,
94+ timestampMs
95+ } ) ;
96+ }
97+ } catch ( error ) {
98+ this . logger . error ( error ) ;
99+ }
100+ }
101+ }
0 commit comments