iosignal 3.3.0 → 4.5.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.
Files changed (82) hide show
  1. package/.vscode/settings.json +3 -0
  2. package/README.md +38 -559
  3. package/dist/io.d.ts +16 -9
  4. package/dist/io.js +5 -5
  5. package/dist/io.js.map +1 -1
  6. package/dist/io.min.js +5 -5
  7. package/dist/io.min.js.map +1 -1
  8. package/dist/iosignal.js +339 -268
  9. package/dist/types/client/IOCore.d.ts +17 -10
  10. package/dist/types/common/constants.d.ts +27 -37
  11. package/index.js +4 -3
  12. package/package.json +3 -4
  13. package/src/auth/BohoAuth.js +48 -11
  14. package/src/auth/{Auth_File.js → key_providers/FileKeyProvider.js} +8 -20
  15. package/src/auth/{Auth_Redis.js → key_providers/RedisKeyProvider.js} +4 -7
  16. package/src/auth/key_providers/StringKeyProvider.js +54 -0
  17. package/src/client/IOCongSocket.js +5 -3
  18. package/src/client/IOCore.js +118 -89
  19. package/src/client/IOWS.js +5 -3
  20. package/src/client/browser/IOWebSocket.js +5 -3
  21. package/src/common/constants.js +31 -44
  22. package/src/common/util.js +3 -0
  23. package/src/server/Manager.js +13 -12
  24. package/src/server/Remote.js +4 -1
  25. package/src/server/RemoteCore.js +23 -17
  26. package/src/server/Server.js +7 -7
  27. package/README.ko.md +0 -566
  28. package/auth_file.js +0 -23
  29. package/auth_file.json +0 -13
  30. package/docs/iosignal_architecture.png +0 -0
  31. package/examples/react-chat-js/dist/assets/index-Cy4qaVcJ.js +0 -56
  32. package/examples/react-chat-js/dist/assets/index-NETrjYhz.css +0 -1
  33. package/examples/react-chat-js/dist/assets/index-e2KOTYW8.js +0 -65
  34. package/examples/react-chat-js/dist/index.html +0 -14
  35. package/examples/react-chat-js/index.html +0 -13
  36. package/examples/react-chat-js/package-lock.json +0 -1723
  37. package/examples/react-chat-js/package.json +0 -20
  38. package/examples/react-chat-js/src/App.css +0 -61
  39. package/examples/react-chat-js/src/App.jsx +0 -125
  40. package/examples/react-chat-js/src/index.css +0 -13
  41. package/examples/react-chat-js/src/main.jsx +0 -13
  42. package/examples/react-chat-js/src/shared_io.js +0 -12
  43. package/examples/react-chat-js/vite.config.js +0 -7
  44. package/examples/server/index.js +0 -65
  45. package/examples/server/package-lock.json +0 -878
  46. package/examples/server/package.json +0 -13
  47. package/examples/server/public/index.html +0 -26
  48. package/examples/svelte-chat-js/.devtools +0 -3
  49. package/examples/svelte-chat-js/README.md +0 -38
  50. package/examples/svelte-chat-js/package-lock.json +0 -1421
  51. package/examples/svelte-chat-js/package.json +0 -23
  52. package/examples/svelte-chat-js/src/app.css +0 -105
  53. package/examples/svelte-chat-js/src/app.d.ts +0 -13
  54. package/examples/svelte-chat-js/src/app.html +0 -13
  55. package/examples/svelte-chat-js/src/lib/images/github.svg +0 -1
  56. package/examples/svelte-chat-js/src/lib/images/svelte-logo.svg +0 -1
  57. package/examples/svelte-chat-js/src/lib/images/svelte-welcome.png +0 -0
  58. package/examples/svelte-chat-js/src/lib/images/svelte-welcome.webp +0 -0
  59. package/examples/svelte-chat-js/src/lib/shared_io.js +0 -12
  60. package/examples/svelte-chat-js/src/routes/+layout.svelte +0 -5
  61. package/examples/svelte-chat-js/src/routes/+page.js +0 -3
  62. package/examples/svelte-chat-js/src/routes/+page.svelte +0 -167
  63. package/examples/svelte-chat-js/static/favicon.svg +0 -1
  64. package/examples/svelte-chat-js/static/global.css +0 -19
  65. package/examples/svelte-chat-js/static/robots.txt +0 -3
  66. package/examples/svelte-chat-js/svelte.config.js +0 -23
  67. package/examples/svelte-chat-js/vite.config.js +0 -6
  68. package/src/auth/Auth_Env.js +0 -76
  69. package/test-browser/favicon.ico +0 -0
  70. package/test-browser/index.html +0 -95
  71. package/test-browser/server.js +0 -12
  72. package/test-nodejs/client-playToggle.js +0 -19
  73. package/test-nodejs/client.js +0 -17
  74. package/test-nodejs/client_api_reply.js +0 -15
  75. package/test-nodejs/server-Auth_Env.js +0 -19
  76. package/test-nodejs/server-Auth_File.js +0 -12
  77. package/test-nodejs/server-api_reply.js +0 -6
  78. package/test-nodejs/server-api_sudo.js +0 -7
  79. package/test-nodejs/server.js +0 -20
  80. package/test-nodejs/signalPack.js +0 -34
  81. package/test-nodejs/signaling.js +0 -61
  82. package/test-nodejs/simple-server.js +0 -13
package/dist/iosignal.js CHANGED
@@ -9,11 +9,11 @@ import require$$7 from 'url';
9
9
  import require$$0 from 'zlib';
10
10
  import fs, { readFileSync } from 'fs';
11
11
  import path from 'path';
12
- import require$$2 from 'os';
12
+ import require$$2, { networkInterfaces } from 'os';
13
13
  import require$$0$1 from 'buffer';
14
14
  import { memoryUsage } from 'process';
15
15
 
16
- var version$1 = "3.3.0";
16
+ var version$1 = "4.5.0";
17
17
  var pkg = {
18
18
  version: version$1};
19
19
 
@@ -381,53 +381,41 @@ var eventemitter3Exports = requireEventemitter3();
381
381
  var EventEmitter = /*@__PURE__*/getDefaultExportFromCjs(eventemitter3Exports);
382
382
 
383
383
  /**
384
- * @typedef {object} STATES
385
- * @property {number} OPENING
384
+ * @typedef {object} STATE
385
+ * @property {number} CONNECTING
386
386
  * @property {number} OPEN
387
- * @property {number} CLOSING
388
- * @property {number} CLOSED
389
387
  * @property {number} SERVER_READY
390
- * @property {number} AUTH_FAIL
391
- * @property {number} AUTH_READY
388
+ * @property {number} AUTH_ACK
389
+ * @property {number} CLOSING
392
390
  * @property {number} READY
391
+ * @property {number} AUTH_FAIL
392
+ * @property {number} AUTH_CLEAR
393
+ * @property {number} CLOSING
394
+ * @property {number} CLOSED
395
+ * @property {number} STOP
393
396
  * @property {number} REDIRECTING
394
397
  */
395
- // Client STATES: name and number
396
- const STATES = {
397
- OPENING: 0,
398
- OPEN: 1,
399
- CLOSING: 2,
400
- CLOSED: 3,
401
- SERVER_READY: 4,
402
- AUTH_FAIL: 5,
403
- AUTH_READY: 6,
404
- READY: 7,
405
- REDIRECTING: 8
398
+ // Client STATE: name and number
399
+ const STATE = {
400
+ CONNECTING: 0,
401
+ OPEN: 1,
402
+ SERVER_READY: 10,
403
+ AUTH_REQ: 11,
404
+ AUTH_NONCE: 12,
405
+ AUTH_HMAC: 13,
406
+ AUTH_ACK: 14,
407
+ AUTH_FAIL: 15,
408
+ AUTH_CLEAR: 16,
409
+ CID_REQ: 17,
410
+ CID_RES: 18,
411
+ READY: 19,
412
+ CLOSING: 2,
413
+ CLOSED: 3,
414
+ STOP: 4,
415
+ REDIRECTING: 5
406
416
  };
407
- for (let c in STATES) { STATES[STATES[c]] = c; }
417
+ for (let c in STATE) { STATE[STATE[c]] = c; }
408
418
 
409
- /**
410
- * @typedef {object} CLIENT_STATE
411
- * @property {number} INIT
412
- * @property {number} SENT_SERVER_READY
413
- * @property {number} RECV_AUTH_REQ
414
- * @property {number} SENT_SERVER_NONCE
415
- * @property {number} RECV_AUTH_HMAC
416
- * @property {number} AUTH_FAIL
417
- * @property {number} AUTH_READY
418
- * @property {number} CID_READY
419
- */
420
- const CLIENT_STATE = {
421
- INIT: 0,
422
- SENT_SERVER_READY: 1,
423
- RECV_AUTH_REQ: 2,
424
- SENT_SERVER_NONCE: 3,
425
- RECV_AUTH_HMAC: 4,
426
- AUTH_FAIL: 5,
427
- AUTH_READY: 6,
428
- CID_READY: 7
429
- };
430
- for (let c in CLIENT_STATE) { CLIENT_STATE[CLIENT_STATE[c]] = c; }
431
419
 
432
420
  /**
433
421
  * @typedef {object} ENC_MODE
@@ -456,7 +444,7 @@ for (let c in ENC_MODE) { ENC_MODE[ENC_MODE[c]] = c; }
456
444
  const SIZE_LIMIT = {
457
445
  TAG_LEN1: 255,
458
446
  TAG_LEN2: 65535,
459
- CONNECTION_CHECKER_PERIOD: 3000,
447
+ CONNECTION_CHECKER_PERIOD: 30000,
460
448
  PROMISE_TIMEOUT: 5000,
461
449
  DID: 8,
462
450
  CID: 12
@@ -488,7 +476,7 @@ for (let c in PAYLOAD_TYPE) { PAYLOAD_TYPE[PAYLOAD_TYPE[c]] = c; }
488
476
  * @property {number} CID_REQ
489
477
  * @property {number} CID_RES
490
478
  * @property {number} QUOTA_LEVEL
491
- * @property {number} SERVER_CLEAR_AUTH
479
+ * @property {number} AUTH_CLEAR
492
480
  * @property {number} SERVER_REDIRECT
493
481
  * @property {number} LOOP
494
482
  * @property {number} ECHO
@@ -546,9 +534,8 @@ let IOMsg = {
546
534
  CID_REQ: 0xC1,
547
535
  CID_RES: 0xC2,
548
536
  QUOTA_LEVEL: 0xC3,
549
- SERVER_CLEAR_AUTH: 0xC4,
537
+ AUTH_CLEAR: 0xC4,
550
538
  SERVER_REDIRECT: 0xC5,
551
-
552
539
  // ..
553
540
  LOOP: 0xCB,
554
541
  ECHO: 0xCC,
@@ -789,7 +776,7 @@ const t=new Uint32Array([1116352408,1899447441,3049323471,3921009573,961987163,1
789
776
  * @typedef {import('../common/constants.js').PAYLOAD_TYPE} PAYLOAD_TYPE
790
777
  * @typedef {import('../common/constants.js').SIZE_LIMIT} SIZE_LIMIT
791
778
  * @typedef {import('../common/constants.js').ENC_MODE} ENC_MODE
792
- * @typedef {import('../common/constants.js').STATES} STATES
779
+ * @typedef {import('../common/constants.js').STATE} STATE
793
780
  * @typedef {import('../common/quotaTable.js').quotaTable} quotaTable
794
781
 
795
782
  * @typedef {import('boho').Boho} Boho
@@ -843,7 +830,7 @@ class IOCore extends EventEmitter {
843
830
  * Current connection state (number).
844
831
  * @type {number}
845
832
  */
846
- this.state = STATES.CLOSED; // Number type
833
+ this.state = STATE.CLOSED; // Number type
847
834
  /**
848
835
  * Current connection state (string).
849
836
  * @type {string}
@@ -985,7 +972,7 @@ class IOCore extends EventEmitter {
985
972
  close() {
986
973
  if (this._closed) return;
987
974
  this._closed = true;
988
- // console.log('####### IOCOre.js close() called')
975
+ // console.log('####### IOCOre.js close() called')
989
976
  // If auto-reconnect is disabled, we must stop the keep-alive timer.
990
977
  if (this.autoReconnect === false) {
991
978
  clearInterval(this.connectionCheckerIntervalID);
@@ -1000,7 +987,7 @@ class IOCore extends EventEmitter {
1000
987
  // To avoid the error, we wait for the connection to open, then immediately close it.
1001
988
  // We also clear other handlers to prevent any other logic from running.
1002
989
  const socket = this.socket;
1003
- socket.onopen = () => { if(socket) socket.close(); };
990
+ socket.onopen = () => { if (socket) socket.close(); };
1004
991
  socket.onmessage = null;
1005
992
  socket.onerror = null;
1006
993
  socket.onclose = null;
@@ -1008,7 +995,7 @@ class IOCore extends EventEmitter {
1008
995
  try {
1009
996
  // For other sockets (like TCP) or other WebSocket states, close directly.
1010
997
  this.socket.close?.();
1011
- } catch {}
998
+ } catch { }
1012
999
  }
1013
1000
  this.socket = null;
1014
1001
  }
@@ -1016,14 +1003,15 @@ class IOCore extends EventEmitter {
1016
1003
  this.emit('closed');
1017
1004
  this.stateChange('closed');
1018
1005
  }
1019
-
1006
+
1020
1007
  /**
1021
1008
  * Disables auto-reconnect and closes the current connection.
1022
1009
  * The instance can be re-opened manually later. For complete cleanup, use destroy().
1023
- */
1024
- stop() {
1025
- this.autoReconnect = false;
1026
- this.close();
1010
+ */
1011
+ stop() {
1012
+ this.autoReconnect = false;
1013
+ this.close();
1014
+ this.stateChange('stop','stop');
1027
1015
  }
1028
1016
 
1029
1017
  /**
@@ -1039,7 +1027,7 @@ class IOCore extends EventEmitter {
1039
1027
 
1040
1028
  // Help GC
1041
1029
  this.boho = null;
1042
- }
1030
+ }
1043
1031
 
1044
1032
  /**
1045
1033
  * The core keep-alive logic. It checks if auto-reconnect is enabled.
@@ -1056,8 +1044,8 @@ class IOCore extends EventEmitter {
1056
1044
  * @param {string} url2 - The new URL to redirect to.
1057
1045
  */
1058
1046
  redirect(url2) {
1047
+ this.stateChange('redirecting','redirecting');
1059
1048
  this.close();
1060
- this.stateChange('redirecting');
1061
1049
  this.createConnection(url2);
1062
1050
  }
1063
1051
 
@@ -1114,29 +1102,27 @@ class IOCore extends EventEmitter {
1114
1102
 
1115
1103
  /**
1116
1104
  * Manually logs in with provided ID and key.
1117
- * @param {string} id - The user ID.
1105
+ * @param {string} id - The user ID. or 'id.key'
1118
1106
  * @param {string} key - The user key.
1119
- * @returns {boolean}
1107
+ * @returns {this}
1120
1108
  */
1121
1109
  login(id, key) {
1122
- if (!this.auth(id, key)) return false;
1123
-
1110
+ this.auth(id, key);
1124
1111
  this.useAuth = true;
1125
1112
  let auth_pack = this.boho.auth_req();
1126
1113
  this.send(auth_pack);
1127
- return true
1114
+ return this
1128
1115
  }
1129
1116
 
1130
1117
  /**
1131
1118
  * Sets up authentication for auto-login.
1132
- * @param {string} id - The user ID.
1119
+ * @param {string} id - The user ID. or 'id.Key'
1133
1120
  * @param {string} key - The user key.
1134
- * @returns {boolean}
1121
+ * @returns {this}
1135
1122
  */
1136
1123
  auth(id, key) {
1137
1124
  if (!id && !key) {
1138
1125
  this.emit('error', new Error('auth failed. no id and key.'));
1139
- return false
1140
1126
  }
1141
1127
 
1142
1128
  if (!key && id.includes('.')) {
@@ -1146,10 +1132,9 @@ class IOCore extends EventEmitter {
1146
1132
  this.boho.set_key(key);
1147
1133
  } else {
1148
1134
  this.emit('error', new Error('auth failed. no id or key.'));
1149
- return false
1150
1135
  }
1151
1136
  this.useAuth = true;
1152
- return true
1137
+ return this
1153
1138
  }
1154
1139
 
1155
1140
  /**
@@ -1209,13 +1194,13 @@ class IOCore extends EventEmitter {
1209
1194
  case IOMsg.ECHO:
1210
1195
  try {
1211
1196
  let str = decoder$4.decode(buffer.subarray(1));
1212
-
1197
+
1213
1198
  this.emit('echo', str);
1214
1199
  } catch (error) {
1215
1200
  this.emit('error', new Error('ECHO data error'));
1216
1201
  }
1217
1202
  break;
1218
-
1203
+
1219
1204
  case IOMsg.IAM_RES:
1220
1205
  try {
1221
1206
  let str = decoder$4.decode(buffer.subarray(1));
@@ -1224,8 +1209,7 @@ class IOCore extends EventEmitter {
1224
1209
  if (jsonInfo.nick) { this.nick = jsonInfo.nick; }
1225
1210
  if (jsonInfo.did) { this.did = jsonInfo.did; }
1226
1211
  if (jsonInfo.uid) { this.uid = jsonInfo.uid; }
1227
- // TIP. cid from [CID_RES], level from [QUOTA_LEVEL]
1228
- this.emit('iam_res', str );
1212
+ this.emit('iam_res', str);
1229
1213
  } catch (error) {
1230
1214
  this.emit('error', new Error('IAM_RES data error'));
1231
1215
  }
@@ -1237,21 +1221,21 @@ class IOCore extends EventEmitter {
1237
1221
 
1238
1222
  // **IMPORTANT** change state before subscribe.
1239
1223
  this.stateChange('ready', 'cid_ready');
1240
- this.subscribe_memory_channels();
1224
+ this.subscribe_memory_channels(); // tags registered by listen() or link().
1241
1225
  break;
1242
1226
 
1243
1227
  case IOMsg.QUOTA_LEVEL:
1244
1228
  let quotaLevel = buffer[1];
1245
1229
  this.level = quotaLevel;
1246
1230
  this.quota = quotaTable[quotaLevel];
1247
- console.log('[QUOTA_LEVEL]', JSON.stringify(this.quota));
1231
+ // console.log('[QUOTA_LEVEL]', JSON.stringify(this.quota))
1248
1232
  break;
1249
1233
 
1250
- case IOMsg.SERVER_CLEAR_AUTH:
1234
+ case IOMsg.AUTH_CLEAR:
1251
1235
  this.useAuth = false;
1252
1236
  this.boho.clearAuth();
1237
+ this.stateChange('auth_clear', 'server request auth_clear.');
1253
1238
  this.stop();
1254
- console.log('[SERVER_CLEAR_AUTH] io stop.');
1255
1239
  break;
1256
1240
 
1257
1241
  case IOMsg.SERVER_REDIRECT:
@@ -1325,8 +1309,11 @@ class IOCore extends EventEmitter {
1325
1309
  switch (payloadType) {
1326
1310
 
1327
1311
  case PAYLOAD_TYPE.EMPTY:
1328
- if (tag.indexOf('@') === 0) this.emit('@', null, tag);
1329
- else this.emit(tag, null, tag);
1312
+ if (tag.indexOf('@') === 0) this.emit('@', tag, null);
1313
+ else {
1314
+ this.emit(tag, tag, null);
1315
+ this.emit('message', tag, null);
1316
+ }
1330
1317
  break;
1331
1318
 
1332
1319
  case PAYLOAD_TYPE.TEXT:
@@ -1337,33 +1324,48 @@ class IOCore extends EventEmitter {
1337
1324
  payloadStringWithoutNull = payloadBuffer.subarray(0, payloadBuffer.byteLength - 1);
1338
1325
  }
1339
1326
  let oneString = decoder$4.decode(payloadStringWithoutNull);
1340
- if (tag.indexOf('@') === 0) this.emit('@', oneString, tag);
1341
- if (tag !== '@') this.emit(tag, oneString, tag);
1327
+ if (tag.indexOf('@') === 0) this.emit('@', tag, oneString);
1328
+ if (tag !== '@') {
1329
+ this.emit(tag, tag, oneString);
1330
+ this.emit('message', tag, oneString);
1331
+ }
1342
1332
  break;
1343
1333
 
1344
1334
  case PAYLOAD_TYPE.BINARY:
1345
- if (tag.indexOf('@') === 0) this.emit('@', payloadBuffer, tag);
1346
- if (tag !== '@') this.emit(tag, payloadBuffer, tag);
1335
+ if (tag.indexOf('@') === 0) this.emit('@', tag, payloadBuffer);
1336
+ if (tag !== '@') {
1337
+ this.emit(tag, tag, payloadBuffer);
1338
+ this.emit('message', tag, payloadBuffer);
1339
+ }
1347
1340
  break;
1348
1341
 
1349
1342
  case PAYLOAD_TYPE.OBJECT:
1350
1343
  let oneObjectBuffer = decoder$4.decode(payloadBuffer);
1351
1344
  let oneJSONObject = JSON.parse(oneObjectBuffer);
1352
- if (tag.indexOf('@') === 0) this.emit('@', oneJSONObject, tag);
1353
- if (tag !== '@') this.emit(tag, oneJSONObject, tag);
1345
+ if (tag.indexOf('@') === 0) this.emit('@', tag, oneJSONObject);
1346
+ if (tag !== '@') {
1347
+ this.emit(tag, tag, oneJSONObject);
1348
+ this.emit('message', tag, oneJSONObject);
1349
+ }
1354
1350
  break;
1355
1351
 
1356
1352
  case PAYLOAD_TYPE.MJSON:
1357
1353
  let mjsonBuffer = decoder$4.decode(payloadBuffer);
1358
1354
  let mjson = JSON.parse(mjsonBuffer);
1359
- if (tag.indexOf('@') === 0) this.emit('@', ...mjson, tag);
1360
- if (tag !== '@') this.emit(tag, ...mjson, tag);
1355
+ if (tag.indexOf('@') === 0) this.emit('@', tag, ...mjson);
1356
+ if (tag !== '@') {
1357
+ this.emit(tag, tag, ...mjson);
1358
+ this.emit('message', tag, ...mjson);
1359
+ }
1361
1360
  break;
1362
1361
 
1363
1362
  case PAYLOAD_TYPE.MBA:
1364
1363
  let mbaObject = M$1.unpack(payloadBuffer);
1365
- if (tag.indexOf('@') === 0) this.emit('@', ...mbaObject.args, tag);
1366
- if (tag !== '@') this.emit(tag, ...mbaObject.args, tag);
1364
+ if (tag.indexOf('@') === 0) this.emit('@', tag, ...mbaObject.args);
1365
+ if (tag !== '@') {
1366
+ this.emit(tag, tag, ...mbaObject.args);
1367
+ this.emit('message', tag, ...mbaObject.args);
1368
+ }
1367
1369
  break;
1368
1370
 
1369
1371
  default:
@@ -1371,7 +1373,8 @@ class IOCore extends EventEmitter {
1371
1373
  }
1372
1374
 
1373
1375
  } catch (err) {
1374
- this.emit('error', new Error('signal parse err'));
1376
+ // this.emit('error', new Error('IOCore IOMsg.SIGNAL parser err', err))
1377
+ console.log('IOCore IOMsg.SIGNAL parser err', err);
1375
1378
  }
1376
1379
  break;
1377
1380
 
@@ -1380,24 +1383,25 @@ class IOCore extends EventEmitter {
1380
1383
  break;
1381
1384
 
1382
1385
  case tt.BohoMsg.AUTH_NONCE:
1386
+ this.stateChange('auth_nonce','server sent auth_nonce.');
1383
1387
  let auth_hmac = this.boho.auth_hmac(buffer);
1384
1388
  if (auth_hmac) {
1385
1389
  this.send(auth_hmac);
1386
1390
  } else {
1387
- this.stateChange('auth_fail', 'Invalid local auth_hmac.');
1391
+ this.stateChange('auth_fail', 'boho.auth_hmac() invalid auth_nonce.');
1388
1392
  }
1389
1393
  break;
1390
1394
 
1391
1395
  case tt.BohoMsg.AUTH_FAIL:
1392
- this.stateChange('auth_fail', 'server reject auth.');
1396
+ this.stateChange('auth_fail', 'auth_fail from server.');
1393
1397
  break;
1394
1398
 
1395
1399
  case tt.BohoMsg.AUTH_ACK:
1396
1400
  if (this.boho.check_auth_ack_hmac(buffer)) {
1397
- this.stateChange('auth_ready', 'server sent auth_ack');
1401
+ this.stateChange('auth_ack', 'server sent auth_ack');
1398
1402
  this.send(Buffer$1.from([IOMsg.CID_REQ]));
1399
1403
  } else {
1400
- this.stateChange('auth_fail', 'invalid server_hmac');
1404
+ this.stateChange('auth_fail', 'check_auth_ack_hmac() invalid server_hmac');
1401
1405
  }
1402
1406
  break;
1403
1407
 
@@ -1584,14 +1588,15 @@ class IOCore extends EventEmitter {
1584
1588
 
1585
1589
 
1586
1590
  /**
1587
- * Publishes a signal.
1591
+ * alias of signal()
1592
+ * Sends a signal with a tag and arguments.
1593
+ * @param {string} tag - The signal tag.
1588
1594
  * @param {...any} args - Arguments for the signal.
1589
1595
  */
1590
- publish(...args) {
1591
- this.signal(...args);
1596
+ publish(tag, ...args) {
1597
+ this.signal(tag, ...args);
1592
1598
  }
1593
1599
 
1594
-
1595
1600
  /**
1596
1601
  * Sends a signal with a tag and arguments.
1597
1602
  * @param {string} tag - The signal tag.
@@ -1600,7 +1605,6 @@ class IOCore extends EventEmitter {
1600
1605
  */
1601
1606
  signal(tag, ...args) {
1602
1607
  if (typeof tag !== 'string') throw TypeError('tag should be string.')
1603
-
1604
1608
  let signalPack = getSignalPack(tag, ...args);
1605
1609
  this.send_enc_mode(signalPack);
1606
1610
  }
@@ -1644,7 +1648,6 @@ class IOCore extends EventEmitter {
1644
1648
  }
1645
1649
 
1646
1650
 
1647
-
1648
1651
  /**
1649
1652
  * Sets a value in the store.
1650
1653
  * @param {string} storeName - The name of the store.
@@ -1712,21 +1715,24 @@ class IOCore extends EventEmitter {
1712
1715
  */
1713
1716
  subscribe(tag) {
1714
1717
  if (typeof tag !== 'string') throw TypeError('tag should be string.')
1715
- if (this.state !== STATES.READY) return
1718
+ if (tag.length > SIZE_LIMIT.TAG_LEN1) throw TypeError('please check tag string length limit:' + SIZE_LIMIT.TAG_LEN1)
1719
+ if (this.state !== STATE.READY) return
1716
1720
 
1717
- let tagList = tag.split(',');
1718
- tagList.forEach(tag => {
1719
- this.channels.add(tag);
1720
- });
1721
+ // subscribe 사용시, 사용 'ready' 이벤트시 메뉴얼 등록되므로 여기에 등록하면 2중 호출된다.
1722
+ // 즉, channels 등록은 listen 같은 자동화 구독시만 사용.
1723
+ // let tagList = tag.split(',')
1724
+ // tagList.forEach(tag => {
1725
+ // this.channels.add(tag)
1726
+ // })
1721
1727
 
1722
- let tagEncoded = encoder$1.encode(tag);
1723
- if (tagEncoded.byteLength > SIZE_LIMIT.TAG_LEN1) throw TypeError('please use tag string bytelength below:' + SIZE_LIMIT.TAG_LEN1)
1724
-
1725
- this.send_enc_mode(
1726
- Buffer$1.concat([
1727
- M$1.NB('8', IOMsg.SUBSCRIBE),
1728
- M$1.NB('8', tagEncoded.byteLength),
1729
- tagEncoded]));
1728
+ try {
1729
+ let tagEncoded = encoder$1.encode(tag);
1730
+ this.send_enc_mode(
1731
+ Buffer$1.concat([
1732
+ M$1.NB('8', IOMsg.SUBSCRIBE),
1733
+ M$1.NB('8', tagEncoded.byteLength),
1734
+ tagEncoded]));
1735
+ } catch (error) { }
1730
1736
  }
1731
1737
 
1732
1738
  /**
@@ -1737,20 +1743,22 @@ class IOCore extends EventEmitter {
1737
1743
  */
1738
1744
  subscribe_promise(tag) {
1739
1745
  if (typeof tag !== 'string') throw TypeError('tag should be string.')
1740
- if (this.state !== STATES.READY) {
1746
+ if (tag.length > SIZE_LIMIT.TAG_LEN2) throw TypeError('please check tag string length limit:' + SIZE_LIMIT.TAG_LEN2)
1747
+
1748
+ if (this.state !== STATE.READY) {
1741
1749
  return Promise.reject('subscribe_promise:: connection is not ready')
1742
1750
  }
1743
1751
 
1744
- let tagEncoded = encoder$1.encode(tag);
1745
- if (tagEncoded.byteLength > SIZE_LIMIT.TAG_LEN2) throw TypeError('please use tag string bytelength: ' + SIZE_LIMIT.TAG_LEN2)
1746
-
1747
- this.send_enc_mode(
1748
- Buffer$1.concat([
1749
- M$1.NB('8', IOMsg.SUBSCRIBE_REQ),
1750
- M$1.NB('16', ++this.mid),
1751
- M$1.NB('16', tagEncoded.byteLength),
1752
- tagEncoded]));
1753
- return this.setMsgPromise(this.mid)
1752
+ try {
1753
+ let tagEncoded = encoder$1.encode(tag);
1754
+ this.send_enc_mode(
1755
+ Buffer$1.concat([
1756
+ M$1.NB('8', IOMsg.SUBSCRIBE_REQ),
1757
+ M$1.NB('16', ++this.mid),
1758
+ M$1.NB('16', tagEncoded.byteLength),
1759
+ tagEncoded]));
1760
+ return this.setMsgPromise(this.mid)
1761
+ } catch (error) { }
1754
1762
  }
1755
1763
 
1756
1764
  /**
@@ -1812,7 +1820,7 @@ class IOCore extends EventEmitter {
1812
1820
  }
1813
1821
  this.on(tag, handler);
1814
1822
  // do not subscribe now.
1815
- // will subscribe when receive CID_RES signal from server.
1823
+ // will subscribe when io state is 'ready'. (receive CID_RES from server)
1816
1824
 
1817
1825
  }
1818
1826
 
@@ -1923,10 +1931,10 @@ class IOCore extends EventEmitter {
1923
1931
  */
1924
1932
  getStateName() {
1925
1933
  //state <number>
1926
- //value of constant STATES.NAME < number >
1927
- //type of constant STATES.NAME name < string uppercase >
1934
+ //value of constant STATE.NAME < number >
1935
+ //type of constant STATE.NAME name < string uppercase >
1928
1936
  //stateName,eventName <string lowercase>
1929
- return (STATES[this.state]).toLowerCase()
1937
+ return (STATE[this.state]).toLowerCase()
1930
1938
  }
1931
1939
 
1932
1940
  /**
@@ -1947,15 +1955,23 @@ class IOCore extends EventEmitter {
1947
1955
  * Changes the connection state and emits events.
1948
1956
  * @param {string} state - The new state name (e.g., 'ready', 'closed').
1949
1957
  * @param {string} [emitEventAndMessage] - Optional message to emit with the state change event.
1958
+ *
1959
+ * 주의.
1960
+ * 1. 상태가 변경 될 때만 'change' 이벤트 호출된다.
1961
+ * 2. emitEventAndMessage 옵션 값이 지정되야 해당 이벤트 이름이 호출된다.
1962
+ * 보통 이벤트 이름과 동일하게 적거나 이벤트 상황 안내문을 넣는다.
1950
1963
  */
1951
1964
  stateChange(state, emitEventAndMessage) {
1952
- // STATES constant name : string upperCase
1953
- // eventName, .stateName : string lowerCase
1954
- // .state : number
1965
+ // STATE constant name <string> upperCase
1966
+ // eventName and .stateName <string> lowerCase
1967
+ // .state <number>
1955
1968
  // console.log('### stateChange reason:', emitEventAndMessage )
1956
1969
  let eventName = state.toLowerCase();
1957
- this.state = STATES[state.toUpperCase()]; // state: number
1958
- if (emitEventAndMessage) this.emit(eventName, emitEventAndMessage);
1970
+ this.state = STATE[state.toUpperCase()]; // state: number
1971
+
1972
+ if (emitEventAndMessage) {
1973
+ this.emit(eventName, emitEventAndMessage);
1974
+ }
1959
1975
 
1960
1976
  if (this.stateName !== eventName) {
1961
1977
  this.stateName = eventName;
@@ -2133,8 +2149,10 @@ class IOCongSocket extends IOCore {
2133
2149
  keepAlive() {
2134
2150
  if (!this.autoReconnect) return;
2135
2151
  // Reconnect only if the socket is fully destroyed and the state is closed.
2136
- if ((!this.socket || this.socket.destroyed) && this.state === STATES.CLOSED) {
2152
+ if ((!this.socket || this.socket.destroyed)) {
2137
2153
  this.open();
2154
+ }else {
2155
+ this.ping();
2138
2156
  }
2139
2157
  }
2140
2158
 
@@ -2146,7 +2164,7 @@ class IOCongSocket extends IOCore {
2146
2164
  urlObj = new URL('cong://' + url);
2147
2165
  }
2148
2166
  this.socket = net.createConnection(urlObj.port, urlObj.hostname);
2149
- this.stateChange('opening');
2167
+ this.stateChange('connecting','connecting');
2150
2168
 
2151
2169
  this.socket.on('connect', () => {
2152
2170
  this.congRx = new CongRx();
@@ -7560,15 +7578,17 @@ class IOWS extends IOCore {
7560
7578
  keepAlive() {
7561
7579
  if (!this.autoReconnect) return;
7562
7580
  // Reconnect only if the socket is closed and the state reflects that.
7563
- if ((!this.socket || this.socket.readyState === WebSocket.CLOSED) && this.state === STATES.CLOSED) {
7581
+ if ((!this.socket || this.socket.readyState === WebSocket.CLOSED) ) {
7564
7582
  this.open();
7583
+ }else {
7584
+ this.ping();
7565
7585
  }
7566
7586
  }
7567
7587
 
7568
7588
  createConnection(url) {
7569
7589
  // node WebSocket
7570
7590
  this.socket = new WebSocket(url);
7571
- this.stateChange('opening');
7591
+ this.stateChange('connecting','connecting');
7572
7592
 
7573
7593
  this.socket.onopen = () => {
7574
7594
  this.socket.on('message', this.onWebSocketMessage.bind(this));
@@ -7694,7 +7714,7 @@ class RemoteCore {
7694
7714
 
7695
7715
  this.state;
7696
7716
  this.stateLog = [];
7697
- this.setState(CLIENT_STATE.INIT);
7717
+ this.setState(STATE.OPEN);
7698
7718
 
7699
7719
  }
7700
7720
 
@@ -7703,7 +7723,7 @@ class RemoteCore {
7703
7723
  setState(state) {
7704
7724
  this.state = state;
7705
7725
  if (serverOption.debug.showAuthInfo) {
7706
- // let s = state + ":" + CLIENT_STATE[ state]
7726
+ state = state + ":" + STATE[ state];
7707
7727
  this.stateLog.push( state );
7708
7728
  console.log(this.stateLog.join('>'));
7709
7729
  }
@@ -7714,24 +7734,30 @@ class RemoteCore {
7714
7734
  }
7715
7735
 
7716
7736
  getStateName() { // <String>
7717
- return (CLIENT_STATE[this.state]).toLowerCase()
7737
+ return (STATE[this.state]).toLowerCase()
7718
7738
  }
7719
7739
 
7720
7740
 
7721
7741
  showMessageLog(message, isBinary) {
7722
- let from = this.boho.isAuthorized ? `[FROM]did: ${this.did} cid:${this.cid}` : `[FROM]cid:${this.cid}`;
7742
+ let h = '->S ';
7743
+ let from = this.boho.isAuthorized ? ` did: ${this.did} cid: ${this.cid}` : ` cid: ${this.cid}`;
7744
+ let prnLimit = 20;
7723
7745
  if (isBinary) {
7724
7746
  let msgTypeName = IOMsg[message[0]];
7725
7747
  if (!msgTypeName) msgTypeName = tt.BohoMsg[message[0]];
7726
- msgTypeName = ' [' + msgTypeName + ']';
7727
- if (message.byteLength > 40) {
7728
- console.log(from + msgTypeName + ' LEN:', message.length);
7748
+
7749
+ msgTypeName = '[' + msgTypeName + ']';
7750
+ let size = message.byteLength;
7751
+ if (size < prnLimit) {
7752
+ console.log( h + msgTypeName + from +` [${size}]`, message);
7729
7753
  } else {
7730
- console.log(from + msgTypeName, message);
7754
+ let elseLen = size - prnLimit;
7755
+ let some = message.subarray(0,prnLimit);
7756
+ console.log( h + msgTypeName + from +` [${size}]` , some , ' ... else: ', elseLen);
7731
7757
  }
7732
7758
 
7733
7759
  } else {
7734
- console.log(from + ' [TEXT] %s', message);
7760
+ console.log( h + ' [TEXT] %s' + from, message);
7735
7761
  }
7736
7762
  }
7737
7763
 
@@ -7814,7 +7840,7 @@ class RemoteCore {
7814
7840
  break;
7815
7841
 
7816
7842
  case IOMsg.CID_REQ:
7817
- if (this.state < CLIENT_STATE.SENT_SERVER_READY) {
7843
+ if (this.state < STATE.SERVER_READY) {
7818
7844
  // Protocol violation: CID_REQ was sent before receiving the SERVER_READY signal.
7819
7845
  console.log('CID_REQ before SERVER_READY');
7820
7846
  this.close();
@@ -7831,7 +7857,7 @@ class RemoteCore {
7831
7857
  MB('#cid', this.cid)
7832
7858
  ));
7833
7859
 
7834
- this.setState(CLIENT_STATE.CID_READY);
7860
+ this.setState(STATE.CID_RES);
7835
7861
  break;
7836
7862
 
7837
7863
  case IOMsg.ECHO:
@@ -7937,24 +7963,24 @@ class RemoteCore {
7937
7963
  // Auth
7938
7964
  case tt.BohoMsg.AUTH_REQ:
7939
7965
  if (!this.manager.authManager) return
7940
- if (this.state < CLIENT_STATE.SENT_SERVER_READY) {
7966
+ if (this.state < STATE.SERVER_READY) {
7941
7967
  console.log('protocol error. auth_req before server_ready');
7942
7968
  this.close();
7943
7969
  }
7944
- this.setState(CLIENT_STATE.RECV_AUTH_REQ);
7970
+ this.setState(STATE.AUTH_REQ);
7945
7971
  let auth_nonce_pack = this.boho.auth_nonce();
7946
7972
  // console.log('## auth_nonce_pack', auth_nonce_pack )
7947
7973
  this.send(auth_nonce_pack);
7948
- this.setState(CLIENT_STATE.SENT_SERVER_NONCE);
7974
+ this.setState(STATE.AUTH_NONCE);
7949
7975
  break;
7950
7976
 
7951
7977
  case tt.BohoMsg.AUTH_HMAC:
7952
7978
  if (!this.manager.authManager) return
7953
- if (this.state < CLIENT_STATE.SENT_SERVER_NONCE) {
7979
+ if (this.state < STATE.AUTH_NONCE) {
7954
7980
  console.log('protocol error. auth_hmac before server_nonce');
7955
7981
  this.close();
7956
7982
  }
7957
- this.setState(CLIENT_STATE.RECV_AUTH_HMAC);
7983
+ this.setState(STATE.AUTH_HMAC);
7958
7984
  //async
7959
7985
  this.manager.authManager.verify_auth_hmac(message, this)
7960
7986
  .then(authInfo => {
@@ -8104,11 +8130,42 @@ function isPrivateIP(ip) {
8104
8130
  return false
8105
8131
  }
8106
8132
 
8133
+ function numberWithCommas(x) {
8134
+ return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',')
8135
+ }
8136
+
8107
8137
  function getIPv4HexString(ipStr) {
8108
8138
  let ipv4 = ipStr.split('.');
8109
8139
  return Buffer.from(ipv4).toString('hex')
8110
8140
  }
8111
8141
 
8142
+ function getLocalAddress() {
8143
+ const nets = networkInterfaces();
8144
+ // console.log(nets)
8145
+ const results = Object.create(null); // Or just '{}', an empty object
8146
+ let localAddress = '';
8147
+ for (const name of Object.keys(nets)) {
8148
+ for (const net of nets[name]) {
8149
+ // Skip over non-IPv4 and internal (i.e. 127.0.0.1) addresses
8150
+ // 'IPv4' is in Node <= 17, from 18 it's a number 4 or 6
8151
+ const familyV4Value = typeof net.family === 'string' ? 'IPv4' : 4;
8152
+ if (net.family === familyV4Value && !net.internal) {
8153
+ if (!results[name]) {
8154
+ results[name] = [];
8155
+ }
8156
+ results[name].push(net.address);
8157
+ localAddress = net.address;
8158
+ }
8159
+ }
8160
+ }
8161
+
8162
+ // console.log('localAddress:', localAddress)
8163
+ return localAddress
8164
+ }
8165
+
8166
+
8167
+ const delay = ms => new Promise(resolve => setTimeout(resolve, ms));
8168
+
8112
8169
  const decoder$2 = new TextDecoder();
8113
8170
 
8114
8171
  class Remote extends RemoteCore {
@@ -8280,6 +8337,7 @@ class Remote extends RemoteCore {
8280
8337
  }
8281
8338
  }
8282
8339
 
8340
+ // node.js server side remote.
8283
8341
  send(message, isBinary) {
8284
8342
  this.manager.txBytes += message.byteLength;
8285
8343
  this.socket.txCounter++;
@@ -8291,13 +8349,15 @@ class Remote extends RemoteCore {
8291
8349
  this.socket.send(message);
8292
8350
  }
8293
8351
  } else {
8352
+ console.log('server(ws)Remote.send() called. not open state. current readyState:', this.socket.readyState );
8294
8353
  this.close(true);
8295
8354
  }
8296
-
8355
+
8297
8356
  } else { //CongSocket
8298
8357
  if (this.socket.readyState == 'open') {
8299
8358
  this.socket.write(pack(message));
8300
8359
  } else {
8360
+ console.log('server(cong)Remote.send() called. not open state. current readyState:', this.socket.readyState );
8301
8361
  this.close(true);
8302
8362
  }
8303
8363
  }
@@ -8553,7 +8613,7 @@ class Manager {
8553
8613
  let remote = new Remote(socket, req, this);
8554
8614
  this.remotes.add(remote);
8555
8615
  remote.send(Buffer.from([IOMsg.SERVER_READY]));
8556
- remote.setState(CLIENT_STATE.SENT_SERVER_READY);
8616
+ remote.setState(STATE.SERVER_READY);
8557
8617
  this.lastSSID = remote.ssid;
8558
8618
  let connectionInfo = `+ IP:${remote.ip} #${remote.ssid} ${socket.socketType === 'websocket' ? "WS" : "CS"} `;
8559
8619
  // console.log( connectionInfo)
@@ -8655,8 +8715,8 @@ class Manager {
8655
8715
  serverSignalTo(tag, ...args) {
8656
8716
  let cid = tag.split('@')[0];
8657
8717
  let topic = tag.split('@')[1];
8658
- let sigPack = getSignalPack('@' + topic, ...args);
8659
8718
  if (cid && this.cid2remote.has(cid)) {
8719
+ let sigPack = getSignalPack('@' + topic, ...args);
8660
8720
  let targetRemote = this.cid2remote.get(cid);
8661
8721
  console.log('target', targetRemote.state, targetRemote.cid);
8662
8722
  targetRemote.send_enc_mode(sigPack);
@@ -8668,8 +8728,8 @@ class Manager {
8668
8728
 
8669
8729
  sender(tag, remote, message) {
8670
8730
  if (serverOption.membersOnly && !remote.boho.isAuthorized) {
8671
- console.log("[membersOnly] unAuthorized remote.",tag );
8672
- remote.send(Buffer.from([IOMsg.SERVER_CLEAR_AUTH]));
8731
+ // console.log("### server reject client signal reason: [membersOnly]" ,tag, remote.cid )
8732
+ remote.send(Buffer.from([IOMsg.AUTH_CLEAR]));
8673
8733
  remote.close();
8674
8734
  return ['err', 'unAuthorized']
8675
8735
  }
@@ -8753,13 +8813,18 @@ class Manager {
8753
8813
  // console.log('## null remote' )
8754
8814
  break;
8755
8815
  }
8756
- if (c !== remote) {
8816
+ // change: from v4.x allow receiveSelfMessages.
8817
+ // if (c !== remote) {
8757
8818
  c.send_enc_mode(message);
8758
8819
  sentCounter++;
8759
- }
8820
+ // }
8760
8821
  }
8761
8822
 
8762
- if (serverOption.useQuota.publishCounter) ;
8823
+ // if (serverOption.useQuota.publishCounter) {
8824
+ // // console.log(`pub >> [${tag}] sent: ${sentCounter} [use quota limit: ${remote.quota.publishCounter }] total: ${remotes.size} subscribers. ` )
8825
+ // } else {
8826
+ // // console.log(`pub >> [${tag}] sent: ${sentCounter} [no quota limit] total: ${remotes.size} subscribers. ` )
8827
+ // }
8763
8828
  return ['ok', sentCounter]
8764
8829
  }
8765
8830
  }
@@ -8992,7 +9057,7 @@ class Server extends require$$0$3 {
8992
9057
 
8993
9058
  this.wss.once('listening', () => {
8994
9059
  this.port = this.wss.address().port;
8995
- console.log('wss server bound port:', this.port );
9060
+ // console.log('wss server bound port:', this.port );
8996
9061
  this.listeningServerCount++;
8997
9062
  if (this.listeningServerCount === this.serverCountToListen) {
8998
9063
  this.emit('ready');
@@ -9008,7 +9073,7 @@ class Server extends require$$0$3 {
9008
9073
  });
9009
9074
 
9010
9075
  this.wss.on('close', () => {
9011
- console.log('### WS server is closed.');
9076
+ // console.log('### WS server is closed.')
9012
9077
  });
9013
9078
 
9014
9079
  this.wss.on('connection', (ws, req) => {
@@ -9029,10 +9094,10 @@ class Server extends require$$0$3 {
9029
9094
  process.exit();
9030
9095
  }
9031
9096
  }).on('close', () => {
9032
- console.log('### congsocket server is closed.');
9097
+ // console.log('### congsocket server is closed.')
9033
9098
  }).listen(this.congPort, () => {
9034
9099
  this.congPort = this.congServer.address().port;
9035
- console.log('congsocket server bound port:', this.congPort );
9100
+ // console.log('congsocket server bound port:', this.congPort );
9036
9101
  this.listeningServerCount++;
9037
9102
  if (this.listeningServerCount === this.serverCountToListen) {
9038
9103
  this.emit('ready');
@@ -9067,7 +9132,7 @@ class Server extends require$$0$3 {
9067
9132
  return
9068
9133
  }
9069
9134
  if( api.commands.includes( req.topic )){
9070
- console.log('server api req', req );
9135
+ // console.log('server api req', req )
9071
9136
  await api.request(remote, req);
9072
9137
  }else {
9073
9138
  remote.response(req.mid, STATUS.ERROR, "UNKNOWN_COMMAND");
@@ -9097,7 +9162,7 @@ class Server extends require$$0$3 {
9097
9162
  });
9098
9163
  }
9099
9164
  this.apiNames.add(target);
9100
- console.log(`[API registed: ${target} ] accept commands: ${api.commands}`);
9165
+ // console.log(`[API registed: ${target} ] accept commands: ${api.commands}`)
9101
9166
  return this
9102
9167
  }
9103
9168
 
@@ -9137,7 +9202,7 @@ class Server extends require$$0$3 {
9137
9202
 
9138
9203
  if (this.congServer) {
9139
9204
  this.congServer.close((err) => {
9140
- if (err) console.error('congServer close error', err);
9205
+ // if (err) console.error('congServer close error', err)
9141
9206
  // console.log('cong server closed.')
9142
9207
  onClosed();
9143
9208
  });
@@ -9148,7 +9213,12 @@ class Server extends require$$0$3 {
9148
9213
 
9149
9214
  const decoder = new TextDecoder();
9150
9215
  class BohoAuth {
9151
- constructor() {
9216
+ constructor(keyProvider) {
9217
+ if (!keyProvider) {
9218
+ throw new Error('BohoAuth must be initialized with a keyProvider.');
9219
+ }
9220
+ this.keyProvider = keyProvider;
9221
+
9152
9222
  this.authLogger;
9153
9223
  if (serverOption.fileLogger.auth.use) {
9154
9224
  this.authLogger = new FileLogger(serverOption.fileLogger.auth.path);
@@ -9162,11 +9232,11 @@ class BohoAuth {
9162
9232
  this.authLogger.log(peerInfo);
9163
9233
  }
9164
9234
  // console.log('##### AUTH_FAIL reason: ', reason)
9165
- peer.setState(CLIENT_STATE.AUTH_FAIL);
9235
+ peer.setState(STATE.AUTH_FAIL);
9236
+ peer.send(Buffer.from([tt.BohoMsg.AUTH_FAIL]));
9166
9237
  // add some delay time.
9167
- setTimeout(e => {
9168
- peer.send(Buffer.from([tt.BohoMsg.AUTH_FAIL]));
9169
- }, serverOption.auth.delay_auth_fail);
9238
+ // setTimeout(e => {
9239
+ // }, serverOption.auth.delay_auth_fail)
9170
9240
  }
9171
9241
 
9172
9242
  async verify_auth_hmac(auth_hmac, peer) {
@@ -9187,7 +9257,7 @@ class BohoAuth {
9187
9257
  }
9188
9258
 
9189
9259
  //2. get key of id from DB
9190
- let authInfo = await this.getAuth(id);
9260
+ let authInfo = await this.keyProvider.getAuth(id);
9191
9261
 
9192
9262
  if (!authInfo) {
9193
9263
  this.send_auth_fail(peer, 'NO ID:' + id);
@@ -9238,9 +9308,13 @@ class BohoAuth {
9238
9308
  console.log('## trying RELOGIN with SAME ID. ignored.');
9239
9309
  return
9240
9310
  }
9241
- console.log('### clear_auth and close old connection:', old.cid);
9242
- old.send(Buffer.from([IOMsg.SERVER_CLEAR_AUTH]));
9243
- old.close();
9311
+ console.log('### DUPLICATE_LOGIN detected.', old.cid);
9312
+ let sigPack = M$1.pack(
9313
+ M$1.MB('#MsgType', '8', IOMsg.AUTH_CLEAR),
9314
+ M$1.MB('#reason', 'duplicate login.')
9315
+ );
9316
+ old.send( sigPack);
9317
+ // old.close() client will close and stop.
9244
9318
  peer.close(); // auto relogin
9245
9319
  return
9246
9320
  }
@@ -9287,7 +9361,7 @@ class BohoAuth {
9287
9361
  // console.log("LOGIN: ", `id: ${ peer.did}(${peer.cid})` )
9288
9362
  //10. send ack.
9289
9363
  peer.send(auth_ack);
9290
- peer.setState(CLIENT_STATE.AUTH_READY);
9364
+ peer.setState(STATE.AUTH_ACK);
9291
9365
  if (this.authLogger) {
9292
9366
  let peerInfo = `OK #${peer.ssid} cid: ${peer.cid} did:${peer.did}`;
9293
9367
  if (peer.isAdmin) peerInfo = "#ADMIN# " + peerInfo;
@@ -9300,12 +9374,39 @@ class BohoAuth {
9300
9374
 
9301
9375
  }
9302
9376
 
9377
+ async getAuth(id) {
9378
+ return this.keyProvider.getAuth(id);
9379
+ }
9380
+
9381
+ async getAuthIdList() {
9382
+ if (typeof this.keyProvider.getAuthIdList === 'function') {
9383
+ return this.keyProvider.getAuthIdList();
9384
+ }
9385
+ }
9386
+
9387
+ async addAuth(id, keyStr, cid, level = 0) {
9388
+ if (typeof this.keyProvider.addAuth === 'function') {
9389
+ return this.keyProvider.addAuth(id, keyStr, cid, level);
9390
+ }
9391
+ }
9392
+
9393
+ async delAuth(id) {
9394
+ if (typeof this.keyProvider.delAuth === 'function') {
9395
+ return this.keyProvider.delAuth(id);
9396
+ }
9397
+ }
9398
+
9399
+ async save() {
9400
+ if (typeof this.keyProvider.save === 'function') {
9401
+ return this.keyProvider.save();
9402
+ }
9403
+ }
9404
+
9303
9405
 
9304
9406
  }
9305
9407
 
9306
- class Auth_File extends BohoAuth {
9408
+ class FileKeyProvider {
9307
9409
  constructor(_path) {
9308
- super();
9309
9410
  this.AUTH = new Map();
9310
9411
  let pathObj = path.parse(_path);
9311
9412
  this.path = path.resolve(_path);
@@ -9323,20 +9424,9 @@ class Auth_File extends BohoAuth {
9323
9424
  }
9324
9425
  }
9325
9426
 
9326
-
9327
- async getAuth(id) {
9328
- return this.AUTH.get(id)
9329
- }
9330
-
9331
- async getAuthIdList() {
9332
- return Array.from(this.AUTH.keys())
9333
- }
9334
-
9335
9427
  //loaded when server start.
9336
9428
  loadAuthInfoFile_JS(path) {
9337
-
9338
9429
  import(path).then((file) => {
9339
-
9340
9430
  console.log(file.authInfo);
9341
9431
  file.authInfo.forEach(item => {
9342
9432
  this.addAuth(...item);
@@ -9345,8 +9435,6 @@ class Auth_File extends BohoAuth {
9345
9435
  }).catch(e => {
9346
9436
  console.log(e);
9347
9437
  });
9348
-
9349
-
9350
9438
  }
9351
9439
 
9352
9440
  loadAuthInfoFile_JSON(path) {
@@ -9357,81 +9445,12 @@ class Auth_File extends BohoAuth {
9357
9445
  this.addAuth(...item);
9358
9446
  });
9359
9447
  console.log('total AUTH INFO size: ', this.AUTH.size);
9360
-
9361
- }
9362
-
9363
- addAuth(id, keyStr, cid, level = 0) {
9364
- let Base64hashKey = Buffer.from(tt.sha256.hash(keyStr)).toString('base64');
9365
- this.AUTH.set(id, { key: Base64hashKey, cid: cid, level: level });
9366
- }
9367
-
9368
-
9369
-
9370
- }
9371
-
9372
- /**
9373
- * Auth_Env.js
9374
- *
9375
- * You can use environment variable BOHO_AUTH.
9376
- *
9377
- * BOHO_AUTH=id1.key1.level,id2:key2.level
9378
- *
9379
- * or constructor parameter.
9380
- *
9381
- *
9382
- * [ authInfo ]
9383
- * id: <String> max 8chars
9384
- * key: <String>
9385
- * level: <Numbrer> range: 0~255
9386
- * separator: ','
9387
- *
9388
- * example => process.env.BOHO_AUTH=id1.key1.255,id2.key2.200
9389
- */
9390
-
9391
-
9392
- class Auth_Env extends BohoAuth {
9393
- constructor(authInfo) {
9394
- super();
9395
- let id_keys;
9396
- if (authInfo) {
9397
- id_keys = authInfo.split(',');
9398
- } else if (process.env.BOHO_AUTH) {
9399
- authInfo = process.env.BOHO_AUTH;
9400
- id_keys = process.env.BOHO_AUTH.split(',');
9401
- } else {
9402
- console.log("Auth_Env: None of process.env.BOHO_AUTH or authInfo");
9403
- process.exit();
9404
- }
9405
-
9406
- this.AUTH = new Map();
9407
-
9408
- if (id_keys.length >= 1) {
9409
- id_keys.forEach(v => {
9410
-
9411
- let did = v.split('.')[0];
9412
- let key = v.split('.')[1];
9413
- let level = v.split('.')[2];
9414
- level = parseInt(level);
9415
-
9416
- if (did && key && typeof level == 'number') {
9417
- let cid = did;
9418
- this.addAuth(did, key, cid, level);
9419
- } else {
9420
- console.log("Wrong process.env.BOHO_AUTH authentication value.", id, key, level);
9421
- process.exit();
9422
- }
9423
- });
9424
-
9425
- } else {
9426
- console.log("Wrong process.env.BOHO_AUTH authentication value.");
9427
- process.exit();
9428
- }
9429
-
9430
9448
  }
9431
9449
 
9432
9450
  async getAuth(id) {
9433
9451
  return this.AUTH.get(id)
9434
9452
  }
9453
+
9435
9454
  async getAuthIdList() {
9436
9455
  return Array.from(this.AUTH.keys())
9437
9456
  }
@@ -9440,16 +9459,14 @@ class Auth_Env extends BohoAuth {
9440
9459
  let Base64hashKey = Buffer.from(tt.sha256.hash(keyStr)).toString('base64');
9441
9460
  this.AUTH.set(id, { key: Base64hashKey, cid: cid, level: level });
9442
9461
  }
9443
-
9444
9462
  }
9445
9463
 
9446
9464
  const DEVICE_PREFIX = "device:";
9447
9465
 
9448
- class Auth_Redis extends BohoAuth {
9466
+ class RedisKeyProvider {
9449
9467
  constructor(redisClient) {
9450
- super();
9451
9468
  if (!redisClient) {
9452
- throw new Error("AuthRedis constructor: no redisClient")
9469
+ throw new Error("RedisKeyProvider constructor: no redisClient")
9453
9470
  }
9454
9471
  this.redis = redisClient;
9455
9472
  }
@@ -9458,7 +9475,8 @@ class Auth_Redis extends BohoAuth {
9458
9475
  async getAuth(id) {
9459
9476
  try {
9460
9477
  let result = await this.redis.hGetAll(DEVICE_PREFIX + id);
9461
- if (result.key) return result
9478
+ if (result && result.key) return result
9479
+ else return null;
9462
9480
  } catch (error) {
9463
9481
  console.log('getAuth', error);
9464
9482
  }
@@ -9487,7 +9505,60 @@ class Auth_Redis extends BohoAuth {
9487
9505
  async save(id) {
9488
9506
  return this.redis.save()
9489
9507
  }
9508
+ }
9490
9509
 
9510
+ /**
9511
+ * StringKeyProvider.js
9512
+ *
9513
+ * authInfo <String>
9514
+ * id: max 8chars
9515
+ * key: ~44 base64 chars recomended
9516
+ * cid:
9517
+ * level: range: 0~255
9518
+ * separator: ',' for multiple devices.
9519
+ *
9520
+ * example => authInfo = 'id1.key1.cid1.255,id2.key2.cid2.200'
9521
+ */
9522
+
9523
+
9524
+ class StringKeyProvider {
9525
+ constructor(authInfo) {
9526
+
9527
+ if (!authInfo) {
9528
+ throw TypeError("StringKeyProvider constructor() missing authInfo.")
9529
+ }
9530
+
9531
+ let id_keys = authInfo.split(',');
9532
+
9533
+ this.AUTH = new Map();
9534
+
9535
+ if (id_keys.length >= 1) {
9536
+ id_keys.forEach(v => {
9537
+ let [ did, key, cid, level ] = v.split('.');
9538
+ level = parseInt(level);
9539
+ if (did && key && cid && typeof level == 'number') {
9540
+ this.addAuth(did, key, cid, level);
9541
+ } else {
9542
+ throw TypeError("StringKeyProvider invalid authInfo.")
9543
+ }
9544
+ });
9545
+ } else {
9546
+ throw TypeError("StringKeyProvider invalid authInfo.")
9547
+ }
9548
+
9549
+ }
9550
+
9551
+ async getAuth(id) {
9552
+ return this.AUTH.get(id)
9553
+ }
9554
+ async getAuthIdList() {
9555
+ return Array.from(this.AUTH.keys())
9556
+ }
9557
+
9558
+ addAuth(id, keyStr, cid, level = 0) {
9559
+ let Base64hashKey = Buffer.from(tt.sha256.hash(keyStr)).toString('base64');
9560
+ this.AUTH.set(id, { key: Base64hashKey, cid: cid, level: level });
9561
+ }
9491
9562
  }
9492
9563
 
9493
9564
  const commands$1 = ['echo', 'date', 'unixtime'];
@@ -9672,4 +9743,4 @@ class RedisAPI {
9672
9743
 
9673
9744
  const version = pkg.version;
9674
9745
 
9675
- export { API_TYPE, Auth_Env, Auth_File, Auth_Redis, tt as Boho, BohoAuth, CLIENT_STATE, CongRx, ENC_MODE, FileLogger, IOWS as IO, IOCongSocket, IOMsg, M$1 as MBP, PAYLOAD_TYPE, RedisAPI, SIZE_LIMIT, STATES, STATUS$1 as STATUS, Server, api_reply, api_sudo, getPayloadFromSignalPack, getSignalPack, pack, parsePayload, serverOption, version };
9746
+ export { API_TYPE, tt as Boho, BohoAuth, CongRx, ENC_MODE, FileKeyProvider, FileLogger, IOWS as IO, IOCongSocket, IOMsg, M$1 as MBP, PAYLOAD_TYPE, RedisAPI, RedisKeyProvider, SIZE_LIMIT, STATE, STATUS$1 as STATUS, Server, StringKeyProvider, api_reply, api_sudo, delay, getIPv4HexString, getLocalAddress, getPayloadFromSignalPack, getSignalPack, isPrivateIP, numberWithCommas, pack, parsePayload, serverOption, version };