@futdevpro/nts-dynamo 1.15.7 → 1.15.9

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 (90) hide show
  1. package/__documentations/nts-integration-tests-2026-03-17.md +26 -0
  2. package/build/_collections/default-fallback-cache-max-age.const.d.ts +3 -0
  3. package/build/_collections/default-fallback-cache-max-age.const.d.ts.map +1 -0
  4. package/build/_collections/default-fallback-cache-max-age.const.js +6 -0
  5. package/build/_collections/default-fallback-cache-max-age.const.js.map +1 -0
  6. package/build/_collections/default-not-found-page.const.d.ts +6 -0
  7. package/build/_collections/default-not-found-page.const.d.ts.map +1 -0
  8. package/build/_collections/default-not-found-page.const.js +25 -0
  9. package/build/_collections/default-not-found-page.const.js.map +1 -0
  10. package/build/_collections/default-socket-path.const.d.ts +3 -0
  11. package/build/_collections/default-socket-path.const.d.ts.map +1 -0
  12. package/build/_collections/default-socket-path.const.js +6 -0
  13. package/build/_collections/default-socket-path.const.js.map +1 -0
  14. package/build/_models/control-models/http-settings.control-model.d.ts +7 -0
  15. package/build/_models/control-models/http-settings.control-model.d.ts.map +1 -1
  16. package/build/_models/control-models/http-settings.control-model.js +7 -0
  17. package/build/_models/control-models/http-settings.control-model.js.map +1 -1
  18. package/build/_models/interfaces/static-client-settings.interface.d.ts +29 -0
  19. package/build/_models/interfaces/static-client-settings.interface.d.ts.map +1 -0
  20. package/build/_models/interfaces/static-client-settings.interface.js +3 -0
  21. package/build/_models/interfaces/static-client-settings.interface.js.map +1 -0
  22. package/build/_modules/mock/app-extended-server.mock.js +2 -2
  23. package/build/_modules/mock/app-extended-server.mock.js.map +1 -1
  24. package/build/_modules/mock/app-integration-test.mock.d.ts +20 -0
  25. package/build/_modules/mock/app-integration-test.mock.d.ts.map +1 -0
  26. package/build/_modules/mock/app-integration-test.mock.js +46 -0
  27. package/build/_modules/mock/app-integration-test.mock.js.map +1 -0
  28. package/build/_modules/mock/app-server.mock.js +2 -2
  29. package/build/_modules/mock/app-server.mock.js.map +1 -1
  30. package/build/_modules/mock/socket-client.mock.js +1 -1
  31. package/build/_modules/mock/socket-client.mock.js.map +1 -1
  32. package/build/_modules/mock/socket-server.mock.d.ts +1 -1
  33. package/build/_modules/mock/socket-server.mock.d.ts.map +1 -1
  34. package/build/_modules/mock/socket-server.mock.js +2 -8
  35. package/build/_modules/mock/socket-server.mock.js.map +1 -1
  36. package/build/_modules/socket/_models/socket-server-service-params.control-model.d.ts +4 -2
  37. package/build/_modules/socket/_models/socket-server-service-params.control-model.d.ts.map +1 -1
  38. package/build/_modules/socket/_models/socket-server-service-params.control-model.js +3 -1
  39. package/build/_modules/socket/_models/socket-server-service-params.control-model.js.map +1 -1
  40. package/build/_modules/socket/_services/socket-server.service.d.ts +9 -2
  41. package/build/_modules/socket/_services/socket-server.service.d.ts.map +1 -1
  42. package/build/_modules/socket/_services/socket-server.service.js +27 -11
  43. package/build/_modules/socket/_services/socket-server.service.js.map +1 -1
  44. package/build/_modules/socket/app-extended.server.d.ts +9 -2
  45. package/build/_modules/socket/app-extended.server.d.ts.map +1 -1
  46. package/build/_modules/socket/app-extended.server.js +42 -25
  47. package/build/_modules/socket/app-extended.server.js.map +1 -1
  48. package/build/_services/base/data.service.d.ts.map +1 -1
  49. package/build/_services/base/data.service.js +4 -5
  50. package/build/_services/base/data.service.js.map +1 -1
  51. package/build/_services/base/db.service.d.ts.map +1 -1
  52. package/build/_services/base/db.service.js +8 -0
  53. package/build/_services/base/db.service.js.map +1 -1
  54. package/build/_services/server/app.server.d.ts +18 -0
  55. package/build/_services/server/app.server.d.ts.map +1 -1
  56. package/build/_services/server/app.server.js +74 -2
  57. package/build/_services/server/app.server.js.map +1 -1
  58. package/build/index.d.ts +4 -0
  59. package/build/index.d.ts.map +1 -1
  60. package/build/index.js +4 -0
  61. package/build/index.js.map +1 -1
  62. package/nodemon.json +2 -1
  63. package/package.json +4 -4
  64. package/src/_collections/default-fallback-cache-max-age.const.spec.ts +11 -0
  65. package/src/_collections/default-fallback-cache-max-age.const.ts +2 -0
  66. package/src/_collections/default-not-found-page.const.spec.ts +19 -0
  67. package/src/_collections/default-not-found-page.const.ts +22 -0
  68. package/src/_collections/default-socket-path.const.spec.ts +12 -0
  69. package/src/_collections/default-socket-path.const.ts +2 -0
  70. package/src/_models/control-models/http-settings.control-model.spec.ts +11 -0
  71. package/src/_models/control-models/http-settings.control-model.ts +8 -0
  72. package/src/_models/interfaces/static-client-settings.interface.spec.ts +29 -0
  73. package/src/_models/interfaces/static-client-settings.interface.ts +28 -0
  74. package/src/_modules/mock/app-extended-server.mock.ts +2 -2
  75. package/src/_modules/mock/app-integration-test.mock.ts +51 -0
  76. package/src/_modules/mock/app-server.mock.ts +2 -2
  77. package/src/_modules/mock/socket-client.mock.spec.ts +1 -1
  78. package/src/_modules/mock/socket-client.mock.ts +1 -1
  79. package/src/_modules/mock/socket-server.mock.spec.ts +1 -1
  80. package/src/_modules/mock/socket-server.mock.ts +5 -13
  81. package/src/_modules/socket/_models/socket-server-service-params.control-model.spec.ts +13 -0
  82. package/src/_modules/socket/_models/socket-server-service-params.control-model.ts +4 -2
  83. package/src/_modules/socket/_services/socket-server.service.ts +37 -6
  84. package/src/_modules/socket/app-extended.integration.spec.ts +85 -0
  85. package/src/_modules/socket/app-extended.server.ts +47 -40
  86. package/src/_services/base/data.service.ts +4 -7
  87. package/src/_services/base/db.service.ts +11 -3
  88. package/src/_services/route/routing-module.service.spec.ts +15 -0
  89. package/src/_services/server/app.server.ts +107 -2
  90. package/src/index.ts +4 -0
@@ -85,7 +85,7 @@ export class DyNTS_AppFull_Mock extends DyNTS_App {
85
85
 
86
86
  getPortSettings(): DyNTS_Http_Settings {
87
87
  return {
88
- httpPort: 20101,
88
+ httpPort: 0,
89
89
  };
90
90
  }
91
91
 
@@ -172,7 +172,7 @@ export class DyNTS_AppTEST_Mock extends DyNTS_App {
172
172
 
173
173
  getPortSettings(): DyNTS_Http_Settings {
174
174
  return {
175
- httpPort: 10203,
175
+ httpPort: 0,
176
176
  };
177
177
  }
178
178
 
@@ -34,7 +34,7 @@ describe('| SocketClient_Mock', () => {
34
34
  expect(params).toEqual(jasmine.any(DyNTS_SocketClientService_Params));
35
35
  expect(params.service).toBe('-test-socket-target-');
36
36
  expect(params.name).toBe('socket-client-mock');
37
- expect(params.port).toBe(30303);
37
+ expect(params.port).toBe(0);
38
38
  expect(params.socketOptions.reconnection).toBeTrue();
39
39
  });
40
40
  });
@@ -34,7 +34,7 @@ export class SocketClient_Mock extends DyNTS_SocketClient_ServiceBase {
34
34
  return new DyNTS_SocketClientService_Params({
35
35
  service: '-test-socket-target-',
36
36
  name: 'socket-client-mock',
37
- port: 30303,
37
+ port: 0,
38
38
  socketOptions: {
39
39
  reconnection: true,
40
40
  },
@@ -20,7 +20,7 @@ describe('| DyNTS_SocketServer_Mock', () => {
20
20
 
21
21
  expect(params).toBeInstanceOf(DyNTS_SocketServerService_Params);
22
22
  expect(params.name).toBe('test');
23
- expect(params.port).toBe(19393); // Updated from 9393 to 19393
23
+ expect(params.port).toBe(0);
24
24
  });
25
25
 
26
26
  it('| should return an empty array for incoming events', () => {
@@ -1,6 +1,5 @@
1
1
  import { DyFM_SocketEvent } from '@futdevpro/fsm-dynamo/socket';
2
2
  import { Socket } from 'socket.io';
3
- import * as net from 'net';
4
3
 
5
4
  import {
6
5
  DyNTS_SocketPresence
@@ -10,28 +9,21 @@ import {
10
9
  } from '../socket/_models/socket-server-service-params.control-model';
11
10
  import { DyNTS_SocketServerService } from '../socket/_services/socket-server.service';
12
11
 
13
- /**
14
- * Utility function to find an available port starting from a base port
15
- */
16
- const findAvailablePort = (basePort: number): number => {
17
- return basePort; // For now, return the base port, but this could be enhanced
18
- };
19
-
20
12
  /**
21
13
  * Mock Socket Server Service for testing purposes
22
- * Uses a non-privileged port to avoid EACCES errors on Windows
14
+ * Uses port 0 so the OS assigns a random available port
23
15
  */
24
- export class DyNTS_SocketServer_Mock extends
16
+ export class DyNTS_SocketServer_Mock extends
25
17
  DyNTS_SocketServerService<DyNTS_SocketPresence> {
26
-
18
+
27
19
  static getInstance(): DyNTS_SocketServer_Mock {
28
20
  return DyNTS_SocketServer_Mock.getSingletonInstance();
29
21
  }
30
-
22
+
31
23
  getServiceParams(): DyNTS_SocketServerService_Params<any> {
32
24
  return new DyNTS_SocketServerService_Params({
33
25
  name: 'test',
34
- port: findAvailablePort(19393), // Changed from 9393 to 19393 (non-privileged port)
26
+ port: 0,
35
27
  });
36
28
  }
37
29
 
@@ -30,4 +30,17 @@ describe('| DyNTS_SocketServerService_Params', () => {
30
30
  expect(instance.port).toBe(8080);
31
31
  expect(instance.security).toBe(DyNTS_global_settings.defaultSocketSecurity);
32
32
  });
33
+
34
+ it('| should allow port to be omitted (optional, default used by app-extended)', () => {
35
+ const params = {
36
+ name: 'TestSocketNoPort',
37
+ security: DyNTS_SocketSecurity.open,
38
+ };
39
+
40
+ const instance = new DyNTS_SocketServerService_Params(params);
41
+
42
+ expect(instance.name).toBe('TestSocketNoPort');
43
+ expect(instance.port).toBeUndefined();
44
+ expect(instance.security).toBe(DyNTS_SocketSecurity.open);
45
+ });
33
46
  });
@@ -3,11 +3,13 @@ import { DyNTS_global_settings } from '../../../_collections/global-settings.con
3
3
  import { DyNTS_SocketSecurity } from '../_enums/socket-security.enum';
4
4
 
5
5
  /**
6
- * socket handling and clarification is under development
6
+ * Socket service paraméterek.
7
+ * A port opcionális: ha nincs megadva, az app-extended a getPortSettings() httpPort/httpsPort értékét használja (ugyanaz a port, mint a HTTP/HTTPS).
7
8
  */
8
9
  export class DyNTS_SocketServerService_Params<T = any> {
9
10
  name: string;
10
- port: number;
11
+ /** Opcionális: ha nincs megadva, az extended app a HTTP/HTTPS portot használja (same port). */
12
+ port?: number;
11
13
  security?: DyNTS_SocketSecurity;
12
14
 
13
15
  constructor(
@@ -21,6 +21,14 @@ import {
21
21
  DyNTS_SocketServerService_Params
22
22
  } from '../_models/socket-server-service-params.control-model';
23
23
 
24
+ /** Opciók a setupSocketServer híváshoz: attacholt HTTP szerver esetén ne hívjuk a listen()-t. */
25
+ export interface DyNTS_SocketServerSetupOptions {
26
+ /** Ha true, a Socket.IO egy már listenelő HTTP/HTTPS szerverre van attacholva – ne hívjuk a listen()-t. */
27
+ attachedToExistingServer?: boolean;
28
+ /** A tényleges port (pl. defaultolt httpPort/httpsPort) – logokban használjuk. */
29
+ effectivePort?: number;
30
+ }
31
+
24
32
  /**
25
33
  * You need to define the following functions:
26
34
  * ```ts
@@ -42,7 +50,7 @@ export abstract class DyNTS_SocketServerService<
42
50
 
43
51
  protected params: DyNTS_SocketServerService_Params;
44
52
  get name(): string { return this.params.name; }
45
- get port(): number { return this.params.port; }
53
+ get port(): number | undefined { return this.params.port; }
46
54
  get security(): DyNTS_SocketSecurity { return this.params.security; }
47
55
  get activeSubscriptionsCount(): number { return this.presences.length; }
48
56
 
@@ -270,11 +278,14 @@ export abstract class DyNTS_SocketServerService<
270
278
  async setupSocketServer(
271
279
  newSocketServer: SocketIO.Server,
272
280
  security: DyNTS_SocketSecurity.open | DyNTS_SocketSecurity.secure,
273
- successCallback: () => void
281
+ successCallback: () => void,
282
+ options?: DyNTS_SocketServerSetupOptions
274
283
  ): Promise<SocketIO.Server> {
275
284
  try {
276
285
  if (this.highDetailedLogs) console.log('\nfn:. setupSocketServer');
277
286
 
287
+ const effectivePort: number | undefined = options?.effectivePort ?? this.params.port;
288
+
278
289
  newSocketServer.on(DyFM_SocketEvent_Key.incomingNewConnection, async (socket: SocketIO.Socket) => {
279
290
  try {
280
291
  let issuer: string;
@@ -497,16 +508,35 @@ export abstract class DyNTS_SocketServerService<
497
508
  await this.closeSocket(socket, `${this.params.name} on connection error`, error);
498
509
  }
499
510
  });
500
-
511
+
512
+ const isAttach: boolean =
513
+ options?.attachedToExistingServer === true ||
514
+ (this.params.port === undefined && options?.effectivePort !== undefined);
515
+
516
+ if (isAttach) {
517
+ DyFM_Log.success(
518
+ `\nsocket server setup finished: "${this.params.name}"` +
519
+ (effectivePort !== undefined ? ` (attached to server on port: ${effectivePort})` : ' (attached to existing server)')
520
+ );
521
+ successCallback();
522
+ } else {
501
523
  try {
502
- newSocketServer.listen(this.params.port);
524
+ const portToListen: number | undefined = this.params.port ?? options?.effectivePort;
525
+ if (portToListen === undefined) {
526
+ throw new Error(
527
+ `PORT NOT SET: "${this.params.name}". ` +
528
+ `Either set port in getServiceParams() or pass effectivePort in setupSocketServer options.`
529
+ );
530
+ }
531
+ newSocketServer.listen(portToListen);
503
532
 
504
533
  DyFM_Log.success(
505
534
  `\nsocket server setup finished: "${this.params.name}"` +
506
- `\nsocket server listening on port: "${this.params.port}"`
535
+ `\nsocket server listening on port: "${portToListen}"`
507
536
  );
508
537
  } catch (listenError) {
509
- const errorMessage = `Failed to bind socket server "${this.params.name}" to port ${this.params.port}. ` +
538
+ const portForMsg: number | undefined = this.params.port ?? options?.effectivePort;
539
+ const errorMessage = `Failed to bind socket server "${this.params.name}" to port ${portForMsg}. ` +
510
540
  `This could be due to port already in use or insufficient permissions. ` +
511
541
  `Please ensure the port is available and try again.`;
512
542
 
@@ -515,6 +545,7 @@ export abstract class DyNTS_SocketServerService<
515
545
  }
516
546
 
517
547
  successCallback();
548
+ }
518
549
 
519
550
  if (security === DyNTS_SocketSecurity.open) {
520
551
  this.openSocketServer = newSocketServer;
@@ -0,0 +1,85 @@
1
+ import * as Fs from 'fs';
2
+ import * as Os from 'os';
3
+ import * as Path from 'path';
4
+
5
+ import Axios from 'axios';
6
+
7
+ import { DyNTS_defaultNotFoundPageHtml } from '../../_collections/default-not-found-page.const';
8
+ import { DyNTS_AppIntegrationTest_Mock } from '../mock/app-integration-test.mock';
9
+
10
+ let INTEGRATION_BASE_URL: string = '';
11
+ const INTEGRATION_READY_TIMEOUT_MS: number = 10000;
12
+
13
+ describe('| DyNTS_AppExtended (integration)', (): void => {
14
+ beforeAll((): void => {
15
+ jasmine.DEFAULT_TIMEOUT_INTERVAL = INTEGRATION_READY_TIMEOUT_MS + 5000;
16
+ });
17
+
18
+ let app: DyNTS_AppIntegrationTest_Mock;
19
+ let tempDir: string = '';
20
+
21
+ beforeAll(async (): Promise<void> => {
22
+ tempDir = Fs.mkdtempSync(Path.join(Os.tmpdir(), 'dynts-int-'));
23
+ const indexContent: string = '<!DOCTYPE html><html><body>Integration test index</body></html>';
24
+ Fs.writeFileSync(Path.join(tempDir, 'index.html'), indexContent, { encoding: 'utf-8' });
25
+
26
+ DyNTS_AppIntegrationTest_Mock.integrationStaticRoot = tempDir;
27
+ app = new DyNTS_AppIntegrationTest_Mock();
28
+ await app.ready(INTEGRATION_READY_TIMEOUT_MS);
29
+
30
+ INTEGRATION_BASE_URL = `http://localhost:${app.resolvedPort}`;
31
+ });
32
+
33
+ afterAll(async (): Promise<void> => {
34
+ try {
35
+ if (app?.started) {
36
+ await app.stop();
37
+ }
38
+ } catch {
39
+ // stop() hiba (pl. socket server már leállt) ne szakítsa meg a suite-ot
40
+ }
41
+ if (tempDir && Fs.existsSync(tempDir)) {
42
+ Fs.rmSync(tempDir, { recursive: true });
43
+ }
44
+ });
45
+
46
+ it('| app elindul és started true', (): void => {
47
+ expect(app.started).toBe(true);
48
+ });
49
+
50
+ it('| GET nem létező path -> 404 és default "Page not found" HTML', async (): Promise<void> => {
51
+ const response = await Axios.get(`${INTEGRATION_BASE_URL}/nonexistent-path-xyz`, {
52
+ validateStatus: (): boolean => true,
53
+ responseType: 'text',
54
+ });
55
+ expect(response.status).toBe(404);
56
+ expect(response.headers['content-type']).toContain('html');
57
+ expect(typeof response.data).toBe('string');
58
+ expect((response.data as string).trim()).toContain('Page not found');
59
+ expect(response.data).toBe(DyNTS_defaultNotFoundPageHtml);
60
+ });
61
+
62
+ it('| GET /index.html -> 200 és static tartalom', async (): Promise<void> => {
63
+ const response = await Axios.get(`${INTEGRATION_BASE_URL}/index.html`, {
64
+ responseType: 'text',
65
+ });
66
+ expect(response.status).toBe(200);
67
+ expect(response.data).toContain('Integration test index');
68
+ });
69
+
70
+ it('| GET API base path alatt (mock route) -> 200', async (): Promise<void> => {
71
+ const response = await Axios.get(`${INTEGRATION_BASE_URL}/api/test-0/test-base`, {
72
+ responseType: 'text',
73
+ });
74
+ expect(response.status).toBe(200);
75
+ expect(response.data).toBe('test-base');
76
+ });
77
+
78
+ it('| GET API másik endpoint -> 200', async (): Promise<void> => {
79
+ const response = await Axios.get(`${INTEGRATION_BASE_URL}/api/test-0/test-simple`, {
80
+ responseType: 'text',
81
+ });
82
+ expect(response.status).toBe(200);
83
+ expect(response.data).toBe('test-simple');
84
+ });
85
+ });
@@ -12,6 +12,7 @@ import {
12
12
  DyFM_Log,
13
13
  } from '@futdevpro/fsm-dynamo';
14
14
 
15
+ import { DyNTS_defaultSocketPath } from '../../_collections/default-socket-path.const';
15
16
  import { DyNTS_global_settings } from '../../_collections/global-settings.const';
16
17
  import { DyNTS_RouteSecurity } from '../../_enums/route-security.enum';
17
18
  import {
@@ -112,7 +113,7 @@ import { DyNTS_SocketServerService } from './_services/socket-server.service';
112
113
  *
113
114
  * ...
114
115
  *
115
- * // Setting up Sockets
116
+ * // Setting up Sockets (port opcionális – ha nincs megadva, a HTTP/HTTPS portot használjuk, same port)
116
117
  * this.socketServices = [
117
118
  * NotificationService.getInstance(),
118
119
  * ChatService.getInstance(),
@@ -130,9 +131,12 @@ export abstract class DyNTS_AppExtended extends DyNTS_App {
130
131
 
131
132
  private socketSecurity: DyNTS_RouteSecurity;
132
133
 
133
- private httpSocketServer: Http.Server;
134
+ /** Csak akkor van értékadva, ha nincs base httpServer (pl. nincs open route) és open socket van. */
135
+ private httpSocketServer: Http.Server | undefined;
134
136
 
135
137
  /**
138
+ * Socket szolgáltatások. A port opcionális: ha nincs megadva, az extended app
139
+ * a getPortSettings() httpPort/httpsPort értékét használja (WebSocket és HTTP ugyanazon a porton).
136
140
  * @example
137
141
  * // Setting up Sockets
138
142
  * this.socketServices = [
@@ -431,19 +435,25 @@ export abstract class DyNTS_AppExtended extends DyNTS_App {
431
435
  }
432
436
 
433
437
  /**
434
- *
438
+ * Socket szerverek felállítása. By default a socket a HTTP/HTTPS porton fut (same port);
439
+ * ha a service.port nincs megadva, a getPortSettings() httpPort/httpsPort értékét használjuk.
440
+ * Ha a base app már listenel (httpServer/httpsServer), arra attacholjuk a Socket.IO-t,
441
+ * különben külön httpSocketServer-t hozunk létre és listenelünk.
442
+ * A Socket.IO path a portSettings.socketPath-ből jön (default: DyNTS_defaultSocketPath); a kliensnek ugyanazt kell használnia.
435
443
  */
436
444
  private async setupSocketServerServices(): Promise<void> {
437
445
  try {
438
446
  if (this.fnLogs) console.log('\nfn:. setupSocketServerServices');
439
- /* let httpServer: Http.Server; */
447
+
448
+ const useBaseHttpServer: boolean = !!this.httpServer;
449
+ const useBaseHttpsServer: boolean = !!this.httpsServer;
440
450
 
441
451
  if (this.socketSecurity !== DyNTS_RouteSecurity.secure) {
442
452
  if (!this.openExpress) {
443
453
  await this.initOpenExpress();
444
454
  }
445
455
 
446
- if (!this.httpSocketServer) {
456
+ if (!useBaseHttpServer && !this.httpSocketServer) {
447
457
  this.httpSocketServer = Http.createServer(this.openExpress);
448
458
  }
449
459
  }
@@ -464,8 +474,10 @@ export abstract class DyNTS_AppExtended extends DyNTS_App {
464
474
 
465
475
  errorMsg += '\n\nThe socket services setted to use secure server:';
466
476
  this.socketServices.forEach((service: DyNTS_SocketServerService<any>): void => {
467
- if (service.security != DyNTS_SocketSecurity.open) {
468
- errorMsg += `\n ${service?.name} (port: ${service.port})`;
477
+ if (service.security !== DyNTS_SocketSecurity.open) {
478
+ const effPort: number | undefined = service.port ??
479
+ this.portSettings.httpsPort;
480
+ errorMsg += `\n ${service?.name} (port: ${effPort})`;
469
481
  }
470
482
  });
471
483
 
@@ -486,49 +498,33 @@ export abstract class DyNTS_AppExtended extends DyNTS_App {
486
498
  this.socketServices,
487
499
  async (service: DyNTS_SocketServerService<any>): Promise<void> => {
488
500
  try {
489
- this.systemControlsExt.httpSocketServer.init = true;
490
-
491
- if (!service.port) {
492
- throw new Error(
493
- `PORT NOT SET: "${service.name}"`
494
- );
495
- }
496
-
497
- const existingPorts: DyNTS_SocketServerService<any>[] = this.socketServices.filter(
498
- (mod: DyNTS_SocketServerService<any>): boolean => mod.port === service.port
499
- );
501
+ const effectivePort: number | undefined = service.security === DyNTS_SocketSecurity.open
502
+ ? (service.port ?? this.portSettings.httpPort)
503
+ : (service.port ?? this.portSettings.httpsPort);
500
504
 
501
- if (
502
- 1 < existingPorts.length ||
503
- this.portSettings.httpPort === service.port ||
504
- this.portSettings.httpsPort === service.port
505
- ) {
506
- const error = new Error(
507
- `PORT DUPLICATION: "${service.port}"` +
508
- `\n same ports at: ${existingPorts.map(
509
- (sss: DyNTS_SocketServerService<any>): string => sss.name
510
- ).join(', ')}`
505
+ if (effectivePort === undefined) {
506
+ throw new Error(
507
+ `PORT NOT SET: "${service.name}". ` +
508
+ `Set port in getServiceParams() or ensure getPortSettings() returns httpPort/httpsPort.`
511
509
  );
512
- const errorStack: string[] = error.stack?.split('\n');
513
-
514
- errorStack.splice(1, 4);
515
- error.stack = errorStack.join('\n');
516
-
517
- DyFM_Log.error(`\n${error.message}`);
518
-
519
- throw error;
520
510
  }
521
511
 
522
512
  if (service.security === DyNTS_SocketSecurity.open) {
513
+ this.systemControlsExt.httpSocketServer.init = true;
514
+ const serverForOpen: Http.Server = useBaseHttpServer
515
+ ? this.httpServer
516
+ : this.httpSocketServer!;
523
517
  if (this.logSetup) console.log(
524
- `\nsocket setup (open): ${service?.name}:${service.port}`
518
+ `\nsocket setup (open): ${service?.name}:${effectivePort}` +
519
+ (useBaseHttpServer ? ' (shared with HTTP)' : '')
525
520
  );
526
521
  this.httpSocketSettingUpCount++;
527
522
  this.systemControlsExt.httpSocketServer.started = false;
528
523
 
524
+ const socketPath: string = this.portSettings.socketPath ?? DyNTS_defaultSocketPath;
529
525
  this.allSocketServers.push(
530
526
  await service.setupSocketServer(
531
- new SocketIO.Server(this.httpSocketServer),
527
+ new SocketIO.Server(serverForOpen, { path: socketPath }),
532
528
  DyNTS_SocketSecurity.open,
533
529
  (): void => {
534
530
  this.httpSocketSettingUpCount--;
@@ -536,20 +532,27 @@ export abstract class DyNTS_AppExtended extends DyNTS_App {
536
532
  if (this.httpSocketSettingUpCount === 0) {
537
533
  this.systemControlsExt.httpSocketServer.started = true;
538
534
  }
535
+ },
536
+ {
537
+ attachedToExistingServer: useBaseHttpServer,
538
+ effectivePort: effectivePort,
539
539
  }
540
540
  )
541
541
  );
542
542
 
543
543
  } else if (service.security === DyNTS_SocketSecurity.secure) {
544
+ this.systemControlsExt.httpsSocketServer.init = true;
544
545
  if (this.logSetup) console.log(
545
- `\nsocket setup (secure): ${service?.name}:${service.port}`
546
+ `\nsocket setup (secure): ${service?.name}:${effectivePort}` +
547
+ (useBaseHttpsServer ? ' (shared with HTTPS)' : '')
546
548
  );
547
549
  this.httpsSocketSettingUpCount++;
548
550
  this.systemControlsExt.httpsSocketServer.started = false;
549
551
 
552
+ const socketPathSecure: string = this.portSettings.socketPath ?? DyNTS_defaultSocketPath;
550
553
  this.allSocketServers.push(
551
554
  await service.setupSocketServer(
552
- new SocketIO.Server(this.httpsServer),
555
+ new SocketIO.Server(this.httpsServer, { path: socketPathSecure }),
553
556
  DyNTS_SocketSecurity.secure,
554
557
  (): void => {
555
558
  this.httpsSocketSettingUpCount--;
@@ -557,6 +560,10 @@ export abstract class DyNTS_AppExtended extends DyNTS_App {
557
560
  if (this.httpsSocketSettingUpCount === 0) {
558
561
  this.systemControlsExt.httpsSocketServer.started = true;
559
562
  }
563
+ },
564
+ {
565
+ attachedToExistingServer: useBaseHttpsServer,
566
+ effectivePort: effectivePort,
560
567
  }
561
568
  )
562
569
  );
@@ -1327,7 +1327,7 @@ export class DyNTS_DataService<T extends DyFM_Metadata> {
1327
1327
  ...this._getDefaultErrorSettings(
1328
1328
  'validateByPropertyParams',
1329
1329
  new Error(
1330
- 'Validation failed! (multiple validation errors)' +
1330
+ `Validation failed! (multiple validation errors for ${this.dataParams.dataName})` +
1331
1331
  validationErrors.map((error: DyFM_Error) => `\n ${DyFM_Error.getAnyMessage(error)}`).join('')
1332
1332
  )
1333
1333
  ),
@@ -1367,10 +1367,7 @@ export class DyNTS_DataService<T extends DyFM_Metadata> {
1367
1367
  ): void {
1368
1368
  // basic required validations
1369
1369
  if (
1370
- (
1371
- propertyParams.required ||
1372
- propertyParams.index
1373
- ) && (
1370
+ propertyParams.required && (
1374
1371
  propertyValue === null ||
1375
1372
  propertyValue === undefined
1376
1373
  )
@@ -1380,7 +1377,7 @@ export class DyNTS_DataService<T extends DyFM_Metadata> {
1380
1377
  'validateProperty',
1381
1378
  new Error(
1382
1379
  `validateProperty failed, "${this.getPropertyKey(propertyParams, isSubObjectOf)}" is missing! ` +
1383
- `(index or required in "${this.dataParams.dataName}" dataParams) ` +
1380
+ `(required in "${this.dataParams.dataName}" dataParams) ` +
1384
1381
  ((data as any)?.name ? `\n The failed data's name: ${(data as any).name}` : '')
1385
1382
  )
1386
1383
  ),
@@ -1391,7 +1388,7 @@ export class DyNTS_DataService<T extends DyFM_Metadata> {
1391
1388
  validationErrors: {
1392
1389
  [propertyParams.key]: {
1393
1390
  message: `validateProperty failed, "${this.getPropertyKey(propertyParams, isSubObjectOf)}" is missing! ` +
1394
- `(index or required in "${this.dataParams.dataName}" dataParams) ` +
1391
+ `(required in "${this.dataParams.dataName}" dataParams) ` +
1395
1392
  ((data as any)?.name ? `\n The failed data's name: ${(data as any).name}` : ''),
1396
1393
  code: `${this.ecBase}DyNTS-DS0-VP1`,
1397
1394
  userMessage: 'The property is required and must be a valid value!',
@@ -1495,11 +1495,17 @@ export class DyNTS_DBService<T extends DyFM_Metadata> {
1495
1495
  }
1496
1496
 
1497
1497
  if (!properties._deletedBy) {
1498
- properties._deletedBy = {
1498
+ properties._deletedBy = {
1499
1499
  key: '_deletedBy', type: DyFM_BasicProperty_Type.string,
1500
1500
  };
1501
1501
  }
1502
1502
 
1503
+ if (!properties._archived) {
1504
+ properties._archived = {
1505
+ key: '_archived', type: DyFM_BasicProperty_Type.date,
1506
+ };
1507
+ }
1508
+
1503
1509
  return properties;
1504
1510
  }
1505
1511
 
@@ -1521,10 +1527,12 @@ export class DyNTS_DBService<T extends DyFM_Metadata> {
1521
1527
  }
1522
1528
 
1523
1529
  if (!properties._archived) {
1524
- properties._archived = {
1530
+ properties._archived = {
1525
1531
  key: '_archived', type: DyFM_BasicProperty_Type.date,
1526
- required: true,
1532
+ required: true,
1527
1533
  };
1534
+ } else if (properties._archived.required === undefined) {
1535
+ properties._archived.required = true;
1528
1536
  }
1529
1537
 
1530
1538
  return properties;
@@ -1,6 +1,7 @@
1
1
 
2
2
  import { DyFM_Error, DyFM_HttpCallType } from '@futdevpro/fsm-dynamo';
3
3
 
4
+ import { DyNTS_global_settings } from '../../_collections/global-settings.const';
4
5
  import { DyNTS_RouteSecurity } from '../../_enums/route-security.enum';
5
6
  import { DyNTS_Endpoint_Params } from '../../_models/control-models/endpoint-params.control-model';
6
7
  import {
@@ -80,4 +81,18 @@ describe('| DyNTS_RoutingModule', () => {
80
81
  routingModule.endpoints = [ mockEndpoint, mockEndpoint ];
81
82
  expect(() => routingModule['mountRoutes']()).toThrowError(/ENDPOINT DUPLICATION/);
82
83
  });
84
+
85
+ it('| should use DyNTS_global_settings.baseUrl as route prefix', () => {
86
+ const originalBaseUrl: string = DyNTS_global_settings.baseUrl;
87
+ try {
88
+ DyNTS_global_settings.baseUrl = '/custom-api';
89
+ const moduleWithCustomBase: DyNTS_RoutingModule = new DyNTS_RoutingModule({
90
+ route: '/foo',
91
+ controllers: [ mockController ],
92
+ });
93
+ expect(moduleWithCustomBase.route).toBe('/custom-api/foo');
94
+ } finally {
95
+ DyNTS_global_settings.baseUrl = originalBaseUrl;
96
+ }
97
+ });
83
98
  });