vani-meeting-server 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dump.rdb +0 -0
- package/package.json +41 -0
- package/pm2.config.js +8 -0
- package/src/ServerHandler.ts +32 -0
- package/src/base/BaseSFUWebsocket.ts +20 -0
- package/src/index.ts +3 -0
- package/src/lib/redis/RedisHandler.ts +150 -0
- package/src/models/Event.ts +36 -0
- package/src/models/MessagePayload.ts +35 -0
- package/src/models/Participant.ts +24 -0
- package/src/models/WebSocketServerStartRequest.ts +14 -0
- package/src/sfu/SFUEachRoomHandler.ts +265 -0
- package/src/sfu/SFUEachRoomUserHandler.ts +468 -0
- package/src/sfu/SFUHandler.ts +67 -0
- package/src/utility/Constant.ts +63 -0
- package/src/utility/EventEmitterHandler.ts +10 -0
- package/src/utility/VaniEventListener.ts +7 -0
- package/src/websocket/EachSocketConnectionHandler.ts +451 -0
- package/src/websocket/WebSocketHandler.ts +30 -0
- package/tsconfig.json +12 -0
|
@@ -0,0 +1,451 @@
|
|
|
1
|
+
import WebSocket, { WebSocketServer } from "ws";
|
|
2
|
+
import { RedisHandler } from "../lib/redis/RedisHandler";
|
|
3
|
+
import { Participant } from "../models/Participant";
|
|
4
|
+
import { v4 as uuidv4 } from 'uuid';
|
|
5
|
+
import { EventEmitterHandler } from "../utility/EventEmitterHandler";
|
|
6
|
+
import { VaniEvent } from "../models/Event";
|
|
7
|
+
import { SFUHandler } from "../sfu/SFUHandler";
|
|
8
|
+
import { BaseSFUWebsocket } from "../base/BaseSFUWebsocket";
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
export enum WebSocketBasicEvents {
|
|
12
|
+
Config = "config",
|
|
13
|
+
JoinRoom = "joinRoom",
|
|
14
|
+
IsSetupDone = "setupDone",
|
|
15
|
+
Ping = "ping",
|
|
16
|
+
Pong = "pong",
|
|
17
|
+
AudioVideoPauseResume = "audioVideoPauseResume",
|
|
18
|
+
OnStartMeetingCalled = "startMeetingCalled",
|
|
19
|
+
OnAudioVideoPauseResume = "audioVideoPauseResume",
|
|
20
|
+
OnAudioVideoStatusUpdated = "audioVideoStatusUpdated",
|
|
21
|
+
OnAudioUnblockRequest = "audioUnblock",
|
|
22
|
+
OnVideoUnblockRequest = "videoUnblock",
|
|
23
|
+
OnAudioBlockRequest = "audioBlock",
|
|
24
|
+
OnVideoBlockRequest = "videoBlock",
|
|
25
|
+
UpdateParticipant = "updateParticipant",
|
|
26
|
+
OnParticipantUpdated = "participantUpdated",
|
|
27
|
+
OnNewJoinee = "newJoinee",
|
|
28
|
+
OnRejoined = "rejoined",
|
|
29
|
+
OnUserOnFoundWhileReconnect = "onUserOnFoundWhileReconnect",
|
|
30
|
+
|
|
31
|
+
OnUserLeft = "userLeft",
|
|
32
|
+
GetAllParticipant = "getParticipant",
|
|
33
|
+
OnServerParticipants = "participants",
|
|
34
|
+
GetMeetingStartTime = "getMeetingStartTime",
|
|
35
|
+
OnMeetingStartTime = "meetingStartTime",
|
|
36
|
+
GetOldMessages = "getOldMessages",
|
|
37
|
+
OnOldMessages = "oldMessages",
|
|
38
|
+
Message = "message",
|
|
39
|
+
OnChat = "chat",
|
|
40
|
+
SelfLeft = "selfLeft",
|
|
41
|
+
}
|
|
42
|
+
export enum SFUMessageType {
|
|
43
|
+
SFUMessage = "sfuMessage",
|
|
44
|
+
GetRouterRtpCapabilities = "routerRtpCapabilities",
|
|
45
|
+
OnRouterRtpCapabilities = "onRouterRtpCapabilities",
|
|
46
|
+
OnTransportConnect = "transportConnect",
|
|
47
|
+
OnCreateTransports = "createTransports",
|
|
48
|
+
OnTransportConnectDone = "transportConnectDone",
|
|
49
|
+
OnSendTransport = "onSendTransport",
|
|
50
|
+
OnConsumeTransport = "onConsumeTransport",
|
|
51
|
+
OnTransportProduceSyncRequest = "transportProduceSync",
|
|
52
|
+
OnTransportDataProduceSyncRequest = "dataTransportProduceSync",
|
|
53
|
+
OnReadyToConsume = "readyToConsume",
|
|
54
|
+
OnStartRTMP = "startRtmp",
|
|
55
|
+
OnProducerClosed = "producerClosed",
|
|
56
|
+
OnPauseProducer = "pauseProducer",
|
|
57
|
+
OnResumeProducer = "resumeProducer",
|
|
58
|
+
OnNewProducer = "onNewProducer",
|
|
59
|
+
ConsumeProductId = "consumeProductId",
|
|
60
|
+
OnServerConsumer = "onServerConsumer",
|
|
61
|
+
ResumeConsumer = "resumeConsumer",
|
|
62
|
+
PauseConsumer = "pauseConsumer",
|
|
63
|
+
OnProduceSyncDone = "produceSyncDone",
|
|
64
|
+
OnSpeakerChanged = "onSpeakerChanged",
|
|
65
|
+
OnRestartIceCandidate = "restartIceCandidate",
|
|
66
|
+
OnRestartIceCandidateResponse = "onRestartIceCandidate",
|
|
67
|
+
OnTrackEnded = "onTrackEnded",
|
|
68
|
+
UpdateSpatialConsumer = "updateSpatialConsumer",
|
|
69
|
+
OnTransportNotFound = "onTransportNotFound",
|
|
70
|
+
GetAllProducers = "getAllProducers",
|
|
71
|
+
OnAllProducers = "onAllProducers",
|
|
72
|
+
OnStopProducer = "stopProducer",
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export enum LocalMessageType {
|
|
78
|
+
OnNewUserJoinedRoom = "onNewUserJoinedRoom",
|
|
79
|
+
OnUserLeft = "OnUserLeft",
|
|
80
|
+
SocketMessage = "socketMessage"
|
|
81
|
+
|
|
82
|
+
}
|
|
83
|
+
export type WebSocketEvents = SFUMessageType | WebSocketBasicEvents | LocalMessageType
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
export interface WebSocketMessageBody {
|
|
89
|
+
interfaceName: 'WebSocketMessageBody'
|
|
90
|
+
type: WebSocketEvents;
|
|
91
|
+
data?: any;
|
|
92
|
+
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export interface ClientMessageBody {
|
|
96
|
+
interfaceName: 'ClientMessageBody'
|
|
97
|
+
shouldSendToSelf: boolean;
|
|
98
|
+
senderParticipant: Participant;
|
|
99
|
+
broadcastMessage: WebSocketMessageBody
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export interface RediSubsciptionDetail {
|
|
103
|
+
topic: string;
|
|
104
|
+
listener: (data: string) => void
|
|
105
|
+
}
|
|
106
|
+
export class EachSocketConnectionHandler extends BaseSFUWebsocket {
|
|
107
|
+
|
|
108
|
+
private uuid = uuidv4();
|
|
109
|
+
private socket: WebSocket.WebSocket
|
|
110
|
+
private selfParticipant?: Participant
|
|
111
|
+
private appId: string = 'Demo'
|
|
112
|
+
private isActive = false
|
|
113
|
+
private roomIds: string[] = []
|
|
114
|
+
private redisSubscribtionDetails: RediSubsciptionDetail[] = []
|
|
115
|
+
private isReconnectionFromUrl = false
|
|
116
|
+
private numberOfPongWaiting = 0;
|
|
117
|
+
private destoryCallback?: (socketHandler : EachSocketConnectionHandler) => void;
|
|
118
|
+
|
|
119
|
+
constructor(socket: WebSocket.WebSocket, isReconnectionFromUrl: boolean) {
|
|
120
|
+
super()
|
|
121
|
+
this.socket = socket;
|
|
122
|
+
this.isReconnectionFromUrl = isReconnectionFromUrl
|
|
123
|
+
this.init = this.init.bind(this)
|
|
124
|
+
this.onNewMessage = this.onNewMessage.bind(this)
|
|
125
|
+
this.onConfig = this.onConfig.bind(this)
|
|
126
|
+
this.joinRoom = this.joinRoom.bind(this)
|
|
127
|
+
this.subscribeToRedisMessages = this.subscribeToRedisMessages.bind(this)
|
|
128
|
+
this.redisSubscribeToTopic = this.redisSubscribeToTopic.bind(this)
|
|
129
|
+
this.onRedisMessage = this.onRedisMessage.bind(this)
|
|
130
|
+
this.onNewUserJoinedRoomWithSameUserId = this.onNewUserJoinedRoomWithSameUserId.bind(this)
|
|
131
|
+
this.cleanUp = this.cleanUp.bind(this)
|
|
132
|
+
this.onSetupDone = this.onSetupDone.bind(this)
|
|
133
|
+
this.getAllParticipants = this.getAllParticipants.bind(this)
|
|
134
|
+
this.startPingPong = this.startPingPong.bind(this)
|
|
135
|
+
this.getMeetingStartTimeAndInfromIfNotExist = this.getMeetingStartTimeAndInfromIfNotExist.bind(this)
|
|
136
|
+
this.onSocketClosed = this.onSocketClosed.bind(this)
|
|
137
|
+
this.onStartMeetingCalled = this.onStartMeetingCalled.bind(this)
|
|
138
|
+
this.onAudioVideoPauseResume = this.onAudioVideoPauseResume.bind(this)
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
public onDestoryCallback(callBack: (socketHandler : EachSocketConnectionHandler) => void) {
|
|
142
|
+
this.destoryCallback = callBack
|
|
143
|
+
}
|
|
144
|
+
public async init() {
|
|
145
|
+
this.socket.on("message", this.onNewMessage)
|
|
146
|
+
this.socket.on("close", this.onSocketClosed)
|
|
147
|
+
this.subscribeToRedisMessages()
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
private async onSocketClosed(event: WebSocket.CloseEvent) {
|
|
151
|
+
console.log("Socket close code", event)
|
|
152
|
+
if (String(event).includes("1005") || String(event).includes("1001") || String(event).includes("1000")) {
|
|
153
|
+
this.onUserLeft()
|
|
154
|
+
}
|
|
155
|
+
else {
|
|
156
|
+
this.isActive = false
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
private async onUserLeft() {
|
|
161
|
+
this.socket.close()
|
|
162
|
+
if (!this.selfParticipant) {
|
|
163
|
+
return
|
|
164
|
+
}
|
|
165
|
+
const roomSFUHandler = SFUHandler.getInstance().getRoomSFUHandler(this.selfParticipant?.roomId!)
|
|
166
|
+
if (roomSFUHandler) {
|
|
167
|
+
roomSFUHandler.onUserLeft(this.selfParticipant)
|
|
168
|
+
}
|
|
169
|
+
this.sendVaniEvent(VaniEvent.OnUserLeft, this.selfParticipant)
|
|
170
|
+
this.redisBroadcastMessageToTopic(this.selfParticipant.roomId!, this.preapreClientMessageBody(false, this.selfParticipant, this.preapreWebSocketMessageBody(WebSocketBasicEvents.OnUserLeft, { participant: this.selfParticipant })))
|
|
171
|
+
RedisHandler.getInstance().removeParticipantForRoom(this.selfParticipant.roomId!, this.selfParticipant)
|
|
172
|
+
SFUHandler.getInstance().checkIfCanCloseRoom(this.selfParticipant.roomId!)
|
|
173
|
+
this.cleanUp()
|
|
174
|
+
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
private async onNewMessage(msgData: string) {
|
|
178
|
+
const paylod: WebSocketMessageBody = JSON.parse(msgData);
|
|
179
|
+
if (paylod.type === WebSocketBasicEvents.Config) {
|
|
180
|
+
this.onConfig(paylod.data)
|
|
181
|
+
}
|
|
182
|
+
else if (paylod.type === WebSocketBasicEvents.JoinRoom) {
|
|
183
|
+
this.joinRoom(paylod.data)
|
|
184
|
+
}
|
|
185
|
+
else if (paylod.type === WebSocketBasicEvents.IsSetupDone) {
|
|
186
|
+
this.onSetupDone(paylod.data)
|
|
187
|
+
}
|
|
188
|
+
else if (paylod.type === WebSocketBasicEvents.SelfLeft) {
|
|
189
|
+
this.onUserLeft()
|
|
190
|
+
}
|
|
191
|
+
else if (paylod.type === WebSocketBasicEvents.Pong) {
|
|
192
|
+
this.numberOfPongWaiting = 0;
|
|
193
|
+
}
|
|
194
|
+
else if (paylod.type === WebSocketBasicEvents.GetMeetingStartTime) {
|
|
195
|
+
const startMeetingTime = await this.getMeetingStartTimeAndInfromIfNotExist()
|
|
196
|
+
this.redisBroadcastMessageToTopic(this.selfParticipant?.userId!, this.preapreClientMessageBody(true, this.selfParticipant!, this.preapreWebSocketMessageBody(WebSocketBasicEvents.OnMeetingStartTime, startMeetingTime)))
|
|
197
|
+
|
|
198
|
+
}
|
|
199
|
+
else if (paylod.type === WebSocketBasicEvents.GetAllParticipant) {
|
|
200
|
+
this.getAllParticipants()
|
|
201
|
+
}
|
|
202
|
+
else if (paylod.type === WebSocketBasicEvents.Message) {
|
|
203
|
+
|
|
204
|
+
if (this.selfParticipant) {
|
|
205
|
+
paylod.data.userId = this.selfParticipant.userId;
|
|
206
|
+
}
|
|
207
|
+
if (paylod.data.shouldPresist && paylod.data.shouldPresist === true) {
|
|
208
|
+
RedisHandler.getInstance().storeMesagesForRoom(this.selfParticipant!.roomId!, paylod.data.message);
|
|
209
|
+
}
|
|
210
|
+
this.redisBroadcastMessageToTopic(this.selfParticipant?.roomId!, this.preapreClientMessageBody(false, this.selfParticipant!, this.preapreWebSocketMessageBody(paylod.data.type, paylod.data.message)))
|
|
211
|
+
|
|
212
|
+
}
|
|
213
|
+
else if (paylod.type === WebSocketBasicEvents.GetOldMessages) {
|
|
214
|
+
const messages = await RedisHandler.getInstance().fetchMessagesForRoom(this.selfParticipant?.roomId!)
|
|
215
|
+
console.log(messages)
|
|
216
|
+
this.redisBroadcastMessageToTopic(this.selfParticipant?.userId!, this.preapreClientMessageBody(true, this.selfParticipant!, this.preapreWebSocketMessageBody(WebSocketBasicEvents.OnOldMessages, messages)))
|
|
217
|
+
|
|
218
|
+
}
|
|
219
|
+
else if (paylod.type === WebSocketBasicEvents.OnStartMeetingCalled) {
|
|
220
|
+
this.onStartMeetingCalled(paylod.data)
|
|
221
|
+
}
|
|
222
|
+
else if (paylod.type === WebSocketBasicEvents.OnAudioVideoPauseResume) {
|
|
223
|
+
this.onAudioVideoPauseResume(paylod.data)
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
else if (paylod.type === SFUMessageType.SFUMessage) {
|
|
227
|
+
const roomSFUHandler = SFUHandler.getInstance().getRoomSFUHandler(this.selfParticipant?.roomId!)
|
|
228
|
+
if (roomSFUHandler) {
|
|
229
|
+
roomSFUHandler.onNewMessage(paylod.data, this.selfParticipant!)
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
}
|
|
234
|
+
private async onAudioVideoPauseResume(data: any) {
|
|
235
|
+
if (this.selfParticipant) {
|
|
236
|
+
if (data.message && data.message.type && data.message.type === "video") {
|
|
237
|
+
if (data.message.status && data.message.status === "pause") {
|
|
238
|
+
this.selfParticipant.isVideoEnable = false
|
|
239
|
+
}
|
|
240
|
+
else if (data.message.status && data.message.status === "resume") {
|
|
241
|
+
this.selfParticipant.isVideoEnable = true
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
else if (data.message && data.message.type && data.message.type === "audio") {
|
|
245
|
+
if (data.message.status && data.message.status === "pause") {
|
|
246
|
+
this.selfParticipant.isAudioEnable = false
|
|
247
|
+
}
|
|
248
|
+
else if (data.message.status && data.message.status === "resume") {
|
|
249
|
+
this.selfParticipant.isAudioEnable = true
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
await RedisHandler.getInstance().addUpdateParticipantForRoom(this.selfParticipant.roomId!, this.selfParticipant)
|
|
253
|
+
this.redisBroadcastMessageToTopic(this.selfParticipant.roomId!, this.preapreClientMessageBody(true, this.selfParticipant, this.preapreWebSocketMessageBody(WebSocketBasicEvents.OnAudioVideoStatusUpdated, { participant: this.selfParticipant })));
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
private async onStartMeetingCalled(data: any) {
|
|
257
|
+
if (this.selfParticipant) {
|
|
258
|
+
this.selfParticipant.isStartMeetingCalled = true
|
|
259
|
+
RedisHandler.getInstance().addUpdateParticipantForRoom(this.selfParticipant.roomId!, this.selfParticipant)
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
private async onConfig(data: any) {
|
|
263
|
+
if (data.participant) {
|
|
264
|
+
this.selfParticipant = Object.assign(new Participant(), data.participant)
|
|
265
|
+
if (this.selfParticipant && this.selfParticipant.userId) {
|
|
266
|
+
this.redisSubscribeToTopic(this.selfParticipant.userId)
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
if (data.appId) {
|
|
270
|
+
this.appId = data.appId
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
private async joinRoom(data: any) {
|
|
275
|
+
const roomId = data.roomId
|
|
276
|
+
const userId = data.id
|
|
277
|
+
this.isActive = true
|
|
278
|
+
if (this.roomIds.includes(roomId) === false) {
|
|
279
|
+
this.roomIds.push(roomId)
|
|
280
|
+
this.redisSubscribeToTopic(roomId)
|
|
281
|
+
}
|
|
282
|
+
//Send Local Event To Close Old Socket for same User id
|
|
283
|
+
const localEventData = { roomId: roomId, senderUUID: this.uuid, userId: userId }
|
|
284
|
+
this.redisBroadcastMessageToTopic(userId, this.preapreWebSocketMessageBody(LocalMessageType.OnNewUserJoinedRoom, localEventData))
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
private async onSetupDone(data: any) {
|
|
288
|
+
if (!this.selfParticipant) {
|
|
289
|
+
this.cleanUp()
|
|
290
|
+
return;
|
|
291
|
+
}
|
|
292
|
+
await SFUHandler.getInstance().checkAndSetupRoom(this.selfParticipant!.roomId!)
|
|
293
|
+
// if (shouldUseSfu) {
|
|
294
|
+
// sfuRoom = sfuHandler.handleNewUserAdded(
|
|
295
|
+
// {
|
|
296
|
+
// userId: participant.userId,
|
|
297
|
+
// roomId: participant.roomId
|
|
298
|
+
// },
|
|
299
|
+
// socket
|
|
300
|
+
// );
|
|
301
|
+
// }
|
|
302
|
+
this.getMeetingStartTimeAndInfromIfNotExist()
|
|
303
|
+
const checkIfOldUserExist = await RedisHandler.getInstance().getParticipantByUserId(this.selfParticipant.roomId!, this.selfParticipant.userId!)
|
|
304
|
+
let isReconnection = false
|
|
305
|
+
if (this.isReconnectionFromUrl && !checkIfOldUserExist) {
|
|
306
|
+
this.redisBroadcastMessageToTopic(this.selfParticipant.userId!, this.preapreClientMessageBody(true, this.selfParticipant, this.preapreWebSocketMessageBody(WebSocketBasicEvents.OnUserOnFoundWhileReconnect, { participant: this.selfParticipant })))
|
|
307
|
+
return
|
|
308
|
+
}
|
|
309
|
+
if (this.isReconnectionFromUrl || checkIfOldUserExist) {
|
|
310
|
+
//send message for rejoin
|
|
311
|
+
this.redisBroadcastMessageToTopic(this.selfParticipant.roomId!, this.preapreClientMessageBody(false, this.selfParticipant, this.preapreWebSocketMessageBody(WebSocketBasicEvents.OnRejoined, { participant: this.selfParticipant })))
|
|
312
|
+
isReconnection = true
|
|
313
|
+
}
|
|
314
|
+
else {
|
|
315
|
+
this.redisBroadcastMessageToTopic(this.selfParticipant.roomId!, this.preapreClientMessageBody(false, this.selfParticipant, this.preapreWebSocketMessageBody(WebSocketBasicEvents.OnNewJoinee, { participant: this.selfParticipant })))
|
|
316
|
+
this.sendVaniEvent(VaniEvent.OnUserJoined, this.selfParticipant)
|
|
317
|
+
isReconnection = false
|
|
318
|
+
}
|
|
319
|
+
await RedisHandler.getInstance().addUpdateParticipantForRoom(this.selfParticipant.roomId!, this.selfParticipant)
|
|
320
|
+
this.getAllParticipants()
|
|
321
|
+
this.redisBroadcastMessageToTopic(this.selfParticipant.userId!, this.preapreClientMessageBody(true, this.selfParticipant, this.preapreWebSocketMessageBody(WebSocketBasicEvents.IsSetupDone, { isReconnection: isReconnection })));
|
|
322
|
+
this.startPingPong()
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
private async getMeetingStartTimeAndInfromIfNotExist(): Promise<number> {
|
|
326
|
+
const meetingStartTime = await RedisHandler.getInstance().fetchMeetingTimeForRoom(this.selfParticipant!.roomId!)
|
|
327
|
+
if (meetingStartTime === undefined) {
|
|
328
|
+
const meetingTime = new Date().getTime()
|
|
329
|
+
RedisHandler.getInstance().storeMeetingTimeForRoom(this.selfParticipant!.roomId!, meetingTime)
|
|
330
|
+
this.sendVaniEvent(VaniEvent.OnNewMeetingStarted, { roomId: this.selfParticipant!.roomId! })
|
|
331
|
+
return meetingTime
|
|
332
|
+
}
|
|
333
|
+
return meetingStartTime
|
|
334
|
+
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
private async getAllParticipants() {
|
|
338
|
+
const allUsers = await RedisHandler.getInstance().getAllParticipants(this.selfParticipant?.roomId!)
|
|
339
|
+
const userMap: { [key: string]: Participant } = {}
|
|
340
|
+
allUsers.forEach((participant: Participant) => {
|
|
341
|
+
userMap[participant.userId!] = participant
|
|
342
|
+
})
|
|
343
|
+
this.redisBroadcastMessageToTopic(this.selfParticipant!.userId!, this.preapreClientMessageBody(true, this.selfParticipant!, this.preapreWebSocketMessageBody(WebSocketBasicEvents.OnServerParticipants, userMap)))
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
private startPingPong() {
|
|
347
|
+
if (this.isActive && this.socket.OPEN && this.selfParticipant) {
|
|
348
|
+
if (this.numberOfPongWaiting > 10) {
|
|
349
|
+
console.log("No Pong More Than 10 Times start")
|
|
350
|
+
console.log(this.selfParticipant)
|
|
351
|
+
console.log("No Pong More Than 10 Times End")
|
|
352
|
+
// isUserLeftMsgSent = true;
|
|
353
|
+
this.onUserLeft();
|
|
354
|
+
return;
|
|
355
|
+
}
|
|
356
|
+
this.redisBroadcastMessageToTopic(this.selfParticipant!.userId!, this.preapreClientMessageBody(true, this.selfParticipant!, this.preapreWebSocketMessageBody(WebSocketBasicEvents.Ping, { userId: this.selfParticipant.userId })))
|
|
357
|
+
this.numberOfPongWaiting = this.numberOfPongWaiting + 1;
|
|
358
|
+
setTimeout(this.startPingPong, 5000)
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
private cleanUp() {
|
|
364
|
+
this.isActive = false;
|
|
365
|
+
this.socket.close(3006)
|
|
366
|
+
try {
|
|
367
|
+
this.socket.removeAllListeners()
|
|
368
|
+
}
|
|
369
|
+
catch (err) {
|
|
370
|
+
|
|
371
|
+
}
|
|
372
|
+
if (this.destoryCallback) {
|
|
373
|
+
this.destoryCallback(this)
|
|
374
|
+
this.destoryCallback = undefined
|
|
375
|
+
}
|
|
376
|
+
this.redisSubscribtionDetails.forEach((eachRedisSubscribtionDetails: RediSubsciptionDetail) => {
|
|
377
|
+
RedisHandler.getInstance().redisSubscriber.unsubscribe(eachRedisSubscribtionDetails.topic, eachRedisSubscribtionDetails.listener)
|
|
378
|
+
})
|
|
379
|
+
this.redisSubscribtionDetails = []
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
//Send EVent Emitter
|
|
383
|
+
|
|
384
|
+
private async sendVaniEvent(event: VaniEvent, data: any) {
|
|
385
|
+
EventEmitterHandler.getInstance().vaniEventEmitter.emit(event, data)
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
//Redis
|
|
389
|
+
|
|
390
|
+
private async onNewUserJoinedRoomWithSameUserId(data: any) {
|
|
391
|
+
console.log("onNewUserJoinedRoomWithSameUserId Start")
|
|
392
|
+
const roomId = data.roomId
|
|
393
|
+
const senderUUID = data.senderUUID
|
|
394
|
+
const userId = data.userId
|
|
395
|
+
if (userId === this.selfParticipant?.userId && this.uuid !== senderUUID && this.roomIds.includes(roomId)) {
|
|
396
|
+
this.isActive = false;
|
|
397
|
+
this.socket.close(3006)
|
|
398
|
+
this.cleanUp()
|
|
399
|
+
console.log("onNewUserJoinedRoomWithSameUserId done")
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
|
|
404
|
+
|
|
405
|
+
|
|
406
|
+
private async sendMessageToClient(data: ClientMessageBody) {
|
|
407
|
+
if ((data.shouldSendToSelf === true || this.selfParticipant?.userId !== data.senderParticipant.userId) && this.isActive && this.socket.OPEN) {
|
|
408
|
+
const sendMessageData = { type: data.broadcastMessage.type, data: { message: data.broadcastMessage.data } }
|
|
409
|
+
this.socket.send(JSON.stringify(sendMessageData))
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
}
|
|
413
|
+
private async onRedisMessage(data: WebSocketMessageBody | ClientMessageBody) {
|
|
414
|
+
|
|
415
|
+
if (data.interfaceName === 'WebSocketMessageBody') {
|
|
416
|
+
const webSocketMessageBody: WebSocketMessageBody = data
|
|
417
|
+
if (webSocketMessageBody.type === LocalMessageType.OnNewUserJoinedRoom) {
|
|
418
|
+
this.onNewUserJoinedRoomWithSameUserId(webSocketMessageBody.data)
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
else if (data.interfaceName === 'ClientMessageBody') {
|
|
422
|
+
const socketMessageBody: ClientMessageBody = data
|
|
423
|
+
this.sendMessageToClient(socketMessageBody)
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
|
|
427
|
+
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
private async subscribeToRedisMessages() {
|
|
431
|
+
RedisHandler.getInstance().redisSubscriber.on('message', (roomId, messageInString) => {
|
|
432
|
+
const message = JSON.parse(messageInString);
|
|
433
|
+
console.debug(message)
|
|
434
|
+
})
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
private async redisSubscribeToTopic(topic: string) {
|
|
438
|
+
const isListenerAlreadyExist = this.redisSubscribtionDetails.find((eachRedisSubscribtionDetails) => eachRedisSubscribtionDetails.topic === topic)
|
|
439
|
+
if (!isListenerAlreadyExist) {
|
|
440
|
+
const listener = (data: string) => {
|
|
441
|
+
this.onRedisMessage(JSON.parse(data))
|
|
442
|
+
|
|
443
|
+
}
|
|
444
|
+
RedisHandler.getInstance().redisSubscriber.subscribe(topic, listener)
|
|
445
|
+
this.redisSubscribtionDetails.push({ topic, listener })
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
|
|
451
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import WebSocket from "ws";
|
|
2
|
+
import { VaniEvent } from "../models/Event";
|
|
3
|
+
import { ServerHandler } from "../ServerHandler";
|
|
4
|
+
import { EventEmitterHandler } from "../utility/EventEmitterHandler";
|
|
5
|
+
import { EachSocketConnectionHandler } from "./EachSocketConnectionHandler";
|
|
6
|
+
|
|
7
|
+
export class WebSocketHandler {
|
|
8
|
+
|
|
9
|
+
private wss: WebSocket.Server;
|
|
10
|
+
private socketConnections: EachSocketConnectionHandler[] = []
|
|
11
|
+
constructor() {
|
|
12
|
+
this.wss = new WebSocket.Server({ port: ServerHandler.getInstance().serverStartRequest.port })
|
|
13
|
+
}
|
|
14
|
+
connect() {
|
|
15
|
+
this.wss.on("connection", (socket, req) => {
|
|
16
|
+
console.log("On New Connection")
|
|
17
|
+
const eachSocketConnection = new EachSocketConnectionHandler(socket, req.url ? req.url.includes('reconnect') : false)
|
|
18
|
+
eachSocketConnection.init();
|
|
19
|
+
eachSocketConnection.onDestoryCallback((socketConnection: EachSocketConnectionHandler) => {
|
|
20
|
+
const index = this.socketConnections.indexOf(socketConnection)
|
|
21
|
+
if (index > -1) {
|
|
22
|
+
this.socketConnections.splice(index, 1)
|
|
23
|
+
}
|
|
24
|
+
})
|
|
25
|
+
this.socketConnections.push(eachSocketConnection)
|
|
26
|
+
})
|
|
27
|
+
EventEmitterHandler.getInstance().vaniEventEmitter.emit(VaniEvent.OnServerStarted, {})
|
|
28
|
+
|
|
29
|
+
}
|
|
30
|
+
}
|
package/tsconfig.json
ADDED