@arcblock/event-hub 1.6.10

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/LICENSE ADDED
@@ -0,0 +1,13 @@
1
+ Copyright 2018-2019 ArcBlock
2
+
3
+ Licensed under the Apache License, Version 2.0 (the "License");
4
+ you may not use this file except in compliance with the License.
5
+ You may obtain a copy of the License at
6
+
7
+ http://www.apache.org/licenses/LICENSE-2.0
8
+
9
+ Unless required by applicable law or agreed to in writing, software
10
+ distributed under the License is distributed on an "AS IS" BASIS,
11
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ See the License for the specific language governing permissions and
13
+ limitations under the License.
package/README.md ADDED
@@ -0,0 +1,55 @@
1
+ # Event Hub
2
+
3
+ A module for local and remote Inter Process Event
4
+
5
+ ![image](./docs/event-hub.svg)
6
+
7
+ ## Usage
8
+
9
+ Install
10
+
11
+ ```shell
12
+ yarn add @arcblock/event-hub
13
+ ```
14
+
15
+ Server
16
+
17
+ ```javascript
18
+ process.env.ABT_NODE_EVENT_PORT=8888;
19
+
20
+ require('@arcblock/event-hub/lib/server.js);
21
+ ```
22
+
23
+ Client
24
+
25
+ ```javascript
26
+ // process 1
27
+
28
+ const Client = require('@arcblock/event-hub/lib/client');
29
+
30
+ const port = ''; // event-hub server port
31
+ const did = ''; // your did
32
+ const sk = ''; // your sk
33
+
34
+ const client = new Client({ port, did, sk, autoConnect: true });
35
+
36
+ const data = { foo: bar };
37
+
38
+ client.broadcast('my-event', data);
39
+ ```
40
+
41
+ ```javascript
42
+ // process 2
43
+
44
+ const Client = require('@arcblock/event-hub/lib/client');
45
+
46
+ const port = ''; // event-hub server port
47
+ const did = ''; // your did
48
+ const sk = ''; // your sk
49
+
50
+ const client = new Client({ port, did, sk, autoConnect: true });
51
+
52
+ client.on('my-event', (data) => {
53
+ // handle data
54
+ });
55
+ ```
@@ -0,0 +1,13 @@
1
+ const { EventEmitter } = require('events');
2
+
3
+ class Client extends EventEmitter {
4
+ broadcast(...args) {
5
+ return this.emit(...args);
6
+ }
7
+
8
+ off(event) {
9
+ this.removeAllListeners(event);
10
+ }
11
+ }
12
+
13
+ module.exports = Client;
package/lib/client.js ADDED
@@ -0,0 +1,116 @@
1
+ // eslint-disable-next-line max-classes-per-file
2
+ const { EventEmitter } = require('events');
3
+ const axon = require('axon');
4
+ const roundrobin = require('axon/lib/plugins/round-robin');
5
+ const queue = require('axon/lib/plugins/queue');
6
+ const { fromSecretKey } = require('@ocap/wallet');
7
+ const JWT = require('@arcblock/jwt');
8
+ const { toTypeInfo } = require('@arcblock/did');
9
+
10
+ const { RESERVED_EVENT_PREFIX, EVENT_AUTH, 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 defaultOpts = {
19
+ port: undefined,
20
+ hostname: '0.0.0.0',
21
+ autoConnect: false,
22
+ did: undefined,
23
+ pk: undefined,
24
+ sk: undefined,
25
+ };
26
+
27
+ class Socket extends axon.SubEmitterSocket {
28
+ constructor(opts) {
29
+ super();
30
+
31
+ this.sock.use((sock) => {
32
+ sock.on('connect', () => {
33
+ const { did, pk, sk } = opts;
34
+ sock.send(EVENT_AUTH, {
35
+ pk,
36
+ token: JWT.sign(did, sk),
37
+ });
38
+
39
+ sock.on(EVENT_AUTH_FAIL, ({ msg } = {}) => {
40
+ this.emit('error', { msg });
41
+ sock.close();
42
+ });
43
+ });
44
+ });
45
+
46
+ this.sock.use(queue());
47
+ this.sock.use(roundrobin({ fallback: this.sock.enqueue }));
48
+ this.send = this.sock.send.bind(this.sock);
49
+ }
50
+ }
51
+
52
+ class Client extends EventEmitter {
53
+ constructor(opts = {}) {
54
+ super();
55
+
56
+ if (!opts.port) {
57
+ throw new Error('port should not be empty');
58
+ }
59
+
60
+ if (!opts.did || !opts.sk) {
61
+ throw new Error('did, sk should not be empty');
62
+ }
63
+
64
+ const wallet = fromSecretKey(opts.sk, toTypeInfo(opts.did));
65
+
66
+ if (wallet.address !== opts.did) {
67
+ throw new Error('did, sk does not match');
68
+ }
69
+
70
+ this.opts = Object.assign({}, defaultOpts, opts, { pk: wallet.publicKey });
71
+ this._client = new Socket(this.opts);
72
+
73
+ this._bindEvent();
74
+
75
+ if (this.opts.autoConnect) {
76
+ this.connect();
77
+ }
78
+ }
79
+
80
+ connect() {
81
+ const { port, hostname } = this.opts;
82
+ this._client.connect(port, hostname);
83
+ }
84
+
85
+ close() {
86
+ this._client.close();
87
+ }
88
+
89
+ broadcast(event, data) {
90
+ checkEvent(event);
91
+ this._client.send(event, data);
92
+ }
93
+
94
+ on(name, fn) {
95
+ checkEvent(name);
96
+ super.on(name, fn);
97
+ this._client.on(name, fn);
98
+ }
99
+
100
+ off(name, fn) {
101
+ checkEvent(name);
102
+ this._client.off(name);
103
+
104
+ if (fn) {
105
+ super.off(name, fn);
106
+ } else {
107
+ super.removeAllListeners(name);
108
+ }
109
+ }
110
+
111
+ _bindEvent() {
112
+ this._client.on('error', this.emit.bind(this, 'error'));
113
+ }
114
+ }
115
+
116
+ module.exports = Client;
@@ -0,0 +1,5 @@
1
+ module.exports = {
2
+ RESERVED_EVENT_PREFIX: 'EVENT_HUB:',
3
+ EVENT_AUTH: 'EVENT_HUB:AUTH',
4
+ EVENT_AUTH_FAIL: 'EVENT_HUB:AUTH_FAIL',
5
+ };
package/lib/index.js ADDED
@@ -0,0 +1,23 @@
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
+ }
@@ -0,0 +1,10 @@
1
+ /* eslint-disable no-console */
2
+ const Server = require('./server');
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
+ const server = new Server();
8
+ server.bind(port, hostname);
9
+
10
+ module.exports = server;
package/lib/server.js ADDED
@@ -0,0 +1,83 @@
1
+ /* eslint-disable no-console */
2
+ const axon = require('axon');
3
+ const Message = require('amp-message');
4
+
5
+ const JWT = require('@arcblock/jwt');
6
+
7
+ const { EVENT_AUTH, EVENT_AUTH_FAIL } = require('./constant');
8
+
9
+ const getDid = (jwt) => jwt.iss.replace(/^did:abt:/, '');
10
+
11
+ class Server extends axon.Socket {
12
+ constructor() {
13
+ super();
14
+ this.on('message', (event, data, sock) => {
15
+ const { channel } = sock;
16
+ if (!sock.channel) {
17
+ console.error('skip message of unauthenticated socket');
18
+ return;
19
+ }
20
+ this.send(event, data, channel);
21
+ });
22
+
23
+ this.on('connect', (socket) => {
24
+ console.log('event-hub client connected', socket._peername);
25
+ });
26
+ this.on('reconnect attempt', (socket) => {
27
+ console.log('event-hub client reconnect', socket._peername);
28
+ });
29
+ this.on('disconnect', (socket) => {
30
+ console.log('event-hub client disconnected', socket._peername);
31
+ });
32
+ this.on('drop', (args) => {
33
+ console.log('event-hub server drop message', args);
34
+ });
35
+ this.on('error', (err) => {
36
+ console.log('event-hub server error', err);
37
+ });
38
+ }
39
+
40
+ onmessage(sock) {
41
+ return (buf) => {
42
+ const msg = new Message(buf);
43
+ const [event, data] = msg.args;
44
+ if (event === EVENT_AUTH) {
45
+ try {
46
+ // verify
47
+ const { pk, token } = data;
48
+ if (!JWT.verify(token, pk)) {
49
+ throw new Error('token verify failed');
50
+ }
51
+
52
+ // set channel as did
53
+ const did = getDid(JWT.decode(token));
54
+ sock.channel = did;
55
+ } catch (err) {
56
+ console.error(err);
57
+ const resBuf = this.pack([EVENT_AUTH_FAIL, { msg: err.message }]);
58
+ sock.write(resBuf);
59
+ }
60
+ return;
61
+ }
62
+
63
+ this.emit('message', event, data, sock);
64
+ };
65
+ }
66
+
67
+ send(event, data, channel) {
68
+ const { socks } = this;
69
+
70
+ const buf = this.pack([event, data]);
71
+
72
+ for (let i = 0; i < socks.length; i++) {
73
+ const sock = socks[i];
74
+ if (sock.channel === channel) {
75
+ if (sock.writable) sock.write(buf);
76
+ }
77
+ }
78
+
79
+ return this;
80
+ }
81
+ }
82
+
83
+ module.exports = Server;
package/package.json ADDED
@@ -0,0 +1,36 @@
1
+ {
2
+ "name": "@arcblock/event-hub",
3
+ "publishConfig": {
4
+ "access": "public"
5
+ },
6
+ "version": "1.6.10",
7
+ "description": "A nodejs module for local and remote Inter Process Event",
8
+ "main": "lib/index.js",
9
+ "files": [
10
+ "lib",
11
+ "single.js"
12
+ ],
13
+ "scripts": {
14
+ "lint": "eslint tests lib",
15
+ "lint:fix": "eslint --fix tests lib",
16
+ "test": "jest --forceExit --detectOpenHandles",
17
+ "coverage": "npm run test -- --coverage"
18
+ },
19
+ "keywords": [],
20
+ "author": "arcblock <engineer@arcblock.io>",
21
+ "contributors": [
22
+ "wangshijun <shijun@arcblock.io> (https://github.com/wangshijun)"
23
+ ],
24
+ "license": "MIT",
25
+ "dependencies": {
26
+ "@arcblock/did": "1.6.10",
27
+ "@arcblock/jwt": "1.6.10",
28
+ "@ocap/wallet": "1.6.10",
29
+ "amp-message": "~0.1.1",
30
+ "axon": "^2.0.3"
31
+ },
32
+ "devDependencies": {
33
+ "jest": "^27.3.1"
34
+ },
35
+ "gitHead": "ab272e8db3a15c6571cc7fae7cc3d3e0fdd4bdb1"
36
+ }
package/single.js ADDED
@@ -0,0 +1,3 @@
1
+ const Client = require('./lib/client-fallback');
2
+
3
+ module.exports = new Client();