iosignal 3.2.0 → 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.vscode/settings.json +3 -0
- package/README.en.md +56 -0
- package/README.md +39 -782
- package/dist/io.d.ts +10 -3
- package/dist/io.js +4 -4
- package/dist/io.js.map +1 -1
- package/dist/io.min.js +4 -4
- package/dist/io.min.js.map +1 -1
- package/dist/iosignal.js +266 -217
- package/dist/types/client/IOCore.d.ts +10 -3
- package/package.json +3 -4
- package/src/api/RedisAPI.js +19 -31
- package/src/api/api_reply.js +14 -6
- package/src/api/api_sudo.js +2 -8
- package/src/auth/Auth_Redis.js +8 -3
- package/src/auth/BohoAuth.js +12 -12
- package/src/client/CongPacket.js +2 -0
- package/src/client/IOCore.js +98 -52
- package/src/server/Manager.js +12 -12
- package/src/server/Metric.js +1 -2
- package/src/server/RemoteCore.js +27 -34
- package/src/server/Server.js +69 -60
- package/src/server/serverOption.js +3 -2
- package/README.ko.md +0 -566
- package/docs/iosignal_architecture.png +0 -0
- package/examples/react-chat-js/dist/assets/index-Cy4qaVcJ.js +0 -56
- package/examples/react-chat-js/dist/assets/index-NETrjYhz.css +0 -1
- package/examples/react-chat-js/dist/index.html +0 -14
- package/examples/react-chat-js/index.html +0 -13
- package/examples/react-chat-js/package-lock.json +0 -1723
- package/examples/react-chat-js/package.json +0 -20
- package/examples/react-chat-js/src/App.css +0 -61
- package/examples/react-chat-js/src/App.jsx +0 -125
- package/examples/react-chat-js/src/index.css +0 -13
- package/examples/react-chat-js/src/main.jsx +0 -13
- package/examples/react-chat-js/src/shared_io.js +0 -12
- package/examples/react-chat-js/vite.config.js +0 -7
- package/examples/server/index.js +0 -12
- package/examples/server/package-lock.json +0 -47
- package/examples/server/package.json +0 -12
- package/examples/svelte-chat-js/.devtools +0 -3
- package/examples/svelte-chat-js/README.md +0 -38
- package/examples/svelte-chat-js/package-lock.json +0 -1505
- package/examples/svelte-chat-js/package.json +0 -22
- package/examples/svelte-chat-js/src/app.css +0 -105
- package/examples/svelte-chat-js/src/app.d.ts +0 -13
- package/examples/svelte-chat-js/src/app.html +0 -12
- package/examples/svelte-chat-js/src/lib/images/github.svg +0 -1
- package/examples/svelte-chat-js/src/lib/images/svelte-logo.svg +0 -1
- package/examples/svelte-chat-js/src/lib/images/svelte-welcome.png +0 -0
- package/examples/svelte-chat-js/src/lib/images/svelte-welcome.webp +0 -0
- package/examples/svelte-chat-js/src/lib/shared_io.js +0 -12
- package/examples/svelte-chat-js/src/routes/+layout.svelte +0 -9
- package/examples/svelte-chat-js/src/routes/+page.js +0 -3
- package/examples/svelte-chat-js/src/routes/+page.svelte +0 -162
- package/examples/svelte-chat-js/static/favicon.svg +0 -1
- package/examples/svelte-chat-js/static/global.css +0 -19
- package/examples/svelte-chat-js/static/robots.txt +0 -3
- package/examples/svelte-chat-js/svelte.config.js +0 -15
- package/examples/svelte-chat-js/vite.config.js +0 -6
- package/test-browser/favicon.ico +0 -0
- package/test-browser/index.html +0 -95
- package/test-browser/server.js +0 -12
- package/test-nodejs/client-playToggle.js +0 -19
- package/test-nodejs/client.js +0 -17
- package/test-nodejs/client_api_reply.js +0 -15
- package/test-nodejs/server-Auth_Env.js +0 -19
- package/test-nodejs/server-Auth_File.js +0 -12
- package/test-nodejs/server-api_reply.js +0 -6
- package/test-nodejs/server-api_sudo.js +0 -7
- package/test-nodejs/server.js +0 -20
- package/test-nodejs/signalPack.js +0 -34
- package/test-nodejs/signaling.js +0 -61
- package/test-nodejs/simple-server.js +0 -13
- /package/{auth_file.js → auth_file_sample.js} +0 -0
- /package/{auth_file.json → auth_file_sample.json} +0 -0
|
@@ -208,6 +208,8 @@ export class IOCore extends EventEmitter<string | symbol, any> {
|
|
|
208
208
|
* @param {Buffer} buffer - The incoming data buffer.
|
|
209
209
|
*/
|
|
210
210
|
onData(buffer: Buffer): void;
|
|
211
|
+
did: any;
|
|
212
|
+
uid: any;
|
|
211
213
|
/**
|
|
212
214
|
* Sends an IAM (I Am) message to the server.
|
|
213
215
|
* @param {string} [title] - Optional title for the IAM message.
|
|
@@ -299,9 +301,9 @@ export class IOCore extends EventEmitter<string | symbol, any> {
|
|
|
299
301
|
*/
|
|
300
302
|
get(storeName: string): Promise<any>;
|
|
301
303
|
/**
|
|
302
|
-
* Sends a request to a target and topic.
|
|
303
|
-
* @param {string} target - The target of the request.
|
|
304
|
-
* @param {string} topic - The topic of the request.
|
|
304
|
+
* Sends a request to a target and topic.(remote api call)
|
|
305
|
+
* @param {string} target - The target(api name) of the request.
|
|
306
|
+
* @param {string} topic - The topic(api function name) of the request.
|
|
305
307
|
* @param {...any} args - Optional arguments for the request.
|
|
306
308
|
* @returns {Promise<any>}
|
|
307
309
|
*/
|
|
@@ -393,6 +395,11 @@ export class IOCore extends EventEmitter<string | symbol, any> {
|
|
|
393
395
|
* Changes the connection state and emits events.
|
|
394
396
|
* @param {string} state - The new state name (e.g., 'ready', 'closed').
|
|
395
397
|
* @param {string} [emitEventAndMessage] - Optional message to emit with the state change event.
|
|
398
|
+
*
|
|
399
|
+
* 주의.
|
|
400
|
+
* 1. 상태가 변경 될 때만 'change' 이벤트 호출된다.
|
|
401
|
+
* 2. emitEventAndMessage 옵션 값이 지정되야 해당 이벤트 이름이 호출된다.
|
|
402
|
+
* 보통 이벤트 이름과 동일하게 적거나 이벤트 상황 안내문을 넣는다.
|
|
396
403
|
*/
|
|
397
404
|
stateChange(state: string, emitEventAndMessage?: string): void;
|
|
398
405
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "iosignal",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "4.0.0",
|
|
4
4
|
"description": "iosignal",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -18,11 +18,10 @@
|
|
|
18
18
|
"unpkg": "./dist/io.min.js",
|
|
19
19
|
"scripts": {
|
|
20
20
|
"build": "rollup -c && npm run build:types",
|
|
21
|
-
"build:types": "tsc --project tsconfig.json"
|
|
22
|
-
"start": "node ./test-browser/server.js"
|
|
21
|
+
"build:types": "tsc --project tsconfig.json"
|
|
23
22
|
},
|
|
24
23
|
"engines": {
|
|
25
|
-
"node": ">=
|
|
24
|
+
"node": ">=22.0.0"
|
|
26
25
|
},
|
|
27
26
|
"author": {
|
|
28
27
|
"name": "Taeo Lee",
|
package/src/api/RedisAPI.js
CHANGED
|
@@ -1,14 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RedisAPI <IOSignal API>
|
|
3
|
+
*
|
|
4
|
+
* iosignal 클라이언트의 redis 질의 요청을 받으면,
|
|
5
|
+
* redisClient 로 연결된 redis-server 에게 질의 후 결과를 되돌려 준다.
|
|
6
|
+
*
|
|
7
|
+
* client ex:
|
|
8
|
+
* await io.req('redis','hget','user:id' )
|
|
9
|
+
*
|
|
10
|
+
*/
|
|
1
11
|
import { STATUS } from './api_constant.js'
|
|
2
12
|
|
|
3
13
|
const MIN_LEVEL = 200;
|
|
4
14
|
const COMMANDS = [
|
|
5
|
-
'GET', 'SET',
|
|
6
|
-
'
|
|
7
|
-
'
|
|
8
|
-
'
|
|
9
|
-
'EXISTS', 'SREM',
|
|
10
|
-
'DEL', 'KEYS',
|
|
11
|
-
'SAVE'
|
|
15
|
+
'GET', 'SET', 'HGETALL', 'HGET', 'HSET', 'SADD', 'SISMEMBER',
|
|
16
|
+
'SMEMBERS', 'EXISTS', 'SREM', 'DEL', 'KEYS', 'SAVE',
|
|
17
|
+
'get', 'set', 'hGetAll', 'hGet', 'hSet', 'sAdd', 'sIsMember',
|
|
18
|
+
'sMembers', 'exists', 'sRem', 'del', 'keys', 'save'
|
|
12
19
|
]
|
|
13
20
|
|
|
14
21
|
export class RedisAPI {
|
|
@@ -21,42 +28,23 @@ export class RedisAPI {
|
|
|
21
28
|
this.commands = COMMANDS
|
|
22
29
|
}
|
|
23
30
|
|
|
24
|
-
|
|
25
31
|
checkPermission(remote, req) {
|
|
26
|
-
|
|
27
|
-
if (remote.level >= this.minLevel) {
|
|
28
|
-
return true
|
|
29
|
-
} else {
|
|
30
|
-
return false
|
|
31
|
-
}
|
|
32
|
+
return (remote.level >= this.minLevel) ? true : false;
|
|
32
33
|
}
|
|
33
34
|
|
|
34
|
-
|
|
35
35
|
async request(remote, req) {
|
|
36
|
-
|
|
37
36
|
let result;
|
|
38
|
-
let status = STATUS.OK;
|
|
39
37
|
try {
|
|
40
38
|
let cmd = req.topic
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
console.log('RedisAPI call:', cmd, req.args)
|
|
44
|
-
|
|
45
|
-
if (req.args.length > 0) {
|
|
46
|
-
result = await this.redisClient[cmd](...req.args)
|
|
47
|
-
} else {
|
|
48
|
-
result = await this.redisClient[cmd]()
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
|
|
39
|
+
if (req.args?.length > 0) {
|
|
40
|
+
result = await this.redisClient[cmd](...req.args)
|
|
52
41
|
} else {
|
|
53
|
-
|
|
42
|
+
result = await this.redisClient[cmd]()
|
|
54
43
|
}
|
|
55
|
-
remote.response(req.mid,
|
|
44
|
+
remote.response(req.mid, STATUS.OK, result)
|
|
56
45
|
} catch (e) {
|
|
57
46
|
remote.response(req.mid, STATUS.ERROR, e.message)
|
|
58
47
|
}
|
|
59
|
-
|
|
60
48
|
}
|
|
61
49
|
|
|
62
50
|
}
|
package/src/api/api_reply.js
CHANGED
|
@@ -1,17 +1,19 @@
|
|
|
1
1
|
|
|
2
|
+
/**
|
|
3
|
+
* simple IOSignal API example.
|
|
4
|
+
*/
|
|
2
5
|
|
|
3
6
|
import { STATUS } from './api_constant.js'
|
|
7
|
+
export const commands = ['echo', 'date', 'unixtime'];
|
|
4
8
|
|
|
5
9
|
const MIN_LEVEL = 0;
|
|
6
|
-
|
|
7
10
|
export function checkPermission(remote) {
|
|
8
|
-
|
|
9
|
-
return true
|
|
10
|
-
} else {
|
|
11
|
-
return false
|
|
12
|
-
}
|
|
11
|
+
return (remote.level >= MIN_LEVEL) ? true : false;
|
|
13
12
|
}
|
|
14
13
|
|
|
14
|
+
/**
|
|
15
|
+
* client api call example: await io.req('reply','echo','hello')
|
|
16
|
+
*/
|
|
15
17
|
export async function echo(remote, req) {
|
|
16
18
|
if (!req.args)
|
|
17
19
|
remote.response(req.mid, STATUS.ERROR, 'no message to echo')
|
|
@@ -19,11 +21,17 @@ export async function echo(remote, req) {
|
|
|
19
21
|
remote.response(req.mid, STATUS.OK, req.args)
|
|
20
22
|
}
|
|
21
23
|
|
|
24
|
+
/**
|
|
25
|
+
* client api call example: await io.req('reply','date')
|
|
26
|
+
*/
|
|
22
27
|
export async function date(remote, req) {
|
|
23
28
|
let r = new Date().toUTCString()
|
|
24
29
|
remote.response(req.mid, STATUS.OK, r)
|
|
25
30
|
}
|
|
26
31
|
|
|
32
|
+
/**
|
|
33
|
+
* client api call example: await io.req('reply','unixtime')
|
|
34
|
+
*/
|
|
27
35
|
export async function unixtime(remote, req) {
|
|
28
36
|
let r = Math.floor(Date.now() / 1000)
|
|
29
37
|
remote.response(req.mid, STATUS.OK, r)
|
package/src/api/api_sudo.js
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
import { STATUS } from './api_constant.js'
|
|
2
2
|
|
|
3
|
-
const MIN_LEVEL = 255;
|
|
4
|
-
|
|
5
3
|
export const commands = [
|
|
6
4
|
'cid', 'remotes', 'clients',
|
|
7
5
|
'channels', 'subscribers',
|
|
@@ -11,13 +9,9 @@ export const commands = [
|
|
|
11
9
|
'adddevice', 'getdevice', 'deldevice'
|
|
12
10
|
]
|
|
13
11
|
|
|
14
|
-
|
|
12
|
+
const MIN_LEVEL = 255;
|
|
15
13
|
export function checkPermission(remote) {
|
|
16
|
-
|
|
17
|
-
return true
|
|
18
|
-
} else {
|
|
19
|
-
return false
|
|
20
|
-
}
|
|
14
|
+
return (remote.level >= MIN_LEVEL) ? true : false;
|
|
21
15
|
}
|
|
22
16
|
|
|
23
17
|
export async function request(remote, req) {
|
package/src/auth/Auth_Redis.js
CHANGED
|
@@ -13,11 +13,15 @@ export class Auth_Redis extends BohoAuth {
|
|
|
13
13
|
|
|
14
14
|
// get device key from DB. (for Boho auth.)
|
|
15
15
|
async getAuth(id) {
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
try {
|
|
17
|
+
let result = await this.redis.hGetAll(DEVICE_PREFIX + id)
|
|
18
|
+
if (result.key) return result
|
|
19
|
+
} catch (error) {
|
|
20
|
+
console.log('getAuth', error)
|
|
21
|
+
}
|
|
18
22
|
}
|
|
19
23
|
|
|
20
|
-
|
|
24
|
+
//api_sudo call
|
|
21
25
|
async getAuthIdList() {
|
|
22
26
|
let result = await this.redis.keys(DEVICE_PREFIX + '*')
|
|
23
27
|
result = result.map(v => {
|
|
@@ -32,6 +36,7 @@ export class Auth_Redis extends BohoAuth {
|
|
|
32
36
|
return this.redis.hSet(DEVICE_PREFIX + id, { 'key': Base64hashKey, 'cid': cid, 'level': level })
|
|
33
37
|
}
|
|
34
38
|
|
|
39
|
+
//api_sudo call
|
|
35
40
|
async delAuth(id) {
|
|
36
41
|
return this.redis.del(DEVICE_PREFIX + id)
|
|
37
42
|
}
|
package/src/auth/BohoAuth.js
CHANGED
|
@@ -20,10 +20,9 @@ export class BohoAuth {
|
|
|
20
20
|
let peerInfo = `FAIL #${peer.ssid} reason:${reason} `
|
|
21
21
|
this.authLogger.log(peerInfo)
|
|
22
22
|
}
|
|
23
|
-
|
|
24
|
-
console.log('## AUTH_FAIL reason:', reason)
|
|
25
|
-
// peer.send( Buffer.from( [Boho.BohoMsg.AUTH_FAIL] ))
|
|
23
|
+
// console.log('##### AUTH_FAIL reason: ', reason)
|
|
26
24
|
peer.setState(CLIENT_STATE.AUTH_FAIL)
|
|
25
|
+
// add some delay time.
|
|
27
26
|
setTimeout(e => {
|
|
28
27
|
peer.send(Buffer.from([Boho.BohoMsg.AUTH_FAIL]))
|
|
29
28
|
}, serverOption.auth.delay_auth_fail)
|
|
@@ -33,30 +32,31 @@ export class BohoAuth {
|
|
|
33
32
|
try {
|
|
34
33
|
//1. unpack
|
|
35
34
|
let infoPack = MBP.unpack(auth_hmac, Boho.Meta.AUTH_HMAC)
|
|
35
|
+
|
|
36
36
|
if (!infoPack) {
|
|
37
37
|
this.send_auth_fail(peer, 'unpack auth_pack fail');
|
|
38
38
|
return
|
|
39
39
|
}
|
|
40
|
-
|
|
40
|
+
|
|
41
41
|
let id = ""
|
|
42
42
|
if (infoPack.id8.includes(0)) {
|
|
43
43
|
id = decoder.decode(infoPack.id8.subarray(0, infoPack.id8.indexOf(0)));
|
|
44
44
|
} else {
|
|
45
45
|
id = decoder.decode(infoPack.id8);
|
|
46
46
|
}
|
|
47
|
-
|
|
47
|
+
|
|
48
48
|
//2. get key of id from DB
|
|
49
49
|
let authInfo = await this.getAuth(id)
|
|
50
50
|
|
|
51
|
-
// console.log('##### authInfo',authInfo )
|
|
52
|
-
if (serverOption.debug.showAuthInfo) {
|
|
53
|
-
}
|
|
54
|
-
|
|
55
51
|
if (!authInfo) {
|
|
56
52
|
this.send_auth_fail(peer, 'NO ID:' + id);
|
|
57
53
|
return
|
|
58
54
|
}
|
|
59
55
|
|
|
56
|
+
if (serverOption.debug.showAuthInfo) {
|
|
57
|
+
console.log('[debug]showAuthInfo',authInfo )
|
|
58
|
+
}
|
|
59
|
+
|
|
60
60
|
// console.log('db authInfo.key: ', authInfo.key)
|
|
61
61
|
peer.boho.copy_id8(infoPack.id8)
|
|
62
62
|
// type of key
|
|
@@ -107,6 +107,7 @@ export class BohoAuth {
|
|
|
107
107
|
|
|
108
108
|
//6. delete current (temp rand)cid if exist.
|
|
109
109
|
if (peer.cid) {
|
|
110
|
+
console.log('duplicate cid')
|
|
110
111
|
peer.manager.cid2remote.delete(peer.cid)
|
|
111
112
|
}
|
|
112
113
|
|
|
@@ -114,6 +115,7 @@ export class BohoAuth {
|
|
|
114
115
|
peer.did = id
|
|
115
116
|
peer.cid = authInfo.cid
|
|
116
117
|
peer.nick = authInfo.cid // temporary: nick as cid
|
|
118
|
+
if( authInfo.uid ) peer.uid = authInfo.uid
|
|
117
119
|
|
|
118
120
|
//8. setting quota level.
|
|
119
121
|
let quotaLevel = serverOption.defaultQuotaIndex;
|
|
@@ -152,13 +154,11 @@ export class BohoAuth {
|
|
|
152
154
|
}
|
|
153
155
|
return authInfo
|
|
154
156
|
} catch (error) {
|
|
155
|
-
this.send_auth_fail(peer, 'caught: unknown error' + error);
|
|
157
|
+
this.send_auth_fail(peer, '[BohoAuth]caught: unknown error:' + error);
|
|
156
158
|
}
|
|
157
159
|
|
|
158
160
|
}
|
|
159
161
|
|
|
160
162
|
|
|
161
|
-
|
|
162
|
-
|
|
163
163
|
}
|
|
164
164
|
|
package/src/client/CongPacket.js
CHANGED
|
@@ -11,6 +11,8 @@ export const CongType = {
|
|
|
11
11
|
// support LittleEndian system.
|
|
12
12
|
export function pack(payload) {
|
|
13
13
|
|
|
14
|
+
if( payload.byteLength == undefined ) payload = Buffer.from(payload)
|
|
15
|
+
|
|
14
16
|
if (payload.byteLength < 256) { //one byte len
|
|
15
17
|
return MBP.pack(
|
|
16
18
|
MBP.MB('#type', '8', CongType.TYPE_LEN1),
|
package/src/client/IOCore.js
CHANGED
|
@@ -207,7 +207,7 @@ export class IOCore extends EventEmitter {
|
|
|
207
207
|
close() {
|
|
208
208
|
if (this._closed) return;
|
|
209
209
|
this._closed = true;
|
|
210
|
-
|
|
210
|
+
// console.log('####### IOCOre.js close() called')
|
|
211
211
|
// If auto-reconnect is disabled, we must stop the keep-alive timer.
|
|
212
212
|
if (this.autoReconnect === false) {
|
|
213
213
|
clearInterval(this.connectionCheckerIntervalID);
|
|
@@ -430,13 +430,25 @@ export class IOCore extends EventEmitter {
|
|
|
430
430
|
case IOMsg.PONG:
|
|
431
431
|
break;
|
|
432
432
|
|
|
433
|
+
case IOMsg.ECHO:
|
|
434
|
+
try {
|
|
435
|
+
let str = decoder.decode(buffer.subarray(1))
|
|
436
|
+
|
|
437
|
+
this.emit('echo', str)
|
|
438
|
+
} catch (error) {
|
|
439
|
+
this.emit('error', new Error('ECHO data error'))
|
|
440
|
+
}
|
|
441
|
+
break;
|
|
442
|
+
|
|
433
443
|
case IOMsg.IAM_RES:
|
|
434
444
|
try {
|
|
435
445
|
let str = decoder.decode(buffer.subarray(1))
|
|
436
446
|
let jsonInfo = JSON.parse(str)
|
|
437
|
-
if (jsonInfo.ip) {
|
|
438
|
-
|
|
439
|
-
}
|
|
447
|
+
if (jsonInfo.ip) { this.ip = jsonInfo.ip; }
|
|
448
|
+
if (jsonInfo.nick) { this.nick = jsonInfo.nick; }
|
|
449
|
+
if (jsonInfo.did) { this.did = jsonInfo.did; }
|
|
450
|
+
if (jsonInfo.uid) { this.uid = jsonInfo.uid; }
|
|
451
|
+
this.emit('iam_res', str )
|
|
440
452
|
} catch (error) {
|
|
441
453
|
this.emit('error', new Error('IAM_RES data error'))
|
|
442
454
|
}
|
|
@@ -447,7 +459,7 @@ export class IOCore extends EventEmitter {
|
|
|
447
459
|
this.cid = cidStr;
|
|
448
460
|
|
|
449
461
|
// **IMPORTANT** change state before subscribe.
|
|
450
|
-
this.stateChange('ready', 'cid_ready')
|
|
462
|
+
this.stateChange('ready', 'cid_ready')
|
|
451
463
|
this.subscribe_memory_channels()
|
|
452
464
|
break;
|
|
453
465
|
|
|
@@ -455,13 +467,14 @@ export class IOCore extends EventEmitter {
|
|
|
455
467
|
let quotaLevel = buffer[1]
|
|
456
468
|
this.level = quotaLevel;
|
|
457
469
|
this.quota = quotaTable[quotaLevel];
|
|
458
|
-
console.log('
|
|
470
|
+
console.log('[QUOTA_LEVEL]', JSON.stringify(this.quota))
|
|
459
471
|
break;
|
|
460
472
|
|
|
461
473
|
case IOMsg.SERVER_CLEAR_AUTH:
|
|
462
474
|
this.useAuth = false;
|
|
463
475
|
this.boho.clearAuth();
|
|
464
476
|
this.stop();
|
|
477
|
+
console.log('[SERVER_CLEAR_AUTH] io stop.')
|
|
465
478
|
break;
|
|
466
479
|
|
|
467
480
|
case IOMsg.SERVER_REDIRECT:
|
|
@@ -538,8 +551,11 @@ export class IOCore extends EventEmitter {
|
|
|
538
551
|
switch (payloadType) {
|
|
539
552
|
|
|
540
553
|
case PAYLOAD_TYPE.EMPTY:
|
|
541
|
-
if (tag.indexOf('@') === 0) this.emit('@',
|
|
542
|
-
else
|
|
554
|
+
if (tag.indexOf('@') === 0) this.emit('@', tag, null)
|
|
555
|
+
else {
|
|
556
|
+
this.emit(tag, tag, null)
|
|
557
|
+
this.emit('message', tag, null )
|
|
558
|
+
}
|
|
543
559
|
break;
|
|
544
560
|
|
|
545
561
|
case PAYLOAD_TYPE.TEXT:
|
|
@@ -550,33 +566,48 @@ export class IOCore extends EventEmitter {
|
|
|
550
566
|
payloadStringWithoutNull = payloadBuffer.subarray(0, payloadBuffer.byteLength - 1)
|
|
551
567
|
}
|
|
552
568
|
let oneString = decoder.decode(payloadStringWithoutNull)
|
|
553
|
-
if (tag.indexOf('@') === 0) this.emit('@',
|
|
554
|
-
if (tag !== '@')
|
|
569
|
+
if (tag.indexOf('@') === 0) this.emit('@', tag, oneString)
|
|
570
|
+
if (tag !== '@'){
|
|
571
|
+
this.emit(tag, tag, oneString)
|
|
572
|
+
this.emit('message', tag, oneString)
|
|
573
|
+
}
|
|
555
574
|
break;
|
|
556
575
|
|
|
557
576
|
case PAYLOAD_TYPE.BINARY:
|
|
558
|
-
if (tag.indexOf('@') === 0) this.emit('@',
|
|
559
|
-
if (tag !== '@')
|
|
577
|
+
if (tag.indexOf('@') === 0) this.emit('@', tag, payloadBuffer)
|
|
578
|
+
if (tag !== '@'){
|
|
579
|
+
this.emit(tag, tag, payloadBuffer)
|
|
580
|
+
this.emit('message', tag, payloadBuffer)
|
|
581
|
+
}
|
|
560
582
|
break;
|
|
561
583
|
|
|
562
584
|
case PAYLOAD_TYPE.OBJECT:
|
|
563
585
|
let oneObjectBuffer = decoder.decode(payloadBuffer)
|
|
564
586
|
let oneJSONObject = JSON.parse(oneObjectBuffer)
|
|
565
|
-
if (tag.indexOf('@') === 0) this.emit('@',
|
|
566
|
-
if (tag !== '@')
|
|
587
|
+
if (tag.indexOf('@') === 0) this.emit('@', tag, oneJSONObject)
|
|
588
|
+
if (tag !== '@'){
|
|
589
|
+
this.emit(tag, tag, oneJSONObject)
|
|
590
|
+
this.emit('message', tag, oneJSONObject)
|
|
591
|
+
}
|
|
567
592
|
break;
|
|
568
593
|
|
|
569
594
|
case PAYLOAD_TYPE.MJSON:
|
|
570
595
|
let mjsonBuffer = decoder.decode(payloadBuffer)
|
|
571
596
|
let mjson = JSON.parse(mjsonBuffer)
|
|
572
|
-
if (tag.indexOf('@') === 0) this.emit('@', ...mjson
|
|
573
|
-
if (tag !== '@')
|
|
597
|
+
if (tag.indexOf('@') === 0) this.emit('@', tag, ...mjson)
|
|
598
|
+
if (tag !== '@'){
|
|
599
|
+
this.emit(tag, tag, ...mjson)
|
|
600
|
+
this.emit('message', tag, ...mjson)
|
|
601
|
+
}
|
|
574
602
|
break;
|
|
575
603
|
|
|
576
604
|
case PAYLOAD_TYPE.MBA:
|
|
577
605
|
let mbaObject = MBP.unpack(payloadBuffer)
|
|
578
|
-
if (tag.indexOf('@') === 0) this.emit('@', ...mbaObject.args
|
|
579
|
-
if (tag !== '@')
|
|
606
|
+
if (tag.indexOf('@') === 0) this.emit('@', tag, ...mbaObject.args)
|
|
607
|
+
if (tag !== '@'){
|
|
608
|
+
this.emit(tag, tag, ...mbaObject.args)
|
|
609
|
+
this.emit('message', tag, ...mbaObject.args )
|
|
610
|
+
}
|
|
580
611
|
break;
|
|
581
612
|
|
|
582
613
|
default:
|
|
@@ -668,7 +699,7 @@ export class IOCore extends EventEmitter {
|
|
|
668
699
|
*/
|
|
669
700
|
echo(args) {
|
|
670
701
|
if (args) {
|
|
671
|
-
console.log('echo args:', args)
|
|
702
|
+
// console.log('send echo args:', args)
|
|
672
703
|
this.send_enc_mode(MBP.pack(
|
|
673
704
|
MBP.MB('#MsgType', '8', IOMsg.ECHO),
|
|
674
705
|
MBP.MB('#msg', args)
|
|
@@ -757,13 +788,13 @@ export class IOCore extends EventEmitter {
|
|
|
757
788
|
*/
|
|
758
789
|
setMsgPromise(mid) {
|
|
759
790
|
return new Promise((resolve, reject) => {
|
|
760
|
-
|
|
761
|
-
setTimeout(e => {
|
|
791
|
+
const timeoutId = setTimeout(e => {
|
|
762
792
|
if (this.promiseMap.has(mid)) {
|
|
763
793
|
reject('timeout');
|
|
764
794
|
this.promiseMap.delete(mid)
|
|
765
795
|
}
|
|
766
796
|
}, this.promiseTimeOut);
|
|
797
|
+
this.promiseMap.set(mid, [resolve, reject, timeoutId]);
|
|
767
798
|
})
|
|
768
799
|
}
|
|
769
800
|
|
|
@@ -777,7 +808,8 @@ export class IOCore extends EventEmitter {
|
|
|
777
808
|
if (!res) return
|
|
778
809
|
|
|
779
810
|
if (this.promiseMap.has(res.mid)) {
|
|
780
|
-
let [resolve, reject] = this.promiseMap.get(res.mid)
|
|
811
|
+
let [resolve, reject, timeoutId] = this.promiseMap.get(res.mid)
|
|
812
|
+
clearTimeout(timeoutId)
|
|
781
813
|
this.promiseMap.delete(res.mid)
|
|
782
814
|
|
|
783
815
|
if (res.status < 128) {
|
|
@@ -886,9 +918,9 @@ export class IOCore extends EventEmitter {
|
|
|
886
918
|
|
|
887
919
|
|
|
888
920
|
/**
|
|
889
|
-
* Sends a request to a target and topic.
|
|
890
|
-
* @param {string} target - The target of the request.
|
|
891
|
-
* @param {string} topic - The topic of the request.
|
|
921
|
+
* Sends a request to a target and topic.(remote api call)
|
|
922
|
+
* @param {string} target - The target(api name) of the request.
|
|
923
|
+
* @param {string} topic - The topic(api function name) of the request.
|
|
892
924
|
* @param {...any} args - Optional arguments for the request.
|
|
893
925
|
* @returns {Promise<any>}
|
|
894
926
|
*/
|
|
@@ -924,21 +956,24 @@ export class IOCore extends EventEmitter {
|
|
|
924
956
|
*/
|
|
925
957
|
subscribe(tag) {
|
|
926
958
|
if (typeof tag !== 'string') throw TypeError('tag should be string.')
|
|
959
|
+
if (tag.length > SIZE_LIMIT.TAG_LEN1) throw TypeError('please check tag string length limit:' + SIZE_LIMIT.TAG_LEN1)
|
|
927
960
|
if (this.state !== STATES.READY) return
|
|
928
961
|
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
962
|
+
// subscribe 사용시, 사용 'ready' 이벤트시 메뉴얼 등록되므로 여기에 등록하면 2중 호출된다.
|
|
963
|
+
// 즉, channels 등록은 listen 같은 자동화 구독시만 사용.
|
|
964
|
+
// let tagList = tag.split(',')
|
|
965
|
+
// tagList.forEach(tag => {
|
|
966
|
+
// this.channels.add(tag)
|
|
967
|
+
// })
|
|
968
|
+
|
|
969
|
+
try {
|
|
970
|
+
let tagEncoded = encoder.encode(tag)
|
|
971
|
+
this.send_enc_mode(
|
|
972
|
+
Buffer.concat([
|
|
973
|
+
MBP.NB('8', IOMsg.SUBSCRIBE),
|
|
974
|
+
MBP.NB('8', tagEncoded.byteLength),
|
|
975
|
+
tagEncoded]))
|
|
976
|
+
} catch (error) { }
|
|
942
977
|
}
|
|
943
978
|
|
|
944
979
|
/**
|
|
@@ -949,20 +984,22 @@ export class IOCore extends EventEmitter {
|
|
|
949
984
|
*/
|
|
950
985
|
subscribe_promise(tag) {
|
|
951
986
|
if (typeof tag !== 'string') throw TypeError('tag should be string.')
|
|
987
|
+
if (tag.length > SIZE_LIMIT.TAG_LEN2) throw TypeError('please check tag string length limit:' + SIZE_LIMIT.TAG_LEN2)
|
|
988
|
+
|
|
952
989
|
if (this.state !== STATES.READY) {
|
|
953
990
|
return Promise.reject('subscribe_promise:: connection is not ready')
|
|
954
991
|
}
|
|
955
992
|
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
993
|
+
try {
|
|
994
|
+
let tagEncoded = encoder.encode(tag)
|
|
995
|
+
this.send_enc_mode(
|
|
996
|
+
Buffer.concat([
|
|
997
|
+
MBP.NB('8', IOMsg.SUBSCRIBE_REQ),
|
|
998
|
+
MBP.NB('16', ++this.mid),
|
|
999
|
+
MBP.NB('16', tagEncoded.byteLength),
|
|
1000
|
+
tagEncoded]))
|
|
1001
|
+
return this.setMsgPromise(this.mid)
|
|
1002
|
+
} catch (error) { }
|
|
966
1003
|
}
|
|
967
1004
|
|
|
968
1005
|
/**
|
|
@@ -1159,14 +1196,23 @@ export class IOCore extends EventEmitter {
|
|
|
1159
1196
|
* Changes the connection state and emits events.
|
|
1160
1197
|
* @param {string} state - The new state name (e.g., 'ready', 'closed').
|
|
1161
1198
|
* @param {string} [emitEventAndMessage] - Optional message to emit with the state change event.
|
|
1199
|
+
*
|
|
1200
|
+
* 주의.
|
|
1201
|
+
* 1. 상태가 변경 될 때만 'change' 이벤트 호출된다.
|
|
1202
|
+
* 2. emitEventAndMessage 옵션 값이 지정되야 해당 이벤트 이름이 호출된다.
|
|
1203
|
+
* 보통 이벤트 이름과 동일하게 적거나 이벤트 상황 안내문을 넣는다.
|
|
1162
1204
|
*/
|
|
1163
1205
|
stateChange(state, emitEventAndMessage) {
|
|
1164
|
-
// STATES constant name
|
|
1165
|
-
// eventName
|
|
1166
|
-
// .state
|
|
1206
|
+
// STATES constant name <string> upperCase
|
|
1207
|
+
// eventName and .stateName <string> lowerCase
|
|
1208
|
+
// .state <number>
|
|
1209
|
+
// console.log('### stateChange reason:', emitEventAndMessage )
|
|
1167
1210
|
let eventName = state.toLowerCase()
|
|
1168
1211
|
this.state = STATES[state.toUpperCase()] // state: number
|
|
1169
|
-
|
|
1212
|
+
|
|
1213
|
+
if (emitEventAndMessage){
|
|
1214
|
+
this.emit(eventName, emitEventAndMessage)
|
|
1215
|
+
}
|
|
1170
1216
|
|
|
1171
1217
|
if (this.stateName !== eventName) {
|
|
1172
1218
|
this.stateName = eventName
|
package/src/server/Manager.js
CHANGED
|
@@ -181,8 +181,8 @@ export class Manager {
|
|
|
181
181
|
serverSignalTo(tag, ...args) {
|
|
182
182
|
let cid = tag.split('@')[0]
|
|
183
183
|
let topic = tag.split('@')[1]
|
|
184
|
-
let sigPack = getSignalPack('@' + topic, ...args)
|
|
185
184
|
if (cid && this.cid2remote.has(cid)) {
|
|
185
|
+
let sigPack = getSignalPack('@' + topic, ...args)
|
|
186
186
|
let targetRemote = this.cid2remote.get(cid)
|
|
187
187
|
console.log('target', targetRemote.state, targetRemote.cid)
|
|
188
188
|
targetRemote.send_enc_mode(sigPack)
|
|
@@ -193,11 +193,11 @@ export class Manager {
|
|
|
193
193
|
}
|
|
194
194
|
|
|
195
195
|
sender(tag, remote, message) {
|
|
196
|
-
if (serverOption.
|
|
197
|
-
|
|
196
|
+
if (serverOption.membersOnly && !remote.boho.isAuthorized) {
|
|
197
|
+
console.log("[membersOnly] unAuthorized remote.",tag )
|
|
198
198
|
remote.send(Buffer.from([IOMsg.SERVER_CLEAR_AUTH]))
|
|
199
199
|
remote.close()
|
|
200
|
-
return ['err', '
|
|
200
|
+
return ['err', 'unAuthorized']
|
|
201
201
|
}
|
|
202
202
|
|
|
203
203
|
if (tag.indexOf('$') == 0) return ['err', 'prefix $ is reserved for userSet tag.']
|
|
@@ -284,17 +284,18 @@ export class Manager {
|
|
|
284
284
|
// console.log('## null remote' )
|
|
285
285
|
break;
|
|
286
286
|
}
|
|
287
|
-
|
|
287
|
+
// change: from v4.x allow receiveSelfMessages.
|
|
288
|
+
// if (c !== remote) {
|
|
288
289
|
c.send_enc_mode(message);
|
|
289
290
|
sentCounter++;
|
|
290
|
-
}
|
|
291
|
+
// }
|
|
291
292
|
}
|
|
292
293
|
|
|
293
|
-
if (serverOption.useQuota.publishCounter) {
|
|
294
|
-
|
|
295
|
-
} else {
|
|
296
|
-
|
|
297
|
-
}
|
|
294
|
+
// if (serverOption.useQuota.publishCounter) {
|
|
295
|
+
// // console.log(`pub >> [${tag}] sent: ${sentCounter} [use quota limit: ${remote.quota.publishCounter }] total: ${remotes.size} subscribers. ` )
|
|
296
|
+
// } else {
|
|
297
|
+
// // console.log(`pub >> [${tag}] sent: ${sentCounter} [no quota limit] total: ${remotes.size} subscribers. ` )
|
|
298
|
+
// }
|
|
298
299
|
return ['ok', sentCounter]
|
|
299
300
|
}
|
|
300
301
|
}
|
|
@@ -444,6 +445,5 @@ export class Manager {
|
|
|
444
445
|
this.retain_messages.clear();
|
|
445
446
|
this.channel_map.clear();
|
|
446
447
|
|
|
447
|
-
console.log('Manager closed');
|
|
448
448
|
}
|
|
449
449
|
}
|
package/src/server/Metric.js
CHANGED
|
@@ -118,8 +118,7 @@ export class Metric {
|
|
|
118
118
|
ip: remote.ip,
|
|
119
119
|
uptime: Math.trunc((Date.now() - remote.socket.openTime) / 1000),
|
|
120
120
|
nick: remote.nick,
|
|
121
|
-
ssid: remote.ssid
|
|
122
|
-
echo: remote.lastEchoMessage
|
|
121
|
+
ssid: remote.ssid
|
|
123
122
|
}
|
|
124
123
|
} else if (mode == 3) {
|
|
125
124
|
return {
|