@futdevpro/nts-dynamo 1.9.15 → 1.9.16

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.
Files changed (117) hide show
  1. package/.copilot/patterns.json +7 -7
  2. package/.github/workflows/main.yml +206 -0
  3. package/HOWTO.md +15 -15
  4. package/README.md +140 -140
  5. package/build/_models/control-models/endpoint-params.control-model.d.ts.map +1 -1
  6. package/build/_models/control-models/endpoint-params.control-model.js +2 -0
  7. package/build/_models/control-models/endpoint-params.control-model.js.map +1 -1
  8. package/build/_models/control-models/socket-event.control-model.js +1 -1
  9. package/build/_services/core/global.service.d.ts.map +1 -1
  10. package/build/_services/core/global.service.js +1 -0
  11. package/build/_services/core/global.service.js.map +1 -1
  12. package/build/_services/route/routing-module.service.d.ts +1 -0
  13. package/build/_services/route/routing-module.service.d.ts.map +1 -1
  14. package/build/_services/route/routing-module.service.js +17 -23
  15. package/build/_services/route/routing-module.service.js.map +1 -1
  16. package/build/_services/server/app.server.d.ts.map +1 -1
  17. package/build/_services/server/app.server.js +4 -1
  18. package/build/_services/server/app.server.js.map +1 -1
  19. package/build/_services/socket/socket-client.service.d.ts.map +1 -1
  20. package/build/_services/socket/socket-client.service.js +1 -0
  21. package/build/_services/socket/socket-client.service.js.map +1 -1
  22. package/nodemon.json +17 -15
  23. package/package.json +5 -5
  24. package/src/_constants/global-settings.const.ts +27 -27
  25. package/src/_constants/index.ts +2 -2
  26. package/src/_constants/mocks/app-extended-server.mock.ts +198 -198
  27. package/src/_constants/mocks/app-params.mock.ts +9 -9
  28. package/src/_constants/mocks/app-server.mock.ts +185 -185
  29. package/src/_constants/mocks/auth-service.mock.ts +28 -28
  30. package/src/_constants/mocks/controller.mock.ts +16 -16
  31. package/src/_constants/mocks/data-model.mock.ts +83 -83
  32. package/src/_constants/mocks/email-service-collection.mock.ts +13 -13
  33. package/src/_constants/mocks/email-service.mock.ts +19 -19
  34. package/src/_constants/mocks/email-template.mock.html +14 -14
  35. package/src/_constants/mocks/endpoint.mock.ts +90 -90
  36. package/src/_constants/mocks/socket-client.mock.ts +43 -43
  37. package/src/_constants/mocks/socket-server.mock.ts +43 -43
  38. package/src/_enums/data-model-type.enum.ts +14 -14
  39. package/src/_enums/data-service-function.enum.ts +15 -15
  40. package/src/_enums/http/http-call-type.enum.ts +12 -12
  41. package/src/_enums/http/http-response-type.enum.ts +7 -7
  42. package/src/_enums/http/socket-event-type.enum.ts +18 -18
  43. package/src/_enums/index.ts +13 -13
  44. package/src/_enums/predefined-data-types.enum.ts +27 -27
  45. package/src/_enums/route-security.enum.ts +12 -12
  46. package/src/_enums/socket-security.enum.ts +11 -11
  47. package/src/_models/control-models/api-call-params.control-model.ts +126 -126
  48. package/src/_models/control-models/app-ext-system-controls.control-model.ts +9 -9
  49. package/src/_models/control-models/app-params.control-model.ts +45 -45
  50. package/src/_models/control-models/app-system-controls.control-model.ts +9 -9
  51. package/src/_models/control-models/endpoint-params.control-model.ts +309 -307
  52. package/src/_models/control-models/http-settings.control-model.ts +29 -29
  53. package/src/_models/control-models/index.ts +13 -13
  54. package/src/_models/control-models/socket-client-service-params.control-model.ts +28 -28
  55. package/src/_models/control-models/socket-event.control-model.ts +150 -150
  56. package/src/_models/control-models/socket-presence.control-model.ts +207 -207
  57. package/src/_models/control-models/socket-server-service-params.control-model.ts +20 -20
  58. package/src/_models/control-models/system-control.control-model.ts +12 -12
  59. package/src/_models/index.ts +9 -9
  60. package/src/_models/interfaces/certification-settings.interface.ts +7 -7
  61. package/src/_models/interfaces/global-service-settings.interface.ts +45 -45
  62. package/src/_models/interfaces/global-settings.interface.ts +83 -83
  63. package/src/_models/interfaces/index.ts +7 -7
  64. package/src/_models/interfaces/routing-module-settings.interface.ts +20 -20
  65. package/src/_models/types/db-filter.type.ts +108 -108
  66. package/src/_models/types/db-update.type.ts +100 -100
  67. package/src/_models/types/index.ts +5 -5
  68. package/src/_modules/api-service.index.ts +12 -12
  69. package/src/_modules/app-extended.index.ts +28 -28
  70. package/src/_modules/app.index.ts +24 -24
  71. package/src/_modules/auth.index.ts +7 -7
  72. package/src/_modules/constants.index.ts +2 -2
  73. package/src/_modules/controller.index.ts +10 -10
  74. package/src/_modules/custom-data/custom-data.controller.ts +69 -69
  75. package/src/_modules/custom-data/custom-data.data-service.ts +20 -20
  76. package/src/_modules/custom-data/get-custom-data-routing-module.util.ts +23 -23
  77. package/src/_modules/custom-data/index.ts +6 -6
  78. package/src/_modules/custom-data-module.index.ts +2 -2
  79. package/src/_modules/data-service.index.ts +9 -9
  80. package/src/_modules/email.index.ts +8 -8
  81. package/src/_modules/enums.index.ts +2 -2
  82. package/src/_modules/extended.index.ts +8 -8
  83. package/src/_modules/models.index.ts +2 -2
  84. package/src/_modules/services.index.ts +2 -2
  85. package/src/_modules/test/get-test-routing-module.util.ts +23 -23
  86. package/src/_modules/test/index.ts +5 -5
  87. package/src/_modules/test/test.controller.ts +115 -115
  88. package/src/_modules/test-module.index.ts +2 -2
  89. package/src/_modules/usage/get-usage-routing-module.util.ts +22 -22
  90. package/src/_modules/usage/index.ts +7 -7
  91. package/src/_modules/usage/usage.controller.ts +120 -120
  92. package/src/_modules/usage/usage.data-service.ts +172 -172
  93. package/src/_modules/usage-module.index.ts +2 -2
  94. package/src/_services/base/data.service.ts +921 -921
  95. package/src/_services/base/db.service.spec.ts +32 -32
  96. package/src/_services/base/db.service.ts +1063 -1063
  97. package/src/_services/base/singleton.service.ts +21 -21
  98. package/src/_services/core/api.service.ts +453 -453
  99. package/src/_services/core/auth.service.ts +172 -172
  100. package/src/_services/core/email.service.ts +678 -678
  101. package/src/_services/core/global.service.ts +270 -269
  102. package/src/_services/core/service-collection.service.ts +5 -5
  103. package/src/_services/index.ts +23 -23
  104. package/src/_services/route/controller.service.ts +129 -129
  105. package/src/_services/route/routing-module.service.ts +293 -273
  106. package/src/_services/server/app-extended.server.spec.ts +76 -76
  107. package/src/_services/server/app-extended.server.ts +520 -520
  108. package/src/_services/server/app.server.spec.ts +67 -67
  109. package/src/_services/server/app.server.ts +1181 -1179
  110. package/src/_services/shared.service.spec.ts +19 -19
  111. package/src/_services/shared.static-service.ts +73 -73
  112. package/src/_services/socket/socket-client.service.ts +236 -235
  113. package/src/_services/socket/socket-server.service.spec.ts +11 -11
  114. package/src/_services/socket/socket-server.service.ts +761 -761
  115. package/src/index.ts +18 -18
  116. package/tsconfig.json +41 -41
  117. package/build/tsconfig.tsbuildinfo +0 -1
@@ -1,761 +1,761 @@
1
-
2
- import * as SocketIO from 'socket.io';
3
-
4
- import {
5
- DynamoFM_AnyError, DynamoFM_Array, DynamoFM_Error, DynamoFM_Error_Settings, DynamoFM_Log
6
- } from '@futdevpro/fsm-dynamo';
7
-
8
- import { DynamoNTS_SingletonService } from '../base/singleton.service';
9
- import { DynamoNTS_globalSettings } from '../../_constants/global-settings.const';
10
- import { DynamoNTS_SocketEventKey } from '../../_enums/http/socket-event-type.enum';
11
- import { DynamoNTS_SocketSecurity } from '../../_enums/socket-security.enum';
12
- import {
13
- DynamoNTS_SocketServerService_Params
14
- } from '../../_models/control-models/socket-server-service-params.control-model';
15
- import {
16
- DynamoNTS_SocketPresence
17
- } from '../../_models/control-models/socket-presence.control-model';
18
- import {
19
- DynamoNTS_SocketEvent, DynamoNTS_SocketEventPreprocessTask, DynamoNTS_SocketEventTask
20
- } from '../../_models/control-models/socket-event.control-model';
21
-
22
- /**
23
- * You need to define the following functions:
24
- * ```ts
25
- * getServiceParams(): DynamoNTS_SocketServerServiceParams;
26
- *
27
- * getIncomingEvents(): DynamoNTS_SocketEvent<any>[];
28
- *
29
- * getPresenceFromSubscrioptionEventContent(
30
- * socketSubscription: T_SubscriptionContent,
31
- * socket: SocketIO.Socket
32
- * ): Promise<T_Presence>;
33
- * ```
34
- */
35
- export abstract class DynamoNTS_SocketServerService<
36
- T_Presence extends DynamoNTS_SocketPresence = DynamoNTS_SocketPresence,
37
- T_SubscriptionContent = any,
38
- T_ErrorContent = any
39
- > extends DynamoNTS_SingletonService {
40
-
41
- protected params: DynamoNTS_SocketServerService_Params;
42
- get name(): string { return this.params.name; }
43
- get port(): number { return this.params.port; }
44
- get security(): DynamoNTS_SocketSecurity { return this.params.security; }
45
- get activeSubscriptionsCount(): number { return this.presences.length; }
46
-
47
- protected openSocketServer: SocketIO.Server;
48
- protected secureSocketServer: SocketIO.Server;
49
-
50
- private connectEvent: DynamoNTS_SocketEvent<SocketIO.Socket>;
51
- protected incomingEvents: DynamoNTS_SocketEvent<any>[];
52
-
53
- private subscriptionEvent: DynamoNTS_SocketEvent<any>;
54
- private unsubscribeEvent: DynamoNTS_SocketEvent<any>;
55
-
56
- /**
57
- * You need to setup a function that converts subscription event content to presence.
58
- * You should do authentication and authorization here.
59
- */
60
- protected abstract getPresenceFromSubscrioptionEventContent(
61
- socketSubscription: T_SubscriptionContent, socket: SocketIO.Socket
62
- ): Promise<T_Presence>;
63
- protected presences: T_Presence[] = [];
64
-
65
- protected logSetup: boolean;
66
- protected logFn: boolean;
67
-
68
- defaultErrorUserMsg =
69
- `We encountered an unhandled Socket Server Error, ` +
70
- `\nplease contact the responsible development team.`;
71
-
72
- constructor(){
73
- super();
74
-
75
- this.asyncConstructor().catch((error: any) => {
76
- DynamoFM_Log.error(
77
- `Socket Server Service setup failed: ${this.params?.name} (${this.params?.port})`,
78
- error
79
- );
80
- });
81
- }
82
-
83
- private async asyncConstructor(): Promise<void> {
84
- try {
85
- this.logSetup = DynamoNTS_globalSettings.logSetup;
86
- this.params = this.getServiceParams();
87
- this.incomingEvents = this.getIncomingEvents() ?? [];
88
-
89
- await this.prepareEvents();
90
-
91
- if (this.logSetup) console.log(
92
- `Socket Server Service setup done: ${this.params.name} (${this.params.port}) ` +
93
- `serurity: ${this.params.security}\n`
94
- );
95
- } catch (error) {
96
- DynamoFM_Log.error(
97
- `Socket Server Service setup failed: ${this.params?.name} (${this.params?.port})`,
98
- error
99
- );
100
-
101
- throw new DynamoFM_Error({
102
- errorCode: 'NTS-SSS-001',
103
- error: error,
104
- });
105
- }
106
- }
107
-
108
- /**
109
- * You must setup events and required services for this function
110
- */
111
- private async prepareEvents(): Promise<void> {
112
- try {
113
- /** Setup connect event */
114
- const connectEventIndex = this.incomingEvents.findIndex(
115
- (event: DynamoNTS_SocketEvent<any>) =>
116
- event.eventKey === DynamoNTS_SocketEventKey.connection
117
- );
118
-
119
- if (0 <= connectEventIndex) {
120
- this.connectEvent = this.incomingEvents.splice(connectEventIndex)[0];
121
- } else {
122
- this.connectEvent = new DynamoNTS_SocketEvent({
123
- eventKey: DynamoNTS_SocketEventKey.connection,
124
- });
125
- }
126
- this.connectEvent.serviceName = this.params.name;
127
-
128
- /** Setup subscriptions events */
129
- if (this.getPresenceFromSubscrioptionEventContent) {
130
- /** Setup subscription event */
131
- const subscriptionEventIndex = this.incomingEvents.findIndex(
132
- (event: DynamoNTS_SocketEvent<any>) =>
133
- event.eventKey === DynamoNTS_SocketEventKey.subscribe
134
- );
135
-
136
- if (0 <= subscriptionEventIndex) {
137
- this.subscriptionEvent = this.incomingEvents.splice(subscriptionEventIndex)[0];
138
- DynamoFM_Log.error(
139
- `You should not set the subscription event, but the subscriptions tasks, ` +
140
- `in case you need additional steps for your subscriptions.`,
141
- `${this.params.name} (${this.params.port})`
142
- );
143
- } else {
144
- this.subscriptionEvent = new DynamoNTS_SocketEvent<T_SubscriptionContent>({
145
- eventKey: DynamoNTS_SocketEventKey.subscribe,
146
- });
147
- }
148
-
149
- if (this.getSubscriptionPreProcessess) {
150
- this.subscriptionEvent.preProcessess.push(...this.getSubscriptionPreProcessess());
151
- }
152
-
153
- if (this.getSubscriptionTasks) {
154
- this.subscriptionEvent.tasks.push(...this.getSubscriptionTasks());
155
- }
156
- this.subscriptionEvent.serviceName = this.params.name;
157
-
158
- /** Setup subscription event */
159
- const unsubscribeEventIndex = this.incomingEvents.findIndex(
160
- (event: DynamoNTS_SocketEvent<any>) =>
161
- event.eventKey === DynamoNTS_SocketEventKey.unsubscribe
162
- );
163
-
164
- if (0 <= unsubscribeEventIndex) {
165
- this.unsubscribeEvent = this.incomingEvents.splice(unsubscribeEventIndex)[0];
166
- DynamoFM_Log.error(
167
- `You should not set the unsubscribe event, but the unsubscribe tasks, ` +
168
- `in case you need additional steps for your unsubscribe.`,
169
- `${this.params.name} (${this.params.port})`
170
- );
171
- } else {
172
- this.unsubscribeEvent = new DynamoNTS_SocketEvent<T_SubscriptionContent>({
173
- eventKey: DynamoNTS_SocketEventKey.unsubscribe,
174
- });
175
- }
176
-
177
- if (this.getUnsubscribePreProcessess) {
178
- this.unsubscribeEvent.preProcessess.push(...this.getUnsubscribePreProcessess());
179
- }
180
-
181
- if (this.getUnsubscribeTasks) {
182
- this.unsubscribeEvent.tasks.push(...this.getUnsubscribeTasks());
183
- }
184
- this.unsubscribeEvent.serviceName = this.params.name;
185
-
186
- /** Setup disconnect event */
187
- const disconnectEventIndex = this.incomingEvents.findIndex(
188
- (event: DynamoNTS_SocketEvent<any>) =>
189
- event.eventKey === DynamoNTS_SocketEventKey.disconnect
190
- );
191
-
192
- if (0 <= disconnectEventIndex) {
193
- this.incomingEvents[disconnectEventIndex].serviceName = this.params.name;
194
- this.incomingEvents[disconnectEventIndex].tasks.push(this.disconnectBaseTask);
195
- } else {
196
- this.incomingEvents.push(
197
- new DynamoNTS_SocketEvent({
198
- serviceName: this.params.name,
199
- eventKey: DynamoNTS_SocketEventKey.disconnect,
200
- tasks: [
201
- this.disconnectBaseTask,
202
- ],
203
- })
204
- );
205
- }
206
-
207
- /** Setup error event */
208
- const errorEventIndex = this.incomingEvents.findIndex(
209
- (event: DynamoNTS_SocketEvent<any>) => event.eventKey === DynamoNTS_SocketEventKey.error
210
- );
211
-
212
- if (errorEventIndex == -1 && this.errorHandling) {
213
- this.incomingEvents.push(new DynamoNTS_SocketEvent<T_ErrorContent>({
214
- serviceName: this.params.name,
215
- eventKey: DynamoNTS_SocketEventKey.error,
216
- tasks: [
217
- this.errorHandling,
218
- ],
219
- }));
220
- }
221
-
222
- this.subscriptionEvent.socketName = this.params.name;
223
- } else {
224
- DynamoFM_Log.error(
225
- 'getPresenceFromSubscrioptionEventContent is not set',
226
- `${this.params.name} (${this.params.port})`
227
- );
228
- }
229
-
230
- /** Set incoming events serviceName */
231
- this.incomingEvents.forEach((event: DynamoNTS_SocketEvent<any>) => {
232
- event.serviceName = this.params.name;
233
- });
234
- } catch (error) {
235
- DynamoFM_Log.error(
236
- `Socket Server Service - Event Preparation setup failed: ` +
237
- `${this.params?.name} (${this.params?.port})`,
238
- error
239
- );
240
-
241
- throw error;
242
- }
243
- }
244
-
245
- // eslint-disable-next-line max-lines-per-function
246
- async setupSocketServer(
247
- newSocketServer: SocketIO.Server,
248
- security: DynamoNTS_SocketSecurity.open | DynamoNTS_SocketSecurity.secure,
249
- successCallback: () => void
250
- ): Promise<SocketIO.Server> {
251
- try {
252
- if (this.logFn) console.log('\nfn:. setupSocketServer');
253
-
254
- newSocketServer.on(DynamoNTS_SocketEventKey.connection, async (socket: SocketIO.Socket) => {
255
- try {
256
- let issuer: string;
257
-
258
- await this.connectEvent.executeEventTasks(socket);
259
-
260
- if (this.getPresenceFromSubscrioptionEventContent) {
261
- socket.on(
262
- DynamoNTS_SocketEventKey.subscribe,
263
- async (content: any) => {
264
- try {
265
- /**
266
- * usually socket logs are in event.executeEventTasks(),
267
- * but subscribe event is an exception from this,
268
- * to be able to check content before getPresenceFromSubscrioptionEventContent
269
- */
270
- if (DynamoNTS_globalSettings.logSocketEventContent) {
271
- DynamoFM_Log.log(
272
- `--=> incoming socket(${this.params.name}) ` +
273
- `event: ${DynamoNTS_SocketEventKey.subscribe};` +
274
- `\ncontent:`, content
275
- );
276
- } else {
277
- DynamoFM_Log.log(
278
- `--=> incoming socket(${this.params.name}) ` +
279
- `event: ${DynamoNTS_SocketEventKey.subscribe}`
280
- );
281
- }
282
-
283
- const presence: T_Presence = await this.getPresenceFromSubscrioptionEventContent(
284
- content, socket
285
- );
286
-
287
- presence.serviceName = this.params.name;
288
- issuer = presence.issuerId;
289
-
290
- this.addSocketToPresence(presence);
291
- await this.subscriptionEvent.executeEventTasks(content, issuer);
292
-
293
- socket.emit(
294
- DynamoNTS_SocketEventKey.subscriptionSuccessful,
295
- 'subscribe was successful',
296
- error => {
297
- DynamoFM_Log.error(
298
- `Emitting subscriptionSuccessful event failed!` +
299
- `\nerror:`, error
300
- );
301
- }
302
- );
303
-
304
- DynamoFM_Log.success(
305
- `<-==> socket(${this.params.name}) subscription successfull (${issuer})`
306
- );
307
- } catch (error) {
308
- DynamoFM_Log.error(
309
- `Socket Subscription failed: ${this.params.name} ` ,
310
- `(${this.params.port}) will disconnect now...`,
311
- error
312
- );
313
- socket.emit(DynamoNTS_SocketEventKey.error, error);
314
- socket.disconnect();
315
- }
316
- }
317
- );
318
-
319
- socket.on(
320
- DynamoNTS_SocketEventKey.unsubscribe,
321
- async (issuer: string) => {
322
- try {
323
- /**
324
- * usually socket logs are in event.executeEventTasks(),
325
- * but unsubscribe event is an exception from this,
326
- * to be able to check content before getPresenceFromSubscrioptionEventContent
327
- */
328
- if (DynamoNTS_globalSettings.logSocketEventContent) {
329
- DynamoFM_Log.log(
330
- `--=> incoming socket(${this.params.name}) ` +
331
- `event: ${DynamoNTS_SocketEventKey.unsubscribe};` +
332
- `\ncontent:`, issuer
333
- );
334
- } else {
335
- DynamoFM_Log.log(
336
- `--=> incoming socket(${this.params.name}) ` +
337
- `event: ${DynamoNTS_SocketEventKey.unsubscribe}`
338
- );
339
- }
340
-
341
- this.removeSubscription(socket, issuer);
342
-
343
- await this.subscriptionEvent.executeEventTasks(issuer, issuer);
344
-
345
- socket.emit(
346
- DynamoNTS_SocketEventKey.unsubscribeSuccessful,
347
- 'unsubscribe was successful',
348
- error => {
349
- DynamoFM_Log.error(
350
- `Emitting unsubscribeSuccessful event failed!` +
351
- `\nerror:`, error
352
- );
353
- }
354
- );
355
-
356
- DynamoFM_Log.success(
357
- `< x > socket(${this.params.name}) unsubscribe successfull (${issuer})`
358
- );
359
- } catch (error) {
360
- DynamoFM_Log.error(
361
- `Socket unsubscribe failed: ${this.params.name} ` ,
362
- `(${this.params.port}) will disconnect now...`,
363
- error
364
- );
365
- socket.emit(DynamoNTS_SocketEventKey.error, error);
366
- }
367
- }
368
- );
369
- }
370
-
371
- this.incomingEvents.forEach((event: DynamoNTS_SocketEvent<any>) => {
372
- socket.on(
373
- event.eventKey,
374
- async (content: any) => {
375
- try {
376
- await event.executeEventTasks(content, issuer);
377
- } catch (error) {
378
- DynamoFM_Log.error(
379
- `Socket Event failed: ${this.params.name} (${this.params.port})`,
380
- error
381
- );
382
- socket.emit(DynamoNTS_SocketEventKey.error, error);
383
- }
384
- }
385
- );
386
- });
387
-
388
- if (DynamoNTS_globalSettings.logMainSocketEvents) {
389
- DynamoFM_Log.success(`< > socket(${this.params.name}): new CONNECTION established`);
390
- }
391
- } catch (error) {
392
- DynamoFM_Log.error(
393
- `Socket Connection failed: ${this.params.name} ` +
394
- `(${this.params.port}) will disconnect now...`,
395
- error
396
- );
397
- socket.emit(DynamoNTS_SocketEventKey.error, error);
398
- socket.disconnect();
399
- }
400
- });
401
-
402
- newSocketServer.listen(this.params.port);
403
-
404
- DynamoFM_Log.success(
405
- `\nsocket server setup finished: ${this.params.name}` +
406
- `\nsocket server listening on port: ${this.params.port}`
407
- );
408
-
409
- successCallback();
410
-
411
- if (security === DynamoNTS_SocketSecurity.open) {
412
- this.openSocketServer = newSocketServer;
413
-
414
- return this.openSocketServer;
415
- } else {
416
- this.secureSocketServer = newSocketServer;
417
-
418
- return this.secureSocketServer;
419
- }
420
- } catch (error) {
421
- DynamoFM_Log.error(
422
- `Socket Server Service - Deep Setup failed: ${this.params?.name} ` +
423
- `(${this.params?.port})`,
424
- error
425
- );
426
-
427
- throw error;
428
- }
429
- }
430
-
431
- protected async addSocketToPresence(newPresence: T_Presence): Promise<void> {
432
- try {
433
- const activePresence: T_Presence = this.presences.find(
434
- (pres: T_Presence) => pres.issuerId === newPresence.issuerId
435
- );
436
-
437
- if (activePresence) {
438
- activePresence.sockets.push(...newPresence.sockets);
439
- } else {
440
- this.presences.push(newPresence);
441
- }
442
- } catch (error) {
443
- DynamoFM_Log.error(
444
- `Socket Subscription failed: ${this.params?.name} (${this.params?.port})`,
445
- error
446
- );
447
-
448
- throw error;
449
- }
450
- }
451
-
452
- private async disconnectBaseTask(socket: SocketIO.Socket, issuer: string): Promise<void> {
453
- try {
454
- await this.removeSubscription(socket, issuer);
455
-
456
- DynamoFM_Log.info(`< x > socket(${this.params.name}) disconnected (${issuer})`);
457
- } catch (error) {
458
- throw new DynamoFM_Error({
459
- ...this._getDefaultErrorSettings(
460
- 'disconnectBaseTask',
461
- error,
462
- issuer
463
- ),
464
-
465
- errorCode: 'NTS-SSS-109',
466
- });
467
- }
468
- }
469
-
470
- private async removeSubscription(socket: SocketIO.Socket, issuer?: string): Promise<void> {
471
- try {
472
- const activePresenceIndex: number = this.presences.findIndex(
473
- (pres: DynamoNTS_SocketPresence) => pres.sockets.includes(socket)
474
- );
475
-
476
- if (activePresenceIndex === -1) {
477
- throw new DynamoFM_Error({
478
- ...this._getDefaultErrorSettings(
479
- 'sendEventForId',
480
- new Error(
481
- `closing socket(${this.params.name}) does not match any in the activePresences`
482
- ),
483
- issuer
484
- ),
485
-
486
- errorCode: 'NTS-SSS-203',
487
- });
488
- }
489
-
490
- const activePresence: DynamoNTS_SocketPresence = this.presences[activePresenceIndex];
491
-
492
- if (activePresence.issuerId !== issuer) {
493
- throw new DynamoFM_Error({
494
- ...this._getDefaultErrorSettings(
495
- 'sendEventForId',
496
- new Error(
497
- `socket subscription for ${issuer} and ${activePresence.issuerId} does not match.`
498
- ),
499
- issuer
500
- ),
501
-
502
- errorCode: 'NTS-SSS-201',
503
- });
504
- }
505
-
506
- const socketIndex = activePresence.sockets.findIndex((s: SocketIO.Socket) => s === socket);
507
-
508
- if (socketIndex === -1) {
509
- throw new DynamoFM_Error({
510
- ...this._getDefaultErrorSettings(
511
- 'sendEventForId',
512
- new Error(
513
- `closing socket(${this.params.name}) does not match any in the activePresences`
514
- ),
515
- issuer
516
- ),
517
-
518
- errorCode: 'NTS-SSS-202',
519
- });
520
- }
521
-
522
- activePresence.sockets.splice(socketIndex);
523
-
524
- if (this.getSubscriptionCloseTasks) {
525
- await DynamoFM_Array.asyncForEach(
526
- this.getSubscriptionCloseTasks(),
527
- async (task: DynamoNTS_SocketEventTask<null>) => {
528
- await task(null, issuer);
529
- }
530
- );
531
- }
532
-
533
- if (activePresence.sockets.length === 0) {
534
- this.presences.splice(activePresenceIndex);
535
- }
536
-
537
- socket.disconnect();
538
- DynamoFM_Log.info(`<x==> socket(${this.params.name}) unsubscription successfull (${issuer})`);
539
- } catch (error) {
540
- throw new DynamoFM_Error({
541
- ...this._getDefaultErrorSettings(
542
- 'sendEventForId',
543
- error,
544
- issuer
545
- ),
546
-
547
- errorCode: 'NTS-SSS-200',
548
- message: `socket(${this.params.name}) Subscription Removal failed: ${this.params?.name}`,
549
- });
550
- }
551
- }
552
-
553
- /* emitServerEvent(event: string, content: any): void {
554
- try {
555
- DynamoFM_Log.log(` <=-- emitting server socket(${this.params.name}) event: ${event}`);
556
-
557
- this.openSocketServer.emit(event, content, error => {
558
- DynamoFM_Log.error(`Emitting server event '${event}' failed!\nerror:`, error);
559
- });
560
- } catch (error) {
561
- throw new DynamoFM_Error({
562
- ...this._getDefaultErrorSettings(
563
- 'emitEvent',
564
- error
565
- ),
566
-
567
- errorCode: 'NTS-SSS-500',
568
- message: `socket(${this.params.name}) Event Emit (${event}) failed: ${this.params?.name}`,
569
- });
570
- }
571
- } */
572
-
573
- idIsSubscribed(id: string): boolean {
574
- try {
575
- return !!this.presences.find(
576
- (presence: DynamoNTS_SocketPresence) => presence.issuerId === id
577
- );
578
- } catch (error) {
579
- throw new DynamoFM_Error({
580
- ...this._getDefaultErrorSettings(
581
- 'idIsSubscribed',
582
- error
583
- ),
584
-
585
- errorCode: 'NTS-SSS-600',
586
- message: `socket(${this.params.name}) ID Subscription Check (${id}) failed`,
587
- });
588
- }
589
- }
590
-
591
- async emitError(presenceIssuerId: string, error: any, issuer: string): Promise<void> {
592
- try {
593
- await this.sendEventForId(presenceIssuerId, DynamoNTS_SocketEventKey.error, error, issuer);
594
- } catch (error) {
595
- throw new DynamoFM_Error({
596
- ...this._getDefaultErrorSettings(
597
- 'emitError',
598
- error,
599
- presenceIssuerId
600
- ),
601
-
602
- errorCode: 'NTS-SSS-700',
603
- message: `socket(${this.params.name}) Error Emit (id: ${presenceIssuerId}) failed`,
604
- });
605
- }
606
- }
607
-
608
- async sendEventForId(id: string, event: string, content: any, issuer: string): Promise<void> {
609
- try {
610
- const presence: DynamoNTS_SocketPresence = this.presences.find(
611
- (pres: DynamoNTS_SocketPresence) => pres.issuerId === id
612
- );
613
-
614
- if (!presence) {
615
- throw new DynamoFM_Error({
616
- ...this._getDefaultErrorSettings(
617
- 'sendEventForId',
618
- new Error(`No active socket with this specific ID: ${id}`),
619
- content?.source
620
- ),
621
-
622
- status: 404,
623
- errorCode: 'NTS-SSS-301',
624
- });
625
- }
626
-
627
- presence.emitEvent(event, content, issuer);
628
-
629
- if (DynamoNTS_globalSettings.logSocketEventContent) {
630
- DynamoFM_Log.success(
631
- ` <--= emitted socket(${this.params.name}) event for presence: ${event}, ` +
632
- `\n presenceId: ${id}, sockets: ${presence.sockets.length}` +
633
- `\n content:`, content
634
- );
635
- } else {
636
- DynamoFM_Log.success(
637
- ` <--= emitted socket(${this.params.name}) event for presence: ${event}, ` +
638
- `\n presenceId: ${id}, sockets: ${presence.sockets.length}`
639
- );
640
- }
641
- } catch (error) {
642
- try {
643
- if (error.flag.includes('DYNAMO') && error?.accitionalInfo?.inactiveSockets) {
644
- const sockets: SocketIO.Socket[] = error.accitionalInfo.inactiveSockets;
645
-
646
- await DynamoFM_Array.asyncForEach(sockets, async (socket: SocketIO.Socket) => {
647
- await this.removeSubscription(socket);
648
- });
649
-
650
- const presence: DynamoNTS_SocketPresence = this.presences.find(
651
- (pres: DynamoNTS_SocketPresence) => pres.issuerId === id
652
- );
653
-
654
- if (!presence) {
655
- throw new DynamoFM_Error({
656
- ...this._getDefaultErrorSettings(
657
- 'sendEventForId',
658
- new Error(`No active socket(${this.params.name}) with this specific ID: ${id}`),
659
- content?.source
660
- ),
661
-
662
- status: 404,
663
- errorCode: 'NTS-SSS-302',
664
- });
665
- }
666
- }
667
- } catch (error) {
668
- throw new DynamoFM_Error({
669
- ...this._getDefaultErrorSettings('sendEventForId', error, content?.source),
670
-
671
- errorCode: 'NTS-SSS-310',
672
- message:
673
- `Error handling of inactive sockets(${this.params.name}) failed!` +
674
- `\n(Socket Event Emit for id (${id}, ${event}) failed)`,
675
- });
676
- }
677
-
678
- throw new DynamoFM_Error({
679
- ...this._getDefaultErrorSettings('sendEventForId', error, content?.source),
680
-
681
- errorCode: 'NTS-SSS-310',
682
- message: `socket(${this.params.name}) Event Emit for id (${id}, ${event}) failed`,
683
- });
684
- }
685
- }
686
-
687
- broadcastEvent(event: string, content: any, issuer: string): void {
688
- try {
689
- DynamoFM_Log.log(` <=-== broadcasting socket(${this.params.name}) event: ${event}`);
690
-
691
- this.presences.forEach((presence: DynamoNTS_SocketPresence) => {
692
- presence.emitEvent(event, content, issuer);
693
- });
694
- } catch (error) {
695
- DynamoFM_Log.error(`socket(${this.params.name}) Event Broadcast (${event}) failed`, error);
696
-
697
- throw new DynamoFM_Error({
698
- ...this._getDefaultErrorSettings('broadcastEvent', error),
699
-
700
- errorCode: 'NTS-SSS-400',
701
- message: `socket(${this.params.name}) Event Broadcast (${event}) failed`,
702
- });
703
- }
704
- }
705
-
706
- private _getDefaultErrorSettings(
707
- fnName: string,
708
- error: DynamoFM_AnyError,
709
- issuer?: string
710
- ): DynamoFM_Error_Settings {
711
- return {
712
- status: (error as DynamoFM_Error)?.___status ?? 500,
713
- message: (error as Error)?.message ?? `${fnName} was UNSUCCESFUL (NTS)`,
714
- addECToUserMsg: !(error as DynamoFM_Error)?.__userMessage,
715
- userMessage: (error as DynamoFM_Error)?.__userMessage ?? this.defaultErrorUserMsg,
716
- issuer: issuer,
717
- issuerService: this.constructor?.name,
718
- error: error,
719
- };
720
- }
721
-
722
- /**
723
- * You must setup params for the service in this function
724
- */
725
- protected abstract getServiceParams(): DynamoNTS_SocketServerService_Params;
726
-
727
- /**
728
- * You must setup events and required services in this function
729
- */
730
- protected abstract getIncomingEvents(): DynamoNTS_SocketEvent<any>[];
731
-
732
- /**
733
- * You can setup tasks for the subscription event in this function
734
- */
735
- protected getSubscriptionTasks?(): DynamoNTS_SocketEventTask<T_SubscriptionContent>[];
736
-
737
- /**
738
- * You can setup tasks for the subscription event in this function
739
- */
740
- protected getSubscriptionCloseTasks?(): DynamoNTS_SocketEventTask<null>[];
741
-
742
- /**
743
- * You can setup preprocessess for the subscription event in this function
744
- */
745
- protected getSubscriptionPreProcessess?(): DynamoNTS_SocketEventPreprocessTask[];
746
-
747
- /**
748
- * You can setup tasks for the unsubscribe event in this function
749
- */
750
- protected getUnsubscribeTasks?(): DynamoNTS_SocketEventTask<T_SubscriptionContent>[];
751
-
752
- /**
753
- * You can setup preprocessess for the unsubscribe event in this function
754
- */
755
- protected getUnsubscribePreProcessess?(): DynamoNTS_SocketEventPreprocessTask[];
756
-
757
- /**
758
- * You can setup tasks for the error event in this function
759
- */
760
- protected errorHandling?: DynamoNTS_SocketEventTask<T_ErrorContent>;
761
- }
1
+
2
+ import * as SocketIO from 'socket.io';
3
+
4
+ import {
5
+ DynamoFM_AnyError, DynamoFM_Array, DynamoFM_Error, DynamoFM_Error_Settings, DynamoFM_Log
6
+ } from '@futdevpro/fsm-dynamo';
7
+
8
+ import { DynamoNTS_SingletonService } from '../base/singleton.service';
9
+ import { DynamoNTS_globalSettings } from '../../_constants/global-settings.const';
10
+ import { DynamoNTS_SocketEventKey } from '../../_enums/http/socket-event-type.enum';
11
+ import { DynamoNTS_SocketSecurity } from '../../_enums/socket-security.enum';
12
+ import {
13
+ DynamoNTS_SocketServerService_Params
14
+ } from '../../_models/control-models/socket-server-service-params.control-model';
15
+ import {
16
+ DynamoNTS_SocketPresence
17
+ } from '../../_models/control-models/socket-presence.control-model';
18
+ import {
19
+ DynamoNTS_SocketEvent, DynamoNTS_SocketEventPreprocessTask, DynamoNTS_SocketEventTask
20
+ } from '../../_models/control-models/socket-event.control-model';
21
+
22
+ /**
23
+ * You need to define the following functions:
24
+ * ```ts
25
+ * getServiceParams(): DynamoNTS_SocketServerServiceParams;
26
+ *
27
+ * getIncomingEvents(): DynamoNTS_SocketEvent<any>[];
28
+ *
29
+ * getPresenceFromSubscrioptionEventContent(
30
+ * socketSubscription: T_SubscriptionContent,
31
+ * socket: SocketIO.Socket
32
+ * ): Promise<T_Presence>;
33
+ * ```
34
+ */
35
+ export abstract class DynamoNTS_SocketServerService<
36
+ T_Presence extends DynamoNTS_SocketPresence = DynamoNTS_SocketPresence,
37
+ T_SubscriptionContent = any,
38
+ T_ErrorContent = any
39
+ > extends DynamoNTS_SingletonService {
40
+
41
+ protected params: DynamoNTS_SocketServerService_Params;
42
+ get name(): string { return this.params.name; }
43
+ get port(): number { return this.params.port; }
44
+ get security(): DynamoNTS_SocketSecurity { return this.params.security; }
45
+ get activeSubscriptionsCount(): number { return this.presences.length; }
46
+
47
+ protected openSocketServer: SocketIO.Server;
48
+ protected secureSocketServer: SocketIO.Server;
49
+
50
+ private connectEvent: DynamoNTS_SocketEvent<SocketIO.Socket>;
51
+ protected incomingEvents: DynamoNTS_SocketEvent<any>[];
52
+
53
+ private subscriptionEvent: DynamoNTS_SocketEvent<any>;
54
+ private unsubscribeEvent: DynamoNTS_SocketEvent<any>;
55
+
56
+ /**
57
+ * You need to setup a function that converts subscription event content to presence.
58
+ * You should do authentication and authorization here.
59
+ */
60
+ protected abstract getPresenceFromSubscrioptionEventContent(
61
+ socketSubscription: T_SubscriptionContent, socket: SocketIO.Socket
62
+ ): Promise<T_Presence>;
63
+ protected presences: T_Presence[] = [];
64
+
65
+ protected logSetup: boolean;
66
+ protected logFn: boolean;
67
+
68
+ defaultErrorUserMsg =
69
+ `We encountered an unhandled Socket Server Error, ` +
70
+ `\nplease contact the responsible development team.`;
71
+
72
+ constructor(){
73
+ super();
74
+
75
+ this.asyncConstructor().catch((error: any) => {
76
+ DynamoFM_Log.error(
77
+ `Socket Server Service setup failed: ${this.params?.name} (${this.params?.port})`,
78
+ error
79
+ );
80
+ });
81
+ }
82
+
83
+ private async asyncConstructor(): Promise<void> {
84
+ try {
85
+ this.logSetup = DynamoNTS_globalSettings.logSetup;
86
+ this.params = this.getServiceParams();
87
+ this.incomingEvents = this.getIncomingEvents() ?? [];
88
+
89
+ await this.prepareEvents();
90
+
91
+ if (this.logSetup) console.log(
92
+ `Socket Server Service setup done: ${this.params.name} (${this.params.port}) ` +
93
+ `serurity: ${this.params.security}\n`
94
+ );
95
+ } catch (error) {
96
+ DynamoFM_Log.error(
97
+ `Socket Server Service setup failed: ${this.params?.name} (${this.params?.port})`,
98
+ error
99
+ );
100
+
101
+ throw new DynamoFM_Error({
102
+ errorCode: 'NTS-SSS-001',
103
+ error: error,
104
+ });
105
+ }
106
+ }
107
+
108
+ /**
109
+ * You must setup events and required services for this function
110
+ */
111
+ private async prepareEvents(): Promise<void> {
112
+ try {
113
+ /** Setup connect event */
114
+ const connectEventIndex = this.incomingEvents.findIndex(
115
+ (event: DynamoNTS_SocketEvent<any>) =>
116
+ event.eventKey === DynamoNTS_SocketEventKey.connection
117
+ );
118
+
119
+ if (0 <= connectEventIndex) {
120
+ this.connectEvent = this.incomingEvents.splice(connectEventIndex)[0];
121
+ } else {
122
+ this.connectEvent = new DynamoNTS_SocketEvent({
123
+ eventKey: DynamoNTS_SocketEventKey.connection,
124
+ });
125
+ }
126
+ this.connectEvent.serviceName = this.params.name;
127
+
128
+ /** Setup subscriptions events */
129
+ if (this.getPresenceFromSubscrioptionEventContent) {
130
+ /** Setup subscription event */
131
+ const subscriptionEventIndex = this.incomingEvents.findIndex(
132
+ (event: DynamoNTS_SocketEvent<any>) =>
133
+ event.eventKey === DynamoNTS_SocketEventKey.subscribe
134
+ );
135
+
136
+ if (0 <= subscriptionEventIndex) {
137
+ this.subscriptionEvent = this.incomingEvents.splice(subscriptionEventIndex)[0];
138
+ DynamoFM_Log.error(
139
+ `You should not set the subscription event, but the subscriptions tasks, ` +
140
+ `in case you need additional steps for your subscriptions.`,
141
+ `${this.params.name} (${this.params.port})`
142
+ );
143
+ } else {
144
+ this.subscriptionEvent = new DynamoNTS_SocketEvent<T_SubscriptionContent>({
145
+ eventKey: DynamoNTS_SocketEventKey.subscribe,
146
+ });
147
+ }
148
+
149
+ if (this.getSubscriptionPreProcessess) {
150
+ this.subscriptionEvent.preProcessess.push(...this.getSubscriptionPreProcessess());
151
+ }
152
+
153
+ if (this.getSubscriptionTasks) {
154
+ this.subscriptionEvent.tasks.push(...this.getSubscriptionTasks());
155
+ }
156
+ this.subscriptionEvent.serviceName = this.params.name;
157
+
158
+ /** Setup subscription event */
159
+ const unsubscribeEventIndex = this.incomingEvents.findIndex(
160
+ (event: DynamoNTS_SocketEvent<any>) =>
161
+ event.eventKey === DynamoNTS_SocketEventKey.unsubscribe
162
+ );
163
+
164
+ if (0 <= unsubscribeEventIndex) {
165
+ this.unsubscribeEvent = this.incomingEvents.splice(unsubscribeEventIndex)[0];
166
+ DynamoFM_Log.error(
167
+ `You should not set the unsubscribe event, but the unsubscribe tasks, ` +
168
+ `in case you need additional steps for your unsubscribe.`,
169
+ `${this.params.name} (${this.params.port})`
170
+ );
171
+ } else {
172
+ this.unsubscribeEvent = new DynamoNTS_SocketEvent<T_SubscriptionContent>({
173
+ eventKey: DynamoNTS_SocketEventKey.unsubscribe,
174
+ });
175
+ }
176
+
177
+ if (this.getUnsubscribePreProcessess) {
178
+ this.unsubscribeEvent.preProcessess.push(...this.getUnsubscribePreProcessess());
179
+ }
180
+
181
+ if (this.getUnsubscribeTasks) {
182
+ this.unsubscribeEvent.tasks.push(...this.getUnsubscribeTasks());
183
+ }
184
+ this.unsubscribeEvent.serviceName = this.params.name;
185
+
186
+ /** Setup disconnect event */
187
+ const disconnectEventIndex = this.incomingEvents.findIndex(
188
+ (event: DynamoNTS_SocketEvent<any>) =>
189
+ event.eventKey === DynamoNTS_SocketEventKey.disconnect
190
+ );
191
+
192
+ if (0 <= disconnectEventIndex) {
193
+ this.incomingEvents[disconnectEventIndex].serviceName = this.params.name;
194
+ this.incomingEvents[disconnectEventIndex].tasks.push(this.disconnectBaseTask);
195
+ } else {
196
+ this.incomingEvents.push(
197
+ new DynamoNTS_SocketEvent({
198
+ serviceName: this.params.name,
199
+ eventKey: DynamoNTS_SocketEventKey.disconnect,
200
+ tasks: [
201
+ this.disconnectBaseTask,
202
+ ],
203
+ })
204
+ );
205
+ }
206
+
207
+ /** Setup error event */
208
+ const errorEventIndex = this.incomingEvents.findIndex(
209
+ (event: DynamoNTS_SocketEvent<any>) => event.eventKey === DynamoNTS_SocketEventKey.error
210
+ );
211
+
212
+ if (errorEventIndex == -1 && this.errorHandling) {
213
+ this.incomingEvents.push(new DynamoNTS_SocketEvent<T_ErrorContent>({
214
+ serviceName: this.params.name,
215
+ eventKey: DynamoNTS_SocketEventKey.error,
216
+ tasks: [
217
+ this.errorHandling,
218
+ ],
219
+ }));
220
+ }
221
+
222
+ this.subscriptionEvent.socketName = this.params.name;
223
+ } else {
224
+ DynamoFM_Log.error(
225
+ 'getPresenceFromSubscrioptionEventContent is not set',
226
+ `${this.params.name} (${this.params.port})`
227
+ );
228
+ }
229
+
230
+ /** Set incoming events serviceName */
231
+ this.incomingEvents.forEach((event: DynamoNTS_SocketEvent<any>) => {
232
+ event.serviceName = this.params.name;
233
+ });
234
+ } catch (error) {
235
+ DynamoFM_Log.error(
236
+ `Socket Server Service - Event Preparation setup failed: ` +
237
+ `${this.params?.name} (${this.params?.port})`,
238
+ error
239
+ );
240
+
241
+ throw error;
242
+ }
243
+ }
244
+
245
+ // eslint-disable-next-line max-lines-per-function
246
+ async setupSocketServer(
247
+ newSocketServer: SocketIO.Server,
248
+ security: DynamoNTS_SocketSecurity.open | DynamoNTS_SocketSecurity.secure,
249
+ successCallback: () => void
250
+ ): Promise<SocketIO.Server> {
251
+ try {
252
+ if (this.logFn) console.log('\nfn:. setupSocketServer');
253
+
254
+ newSocketServer.on(DynamoNTS_SocketEventKey.connection, async (socket: SocketIO.Socket) => {
255
+ try {
256
+ let issuer: string;
257
+
258
+ await this.connectEvent.executeEventTasks(socket);
259
+
260
+ if (this.getPresenceFromSubscrioptionEventContent) {
261
+ socket.on(
262
+ DynamoNTS_SocketEventKey.subscribe,
263
+ async (content: any) => {
264
+ try {
265
+ /**
266
+ * usually socket logs are in event.executeEventTasks(),
267
+ * but subscribe event is an exception from this,
268
+ * to be able to check content before getPresenceFromSubscrioptionEventContent
269
+ */
270
+ if (DynamoNTS_globalSettings.logSocketEventContent) {
271
+ DynamoFM_Log.log(
272
+ `--=> incoming socket(${this.params.name}) ` +
273
+ `event: ${DynamoNTS_SocketEventKey.subscribe};` +
274
+ `\ncontent:`, content
275
+ );
276
+ } else {
277
+ DynamoFM_Log.log(
278
+ `--=> incoming socket(${this.params.name}) ` +
279
+ `event: ${DynamoNTS_SocketEventKey.subscribe}`
280
+ );
281
+ }
282
+
283
+ const presence: T_Presence = await this.getPresenceFromSubscrioptionEventContent(
284
+ content, socket
285
+ );
286
+
287
+ presence.serviceName = this.params.name;
288
+ issuer = presence.issuerId;
289
+
290
+ this.addSocketToPresence(presence);
291
+ await this.subscriptionEvent.executeEventTasks(content, issuer);
292
+
293
+ socket.emit(
294
+ DynamoNTS_SocketEventKey.subscriptionSuccessful,
295
+ 'subscribe was successful',
296
+ error => {
297
+ DynamoFM_Log.error(
298
+ `Emitting subscriptionSuccessful event failed!` +
299
+ `\nerror:`, error
300
+ );
301
+ }
302
+ );
303
+
304
+ DynamoFM_Log.success(
305
+ `<-==> socket(${this.params.name}) subscription successfull (${issuer})`
306
+ );
307
+ } catch (error) {
308
+ DynamoFM_Log.error(
309
+ `Socket Subscription failed: ${this.params.name} ` ,
310
+ `(${this.params.port}) will disconnect now...`,
311
+ error
312
+ );
313
+ socket.emit(DynamoNTS_SocketEventKey.error, error);
314
+ socket.disconnect();
315
+ }
316
+ }
317
+ );
318
+
319
+ socket.on(
320
+ DynamoNTS_SocketEventKey.unsubscribe,
321
+ async (issuer: string) => {
322
+ try {
323
+ /**
324
+ * usually socket logs are in event.executeEventTasks(),
325
+ * but unsubscribe event is an exception from this,
326
+ * to be able to check content before getPresenceFromSubscrioptionEventContent
327
+ */
328
+ if (DynamoNTS_globalSettings.logSocketEventContent) {
329
+ DynamoFM_Log.log(
330
+ `--=> incoming socket(${this.params.name}) ` +
331
+ `event: ${DynamoNTS_SocketEventKey.unsubscribe};` +
332
+ `\ncontent:`, issuer
333
+ );
334
+ } else {
335
+ DynamoFM_Log.log(
336
+ `--=> incoming socket(${this.params.name}) ` +
337
+ `event: ${DynamoNTS_SocketEventKey.unsubscribe}`
338
+ );
339
+ }
340
+
341
+ this.removeSubscription(socket, issuer);
342
+
343
+ await this.subscriptionEvent.executeEventTasks(issuer, issuer);
344
+
345
+ socket.emit(
346
+ DynamoNTS_SocketEventKey.unsubscribeSuccessful,
347
+ 'unsubscribe was successful',
348
+ error => {
349
+ DynamoFM_Log.error(
350
+ `Emitting unsubscribeSuccessful event failed!` +
351
+ `\nerror:`, error
352
+ );
353
+ }
354
+ );
355
+
356
+ DynamoFM_Log.success(
357
+ `< x > socket(${this.params.name}) unsubscribe successfull (${issuer})`
358
+ );
359
+ } catch (error) {
360
+ DynamoFM_Log.error(
361
+ `Socket unsubscribe failed: ${this.params.name} ` ,
362
+ `(${this.params.port}) will disconnect now...`,
363
+ error
364
+ );
365
+ socket.emit(DynamoNTS_SocketEventKey.error, error);
366
+ }
367
+ }
368
+ );
369
+ }
370
+
371
+ this.incomingEvents.forEach((event: DynamoNTS_SocketEvent<any>) => {
372
+ socket.on(
373
+ event.eventKey,
374
+ async (content: any) => {
375
+ try {
376
+ await event.executeEventTasks(content, issuer);
377
+ } catch (error) {
378
+ DynamoFM_Log.error(
379
+ `Socket Event failed: ${this.params.name} (${this.params.port})`,
380
+ error
381
+ );
382
+ socket.emit(DynamoNTS_SocketEventKey.error, error);
383
+ }
384
+ }
385
+ );
386
+ });
387
+
388
+ if (DynamoNTS_globalSettings.logMainSocketEvents) {
389
+ DynamoFM_Log.success(`< > socket(${this.params.name}): new CONNECTION established`);
390
+ }
391
+ } catch (error) {
392
+ DynamoFM_Log.error(
393
+ `Socket Connection failed: ${this.params.name} ` +
394
+ `(${this.params.port}) will disconnect now...`,
395
+ error
396
+ );
397
+ socket.emit(DynamoNTS_SocketEventKey.error, error);
398
+ socket.disconnect();
399
+ }
400
+ });
401
+
402
+ newSocketServer.listen(this.params.port);
403
+
404
+ DynamoFM_Log.success(
405
+ `\nsocket server setup finished: ${this.params.name}` +
406
+ `\nsocket server listening on port: ${this.params.port}`
407
+ );
408
+
409
+ successCallback();
410
+
411
+ if (security === DynamoNTS_SocketSecurity.open) {
412
+ this.openSocketServer = newSocketServer;
413
+
414
+ return this.openSocketServer;
415
+ } else {
416
+ this.secureSocketServer = newSocketServer;
417
+
418
+ return this.secureSocketServer;
419
+ }
420
+ } catch (error) {
421
+ DynamoFM_Log.error(
422
+ `Socket Server Service - Deep Setup failed: ${this.params?.name} ` +
423
+ `(${this.params?.port})`,
424
+ error
425
+ );
426
+
427
+ throw error;
428
+ }
429
+ }
430
+
431
+ protected async addSocketToPresence(newPresence: T_Presence): Promise<void> {
432
+ try {
433
+ const activePresence: T_Presence = this.presences.find(
434
+ (pres: T_Presence) => pres.issuerId === newPresence.issuerId
435
+ );
436
+
437
+ if (activePresence) {
438
+ activePresence.sockets.push(...newPresence.sockets);
439
+ } else {
440
+ this.presences.push(newPresence);
441
+ }
442
+ } catch (error) {
443
+ DynamoFM_Log.error(
444
+ `Socket Subscription failed: ${this.params?.name} (${this.params?.port})`,
445
+ error
446
+ );
447
+
448
+ throw error;
449
+ }
450
+ }
451
+
452
+ private async disconnectBaseTask(socket: SocketIO.Socket, issuer: string): Promise<void> {
453
+ try {
454
+ await this.removeSubscription(socket, issuer);
455
+
456
+ DynamoFM_Log.info(`< x > socket(${this.params.name}) disconnected (${issuer})`);
457
+ } catch (error) {
458
+ throw new DynamoFM_Error({
459
+ ...this._getDefaultErrorSettings(
460
+ 'disconnectBaseTask',
461
+ error,
462
+ issuer
463
+ ),
464
+
465
+ errorCode: 'NTS-SSS-109',
466
+ });
467
+ }
468
+ }
469
+
470
+ private async removeSubscription(socket: SocketIO.Socket, issuer?: string): Promise<void> {
471
+ try {
472
+ const activePresenceIndex: number = this.presences.findIndex(
473
+ (pres: DynamoNTS_SocketPresence) => pres.sockets.includes(socket)
474
+ );
475
+
476
+ if (activePresenceIndex === -1) {
477
+ throw new DynamoFM_Error({
478
+ ...this._getDefaultErrorSettings(
479
+ 'sendEventForId',
480
+ new Error(
481
+ `closing socket(${this.params.name}) does not match any in the activePresences`
482
+ ),
483
+ issuer
484
+ ),
485
+
486
+ errorCode: 'NTS-SSS-203',
487
+ });
488
+ }
489
+
490
+ const activePresence: DynamoNTS_SocketPresence = this.presences[activePresenceIndex];
491
+
492
+ if (activePresence.issuerId !== issuer) {
493
+ throw new DynamoFM_Error({
494
+ ...this._getDefaultErrorSettings(
495
+ 'sendEventForId',
496
+ new Error(
497
+ `socket subscription for ${issuer} and ${activePresence.issuerId} does not match.`
498
+ ),
499
+ issuer
500
+ ),
501
+
502
+ errorCode: 'NTS-SSS-201',
503
+ });
504
+ }
505
+
506
+ const socketIndex = activePresence.sockets.findIndex((s: SocketIO.Socket) => s === socket);
507
+
508
+ if (socketIndex === -1) {
509
+ throw new DynamoFM_Error({
510
+ ...this._getDefaultErrorSettings(
511
+ 'sendEventForId',
512
+ new Error(
513
+ `closing socket(${this.params.name}) does not match any in the activePresences`
514
+ ),
515
+ issuer
516
+ ),
517
+
518
+ errorCode: 'NTS-SSS-202',
519
+ });
520
+ }
521
+
522
+ activePresence.sockets.splice(socketIndex);
523
+
524
+ if (this.getSubscriptionCloseTasks) {
525
+ await DynamoFM_Array.asyncForEach(
526
+ this.getSubscriptionCloseTasks(),
527
+ async (task: DynamoNTS_SocketEventTask<null>) => {
528
+ await task(null, issuer);
529
+ }
530
+ );
531
+ }
532
+
533
+ if (activePresence.sockets.length === 0) {
534
+ this.presences.splice(activePresenceIndex);
535
+ }
536
+
537
+ socket.disconnect();
538
+ DynamoFM_Log.info(`<x==> socket(${this.params.name}) unsubscription successfull (${issuer})`);
539
+ } catch (error) {
540
+ throw new DynamoFM_Error({
541
+ ...this._getDefaultErrorSettings(
542
+ 'sendEventForId',
543
+ error,
544
+ issuer
545
+ ),
546
+
547
+ errorCode: 'NTS-SSS-200',
548
+ message: `socket(${this.params.name}) Subscription Removal failed: ${this.params?.name}`,
549
+ });
550
+ }
551
+ }
552
+
553
+ /* emitServerEvent(event: string, content: any): void {
554
+ try {
555
+ DynamoFM_Log.log(` <=-- emitting server socket(${this.params.name}) event: ${event}`);
556
+
557
+ this.openSocketServer.emit(event, content, error => {
558
+ DynamoFM_Log.error(`Emitting server event '${event}' failed!\nerror:`, error);
559
+ });
560
+ } catch (error) {
561
+ throw new DynamoFM_Error({
562
+ ...this._getDefaultErrorSettings(
563
+ 'emitEvent',
564
+ error
565
+ ),
566
+
567
+ errorCode: 'NTS-SSS-500',
568
+ message: `socket(${this.params.name}) Event Emit (${event}) failed: ${this.params?.name}`,
569
+ });
570
+ }
571
+ } */
572
+
573
+ idIsSubscribed(id: string): boolean {
574
+ try {
575
+ return !!this.presences.find(
576
+ (presence: DynamoNTS_SocketPresence) => presence.issuerId === id
577
+ );
578
+ } catch (error) {
579
+ throw new DynamoFM_Error({
580
+ ...this._getDefaultErrorSettings(
581
+ 'idIsSubscribed',
582
+ error
583
+ ),
584
+
585
+ errorCode: 'NTS-SSS-600',
586
+ message: `socket(${this.params.name}) ID Subscription Check (${id}) failed`,
587
+ });
588
+ }
589
+ }
590
+
591
+ async emitError(presenceIssuerId: string, error: any, issuer: string): Promise<void> {
592
+ try {
593
+ await this.sendEventForId(presenceIssuerId, DynamoNTS_SocketEventKey.error, error, issuer);
594
+ } catch (error) {
595
+ throw new DynamoFM_Error({
596
+ ...this._getDefaultErrorSettings(
597
+ 'emitError',
598
+ error,
599
+ presenceIssuerId
600
+ ),
601
+
602
+ errorCode: 'NTS-SSS-700',
603
+ message: `socket(${this.params.name}) Error Emit (id: ${presenceIssuerId}) failed`,
604
+ });
605
+ }
606
+ }
607
+
608
+ async sendEventForId(id: string, event: string, content: any, issuer: string): Promise<void> {
609
+ try {
610
+ const presence: DynamoNTS_SocketPresence = this.presences.find(
611
+ (pres: DynamoNTS_SocketPresence) => pres.issuerId === id
612
+ );
613
+
614
+ if (!presence) {
615
+ throw new DynamoFM_Error({
616
+ ...this._getDefaultErrorSettings(
617
+ 'sendEventForId',
618
+ new Error(`No active socket with this specific ID: ${id}`),
619
+ content?.source
620
+ ),
621
+
622
+ status: 404,
623
+ errorCode: 'NTS-SSS-301',
624
+ });
625
+ }
626
+
627
+ presence.emitEvent(event, content, issuer);
628
+
629
+ if (DynamoNTS_globalSettings.logSocketEventContent) {
630
+ DynamoFM_Log.success(
631
+ ` <--= emitted socket(${this.params.name}) event for presence: ${event}, ` +
632
+ `\n presenceId: ${id}, sockets: ${presence.sockets.length}` +
633
+ `\n content:`, content
634
+ );
635
+ } else {
636
+ DynamoFM_Log.success(
637
+ ` <--= emitted socket(${this.params.name}) event for presence: ${event}, ` +
638
+ `\n presenceId: ${id}, sockets: ${presence.sockets.length}`
639
+ );
640
+ }
641
+ } catch (error) {
642
+ try {
643
+ if (error.flag.includes('DYNAMO') && error?.accitionalInfo?.inactiveSockets) {
644
+ const sockets: SocketIO.Socket[] = error.accitionalInfo.inactiveSockets;
645
+
646
+ await DynamoFM_Array.asyncForEach(sockets, async (socket: SocketIO.Socket) => {
647
+ await this.removeSubscription(socket);
648
+ });
649
+
650
+ const presence: DynamoNTS_SocketPresence = this.presences.find(
651
+ (pres: DynamoNTS_SocketPresence) => pres.issuerId === id
652
+ );
653
+
654
+ if (!presence) {
655
+ throw new DynamoFM_Error({
656
+ ...this._getDefaultErrorSettings(
657
+ 'sendEventForId',
658
+ new Error(`No active socket(${this.params.name}) with this specific ID: ${id}`),
659
+ content?.source
660
+ ),
661
+
662
+ status: 404,
663
+ errorCode: 'NTS-SSS-302',
664
+ });
665
+ }
666
+ }
667
+ } catch (error) {
668
+ throw new DynamoFM_Error({
669
+ ...this._getDefaultErrorSettings('sendEventForId', error, content?.source),
670
+
671
+ errorCode: 'NTS-SSS-310',
672
+ message:
673
+ `Error handling of inactive sockets(${this.params.name}) failed!` +
674
+ `\n(Socket Event Emit for id (${id}, ${event}) failed)`,
675
+ });
676
+ }
677
+
678
+ throw new DynamoFM_Error({
679
+ ...this._getDefaultErrorSettings('sendEventForId', error, content?.source),
680
+
681
+ errorCode: 'NTS-SSS-310',
682
+ message: `socket(${this.params.name}) Event Emit for id (${id}, ${event}) failed`,
683
+ });
684
+ }
685
+ }
686
+
687
+ broadcastEvent(event: string, content: any, issuer: string): void {
688
+ try {
689
+ DynamoFM_Log.log(` <=-== broadcasting socket(${this.params.name}) event: ${event}`);
690
+
691
+ this.presences.forEach((presence: DynamoNTS_SocketPresence) => {
692
+ presence.emitEvent(event, content, issuer);
693
+ });
694
+ } catch (error) {
695
+ DynamoFM_Log.error(`socket(${this.params.name}) Event Broadcast (${event}) failed`, error);
696
+
697
+ throw new DynamoFM_Error({
698
+ ...this._getDefaultErrorSettings('broadcastEvent', error),
699
+
700
+ errorCode: 'NTS-SSS-400',
701
+ message: `socket(${this.params.name}) Event Broadcast (${event}) failed`,
702
+ });
703
+ }
704
+ }
705
+
706
+ private _getDefaultErrorSettings(
707
+ fnName: string,
708
+ error: DynamoFM_AnyError,
709
+ issuer?: string
710
+ ): DynamoFM_Error_Settings {
711
+ return {
712
+ status: (error as DynamoFM_Error)?.___status ?? 500,
713
+ message: (error as Error)?.message ?? `${fnName} was UNSUCCESFUL (NTS)`,
714
+ addECToUserMsg: !(error as DynamoFM_Error)?.__userMessage,
715
+ userMessage: (error as DynamoFM_Error)?.__userMessage ?? this.defaultErrorUserMsg,
716
+ issuer: issuer,
717
+ issuerService: this.constructor?.name,
718
+ error: error,
719
+ };
720
+ }
721
+
722
+ /**
723
+ * You must setup params for the service in this function
724
+ */
725
+ protected abstract getServiceParams(): DynamoNTS_SocketServerService_Params;
726
+
727
+ /**
728
+ * You must setup events and required services in this function
729
+ */
730
+ protected abstract getIncomingEvents(): DynamoNTS_SocketEvent<any>[];
731
+
732
+ /**
733
+ * You can setup tasks for the subscription event in this function
734
+ */
735
+ protected getSubscriptionTasks?(): DynamoNTS_SocketEventTask<T_SubscriptionContent>[];
736
+
737
+ /**
738
+ * You can setup tasks for the subscription event in this function
739
+ */
740
+ protected getSubscriptionCloseTasks?(): DynamoNTS_SocketEventTask<null>[];
741
+
742
+ /**
743
+ * You can setup preprocessess for the subscription event in this function
744
+ */
745
+ protected getSubscriptionPreProcessess?(): DynamoNTS_SocketEventPreprocessTask[];
746
+
747
+ /**
748
+ * You can setup tasks for the unsubscribe event in this function
749
+ */
750
+ protected getUnsubscribeTasks?(): DynamoNTS_SocketEventTask<T_SubscriptionContent>[];
751
+
752
+ /**
753
+ * You can setup preprocessess for the unsubscribe event in this function
754
+ */
755
+ protected getUnsubscribePreProcessess?(): DynamoNTS_SocketEventPreprocessTask[];
756
+
757
+ /**
758
+ * You can setup tasks for the error event in this function
759
+ */
760
+ protected errorHandling?: DynamoNTS_SocketEventTask<T_ErrorContent>;
761
+ }