iosignal 4.0.0 → 4.6.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.
@@ -159,7 +159,7 @@ export class IOCore extends EventEmitter<string | symbol, any> {
159
159
  /**
160
160
  * Disables auto-reconnect and closes the current connection.
161
161
  * The instance can be re-opened manually later. For complete cleanup, use destroy().
162
- */
162
+ */
163
163
  stop(): void;
164
164
  /**
165
165
  * Permanently destroys the instance, cleaning up all resources.
@@ -167,8 +167,9 @@ export class IOCore extends EventEmitter<string | symbol, any> {
167
167
  */
168
168
  destroy(): void;
169
169
  /**
170
- * The core keep-alive logic. It checks if auto-reconnect is enabled.
171
- * The actual check for the socket's state is implemented in the child classes.
170
+ * The core keep-alive logic.
171
+ * The specific logic for checking the socket's state and reconnecting
172
+ * is implemented keepConnection() in the child classes (IOWS, IOCongSocket, etc.).
172
173
  */
173
174
  keepAlive(): void;
174
175
  /**
@@ -191,18 +192,18 @@ export class IOCore extends EventEmitter<string | symbol, any> {
191
192
  onClose(): void;
192
193
  /**
193
194
  * Manually logs in with provided ID and key.
194
- * @param {string} id - The user ID.
195
+ * @param {string} id - The user ID. or 'id.key'
195
196
  * @param {string} key - The user key.
196
- * @returns {boolean}
197
+ * @returns {this}
197
198
  */
198
- login(id: string, key: string): boolean;
199
+ login(id: string, key: string): this;
199
200
  /**
200
201
  * Sets up authentication for auto-login.
201
- * @param {string} id - The user ID.
202
+ * @param {string} id - The user ID. or 'id.Key'
202
203
  * @param {string} key - The user key.
203
- * @returns {boolean}
204
+ * @returns {this}
204
205
  */
205
- auth(id: string, key: string): boolean;
206
+ auth(id: string, key: string): this;
206
207
  /**
207
208
  * Handles incoming data from the WebSocket.
208
209
  * @param {Buffer} buffer - The incoming data buffer.
@@ -261,10 +262,12 @@ export class IOCore extends EventEmitter<string | symbol, any> {
261
262
  */
262
263
  testPromise(buffer: Buffer): void;
263
264
  /**
264
- * Publishes a signal.
265
+ * alias of signal()
266
+ * Sends a signal with a tag and arguments.
267
+ * @param {string} tag - The signal tag.
265
268
  * @param {...any} args - Arguments for the signal.
266
269
  */
267
- publish(...args: any[]): void;
270
+ publish(tag: string, ...args: any[]): void;
268
271
  /**
269
272
  * Sends a signal with a tag and arguments.
270
273
  * @param {string} tag - The signal tag.
@@ -408,7 +411,7 @@ export type IOMsg = import("../common/constants.js").IOMsg;
408
411
  export type PAYLOAD_TYPE = import("../common/constants.js").PAYLOAD_TYPE;
409
412
  export type SIZE_LIMIT = import("../common/constants.js").SIZE_LIMIT;
410
413
  export type ENC_MODE = import("../common/constants.js").ENC_MODE;
411
- export type STATES = import("../common/constants.js").STATES;
414
+ export type STATE = import("../common/constants.js").STATE;
412
415
  export type quotaTable = any;
413
416
  export type Boho = import("boho").Boho;
414
417
  export type Buffer = import("boho").Buffer;
@@ -48,6 +48,10 @@ export default class IO extends IOCore {
48
48
  * Pings the server when the browser tab becomes visible.
49
49
  */
50
50
  browserVisiblePing(): void;
51
+ /**
52
+ * Keeps the connection alive by re-connecting if closed.
53
+ */
54
+ keepConnection(): void;
51
55
  /**
52
56
  * Creates a new WebSocket connection.
53
57
  * @param {string} url - The WebSocket URL to connect to.
@@ -1,47 +1,34 @@
1
- export type STATES = {
2
- OPENING: number;
1
+ export type STATE = {
2
+ CONNECTING: number;
3
3
  OPEN: number;
4
- CLOSING: number;
5
- CLOSED: number;
6
4
  SERVER_READY: number;
7
- AUTH_FAIL: number;
8
- AUTH_READY: number;
5
+ AUTH_ACK: number;
6
+ CLOSING: number;
9
7
  READY: number;
8
+ AUTH_FAIL: number;
9
+ AUTH_CLEAR: number;
10
+ CLOSED: number;
11
+ STOP: number;
10
12
  REDIRECTING: number;
11
13
  };
12
- export namespace STATES {
13
- let OPENING: number;
14
+ export namespace STATE {
15
+ let CONNECTING: number;
14
16
  let OPEN: number;
15
- let CLOSING: number;
16
- let CLOSED: number;
17
17
  let SERVER_READY: number;
18
+ let AUTH_REQ: number;
19
+ let AUTH_NONCE: number;
20
+ let AUTH_HMAC: number;
21
+ let AUTH_ACK: number;
18
22
  let AUTH_FAIL: number;
19
- let AUTH_READY: number;
23
+ let AUTH_CLEAR: number;
24
+ let CID_REQ: number;
25
+ let CID_RES: number;
20
26
  let READY: number;
27
+ let CLOSING: number;
28
+ let CLOSED: number;
29
+ let STOP: number;
21
30
  let REDIRECTING: number;
22
31
  }
23
- export type CLIENT_STATE = {
24
- INIT: number;
25
- SENT_SERVER_READY: number;
26
- RECV_AUTH_REQ: number;
27
- SENT_SERVER_NONCE: number;
28
- RECV_AUTH_HMAC: number;
29
- AUTH_FAIL: number;
30
- AUTH_READY: number;
31
- CID_READY: number;
32
- };
33
- export namespace CLIENT_STATE {
34
- export let INIT: number;
35
- export let SENT_SERVER_READY: number;
36
- export let RECV_AUTH_REQ: number;
37
- export let SENT_SERVER_NONCE: number;
38
- export let RECV_AUTH_HMAC: number;
39
- let AUTH_FAIL_1: number;
40
- export { AUTH_FAIL_1 as AUTH_FAIL };
41
- let AUTH_READY_1: number;
42
- export { AUTH_READY_1 as AUTH_READY };
43
- export let CID_READY: number;
44
- }
45
32
  export type ENC_MODE = {
46
33
  NO: number;
47
34
  YES: number;
@@ -56,6 +43,7 @@ export type SIZE_LIMIT = {
56
43
  TAG_LEN1: number;
57
44
  TAG_LEN2: number;
58
45
  CONNECTION_CHECKER_PERIOD: number;
46
+ CLIENT_PING_PERIOD: number;
59
47
  PROMISE_TIMEOUT: number;
60
48
  DID: number;
61
49
  CID: number;
@@ -64,6 +52,7 @@ export namespace SIZE_LIMIT {
64
52
  let TAG_LEN1: number;
65
53
  let TAG_LEN2: number;
66
54
  let CONNECTION_CHECKER_PERIOD: number;
55
+ let CLIENT_PING_PERIOD: number;
67
56
  let PROMISE_TIMEOUT: number;
68
57
  let DID: number;
69
58
  let CID: number;
@@ -89,7 +78,7 @@ export type IOMsg = {
89
78
  CID_REQ: number;
90
79
  CID_RES: number;
91
80
  QUOTA_LEVEL: number;
92
- SERVER_CLEAR_AUTH: number;
81
+ AUTH_CLEAR: number;
93
82
  SERVER_REDIRECT: number;
94
83
  LOOP: number;
95
84
  ECHO: number;
@@ -120,10 +109,13 @@ export type IOMsg = {
120
109
  export namespace IOMsg {
121
110
  let SERVER_READY_1: number;
122
111
  export { SERVER_READY_1 as SERVER_READY };
123
- export let CID_REQ: number;
124
- export let CID_RES: number;
112
+ let CID_REQ_1: number;
113
+ export { CID_REQ_1 as CID_REQ };
114
+ let CID_RES_1: number;
115
+ export { CID_RES_1 as CID_RES };
125
116
  export let QUOTA_LEVEL: number;
126
- export let SERVER_CLEAR_AUTH: number;
117
+ let AUTH_CLEAR_1: number;
118
+ export { AUTH_CLEAR_1 as AUTH_CLEAR };
127
119
  export let SERVER_REDIRECT: number;
128
120
  export let LOOP: number;
129
121
  export let ECHO: number;
package/index.js CHANGED
@@ -8,6 +8,7 @@ export { serverOption } from './src/server/serverOption.js'
8
8
  export { FileLogger } from './src/server/FileLogger.js'
9
9
  export { getSignalPack, parsePayload, getPayloadFromSignalPack } from './src/common/payload.js'
10
10
  export * from './src/common/constants.js'
11
+ export * from './src/common/util.js'
11
12
 
12
13
  // MBP
13
14
  export { default as MBP } from 'meta-buffer-pack'
@@ -15,9 +16,9 @@ export { default as MBP } from 'meta-buffer-pack'
15
16
  // boho auth
16
17
  export { default as Boho} from 'boho'
17
18
  export { BohoAuth } from './src/auth/BohoAuth.js'
18
- export { Auth_File } from './src/auth/Auth_File.js'
19
- export { Auth_Env } from './src/auth/Auth_Env.js'
20
- export { Auth_Redis } from './src/auth/Auth_Redis.js'
19
+ export { FileKeyProvider } from './src/auth/key_providers/FileKeyProvider.js'
20
+ export { RedisKeyProvider } from './src/auth/key_providers/RedisKeyProvider.js'
21
+ export { StringKeyProvider } from './src/auth/key_providers/StringKeyProvider.js'
21
22
 
22
23
  // api
23
24
  export * as api_reply from './src/api/api_reply.js'
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "iosignal",
3
- "version": "4.0.0",
3
+ "version": "4.6.0",
4
4
  "description": "iosignal",
5
5
  "type": "module",
6
6
  "exports": {
@@ -2,12 +2,17 @@ import MBP from 'meta-buffer-pack'
2
2
  import Boho from 'boho'
3
3
  import { quotaTable } from '../common/quotaTable.js'
4
4
  import { serverOption } from '../server/serverOption.js'
5
- import { IOMsg, CLIENT_STATE } from '../common/constants.js'
5
+ import { IOMsg, STATE } from '../common/constants.js'
6
6
  import { FileLogger } from '../server/FileLogger.js'
7
7
 
8
8
  const decoder = new TextDecoder()
9
9
  export class BohoAuth {
10
- constructor() {
10
+ constructor(keyProvider) {
11
+ if (!keyProvider) {
12
+ throw new Error('BohoAuth must be initialized with a keyProvider.');
13
+ }
14
+ this.keyProvider = keyProvider;
15
+
11
16
  this.authLogger;
12
17
  if (serverOption.fileLogger.auth.use) {
13
18
  this.authLogger = new FileLogger(serverOption.fileLogger.auth.path)
@@ -21,11 +26,11 @@ export class BohoAuth {
21
26
  this.authLogger.log(peerInfo)
22
27
  }
23
28
  // console.log('##### AUTH_FAIL reason: ', reason)
24
- peer.setState(CLIENT_STATE.AUTH_FAIL)
29
+ peer.setState(STATE.AUTH_FAIL)
30
+ peer.send(Buffer.from([Boho.BohoMsg.AUTH_FAIL]))
25
31
  // add some delay time.
26
- setTimeout(e => {
27
- peer.send(Buffer.from([Boho.BohoMsg.AUTH_FAIL]))
28
- }, serverOption.auth.delay_auth_fail)
32
+ // setTimeout(e => {
33
+ // }, serverOption.auth.delay_auth_fail)
29
34
  }
30
35
 
31
36
  async verify_auth_hmac(auth_hmac, peer) {
@@ -46,7 +51,7 @@ export class BohoAuth {
46
51
  }
47
52
 
48
53
  //2. get key of id from DB
49
- let authInfo = await this.getAuth(id)
54
+ let authInfo = await this.keyProvider.getAuth(id)
50
55
 
51
56
  if (!authInfo) {
52
57
  this.send_auth_fail(peer, 'NO ID:' + id);
@@ -97,9 +102,13 @@ export class BohoAuth {
97
102
  console.log('## trying RELOGIN with SAME ID. ignored.')
98
103
  return
99
104
  }
100
- console.log('### clear_auth and close old connection:', old.cid)
101
- old.send(Buffer.from([IOMsg.SERVER_CLEAR_AUTH]))
102
- old.close()
105
+ console.log('### DUPLICATE_LOGIN detected.', old.cid)
106
+ let sigPack = MBP.pack(
107
+ MBP.MB('#MsgType', '8', IOMsg.AUTH_CLEAR),
108
+ MBP.MB('#reason', 'duplicate login.')
109
+ )
110
+ old.send( sigPack)
111
+ // old.close() client will close and stop.
103
112
  peer.close() // auto relogin
104
113
  return
105
114
  }
@@ -146,7 +155,7 @@ export class BohoAuth {
146
155
  // console.log("LOGIN: ", `id: ${ peer.did}(${peer.cid})` )
147
156
  //10. send ack.
148
157
  peer.send(auth_ack)
149
- peer.setState(CLIENT_STATE.AUTH_READY)
158
+ peer.setState(STATE.AUTH_ACK)
150
159
  if (this.authLogger) {
151
160
  let peerInfo = `OK #${peer.ssid} cid: ${peer.cid} did:${peer.did}`
152
161
  if (peer.isAdmin) peerInfo = "#ADMIN# " + peerInfo
@@ -159,6 +168,34 @@ export class BohoAuth {
159
168
 
160
169
  }
161
170
 
171
+ async getAuth(id) {
172
+ return this.keyProvider.getAuth(id);
173
+ }
174
+
175
+ async getAuthIdList() {
176
+ if (typeof this.keyProvider.getAuthIdList === 'function') {
177
+ return this.keyProvider.getAuthIdList();
178
+ }
179
+ }
180
+
181
+ async addAuth(id, keyStr, cid, level = 0) {
182
+ if (typeof this.keyProvider.addAuth === 'function') {
183
+ return this.keyProvider.addAuth(id, keyStr, cid, level);
184
+ }
185
+ }
186
+
187
+ async delAuth(id) {
188
+ if (typeof this.keyProvider.delAuth === 'function') {
189
+ return this.keyProvider.delAuth(id);
190
+ }
191
+ }
192
+
193
+ async save() {
194
+ if (typeof this.keyProvider.save === 'function') {
195
+ return this.keyProvider.save();
196
+ }
197
+ }
198
+
162
199
 
163
200
  }
164
201
 
@@ -1,11 +1,9 @@
1
- import { BohoAuth } from './BohoAuth.js';
2
1
  import { readFileSync } from 'fs'
3
2
  import Boho from 'boho'
4
3
  import path from 'path'
5
4
 
6
- export class Auth_File extends BohoAuth {
5
+ export class FileKeyProvider {
7
6
  constructor(_path) {
8
- super()
9
7
  this.AUTH = new Map();
10
8
  let pathObj = path.parse(_path)
11
9
  this.path = path.resolve(_path)
@@ -23,20 +21,9 @@ export class Auth_File extends BohoAuth {
23
21
  }
24
22
  }
25
23
 
26
-
27
- async getAuth(id) {
28
- return this.AUTH.get(id)
29
- }
30
-
31
- async getAuthIdList() {
32
- return Array.from(this.AUTH.keys())
33
- }
34
-
35
24
  //loaded when server start.
36
25
  loadAuthInfoFile_JS(path) {
37
-
38
26
  import(path).then((file) => {
39
-
40
27
  console.log(file.authInfo);
41
28
  file.authInfo.forEach(item => {
42
29
  this.addAuth(...item)
@@ -45,8 +32,6 @@ export class Auth_File extends BohoAuth {
45
32
  }).catch(e => {
46
33
  console.log(e)
47
34
  })
48
-
49
-
50
35
  }
51
36
 
52
37
  loadAuthInfoFile_JSON(path) {
@@ -57,15 +42,18 @@ export class Auth_File extends BohoAuth {
57
42
  this.addAuth(...item)
58
43
  })
59
44
  console.log('total AUTH INFO size: ', this.AUTH.size)
45
+ }
46
+
47
+ async getAuth(id) {
48
+ return this.AUTH.get(id)
49
+ }
60
50
 
51
+ async getAuthIdList() {
52
+ return Array.from(this.AUTH.keys())
61
53
  }
62
54
 
63
55
  addAuth(id, keyStr, cid, level = 0) {
64
56
  let Base64hashKey = Buffer.from(Boho.sha256.hash(keyStr)).toString('base64')
65
57
  this.AUTH.set(id, { key: Base64hashKey, cid: cid, level: level })
66
58
  }
67
-
68
-
69
-
70
59
  }
71
-
@@ -1,12 +1,10 @@
1
- import { BohoAuth } from './BohoAuth.js';
2
1
  import Boho from 'boho'
3
2
  const DEVICE_PREFIX = "device:"
4
3
 
5
- export class Auth_Redis extends BohoAuth {
4
+ export class RedisKeyProvider {
6
5
  constructor(redisClient) {
7
- super()
8
6
  if (!redisClient) {
9
- throw new Error("AuthRedis constructor: no redisClient")
7
+ throw new Error("RedisKeyProvider constructor: no redisClient")
10
8
  }
11
9
  this.redis = redisClient
12
10
  }
@@ -15,7 +13,8 @@ export class Auth_Redis extends BohoAuth {
15
13
  async getAuth(id) {
16
14
  try {
17
15
  let result = await this.redis.hGetAll(DEVICE_PREFIX + id)
18
- if (result.key) return result
16
+ if (result && result.key) return result
17
+ else return null;
19
18
  } catch (error) {
20
19
  console.log('getAuth', error)
21
20
  }
@@ -44,6 +43,4 @@ export class Auth_Redis extends BohoAuth {
44
43
  async save(id) {
45
44
  return this.redis.save()
46
45
  }
47
-
48
46
  }
49
-
@@ -0,0 +1,54 @@
1
+ /**
2
+ * StringKeyProvider.js
3
+ *
4
+ * authInfo <String>
5
+ * id: max 8chars
6
+ * key: ~44 base64 chars recomended
7
+ * cid:
8
+ * level: range: 0~255
9
+ * separator: ',' for multiple devices.
10
+ *
11
+ * example => authInfo = 'id1.key1.cid1.255,id2.key2.cid2.200'
12
+ */
13
+
14
+ import Boho from 'boho'
15
+
16
+ export class StringKeyProvider {
17
+ constructor(authInfo) {
18
+
19
+ if (!authInfo) {
20
+ throw TypeError("StringKeyProvider constructor() missing authInfo.")
21
+ }
22
+
23
+ let id_keys = authInfo.split(',')
24
+
25
+ this.AUTH = new Map();
26
+
27
+ if (id_keys.length >= 1) {
28
+ id_keys.forEach(v => {
29
+ let [ did, key, cid, level ] = v.split('.')
30
+ level = parseInt(level)
31
+ if (did && key && cid && typeof level == 'number') {
32
+ this.addAuth(did, key, cid, level)
33
+ } else {
34
+ throw TypeError("StringKeyProvider invalid authInfo.")
35
+ }
36
+ })
37
+ } else {
38
+ throw TypeError("StringKeyProvider invalid authInfo.")
39
+ }
40
+
41
+ }
42
+
43
+ async getAuth(id) {
44
+ return this.AUTH.get(id)
45
+ }
46
+ async getAuthIdList() {
47
+ return Array.from(this.AUTH.keys())
48
+ }
49
+
50
+ addAuth(id, keyStr, cid, level = 0) {
51
+ let Base64hashKey = Buffer.from(Boho.sha256.hash(keyStr)).toString('base64')
52
+ this.AUTH.set(id, { key: Base64hashKey, cid: cid, level: level })
53
+ }
54
+ }
@@ -1,5 +1,5 @@
1
1
  import { IOCore } from "./IOCore.js"
2
- import { STATES } from "../common/constants.js";
2
+ import { SIZE_LIMIT } from "../common/constants.js";
3
3
  import { pack, CongRx } from './CongPacket.js'
4
4
  import net from 'net'
5
5
 
@@ -30,10 +30,10 @@ export class IOCongSocket extends IOCore {
30
30
 
31
31
 
32
32
 
33
- keepAlive() {
33
+ keepConnection() {
34
34
  if (!this.autoReconnect) return;
35
35
  // Reconnect only if the socket is fully destroyed and the state is closed.
36
- if ((!this.socket || this.socket.destroyed) && this.state === STATES.CLOSED) {
36
+ if ((!this.socket || this.socket.destroyed)) {
37
37
  this.open();
38
38
  }
39
39
  }
@@ -46,7 +46,7 @@ export class IOCongSocket extends IOCore {
46
46
  urlObj = new URL('cong://' + url)
47
47
  }
48
48
  this.socket = net.createConnection(urlObj.port, urlObj.hostname)
49
- this.stateChange('opening')
49
+ this.stateChange('connecting','connecting')
50
50
 
51
51
  this.socket.on('connect', () => {
52
52
  this.congRx = new CongRx();