@arcblock/event-hub 1.28.9 → 1.29.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/esm/client.d.mts +43 -0
- package/esm/client.mjs +134 -0
- package/esm/constant.d.mts +7 -0
- package/esm/constant.mjs +8 -0
- package/esm/index.d.mts +7 -0
- package/esm/index.mjs +28 -0
- package/esm/rpc.d.mts +51 -0
- package/esm/rpc.mjs +106 -0
- package/esm/server-abtnode.d.mts +6 -0
- package/esm/server-abtnode.mjs +11 -0
- package/esm/server.d.mts +55 -0
- package/esm/server.mjs +142 -0
- package/esm/single.d.mts +10 -0
- package/esm/single.mjs +16 -0
- package/lib/_virtual/rolldown_runtime.cjs +29 -0
- package/lib/client.cjs +139 -0
- package/lib/client.d.cts +43 -0
- package/lib/constant.cjs +12 -0
- package/lib/constant.d.cts +7 -0
- package/lib/index.cjs +31 -0
- package/lib/index.d.cts +7 -0
- package/lib/rpc.cjs +110 -0
- package/lib/rpc.d.cts +51 -0
- package/lib/server-abtnode.cjs +12 -0
- package/lib/server-abtnode.d.cts +6 -0
- package/lib/server.cjs +147 -0
- package/lib/server.d.cts +55 -0
- package/lib/single.cjs +19 -0
- package/lib/single.d.cts +10 -0
- package/package.json +33 -6
- package/lib/client-fallback.js +0 -13
- package/lib/client.js +0 -183
- package/lib/constant.js +0 -6
- package/lib/index.js +0 -26
- package/lib/rpc.js +0 -119
- package/lib/server-abtnode.js +0 -9
- package/lib/server.js +0 -155
- package/single.js +0 -3
package/package.json
CHANGED
|
@@ -3,14 +3,41 @@
|
|
|
3
3
|
"publishConfig": {
|
|
4
4
|
"access": "public"
|
|
5
5
|
},
|
|
6
|
-
"version": "1.
|
|
6
|
+
"version": "1.29.0",
|
|
7
7
|
"description": "A nodejs module for local and remote Inter Process Event",
|
|
8
|
-
"
|
|
8
|
+
"type": "module",
|
|
9
|
+
"main": "./lib/index.cjs",
|
|
10
|
+
"module": "./esm/index.mjs",
|
|
11
|
+
"types": "./esm/index.d.mts",
|
|
12
|
+
"exports": {
|
|
13
|
+
".": {
|
|
14
|
+
"types": "./esm/index.d.mts",
|
|
15
|
+
"import": "./esm/index.mjs",
|
|
16
|
+
"default": "./lib/index.cjs"
|
|
17
|
+
},
|
|
18
|
+
"./single": {
|
|
19
|
+
"types": "./esm/single.d.mts",
|
|
20
|
+
"import": "./esm/single.mjs",
|
|
21
|
+
"default": "./lib/single.cjs"
|
|
22
|
+
},
|
|
23
|
+
"./lib/*.js": {
|
|
24
|
+
"types": "./esm/*.d.mts",
|
|
25
|
+
"import": "./esm/*.mjs",
|
|
26
|
+
"default": "./lib/*.cjs"
|
|
27
|
+
},
|
|
28
|
+
"./lib/*": {
|
|
29
|
+
"types": "./esm/*.d.mts",
|
|
30
|
+
"import": "./esm/*.mjs",
|
|
31
|
+
"default": "./lib/*.cjs"
|
|
32
|
+
}
|
|
33
|
+
},
|
|
9
34
|
"files": [
|
|
10
35
|
"lib",
|
|
11
|
-
"
|
|
36
|
+
"esm"
|
|
12
37
|
],
|
|
13
38
|
"scripts": {
|
|
39
|
+
"build": "tsdown",
|
|
40
|
+
"prebuild": "rm -rf lib esm",
|
|
14
41
|
"lint": "biome check",
|
|
15
42
|
"lint:fix": "biome check --write",
|
|
16
43
|
"test": "bun test",
|
|
@@ -23,9 +50,9 @@
|
|
|
23
50
|
],
|
|
24
51
|
"license": "MIT",
|
|
25
52
|
"dependencies": {
|
|
26
|
-
"@arcblock/did": "1.
|
|
27
|
-
"@arcblock/jwt": "1.
|
|
28
|
-
"@ocap/wallet": "1.
|
|
53
|
+
"@arcblock/did": "1.29.0",
|
|
54
|
+
"@arcblock/jwt": "1.29.0",
|
|
55
|
+
"@ocap/wallet": "1.29.0",
|
|
29
56
|
"amp-message": "~0.1.2",
|
|
30
57
|
"axon": "^2.0.3"
|
|
31
58
|
},
|
package/lib/client-fallback.js
DELETED
package/lib/client.js
DELETED
|
@@ -1,183 +0,0 @@
|
|
|
1
|
-
const { EventEmitter } = require('node:events');
|
|
2
|
-
const axon = require('axon');
|
|
3
|
-
const roundrobin = require('axon/lib/plugins/round-robin');
|
|
4
|
-
const queue = require('axon/lib/plugins/queue');
|
|
5
|
-
const { fromSecretKey, WalletType } = require('@ocap/wallet');
|
|
6
|
-
const { DidType, isEthereumType } = require('@arcblock/did');
|
|
7
|
-
|
|
8
|
-
const { toTypeInfo } = require('@arcblock/did');
|
|
9
|
-
|
|
10
|
-
const { RESERVED_EVENT_PREFIX, EVENT_AUTH, EVENT_AUTH_SUCCESS, EVENT_AUTH_FAIL } = require('./constant');
|
|
11
|
-
|
|
12
|
-
const checkEvent = (event) => {
|
|
13
|
-
if (event.startsWith(RESERVED_EVENT_PREFIX)) {
|
|
14
|
-
throw new Error(`event cannot start with ${RESERVED_EVENT_PREFIX}`);
|
|
15
|
-
}
|
|
16
|
-
};
|
|
17
|
-
|
|
18
|
-
const getWallet = (appSk, type) => {
|
|
19
|
-
let t = type;
|
|
20
|
-
let sk = appSk;
|
|
21
|
-
|
|
22
|
-
if (isEthereumType(DidType(type))) {
|
|
23
|
-
sk = appSk.slice(0, 66);
|
|
24
|
-
t = WalletType(type);
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
return fromSecretKey(sk, t);
|
|
28
|
-
};
|
|
29
|
-
|
|
30
|
-
const defaultOpts = {
|
|
31
|
-
port: undefined,
|
|
32
|
-
hostname: '0.0.0.0',
|
|
33
|
-
autoConnect: false,
|
|
34
|
-
wallet: undefined,
|
|
35
|
-
did: undefined,
|
|
36
|
-
sk: undefined,
|
|
37
|
-
};
|
|
38
|
-
|
|
39
|
-
class Socket extends axon.SubEmitterSocket {
|
|
40
|
-
constructor(opts) {
|
|
41
|
-
super();
|
|
42
|
-
|
|
43
|
-
const wallet = opts.wallet || getWallet(opts.sk, toTypeInfo(opts.did));
|
|
44
|
-
this._authenticated = false;
|
|
45
|
-
this._pendingMessages = [];
|
|
46
|
-
this._wallet = wallet;
|
|
47
|
-
|
|
48
|
-
this.sock.use((sock) => {
|
|
49
|
-
sock.on('connect', async () => {
|
|
50
|
-
this._authenticated = false;
|
|
51
|
-
|
|
52
|
-
// Use wallet.signJWT() so business can override it for remote signing
|
|
53
|
-
const token = await wallet.signJWT();
|
|
54
|
-
sock.send(EVENT_AUTH, {
|
|
55
|
-
pk: wallet.publicKey,
|
|
56
|
-
token,
|
|
57
|
-
});
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
sock.on('close', () => {
|
|
61
|
-
this._authenticated = false;
|
|
62
|
-
this._pendingMessages = [];
|
|
63
|
-
});
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
// Listen for auth events on the SubEmitterSocket instance (this), not on raw sock
|
|
67
|
-
this.on(EVENT_AUTH_SUCCESS, () => {
|
|
68
|
-
this._authenticated = true;
|
|
69
|
-
|
|
70
|
-
// Send all pending messages after authentication
|
|
71
|
-
const pending = this._pendingMessages.slice();
|
|
72
|
-
this._pendingMessages = [];
|
|
73
|
-
pending.forEach(([event, data]) => this.sock.send(event, data));
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
this.on(EVENT_AUTH_FAIL, ({ msg } = {}) => {
|
|
77
|
-
this.emit('error', { msg });
|
|
78
|
-
this._authenticated = false;
|
|
79
|
-
this._pendingMessages = [];
|
|
80
|
-
this.sock.socks.forEach((s) => s.close());
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
this.sock.use(queue());
|
|
84
|
-
this.sock.use(roundrobin({ fallback: this.sock.enqueue }));
|
|
85
|
-
|
|
86
|
-
const originalSend = this.sock.send.bind(this.sock);
|
|
87
|
-
this.send = (event, data) => {
|
|
88
|
-
// Check both authentication AND connection status
|
|
89
|
-
const hasConnection = this.sock.socks && this.sock.socks.length > 0;
|
|
90
|
-
if (this._authenticated && hasConnection) {
|
|
91
|
-
originalSend(event, data);
|
|
92
|
-
} else {
|
|
93
|
-
this._pendingMessages.push([event, data]);
|
|
94
|
-
}
|
|
95
|
-
};
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
class Client extends EventEmitter {
|
|
100
|
-
constructor(opts = {}) {
|
|
101
|
-
super();
|
|
102
|
-
|
|
103
|
-
if (!opts.port) {
|
|
104
|
-
throw new Error('port should not be empty');
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
if (!opts.wallet) {
|
|
108
|
-
if (!opts.did) {
|
|
109
|
-
throw new Error('did should not be empty when wallet is not provided');
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
// sk should be provided when wallet is not provided
|
|
113
|
-
if (!opts.sk) {
|
|
114
|
-
throw new Error('Either wallet or sk should be provided');
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
const wallet = opts.wallet || getWallet(opts.sk, toTypeInfo(opts.did));
|
|
119
|
-
const did = opts.wallet ? opts.wallet.address : opts.did;
|
|
120
|
-
|
|
121
|
-
if (wallet.address !== did) {
|
|
122
|
-
throw new Error('did does not match the provided wallet/sk');
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
this.opts = Object.assign({}, defaultOpts, opts, { wallet, did });
|
|
126
|
-
this._client = new Socket(this.opts);
|
|
127
|
-
this._wallet = wallet;
|
|
128
|
-
|
|
129
|
-
this._bindEvent();
|
|
130
|
-
|
|
131
|
-
if (this.opts.autoConnect) {
|
|
132
|
-
this.connect();
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
connect() {
|
|
137
|
-
const { port, hostname } = this.opts;
|
|
138
|
-
this._client.connect(port, hostname);
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
close() {
|
|
142
|
-
this._client._authenticated = false;
|
|
143
|
-
this._client._pendingMessages = [];
|
|
144
|
-
this._client.close();
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
broadcast(event, data) {
|
|
148
|
-
checkEvent(event);
|
|
149
|
-
this._client.send(event, data);
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
on(name, fn) {
|
|
153
|
-
checkEvent(name);
|
|
154
|
-
super.on(name, fn);
|
|
155
|
-
this._client.on(name, fn);
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
off(name, fn) {
|
|
159
|
-
checkEvent(name);
|
|
160
|
-
this._client.off(name);
|
|
161
|
-
|
|
162
|
-
if (fn) {
|
|
163
|
-
super.off(name, fn);
|
|
164
|
-
} else {
|
|
165
|
-
super.removeAllListeners(name);
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
/**
|
|
170
|
-
* Get the wallet instance
|
|
171
|
-
* Business can override wallet.signJWT() for remote signing
|
|
172
|
-
* @returns {WalletObject}
|
|
173
|
-
*/
|
|
174
|
-
getWallet() {
|
|
175
|
-
return this._wallet;
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
_bindEvent() {
|
|
179
|
-
this._client.on('error', this.emit.bind(this, 'error'));
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
module.exports = Client;
|
package/lib/constant.js
DELETED
package/lib/index.js
DELETED
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
const Client = require('./client');
|
|
2
|
-
const ClientFallback = require('./client-fallback');
|
|
3
|
-
|
|
4
|
-
const port = Number(process.env.ABT_NODE_EVENT_PORT);
|
|
5
|
-
const hostname = Number(process.env.ABT_NODE_EVENT_HOSTNAME) || '127.0.0.1';
|
|
6
|
-
|
|
7
|
-
let did;
|
|
8
|
-
let sk;
|
|
9
|
-
|
|
10
|
-
if (process.env.BLOCKLET_APP_SK && process.env.BLOCKLET_APP_ID) {
|
|
11
|
-
sk = process.env.BLOCKLET_APP_SK;
|
|
12
|
-
did = process.env.BLOCKLET_APP_ID;
|
|
13
|
-
} else if (process.env.ABT_NODE_SK && process.env.ABT_NODE_DID) {
|
|
14
|
-
sk = process.env.ABT_NODE_SK;
|
|
15
|
-
did = process.env.ABT_NODE_DID;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
if (port && did && sk) {
|
|
19
|
-
module.exports = new Client({ port, hostname, did, sk, autoConnect: true });
|
|
20
|
-
} else {
|
|
21
|
-
// export fallback Client because missing any of port / did / sk
|
|
22
|
-
module.exports = new ClientFallback();
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
module.exports.Client = Client;
|
|
26
|
-
module.exports.ClientFallback = ClientFallback;
|
package/lib/rpc.js
DELETED
|
@@ -1,119 +0,0 @@
|
|
|
1
|
-
const axon = require('axon');
|
|
2
|
-
const Message = require('amp-message');
|
|
3
|
-
const { randomUUID } = require('node:crypto');
|
|
4
|
-
|
|
5
|
-
class Client extends axon.Socket {
|
|
6
|
-
/**
|
|
7
|
-
* @param {number} port
|
|
8
|
-
* @param {string} host
|
|
9
|
-
*/
|
|
10
|
-
constructor(port, host = '127.0.0.1') {
|
|
11
|
-
super();
|
|
12
|
-
this.port = port;
|
|
13
|
-
this.host = host;
|
|
14
|
-
this.online = false;
|
|
15
|
-
this.connectPromise = null;
|
|
16
|
-
// reqId -> { resolve, reject, to, errorPrefix? }
|
|
17
|
-
this.inflight = new Map();
|
|
18
|
-
|
|
19
|
-
this.on('connect', () => {
|
|
20
|
-
this.online = true;
|
|
21
|
-
});
|
|
22
|
-
this.on('reconnect attempt', () => {
|
|
23
|
-
this.online = false;
|
|
24
|
-
});
|
|
25
|
-
this.on('disconnect', () => {
|
|
26
|
-
this.online = false;
|
|
27
|
-
});
|
|
28
|
-
this.on('error', () => {
|
|
29
|
-
/* no-op */
|
|
30
|
-
});
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
onmessage() {
|
|
34
|
-
return (buf) => {
|
|
35
|
-
try {
|
|
36
|
-
const msg = new Message(buf);
|
|
37
|
-
const [event, data] = msg.args;
|
|
38
|
-
|
|
39
|
-
if (data && typeof data === 'object' && data.reqId) {
|
|
40
|
-
const entry = this.inflight.get(data.reqId);
|
|
41
|
-
if (entry) {
|
|
42
|
-
clearTimeout(entry.to);
|
|
43
|
-
this.inflight.delete(data.reqId);
|
|
44
|
-
if (data.ok === false) {
|
|
45
|
-
entry.reject(
|
|
46
|
-
new Error(entry.errorPrefix ? `${entry.errorPrefix}: ${data.error}` : data.error || 'request failed')
|
|
47
|
-
);
|
|
48
|
-
} else {
|
|
49
|
-
entry.resolve(data.data);
|
|
50
|
-
}
|
|
51
|
-
return; // 已消费
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
this.emit(event, data);
|
|
55
|
-
} catch (e) {
|
|
56
|
-
console.error('[axon] decode error', e);
|
|
57
|
-
}
|
|
58
|
-
};
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
sendEvent(event, data) {
|
|
62
|
-
const buf = this.pack([event, data]);
|
|
63
|
-
const s = this.socks[0];
|
|
64
|
-
if (s?.writable) s.write(buf);
|
|
65
|
-
else throw new Error('axon socket not writable/connected');
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
async ensureOnline() {
|
|
69
|
-
if (!this.port || !this.host) {
|
|
70
|
-
throw new Error('PORT and HOST must be set via constructor: new Client(port, host)');
|
|
71
|
-
}
|
|
72
|
-
if (!this.connectPromise) {
|
|
73
|
-
this.connectPromise = new Promise((resolve, reject) => {
|
|
74
|
-
this.connect(this.port, this.host, (err) => (err ? reject(err) : resolve(this)));
|
|
75
|
-
});
|
|
76
|
-
}
|
|
77
|
-
await this.connectPromise;
|
|
78
|
-
if (this.online) return this;
|
|
79
|
-
// 等到重连完成
|
|
80
|
-
await new Promise((r) => this.once('connect', r));
|
|
81
|
-
return this;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
/**
|
|
85
|
-
* 通用 RPC:发送 event,等待响应(默认只按 reqId 匹配)
|
|
86
|
-
* @param {string} event 例如 'pm2/start'
|
|
87
|
-
* @param {object} payload 发送为 { reqId, payload }
|
|
88
|
-
* @param {object} [opts]
|
|
89
|
-
* @param {number} [opts.timeoutMs=120000]
|
|
90
|
-
* @param {string} [opts.respEvent] // 可选:仅用于调试;匹配靠 reqId,不强依赖此项
|
|
91
|
-
* @param {string} [opts.errorPrefix] // 统一错误前缀
|
|
92
|
-
* @returns {Promise<any>}
|
|
93
|
-
*/
|
|
94
|
-
async rpc(event, payload = {}, { timeoutMs = 120000, respEvent, errorPrefix } = {}) {
|
|
95
|
-
await this.ensureOnline();
|
|
96
|
-
const reqId = randomUUID();
|
|
97
|
-
|
|
98
|
-
return new Promise((resolve, reject) => {
|
|
99
|
-
const to = setTimeout(() => {
|
|
100
|
-
this.inflight.delete(reqId);
|
|
101
|
-
reject(new Error(`${errorPrefix || 'request'} timeout`));
|
|
102
|
-
}, timeoutMs);
|
|
103
|
-
|
|
104
|
-
this.inflight.set(reqId, { resolve, reject, to, errorPrefix });
|
|
105
|
-
|
|
106
|
-
this.sendEvent(event, { reqId, payload });
|
|
107
|
-
|
|
108
|
-
if (respEvent) {
|
|
109
|
-
const once = (data) => {
|
|
110
|
-
if (!data || data.reqId !== reqId) return;
|
|
111
|
-
this.off(respEvent, once);
|
|
112
|
-
};
|
|
113
|
-
this.on(respEvent, once);
|
|
114
|
-
}
|
|
115
|
-
});
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
module.exports = Client;
|
package/lib/server-abtnode.js
DELETED
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
const Server = require('./server');
|
|
2
|
-
|
|
3
|
-
const port = Number(process.env.ABT_NODE_EVENT_PORT);
|
|
4
|
-
const hostname = Number(process.env.ABT_NODE_EVENT_HOSTNAME) || '127.0.0.1';
|
|
5
|
-
|
|
6
|
-
const server = new Server();
|
|
7
|
-
server.bind(port, hostname);
|
|
8
|
-
|
|
9
|
-
module.exports = server;
|
package/lib/server.js
DELETED
|
@@ -1,155 +0,0 @@
|
|
|
1
|
-
const axon = require('axon');
|
|
2
|
-
const Message = require('amp-message');
|
|
3
|
-
const JWT = require('@arcblock/jwt');
|
|
4
|
-
const { EVENT_AUTH, EVENT_AUTH_SUCCESS, EVENT_AUTH_FAIL } = require('./constant');
|
|
5
|
-
|
|
6
|
-
const getDid = (jwt) => jwt.iss.replace(/^did:abt:/, '');
|
|
7
|
-
|
|
8
|
-
// 兼容 { reqId, payload } 或直接 payload
|
|
9
|
-
function normalizeReq(input) {
|
|
10
|
-
if (input && typeof input === 'object' && ('payload' in input || 'reqId' in input)) {
|
|
11
|
-
const { reqId, payload } = input;
|
|
12
|
-
return { reqId, payload };
|
|
13
|
-
}
|
|
14
|
-
return { reqId: undefined, payload: input };
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
// 规范响应:默认 { ok: true, data }
|
|
18
|
-
function normalizeRes(result) {
|
|
19
|
-
if (result && typeof result === 'object' && ('ok' in result || 'data' in result || 'error' in result)) {
|
|
20
|
-
return result;
|
|
21
|
-
}
|
|
22
|
-
return { ok: true, data: result };
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
class Server extends axon.Socket {
|
|
26
|
-
constructor(opts = {}) {
|
|
27
|
-
super();
|
|
28
|
-
|
|
29
|
-
this.jwt = opts?.jwt || JWT;
|
|
30
|
-
|
|
31
|
-
// 路由表:event -> { fn, authRequired }
|
|
32
|
-
this.handlers = new Map();
|
|
33
|
-
|
|
34
|
-
this.on('message', async (event, data, sock) => {
|
|
35
|
-
const route = this.handlers.get(event);
|
|
36
|
-
|
|
37
|
-
if (route) {
|
|
38
|
-
const { fn, authRequired } = route;
|
|
39
|
-
// 若该路由要求鉴权且当前连接未鉴权,则拒绝
|
|
40
|
-
if (authRequired && !sock.channel) {
|
|
41
|
-
console.error(`unauthenticated socket blocked for event "${event}"`);
|
|
42
|
-
return;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
const { reqId, payload } = normalizeReq(data);
|
|
46
|
-
|
|
47
|
-
try {
|
|
48
|
-
const ctx = { sock, channel: sock.channel, event, server: this };
|
|
49
|
-
const result = await fn(payload, ctx);
|
|
50
|
-
const res = normalizeRes(result);
|
|
51
|
-
const replyEvent = `${event}:res`;
|
|
52
|
-
|
|
53
|
-
// 已鉴权:按 channel 投递;未鉴权:仅回当前 socket
|
|
54
|
-
if (sock.channel) {
|
|
55
|
-
this.send(replyEvent, { reqId, ...res }, sock.channel);
|
|
56
|
-
} else {
|
|
57
|
-
const buf = this.pack([replyEvent, { reqId, ...res }]);
|
|
58
|
-
if (sock.writable) sock.write(buf);
|
|
59
|
-
}
|
|
60
|
-
} catch (err) {
|
|
61
|
-
const replyEvent = `${event}:res`;
|
|
62
|
-
const payloadErr = { reqId, ok: false, error: err?.message || String(err) };
|
|
63
|
-
if (sock.channel) {
|
|
64
|
-
this.send(replyEvent, payloadErr, sock.channel);
|
|
65
|
-
} else {
|
|
66
|
-
const buf = this.pack([replyEvent, payloadErr]);
|
|
67
|
-
if (sock.writable) sock.write(buf);
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
return; // 路由事件已消费,不再广播
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
// 非路由事件:保持原有逻辑(必须已鉴权,否则跳过)
|
|
74
|
-
if (!sock.channel) {
|
|
75
|
-
console.error('skip message of unauthenticated socket');
|
|
76
|
-
return;
|
|
77
|
-
}
|
|
78
|
-
this.send(event, data, sock.channel);
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
this.on('connect', (socket) => {
|
|
82
|
-
console.log('event-hub client connected', socket._peername);
|
|
83
|
-
});
|
|
84
|
-
this.on('reconnect attempt', (socket) => {
|
|
85
|
-
console.log('event-hub client reconnect', socket._peername);
|
|
86
|
-
});
|
|
87
|
-
this.on('disconnect', (socket) => {
|
|
88
|
-
console.log('event-hub client disconnected', socket._peername);
|
|
89
|
-
});
|
|
90
|
-
this.on('drop', (args) => {
|
|
91
|
-
console.log('event-hub server drop message', args);
|
|
92
|
-
});
|
|
93
|
-
this.on('error', (err) => {
|
|
94
|
-
console.log('event-hub server error', err);
|
|
95
|
-
});
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
onmessage(sock) {
|
|
99
|
-
return (buf) => {
|
|
100
|
-
const msg = new Message(buf);
|
|
101
|
-
const [event, data] = msg.args;
|
|
102
|
-
if (event === EVENT_AUTH) {
|
|
103
|
-
// JWT.verify returns a Promise but is synchronously resolved (no await inside)
|
|
104
|
-
// We use .then() to handle it and send confirmation to client
|
|
105
|
-
const { pk, token } = data;
|
|
106
|
-
this.jwt
|
|
107
|
-
.verify(token, pk)
|
|
108
|
-
.then((valid) => {
|
|
109
|
-
if (!valid) {
|
|
110
|
-
throw new Error('token verify failed');
|
|
111
|
-
}
|
|
112
|
-
const did = getDid(this.jwt.decode(token));
|
|
113
|
-
sock.channel = did;
|
|
114
|
-
// Send authentication success confirmation
|
|
115
|
-
const successBuf = this.pack([EVENT_AUTH_SUCCESS, {}]);
|
|
116
|
-
sock.write(successBuf);
|
|
117
|
-
})
|
|
118
|
-
.catch((err) => {
|
|
119
|
-
console.error(err);
|
|
120
|
-
const resBuf = this.pack([EVENT_AUTH_FAIL, { msg: err.message }]);
|
|
121
|
-
sock.write(resBuf);
|
|
122
|
-
});
|
|
123
|
-
return;
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
this.emit('message', event, data, sock);
|
|
127
|
-
};
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
send(event, data, channel) {
|
|
131
|
-
const { socks } = this;
|
|
132
|
-
const buf = this.pack([event, data]);
|
|
133
|
-
|
|
134
|
-
for (const sock of socks) {
|
|
135
|
-
if (sock.channel === channel && sock.writable) {
|
|
136
|
-
sock.write(buf);
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
return this;
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
/**
|
|
143
|
-
* 注册路由
|
|
144
|
-
* @param {string} event
|
|
145
|
-
* @param {(payload:any, ctx:{sock:any, channel?:string, event:string, server:Server})=>any|Promise<any>} handler
|
|
146
|
-
* @param {{authRequired?: boolean}} [opts] - 默认 true;设为 false 则该事件免鉴权
|
|
147
|
-
*/
|
|
148
|
-
register(event, handler, opts = {}) {
|
|
149
|
-
const authRequired = opts.authRequired !== false; // 默认 true
|
|
150
|
-
this.handlers.set(event, { fn: handler, authRequired });
|
|
151
|
-
return this;
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
module.exports = Server;
|
package/single.js
DELETED