@abtnode/blocklet-services 1.8.1 → 1.8.2
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/api/index.js +1 -0
- package/api/libs/auth.js +3 -4
- package/api/routes/blocklet.js +13 -10
- package/api/services/notification/channel/app-channel.js +114 -0
- package/api/services/notification/channel/did-channel.js +147 -0
- package/api/services/notification/channel/get-hooks-by-channel.js +18 -0
- package/api/services/notification/index.js +60 -164
- package/api/services/notification/util.js +77 -0
- package/build/asset-manifest.json +4 -4
- package/build/index.html +1 -1
- package/build/static/js/{5.aa73241e.chunk.js → 5.b46d3b80.chunk.js} +3 -3
- package/build/static/js/{5.aa73241e.chunk.js.LICENSE.txt → 5.b46d3b80.chunk.js.LICENSE.txt} +0 -0
- package/build/static/js/5.b46d3b80.chunk.js.map +1 -0
- package/package.json +26 -25
- package/build/static/js/5.aa73241e.chunk.js.map +0 -1
package/api/index.js
CHANGED
|
@@ -181,6 +181,7 @@ module.exports = function createServer(node, serverOptions = {}) {
|
|
|
181
181
|
|
|
182
182
|
// API: notification
|
|
183
183
|
notificationService.sendToUser.attach(server);
|
|
184
|
+
notificationService.sendToAppChannel.attach(server);
|
|
184
185
|
|
|
185
186
|
// Middleware: auth info
|
|
186
187
|
server.use(authMiddlewares.bearerToken);
|
package/api/libs/auth.js
CHANGED
|
@@ -4,7 +4,7 @@ const joinUrl = require('url-join');
|
|
|
4
4
|
const DiskStorage = require('@arcblock/did-auth-storage-nedb');
|
|
5
5
|
const { WalletAuthenticator } = require('@arcblock/did-auth');
|
|
6
6
|
const WalletHandlers = require('@blocklet/sdk/lib/wallet-handler');
|
|
7
|
-
const
|
|
7
|
+
const { sendToUser } = require('@blocklet/sdk/lib/util/send-notification');
|
|
8
8
|
const { WELLKNOWN_SERVICE_PATH_PREFIX, NODE_SERVICES_PREFIX } = require('@abtnode/constant');
|
|
9
9
|
|
|
10
10
|
const { getBlockletLogo } = require('../util');
|
|
@@ -62,11 +62,10 @@ module.exports = (node, opts) => {
|
|
|
62
62
|
sendNotificationFn: async (connectedDid, message, { req }) => {
|
|
63
63
|
const { wallet } = await req.getBlockletInfo();
|
|
64
64
|
const sender = {
|
|
65
|
-
|
|
65
|
+
appDid: wallet.address,
|
|
66
66
|
appSk: wallet.secretKey,
|
|
67
|
-
did: req.getBlockletDid(),
|
|
68
67
|
};
|
|
69
|
-
return
|
|
68
|
+
return sendToUser(connectedDid, message, sender, process.env.ABT_NODE_SERVICE_PORT);
|
|
70
69
|
},
|
|
71
70
|
};
|
|
72
71
|
|
package/api/routes/blocklet.js
CHANGED
|
@@ -133,6 +133,18 @@ module.exports = {
|
|
|
133
133
|
|
|
134
134
|
await node.setBlockletInitialized({ did: blocklet.meta.did, owner: { did: userDid, pk } });
|
|
135
135
|
|
|
136
|
+
// 调用 store 管理公开实例
|
|
137
|
+
// 如果一个 blocklet 没有设置 公开实例,启动成功后不应给 store 发请求
|
|
138
|
+
const { publicToStore } = blocklet.settings;
|
|
139
|
+
if (publicToStore) {
|
|
140
|
+
try {
|
|
141
|
+
await handleInstanceInStore(blocklet, { userDid, publicToStore });
|
|
142
|
+
} catch (error) {
|
|
143
|
+
// 即使实例公开不成功,不能影响正常启动流程
|
|
144
|
+
logger.error('failed to send blocklet logo', { did: blocklet.meta.did, error });
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
136
148
|
// create vc
|
|
137
149
|
const vc = createPassportVC({
|
|
138
150
|
issuerName: name,
|
|
@@ -174,20 +186,11 @@ module.exports = {
|
|
|
174
186
|
node
|
|
175
187
|
);
|
|
176
188
|
|
|
177
|
-
// 调用 store 管理公开实例
|
|
178
|
-
const { publicToStore } = blocklet.settings;
|
|
179
|
-
try {
|
|
180
|
-
await handleInstanceInStore(blocklet, { userDid, publicToStore });
|
|
181
|
-
} catch (error) {
|
|
182
|
-
// 即使实例公开不成功,不能影响正常启动流程
|
|
183
|
-
logger.error('failed to send blocklet logo', { did: blocklet.meta.did, error });
|
|
184
|
-
}
|
|
185
|
-
|
|
186
189
|
// send vc to wallet
|
|
187
190
|
const receiver = userDid;
|
|
188
191
|
const token = JWT.sign(wallet.address, wallet.secretKey);
|
|
189
192
|
const data = {
|
|
190
|
-
sender: {
|
|
193
|
+
sender: { token, appDid: wallet.address },
|
|
191
194
|
receiver,
|
|
192
195
|
notification: {
|
|
193
196
|
title: {
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
const get = require('lodash/get');
|
|
2
|
+
|
|
3
|
+
const { CHANNEL_TYPE, parseChannel } = require('@blocklet/meta/lib/channel');
|
|
4
|
+
const { validateNotification } = require('@blocklet/sdk/lib/validators/notification');
|
|
5
|
+
const { NODE_MODES } = require('@abtnode/constant');
|
|
6
|
+
|
|
7
|
+
const { ensureSender, parseNotification, broadcast, EVENTS } = require('../util');
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
*
|
|
11
|
+
* @param {ABTNode} node
|
|
12
|
+
* @param {{
|
|
13
|
+
* {{
|
|
14
|
+
* {String} did
|
|
15
|
+
* {String} token
|
|
16
|
+
* }} sender
|
|
17
|
+
* {String} channel
|
|
18
|
+
* {String} event
|
|
19
|
+
* {Array|Object} notification
|
|
20
|
+
* }} payload
|
|
21
|
+
* @param {WsServer} wsServer
|
|
22
|
+
* @returns
|
|
23
|
+
*/
|
|
24
|
+
const sendToAppChannel = async ({
|
|
25
|
+
sender,
|
|
26
|
+
channel,
|
|
27
|
+
event = EVENTS.MESSAGE,
|
|
28
|
+
notification,
|
|
29
|
+
options,
|
|
30
|
+
node,
|
|
31
|
+
wsServer,
|
|
32
|
+
} = {}) => {
|
|
33
|
+
const { socketId, socketDid } = options || {};
|
|
34
|
+
|
|
35
|
+
const channelInfo = parseChannel(channel);
|
|
36
|
+
|
|
37
|
+
if (channelInfo.type !== CHANNEL_TYPE.APP) {
|
|
38
|
+
throw new Error('Cannot send message to non-app channel');
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const nodeInfo = await node.getNodeInfo();
|
|
42
|
+
|
|
43
|
+
// get socket match
|
|
44
|
+
const socketFilters = {};
|
|
45
|
+
if (socketId) {
|
|
46
|
+
socketFilters.id = socketId;
|
|
47
|
+
}
|
|
48
|
+
if (socketDid) {
|
|
49
|
+
socketFilters[`channel.${channel}.authInfo.did`] = socketDid;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// validate
|
|
53
|
+
|
|
54
|
+
if (nodeInfo.mode !== NODE_MODES.DEBUG) {
|
|
55
|
+
await validateNotification(notification);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// parse sender
|
|
59
|
+
|
|
60
|
+
const senderInfo = await ensureSender({ sender, node, nodeInfo });
|
|
61
|
+
|
|
62
|
+
if (channelInfo.appDid !== senderInfo.wallet.address) {
|
|
63
|
+
throw new Error('Cannot sent message to channel of other app');
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// parse notification
|
|
67
|
+
|
|
68
|
+
const notifications = parseNotification(notification, senderInfo);
|
|
69
|
+
|
|
70
|
+
// send notification
|
|
71
|
+
notifications.forEach((data) => {
|
|
72
|
+
broadcast(wsServer, channel, event, data, { socketFilters });
|
|
73
|
+
});
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
const onAuthenticate = async ({ channel, node }) => {
|
|
77
|
+
const { appDid } = parseChannel(channel);
|
|
78
|
+
|
|
79
|
+
const exist = await node.hasBlocklet({ did: appDid });
|
|
80
|
+
if (!exist) {
|
|
81
|
+
throw new Error(`App does not exist: ${appDid}`);
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
const onJoin = async ({ socket, channel, wsServer }) => {
|
|
86
|
+
const { appDid } = parseChannel(channel);
|
|
87
|
+
|
|
88
|
+
const senderDid = get(socket, `channel.${channel}.authInfo.did`);
|
|
89
|
+
|
|
90
|
+
if (senderDid === appDid) {
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
broadcast(
|
|
95
|
+
wsServer,
|
|
96
|
+
channel,
|
|
97
|
+
'hi',
|
|
98
|
+
{
|
|
99
|
+
sender: {
|
|
100
|
+
socketId: socket.id,
|
|
101
|
+
did: senderDid,
|
|
102
|
+
},
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
socketFilters: {
|
|
106
|
+
[`channel.${channel}.authInfo.did`]: appDid,
|
|
107
|
+
},
|
|
108
|
+
}
|
|
109
|
+
);
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
const onMessage = () => {};
|
|
113
|
+
|
|
114
|
+
module.exports = { onAuthenticate, onJoin, onMessage, sendToAppChannel };
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
const {
|
|
2
|
+
validateReceiver,
|
|
3
|
+
validateNotification,
|
|
4
|
+
validateMessage,
|
|
5
|
+
} = require('@blocklet/sdk/lib/validators/notification');
|
|
6
|
+
const { NODE_MODES } = require('@abtnode/constant');
|
|
7
|
+
|
|
8
|
+
// eslint-disable-next-line global-require
|
|
9
|
+
const logger = require('@abtnode/logger')(`${require('../../../../package.json').name}:notification`);
|
|
10
|
+
const { ensureSender, parseNotification, broadcast, EVENTS } = require('../util');
|
|
11
|
+
const states = require('../../../state');
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
*
|
|
15
|
+
* @param {{
|
|
16
|
+
* {{
|
|
17
|
+
* did: String
|
|
18
|
+
* token: String
|
|
19
|
+
* }} sender
|
|
20
|
+
* receiver: Array|String // user did
|
|
21
|
+
* notification: Array|Object
|
|
22
|
+
* options: Object
|
|
23
|
+
* node: ABTNode
|
|
24
|
+
* wsServer: WsServer
|
|
25
|
+
* }}
|
|
26
|
+
* @returns
|
|
27
|
+
*/
|
|
28
|
+
const sendToDid = async ({ sender, receiver, notification, options, node, wsServer }) => {
|
|
29
|
+
const { keepForOfflineUser = true } = options || {};
|
|
30
|
+
|
|
31
|
+
// validate
|
|
32
|
+
|
|
33
|
+
await validateReceiver(receiver);
|
|
34
|
+
|
|
35
|
+
const nodeInfo = await node.getNodeInfo();
|
|
36
|
+
|
|
37
|
+
if (nodeInfo.mode !== NODE_MODES.DEBUG) {
|
|
38
|
+
await validateNotification(notification);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// parse sender
|
|
42
|
+
|
|
43
|
+
const senderInfo = await ensureSender({ sender, node, nodeInfo });
|
|
44
|
+
|
|
45
|
+
// parse notification
|
|
46
|
+
|
|
47
|
+
const notifications = parseNotification(notification, senderInfo);
|
|
48
|
+
|
|
49
|
+
// parse receiver
|
|
50
|
+
|
|
51
|
+
const receivers = [].concat(receiver);
|
|
52
|
+
|
|
53
|
+
// send notification
|
|
54
|
+
|
|
55
|
+
receivers.forEach((did) => {
|
|
56
|
+
notifications.forEach((data) => {
|
|
57
|
+
broadcast(wsServer, did, EVENTS.MESSAGE, data, async (count) => {
|
|
58
|
+
if (count <= 0 && keepForOfflineUser) {
|
|
59
|
+
logger.info('Online client was not found', { userDid: did });
|
|
60
|
+
await states.message.insert({ did, event: EVENTS.MESSAGE, data });
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
const sendCachedMessages = async (wsServer, did) => {
|
|
68
|
+
try {
|
|
69
|
+
const messages = await states.message.find({ did });
|
|
70
|
+
if (!messages.length) {
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
messages.forEach(({ did: channel, event, data }) => {
|
|
75
|
+
wsServer.broadcast(channel, event, data);
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
await states.message.remove({ did }, { multi: true });
|
|
79
|
+
} catch (error) {
|
|
80
|
+
logger.error('Error on sending cached messages', { error });
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Receive message from channel
|
|
86
|
+
* @param {{
|
|
87
|
+
* {WsServer} wsServer
|
|
88
|
+
* {ABTNode} Node
|
|
89
|
+
* {string} topic
|
|
90
|
+
* {string} event
|
|
91
|
+
* {Object} payload
|
|
92
|
+
* }}
|
|
93
|
+
* @returns
|
|
94
|
+
*/
|
|
95
|
+
const onMessage = async ({ channel: from, event = EVENTS.MESSAGE, payload: message, wsServer, node }) => {
|
|
96
|
+
if (event !== EVENTS.MESSAGE) {
|
|
97
|
+
throw new Error(`Invalid event. expect: "message". got: "${event}"`);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
await validateMessage(message);
|
|
101
|
+
|
|
102
|
+
// validate receiver
|
|
103
|
+
const { receiver, ...data } = message;
|
|
104
|
+
|
|
105
|
+
const blocklet = await node.getBlocklet({ did: receiver.did, attachConfig: false });
|
|
106
|
+
if (!blocklet) {
|
|
107
|
+
const nodeInfo = await node.getNodeInfo();
|
|
108
|
+
|
|
109
|
+
// only throw error if receiver is a blocklet (not server)
|
|
110
|
+
if (nodeInfo.did !== receiver.did) {
|
|
111
|
+
throw new Error(`App is not installed in the server. receiver: ${receiver.did}`);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
logger.info('send message to blocklet', { sender: from, receiver: receiver.did });
|
|
116
|
+
wsServer.broadcast(receiver.did, event, {
|
|
117
|
+
...data,
|
|
118
|
+
sender: {
|
|
119
|
+
did: from,
|
|
120
|
+
},
|
|
121
|
+
});
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
const onJoin = async ({ channel: did, payload, wsServer, node }) => {
|
|
125
|
+
await sendCachedMessages(wsServer, did);
|
|
126
|
+
|
|
127
|
+
if (payload.message) {
|
|
128
|
+
await onMessage({
|
|
129
|
+
channel: did,
|
|
130
|
+
payload: payload.message,
|
|
131
|
+
wsServer,
|
|
132
|
+
node,
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
const onAuthenticate = async ({ channel, did, payload }) => {
|
|
138
|
+
if (did !== channel) {
|
|
139
|
+
throw new Error(`verified did and channel does not match. did: ${did}, channel: ${channel}`);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
if (payload.message) {
|
|
143
|
+
await validateMessage(payload.message);
|
|
144
|
+
}
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
module.exports = { onAuthenticate, onJoin, onMessage, sendToDid };
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
const { CHANNEL_TYPE, parseChannel } = require('@blocklet/meta/lib/channel');
|
|
2
|
+
|
|
3
|
+
const AppChannel = require('./app-channel');
|
|
4
|
+
const DidChannel = require('./did-channel');
|
|
5
|
+
|
|
6
|
+
const getHooksByChannel = (channel) => {
|
|
7
|
+
const { type } = parseChannel(channel);
|
|
8
|
+
if (type === CHANNEL_TYPE.DID) {
|
|
9
|
+
return DidChannel;
|
|
10
|
+
}
|
|
11
|
+
if (type === CHANNEL_TYPE.APP) {
|
|
12
|
+
return AppChannel;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
throw new Error('Unknown channel type');
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
module.exports = getHooksByChannel;
|
|
@@ -1,26 +1,19 @@
|
|
|
1
1
|
/* eslint-disable arrow-parens */
|
|
2
2
|
const JWT = require('@arcblock/jwt');
|
|
3
3
|
const { WsServer } = require('@arcblock/ws');
|
|
4
|
-
const uuid = require('uuid');
|
|
5
4
|
const Cron = require('@abtnode/cron');
|
|
6
|
-
const {
|
|
7
|
-
validateNotification,
|
|
8
|
-
validateReceiver,
|
|
9
|
-
validateMessage,
|
|
10
|
-
} = require('@blocklet/sdk/lib/validators/notification');
|
|
11
5
|
// eslint-disable-next-line global-require
|
|
12
6
|
const logger = require('@abtnode/logger')(`${require('../../../package.json').name}:notification`);
|
|
13
|
-
const { getTeamInfo } = require('@abtnode/auth/lib/auth');
|
|
14
|
-
const { NODE_MODES } = require('@abtnode/constant');
|
|
15
7
|
|
|
16
8
|
const states = require('../../state');
|
|
17
9
|
const { PREFIXES } = require('../../util/constants');
|
|
18
10
|
|
|
19
|
-
const
|
|
11
|
+
const { sendToAppChannel } = require('./channel/app-channel');
|
|
12
|
+
const { sendToDid } = require('./channel/did-channel');
|
|
13
|
+
const getHooksByChannel = require('./channel/get-hooks-by-channel');
|
|
14
|
+
const { getDid } = require('./util');
|
|
20
15
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
const authenticate = (req, cb) => {
|
|
16
|
+
const authenticateConnect = (req, cb) => {
|
|
24
17
|
const { searchParams } = new URL(req.url, `http://${req.headers.host || 'unknown'}`);
|
|
25
18
|
const token = searchParams.get('token');
|
|
26
19
|
const pk = searchParams.get('pk');
|
|
@@ -38,97 +31,7 @@ const authenticate = (req, cb) => {
|
|
|
38
31
|
cb(null, { did: getDid(JWT.decode(token)) });
|
|
39
32
|
};
|
|
40
33
|
|
|
41
|
-
|
|
42
|
-
*
|
|
43
|
-
* @param {ABTNode} node
|
|
44
|
-
* @param {Object} payload
|
|
45
|
-
* {Object} sender: blocklet
|
|
46
|
-
* {String} sender.did: blocklet did
|
|
47
|
-
* {String} sender.token: for the verification
|
|
48
|
-
* {Array|String} receiver: user did
|
|
49
|
-
* {Array|Object} notification
|
|
50
|
-
* @returns
|
|
51
|
-
*/
|
|
52
|
-
const onSendToUser = async (node, payload, wsServer) => {
|
|
53
|
-
const { sender, receiver, notification, options } = payload;
|
|
54
|
-
const { keepForOfflineUser = true } = options || {};
|
|
55
|
-
|
|
56
|
-
await validateReceiver(receiver);
|
|
57
|
-
|
|
58
|
-
const nodeInfo = await node.getNodeInfo();
|
|
59
|
-
|
|
60
|
-
if (nodeInfo.mode !== NODE_MODES.DEBUG) {
|
|
61
|
-
await validateNotification(notification);
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
let senderInfo;
|
|
65
|
-
try {
|
|
66
|
-
senderInfo = await getTeamInfo({ node, nodeInfo, teamDid: sender.did });
|
|
67
|
-
} catch (err) {
|
|
68
|
-
if (err.message === 'Blocklet state must be an object') {
|
|
69
|
-
err.message = `Sender blocklet does not exist: ${sender.did}`;
|
|
70
|
-
}
|
|
71
|
-
throw err;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
const { wallet, name } = senderInfo;
|
|
75
|
-
if (!JWT.verify(sender.token, wallet.publicKey)) {
|
|
76
|
-
throw new Error(`Invalid authentication token for sender blocklet: ${sender.did}`);
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
if (sender.appDid !== wallet.address) {
|
|
80
|
-
throw new Error(`Invalid app did, expected: ${wallet.address}, actual: ${sender.appDid}`);
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
// parse notifications
|
|
84
|
-
|
|
85
|
-
const notifications = [].concat(notification);
|
|
86
|
-
|
|
87
|
-
notifications.forEach((x) => {
|
|
88
|
-
x.id = uuid.v4();
|
|
89
|
-
x.sender = {
|
|
90
|
-
did: sender.appDid,
|
|
91
|
-
name,
|
|
92
|
-
};
|
|
93
|
-
x.createdAt = new Date();
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
// parse receivers
|
|
97
|
-
|
|
98
|
-
const receivers = [].concat(receiver);
|
|
99
|
-
|
|
100
|
-
// send notification
|
|
101
|
-
|
|
102
|
-
const EVENT_NAME = 'message';
|
|
103
|
-
const createTask = async (did, data) => {
|
|
104
|
-
try {
|
|
105
|
-
const count = await new Promise((resolve) => {
|
|
106
|
-
wsServer.broadcast(did, EVENT_NAME, data, ({ count: c } = {}) => {
|
|
107
|
-
resolve(c);
|
|
108
|
-
});
|
|
109
|
-
});
|
|
110
|
-
|
|
111
|
-
if (count <= 0 && keepForOfflineUser) {
|
|
112
|
-
logger.info('Online client was not found', { userDid: did });
|
|
113
|
-
await states.message.insert({ did, event: EVENT_NAME, data });
|
|
114
|
-
}
|
|
115
|
-
} catch (error) {
|
|
116
|
-
logger.error('Failed on broadcast message', { error });
|
|
117
|
-
}
|
|
118
|
-
};
|
|
119
|
-
|
|
120
|
-
const tasks = [];
|
|
121
|
-
receivers.forEach((did) => {
|
|
122
|
-
notifications.forEach((data) => {
|
|
123
|
-
tasks.push(createTask(did, data));
|
|
124
|
-
});
|
|
125
|
-
});
|
|
126
|
-
|
|
127
|
-
// FIXME: Enhance task reliability. e.g. resend after timeout or error; add message status: sent, received, staged
|
|
128
|
-
await Promise.allSettled(tasks);
|
|
129
|
-
};
|
|
130
|
-
|
|
131
|
-
const verify = async ({ topic, payload }) => {
|
|
34
|
+
const authenticateJoinChannel = async ({ topic: channel, payload, node }) => {
|
|
132
35
|
const did = getDid(JWT.decode(payload.token));
|
|
133
36
|
|
|
134
37
|
// Support the web wallet to continue to run for one day
|
|
@@ -143,76 +46,45 @@ const verify = async ({ topic, payload }) => {
|
|
|
143
46
|
throw new Error(`verify did failed: ${did}`);
|
|
144
47
|
}
|
|
145
48
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
if (payload.message) {
|
|
151
|
-
await validateMessage(payload.message);
|
|
152
|
-
}
|
|
153
|
-
};
|
|
154
|
-
|
|
155
|
-
const receiveMessage = async ({ topic, event, payload, wsServer, node }) => {
|
|
156
|
-
if (event !== 'message') {
|
|
157
|
-
throw new Error(`Invalid event. expect: "message". got: "${event}"`);
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
await validateMessage(payload);
|
|
161
|
-
|
|
162
|
-
const { receiver, ...data } = payload;
|
|
163
|
-
|
|
164
|
-
const blocklet = await node.getBlocklet({ did: receiver.did, attachConfig: false });
|
|
165
|
-
if (!blocklet) {
|
|
166
|
-
const nodeInfo = await node.getNodeInfo();
|
|
49
|
+
const hooks = getHooksByChannel(channel);
|
|
50
|
+
await hooks.onAuthenticate({ channel, did, payload, node });
|
|
167
51
|
|
|
168
|
-
|
|
169
|
-
if (nodeInfo.did !== receiver.did) {
|
|
170
|
-
throw new Error(`App is not installed in the server. receiver: ${receiver.did}`);
|
|
171
|
-
}
|
|
172
|
-
}
|
|
52
|
+
const authInfo = { did };
|
|
173
53
|
|
|
174
|
-
|
|
175
|
-
wsServer.broadcast(payload.receiver.did, 'message', {
|
|
176
|
-
...data,
|
|
177
|
-
sender: {
|
|
178
|
-
did: topic,
|
|
179
|
-
},
|
|
180
|
-
});
|
|
54
|
+
return authInfo;
|
|
181
55
|
};
|
|
182
56
|
|
|
183
|
-
const
|
|
184
|
-
|
|
185
|
-
const did = getDid(JWT.decode(payload.token));
|
|
57
|
+
const postJoinChannel = async ({ socket, topic: channel, payload, wsServer, node }) => {
|
|
58
|
+
logger.info('Subscribe notification success', { channel });
|
|
186
59
|
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
messages.forEach(({ did: d, event, data }) => {
|
|
193
|
-
wsServer.broadcast(d, event, data);
|
|
194
|
-
});
|
|
60
|
+
const hooks = getHooksByChannel(channel);
|
|
61
|
+
await hooks.onJoin({ socket, channel, payload, wsServer, node });
|
|
62
|
+
};
|
|
195
63
|
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
64
|
+
/**
|
|
65
|
+
* Receive message from channel
|
|
66
|
+
* @param {{
|
|
67
|
+
* wsServer: WsServer
|
|
68
|
+
* node: ABTNode
|
|
69
|
+
* topic: String
|
|
70
|
+
* event: String
|
|
71
|
+
* payload: Object
|
|
72
|
+
* }}
|
|
73
|
+
* @returns
|
|
74
|
+
*/
|
|
75
|
+
const receiveMessage = async ({ socket, topic: channel, event, payload, wsServer, node }) => {
|
|
76
|
+
const hooks = getHooksByChannel(channel);
|
|
77
|
+
await hooks.onMessage({ socket, channel, event, payload, wsServer, node });
|
|
200
78
|
};
|
|
201
79
|
|
|
202
80
|
const init = ({ node }) => {
|
|
203
81
|
// Create ws server
|
|
204
82
|
const wsServer = new WsServer({
|
|
205
83
|
logger,
|
|
206
|
-
authenticate,
|
|
84
|
+
authenticate: authenticateConnect,
|
|
207
85
|
hooks: {
|
|
208
|
-
|
|
209
|
-
postJoinChannel:
|
|
210
|
-
logger.info('Subscribe notification success', { account: topic });
|
|
211
|
-
await sendCachedMessages(wsServer, payload);
|
|
212
|
-
if (payload.message) {
|
|
213
|
-
await receiveMessage({ topic, event: 'message', payload: payload.message, wsServer, node });
|
|
214
|
-
}
|
|
215
|
-
},
|
|
86
|
+
authenticateJoinChannel: (param) => authenticateJoinChannel({ ...param, wsServer, node }),
|
|
87
|
+
postJoinChannel: (param) => postJoinChannel({ ...param, wsServer, node }),
|
|
216
88
|
receiveMessage: (param) => receiveMessage({ ...param, wsServer, node }),
|
|
217
89
|
},
|
|
218
90
|
});
|
|
@@ -234,9 +106,9 @@ const init = ({ node }) => {
|
|
|
234
106
|
});
|
|
235
107
|
}
|
|
236
108
|
|
|
237
|
-
const
|
|
109
|
+
const onSendToUser = async (req, res) => {
|
|
238
110
|
try {
|
|
239
|
-
await
|
|
111
|
+
await sendToDid({ ...req.body.data, node, wsServer });
|
|
240
112
|
res.status(200).send('');
|
|
241
113
|
} catch (error) {
|
|
242
114
|
logger.error('Send message to user failed', { error });
|
|
@@ -245,16 +117,40 @@ const init = ({ node }) => {
|
|
|
245
117
|
}
|
|
246
118
|
};
|
|
247
119
|
|
|
120
|
+
const onSendToAppChannel = async (req, res) => {
|
|
121
|
+
try {
|
|
122
|
+
await sendToAppChannel({ ...req.body.data, node, wsServer });
|
|
123
|
+
res.status(200).send('');
|
|
124
|
+
} catch (error) {
|
|
125
|
+
logger.error('Send message to channel failed', { error });
|
|
126
|
+
res.statusMessage = error.message;
|
|
127
|
+
res.status(400).send(error.message);
|
|
128
|
+
}
|
|
129
|
+
};
|
|
130
|
+
|
|
248
131
|
// mount
|
|
249
132
|
return {
|
|
250
133
|
sendToUser: {
|
|
251
134
|
attach: (app) => {
|
|
252
135
|
PREFIXES.forEach((prefix) => {
|
|
253
|
-
app.post(`${prefix}/api/
|
|
136
|
+
app.post(`${prefix}/api/send-to-user`, onSendToUser);
|
|
137
|
+
|
|
138
|
+
// backward compatible
|
|
139
|
+
app.post(`${prefix}/api/sendToUser`, onSendToUser);
|
|
254
140
|
});
|
|
255
141
|
},
|
|
256
|
-
exec: (data) =>
|
|
142
|
+
exec: (data) => sendToDid({ ...data, node, wsServer }),
|
|
257
143
|
},
|
|
144
|
+
|
|
145
|
+
sendToAppChannel: {
|
|
146
|
+
attach: (app) => {
|
|
147
|
+
PREFIXES.forEach((prefix) => {
|
|
148
|
+
app.post(`${prefix}/api/send-to-app-channel`, onSendToAppChannel);
|
|
149
|
+
});
|
|
150
|
+
},
|
|
151
|
+
exec: (data) => sendToAppChannel({ ...data, node, wsServer }),
|
|
152
|
+
},
|
|
153
|
+
|
|
258
154
|
attach: (wsRouter) => {
|
|
259
155
|
PREFIXES.forEach((prefix) => {
|
|
260
156
|
wsRouter.use(`${prefix}/websocket`, wsServer.onConnect.bind(wsServer));
|