@abtnode/blocklet-services 1.16.18-beta-aa01bd8e → 1.16.18-beta-bb4ebacc
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 -7
- package/api/routes/env.js +4 -2
- package/api/services/auth/index.js +77 -13
- package/api/services/auth/session.js +48 -1
- package/api/services/notification/blocklet-events-notifier.js +1 -0
- package/api/socket/channel/component.js +6 -5
- package/api/socket/channel/did.js +51 -20
- package/api/socket/util.js +1 -0
- package/build/asset-manifest.json +99 -87
- package/build/index.html +1 -1
- package/build/service-worker.js +1 -1
- package/build/service-worker.js.map +1 -1
- package/build/static/css/{4448.de9040c4.chunk.css → 1233.dba37e64.chunk.css} +1 -1
- package/build/static/js/{1148.e614f706.chunk.js → 1148.5ccba08e.chunk.js} +2 -2
- package/build/static/js/{4448.f0c62086.chunk.js → 1233.276cf2d0.chunk.js} +3 -3
- package/build/static/js/{4448.f0c62086.chunk.js.LICENSE.txt → 1233.276cf2d0.chunk.js.LICENSE.txt} +6 -0
- package/build/static/js/2653.980158ea.chunk.js +2 -0
- package/build/static/js/2664.da443f31.chunk.js +2 -0
- package/build/static/js/2801.33d2f238.chunk.js +2 -0
- package/build/static/js/2940.ce32ab3f.chunk.js +2 -0
- package/build/static/js/3025.7c99c058.chunk.js +2 -0
- package/build/static/js/{3033.6623d19d.chunk.js → 3033.9fe46d7f.chunk.js} +2 -2
- package/build/static/js/{3131.672170fb.chunk.js → 3131.99541aca.chunk.js} +2 -2
- package/build/static/js/3430.dc830483.chunk.js +2 -0
- package/build/static/js/{3688.48e7b69c.chunk.js → 3688.679796d1.chunk.js} +2 -2
- package/build/static/js/3708.7e2ad66b.chunk.js +3 -0
- package/build/static/js/3842.e1bb702b.chunk.js +3 -0
- package/build/static/js/3953.ddf8be86.chunk.js +2 -0
- package/build/static/js/{4023.a2e9db00.chunk.js → 4023.5fe8180a.chunk.js} +2 -2
- package/build/static/js/4076.5ca97365.chunk.js +2 -0
- package/build/static/js/4160.e45b5ba1.chunk.js +2 -0
- package/build/static/js/4587.9a042d46.chunk.js +2 -0
- package/build/static/js/4802.217e3956.chunk.js +2 -0
- package/build/static/js/5070.31a74ba7.chunk.js +2 -0
- package/build/static/js/5468.b54ce65b.chunk.js +2 -0
- package/build/static/js/5547.d68bfde3.chunk.js +3 -0
- package/build/static/js/556.e1875260.chunk.js +3 -0
- package/build/static/js/6032.1001afd3.chunk.js +2 -0
- package/build/static/js/{6139.5867193a.chunk.js → 6139.161b2e7f.chunk.js} +2 -2
- package/build/static/js/6218.9a4feced.chunk.js +2 -0
- package/build/static/js/{6658.090d923f.chunk.js → 6658.98f9956d.chunk.js} +2 -2
- package/build/static/js/{716.9c76ad65.chunk.js → 716.e68425d7.chunk.js} +3 -3
- package/build/static/js/716.e68425d7.chunk.js.LICENSE.txt +5 -0
- package/build/static/js/7305.465df4a1.chunk.js +2 -0
- package/build/static/js/7313.b53b59d8.chunk.js +2 -0
- package/build/static/js/779.73350f02.chunk.js +2 -0
- package/build/static/js/7858.52930f63.chunk.js +2 -0
- package/build/static/js/8016.3ef64a2c.chunk.js +2 -0
- package/build/static/js/{8181.86477cc5.chunk.js → 8181.6c2a7dcb.chunk.js} +2 -2
- package/build/static/js/8393.ea7ef05d.chunk.js +2 -0
- package/build/static/js/840.5bc210dd.chunk.js +2 -0
- package/build/static/js/{8622.66a81bc0.chunk.js → 8622.ebd44812.chunk.js} +2 -2
- package/build/static/js/8641.bec13444.chunk.js +2 -0
- package/build/static/js/8702.bb84d009.chunk.js +2 -0
- package/build/static/js/{8792.8b009f81.chunk.js → 8792.0f55707c.chunk.js} +2 -2
- package/build/static/js/8944.559ade67.chunk.js +2 -0
- package/build/static/js/9033.ebf65926.chunk.js +2 -0
- package/build/static/js/9107.7f32925d.chunk.js +2 -0
- package/build/static/js/9314.f0add972.chunk.js +2 -0
- package/build/static/js/{9596.5251398b.chunk.js → 9596.a4eead04.chunk.js} +2 -2
- package/build/static/js/9865.d5dfa72b.chunk.js +2 -0
- package/build/static/js/{9900.cce3f786.chunk.js → 9900.3b72165e.chunk.js} +2 -2
- package/build/static/js/main.36b0deea.js +3 -0
- package/package.json +27 -26
- package/build/static/js/1343.69031e0b.chunk.js +0 -2
- package/build/static/js/1581.ed4544c6.chunk.js +0 -2
- package/build/static/js/2026.514f995a.chunk.js +0 -2
- package/build/static/js/2362.ab5308a8.chunk.js +0 -2
- package/build/static/js/2506.dd9284b6.chunk.js +0 -2
- package/build/static/js/2653.6610534f.chunk.js +0 -2
- package/build/static/js/2972.2d5268c3.chunk.js +0 -2
- package/build/static/js/3025.aa129833.chunk.js +0 -2
- package/build/static/js/3038.40d611d1.chunk.js +0 -2
- package/build/static/js/3683.6f169a32.chunk.js +0 -2
- package/build/static/js/3920.5c63a2b9.chunk.js +0 -3
- package/build/static/js/4076.bcc72fcc.chunk.js +0 -2
- package/build/static/js/4247.c25f1945.chunk.js +0 -2
- package/build/static/js/4587.906e3023.chunk.js +0 -2
- package/build/static/js/4716.a8a5b75d.chunk.js +0 -2
- package/build/static/js/4764.51208c0d.chunk.js +0 -2
- package/build/static/js/4802.cd83ecef.chunk.js +0 -2
- package/build/static/js/5547.8447edf9.chunk.js +0 -3
- package/build/static/js/556.4d5cc702.chunk.js +0 -3
- package/build/static/js/6032.48c0a152.chunk.js +0 -2
- package/build/static/js/7050.3281ad5f.chunk.js +0 -2
- package/build/static/js/779.383b4b4a.chunk.js +0 -2
- package/build/static/js/7858.4e5b7c88.chunk.js +0 -2
- package/build/static/js/8702.2d858942.chunk.js +0 -2
- package/build/static/js/8944.07bcf75f.chunk.js +0 -2
- package/build/static/js/8983.19b79a23.chunk.js +0 -2
- package/build/static/js/9880.13ac9520.chunk.js +0 -2
- package/build/static/js/main.63103d24.js +0 -3
- /package/build/static/js/{3920.5c63a2b9.chunk.js.LICENSE.txt → 3708.7e2ad66b.chunk.js.LICENSE.txt} +0 -0
- /package/build/static/js/{716.9c76ad65.chunk.js.LICENSE.txt → 3842.e1bb702b.chunk.js.LICENSE.txt} +0 -0
- /package/build/static/js/{5547.8447edf9.chunk.js.LICENSE.txt → 5547.d68bfde3.chunk.js.LICENSE.txt} +0 -0
- /package/build/static/js/{556.4d5cc702.chunk.js.LICENSE.txt → 556.e1875260.chunk.js.LICENSE.txt} +0 -0
- /package/build/static/js/{main.63103d24.js.LICENSE.txt → main.36b0deea.js.LICENSE.txt} +0 -0
package/api/index.js
CHANGED
|
@@ -162,11 +162,6 @@ module.exports = function createServer(node, serverOptions = {}) {
|
|
|
162
162
|
logger.error('send to component error', { error });
|
|
163
163
|
});
|
|
164
164
|
}
|
|
165
|
-
|
|
166
|
-
// backward compatibility, should be removed if BlockletSDK@1.6.14 is not supported anymore
|
|
167
|
-
notificationService.sendToApp.exec({ event, appDid, data }).catch((error) => {
|
|
168
|
-
logger.error('send to app error', { error });
|
|
169
|
-
});
|
|
170
165
|
});
|
|
171
166
|
});
|
|
172
167
|
|
|
@@ -285,7 +280,7 @@ module.exports = function createServer(node, serverOptions = {}) {
|
|
|
285
280
|
server.use(
|
|
286
281
|
`${WELLKNOWN_SERVICE_PATH_PREFIX}${pathname}`,
|
|
287
282
|
checkMemberPermission,
|
|
288
|
-
proxyToDaemon({ proxy,
|
|
283
|
+
proxyToDaemon({ proxy, ...options })
|
|
289
284
|
);
|
|
290
285
|
});
|
|
291
286
|
|
|
@@ -463,7 +458,6 @@ module.exports = function createServer(node, serverOptions = {}) {
|
|
|
463
458
|
|
|
464
459
|
BlockletEventsNotifier.init({ node, notificationService });
|
|
465
460
|
|
|
466
|
-
server.sendToApp = ({ event, appDid, data }) => notificationService.sendToApp.exec({ event, appDid, data });
|
|
467
461
|
server.sendToAppComponents = ({ event, appDid, data }) =>
|
|
468
462
|
notificationService.sendToAppComponents.exec({ event, appDid, data });
|
|
469
463
|
|
package/api/routes/env.js
CHANGED
|
@@ -30,11 +30,13 @@ module.exports = {
|
|
|
30
30
|
apiPrefix: "${pathPrefix.replace(/\/+$/, '')}${WELLKNOWN_SERVICE_PATH_PREFIX}",
|
|
31
31
|
${groupPathPrefix ? `groupPathPrefix: "${groupPathPrefix}",` : ''}
|
|
32
32
|
webWalletUrl: "${info.webWalletUrl || config.webWalletUrl || opts.webWalletUrl}",
|
|
33
|
-
nftDomainUrl: "${info.nftDomainUrl}",
|
|
33
|
+
nftDomainUrl: "${info.nftDomainUrl || ''}",
|
|
34
34
|
passportColor: "${passportColor}",
|
|
35
35
|
serverDid: "${info.did}",
|
|
36
36
|
serverVersion: "${info.version}",
|
|
37
|
-
mode: "${info.mode}"
|
|
37
|
+
mode: "${info.mode}",
|
|
38
|
+
ownerNft: ${JSON.stringify(info.ownerNft || '')},
|
|
39
|
+
launcher: ${JSON.stringify(info.launcher || '')}
|
|
38
40
|
}`);
|
|
39
41
|
});
|
|
40
42
|
},
|
|
@@ -84,12 +84,16 @@ const init = ({ node, options }) => {
|
|
|
84
84
|
};
|
|
85
85
|
|
|
86
86
|
/**
|
|
87
|
-
* @
|
|
88
|
-
*
|
|
89
|
-
*
|
|
90
|
-
*
|
|
91
|
-
*
|
|
92
|
-
*
|
|
87
|
+
* @typedef {object} CheckAuthRes
|
|
88
|
+
* @property {boolean} blocked
|
|
89
|
+
* @property {boolean} authenticated
|
|
90
|
+
* @property {boolean} authorized
|
|
91
|
+
* @property {boolean} ignored
|
|
92
|
+
* @property {string} payable
|
|
93
|
+
*/
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* @returns {CheckAuthRes} res
|
|
93
97
|
*/
|
|
94
98
|
const checkAuth = async ({ req } = {}) => {
|
|
95
99
|
const config = (await req.getServiceConfig(NODE_SERVICES.AUTH)) || {};
|
|
@@ -107,24 +111,74 @@ const init = ({ node, options }) => {
|
|
|
107
111
|
|
|
108
112
|
const teamDid = req.getBlockletDid();
|
|
109
113
|
|
|
114
|
+
// 所有人都可以访问的情况
|
|
110
115
|
if (!config.whoCanAccess || config.whoCanAccess === WHO_CAN_ACCESS.ALL) {
|
|
111
116
|
if (config.blockUnauthenticated && !req.user) {
|
|
112
|
-
return {
|
|
117
|
+
return {
|
|
118
|
+
blocked: true,
|
|
119
|
+
authenticated: false,
|
|
120
|
+
};
|
|
113
121
|
}
|
|
114
122
|
} else if (!req.user) {
|
|
123
|
+
// 需要被邀请才能访问的情况
|
|
115
124
|
const rbac = await node.getRBAC(teamDid);
|
|
116
125
|
const allRoles = await rbac.getRoles(teamDid);
|
|
117
126
|
const payableRole = allRoles.find((x) => x.extra?.acquire?.pay);
|
|
118
|
-
return {
|
|
127
|
+
return {
|
|
128
|
+
blocked: true,
|
|
129
|
+
authenticated: false,
|
|
130
|
+
payable: payableRole?.extra?.acquire?.pay,
|
|
131
|
+
};
|
|
119
132
|
} else if (config.whoCanAccess === WHO_CAN_ACCESS.OWNER && req.user.role !== ROLES.OWNER) {
|
|
120
|
-
|
|
133
|
+
// 需要 owner 才能访问
|
|
134
|
+
return {
|
|
135
|
+
blocked: true,
|
|
136
|
+
authenticated: true,
|
|
137
|
+
authorized: false,
|
|
138
|
+
requiredRoles: [
|
|
139
|
+
{
|
|
140
|
+
name: ROLES.OWNER,
|
|
141
|
+
title: 'Owner',
|
|
142
|
+
description: 'Owner',
|
|
143
|
+
},
|
|
144
|
+
],
|
|
145
|
+
};
|
|
121
146
|
} else if (config.whoCanAccess.startsWith(WHO_CAN_ACCESS_PREFIX_ROLES)) {
|
|
147
|
+
// 指定 passport 才能访问
|
|
148
|
+
// 需要用户拥有的权限
|
|
122
149
|
const roles = getRolesFromAuthConfig(config);
|
|
123
150
|
if (!roles.includes(req.user.role)) {
|
|
124
151
|
const rbac = await node.getRBAC(teamDid);
|
|
152
|
+
// 系统中的所有权限
|
|
125
153
|
const allRoles = await rbac.getRoles(teamDid);
|
|
126
|
-
|
|
127
|
-
return {
|
|
154
|
+
|
|
155
|
+
return {
|
|
156
|
+
blocked: true,
|
|
157
|
+
authenticated: true,
|
|
158
|
+
authorized: false,
|
|
159
|
+
requiredRoles: roles
|
|
160
|
+
.map((item) => {
|
|
161
|
+
if (item.name === ROLES.OWNER) {
|
|
162
|
+
return {
|
|
163
|
+
name: item.name,
|
|
164
|
+
title: 'Owner',
|
|
165
|
+
description: 'Owner',
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
const findRole = allRoles.find((x) => x.name === item);
|
|
169
|
+
if (!findRole) {
|
|
170
|
+
return null;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
return {
|
|
174
|
+
name: findRole.name,
|
|
175
|
+
title: findRole.title,
|
|
176
|
+
description: findRole.description,
|
|
177
|
+
payable: findRole?.extra?.acquire?.pay,
|
|
178
|
+
};
|
|
179
|
+
})
|
|
180
|
+
.filter((x) => x),
|
|
181
|
+
};
|
|
128
182
|
}
|
|
129
183
|
}
|
|
130
184
|
|
|
@@ -254,7 +308,7 @@ const init = ({ node, options }) => {
|
|
|
254
308
|
};
|
|
255
309
|
|
|
256
310
|
middlewares.checkAuth = async (req, res, next) => {
|
|
257
|
-
const { blocked, authenticated, authorized, payable } = await checkAuth({ req });
|
|
311
|
+
const { blocked, authenticated, authorized, payable, requiredRoles } = await checkAuth({ req });
|
|
258
312
|
|
|
259
313
|
if (blocked) {
|
|
260
314
|
if (!authenticated) {
|
|
@@ -269,7 +323,17 @@ const init = ({ node, options }) => {
|
|
|
269
323
|
|
|
270
324
|
if (!authorized) {
|
|
271
325
|
if (req.accepts(['html', 'json']) === 'html') {
|
|
272
|
-
res.redirect(
|
|
326
|
+
res.redirect(
|
|
327
|
+
getRedirectUrl({
|
|
328
|
+
req,
|
|
329
|
+
pagePath: '/login',
|
|
330
|
+
params: {
|
|
331
|
+
authenticated: 1,
|
|
332
|
+
payable,
|
|
333
|
+
requiredRoles: JSON.stringify(requiredRoles),
|
|
334
|
+
},
|
|
335
|
+
})
|
|
336
|
+
);
|
|
273
337
|
} else {
|
|
274
338
|
// Security principles: user should not known the reason
|
|
275
339
|
res.status(404).json({ code: 404, error: REASON_404 });
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
const nocache = require('nocache');
|
|
2
2
|
const joinUrl = require('url-join');
|
|
3
3
|
const pick = require('lodash/pick');
|
|
4
|
+
const omit = require('lodash/omit');
|
|
5
|
+
const merge = require('lodash/merge');
|
|
4
6
|
const {
|
|
5
7
|
WELLKNOWN_SERVICE_PATH_PREFIX,
|
|
6
8
|
USER_AVATAR_URL_PREFIX,
|
|
@@ -9,6 +11,7 @@ const {
|
|
|
9
11
|
} = require('@abtnode/constant');
|
|
10
12
|
const { LOGIN_PROVIDER } = require('@blocklet/constant');
|
|
11
13
|
|
|
14
|
+
const isUrl = require('is-url');
|
|
12
15
|
const { PREFIXES } = require('../../util/constants');
|
|
13
16
|
const { createTokenFn, getDidConnectVersion } = require('../../util');
|
|
14
17
|
|
|
@@ -57,7 +60,7 @@ module.exports = {
|
|
|
57
60
|
.map((x) => pick(x, ['id', 'name', 'title', 'role']));
|
|
58
61
|
|
|
59
62
|
res.json({
|
|
60
|
-
user,
|
|
63
|
+
user: omit(user, ['extra']),
|
|
61
64
|
provider: req.user.provider || LOGIN_PROVIDER.WALLET,
|
|
62
65
|
walletOS: req.user.walletOS,
|
|
63
66
|
});
|
|
@@ -67,6 +70,50 @@ module.exports = {
|
|
|
67
70
|
router.get(sessionApi, nocache(), sessionBearerToken, handleSession);
|
|
68
71
|
router.post(sessionApi, nocache(), sessionBearerToken, handleSession);
|
|
69
72
|
|
|
73
|
+
// update user extra: settings, webhooks
|
|
74
|
+
const extraApi = `${prefix}/api/user/extra`;
|
|
75
|
+
const checkUser = async (req, res, next) => {
|
|
76
|
+
const { token } = req;
|
|
77
|
+
await ensureUser({ req, token });
|
|
78
|
+
if (!req.user) {
|
|
79
|
+
res.status(403).json(null);
|
|
80
|
+
} else {
|
|
81
|
+
next();
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
router.get(extraApi, nocache(), sessionBearerToken, checkUser, async (req, res) => {
|
|
85
|
+
const teamDid = req.getBlockletDid();
|
|
86
|
+
const user = await node.getUser({ teamDid, user: { did: req.user.did } });
|
|
87
|
+
res.json(user.extra || {});
|
|
88
|
+
});
|
|
89
|
+
router.post(extraApi, nocache(), sessionBearerToken, checkUser, async (req, res) => {
|
|
90
|
+
const teamDid = req.getBlockletDid();
|
|
91
|
+
const exist = await node.getUser({ teamDid, user: { did: req.user.did } });
|
|
92
|
+
const user = await node.updateUserExtra({
|
|
93
|
+
teamDid,
|
|
94
|
+
did: req.user.did,
|
|
95
|
+
extra: JSON.stringify(merge({}, exist.extra || {}, req.body)),
|
|
96
|
+
});
|
|
97
|
+
res.json(user.extra);
|
|
98
|
+
});
|
|
99
|
+
router.put(extraApi, nocache(), sessionBearerToken, checkUser, async (req, res) => {
|
|
100
|
+
if (['slack', 'api'].includes(req.body.type) === false) {
|
|
101
|
+
res.status(400).send({ error: 'invalid webhook type' });
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if (isUrl(req.body.url) === false) {
|
|
106
|
+
res.status(400).send({ error: 'invalid webhook url' });
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
await node.sendTestMessage({
|
|
110
|
+
webhook: { type: req.body.type, params: [{ name: 'url', value: req.body.url }] },
|
|
111
|
+
message: `This is a test message from user ${req.user.did}`,
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
res.json({ success: true });
|
|
115
|
+
});
|
|
116
|
+
|
|
70
117
|
router.post(`${prefix}/api/did/refreshSession`, refreshBearerToken, async (req, res) => {
|
|
71
118
|
const token = req.refreshToken;
|
|
72
119
|
if (token) {
|
|
@@ -189,6 +189,7 @@ const init = ({ node, notificationService }) => {
|
|
|
189
189
|
notification: {
|
|
190
190
|
title: message.title[locale] || message.title[DEFAULT_LOCALE],
|
|
191
191
|
body: message.body[locale] || message.body[DEFAULT_LOCALE],
|
|
192
|
+
source: 'app',
|
|
192
193
|
},
|
|
193
194
|
};
|
|
194
195
|
notificationService.sendToUser.exec(input);
|
|
@@ -100,17 +100,18 @@ const sendToAppComponents = async ({ event, appDid, componentDid: inputComponent
|
|
|
100
100
|
const componentDid = component.meta.did;
|
|
101
101
|
|
|
102
102
|
if (!inputComponentDid || componentDid === inputComponentDid) {
|
|
103
|
-
//
|
|
104
|
-
|
|
103
|
+
// appPid is diff with appDid when app development mode
|
|
104
|
+
// appPid is diff with appDid when app has been rotated
|
|
105
|
+
const appPid = app.appPid || appDid;
|
|
105
106
|
|
|
106
107
|
// eslint-disable-next-line no-loop-func
|
|
107
|
-
broadcast(wsServer, getComponentChannel(
|
|
108
|
+
broadcast(wsServer, getComponentChannel(appPid, componentDid), event, notification, async (count) => {
|
|
108
109
|
// FIXME @linchen 组件以 cluster 模式启动时, 是否确保所有组件实例都收到消息?
|
|
109
110
|
if (count <= 0) {
|
|
110
|
-
logger.info('Online component client was not found', {
|
|
111
|
+
logger.info('Online component client was not found', { appPid, componentDid });
|
|
111
112
|
await lock.acquire();
|
|
112
113
|
try {
|
|
113
|
-
await states.message.insert({ did: getCacheId(
|
|
114
|
+
await states.message.insert({ did: getCacheId(appPid, componentDid), event, data: notification });
|
|
114
115
|
lock.release();
|
|
115
116
|
} catch (error) {
|
|
116
117
|
lock.release();
|
|
@@ -8,6 +8,9 @@ const { NODE_MODES } = require('@abtnode/constant');
|
|
|
8
8
|
const { getWalletDid } = require('@blocklet/sdk/lib/did');
|
|
9
9
|
const JWT = require('@arcblock/jwt');
|
|
10
10
|
const pMap = require('p-map');
|
|
11
|
+
const uniqBy = require('lodash/uniqBy');
|
|
12
|
+
const uniq = require('lodash/uniq');
|
|
13
|
+
const get = require('lodash/get');
|
|
11
14
|
|
|
12
15
|
// eslint-disable-next-line global-require
|
|
13
16
|
const logger = require('@abtnode/logger')(`${require('../../../package.json').name}:notification`);
|
|
@@ -34,21 +37,25 @@ const sendToUserDid = async ({ sender, receiver: rawDid, notification, options,
|
|
|
34
37
|
const { keepForOfflineUser = true } = options || {};
|
|
35
38
|
const receiver = Array.isArray(rawDid) ? rawDid : [rawDid];
|
|
36
39
|
|
|
37
|
-
|
|
38
|
-
|
|
40
|
+
let receiverDidList = [];
|
|
41
|
+
let receiverEmailList = [];
|
|
42
|
+
let webhookList = [];
|
|
43
|
+
|
|
44
|
+
const webhookSenders = new Map();
|
|
39
45
|
|
|
40
46
|
// sender.appDid 就是当前 blockletDid,通知的发送方就是这个 blocklet 本身
|
|
41
47
|
const teamDid = sender.appDid;
|
|
42
48
|
|
|
43
49
|
await pMap(receiver, async (item) => {
|
|
44
|
-
const
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
const receiverDid = getWalletDid(
|
|
50
|
-
const receiverEmail =
|
|
51
|
-
|
|
50
|
+
const user = await node.getUser({ teamDid, user: { did: item }, options: { enableConnectedAccount: true } });
|
|
51
|
+
const walletEnabled = get(user, 'extra.notifications.wallet', true);
|
|
52
|
+
const emailEnabled = get(user, 'extra.notifications.email', true);
|
|
53
|
+
const webhooks = get(user, 'extra.webhooks', []);
|
|
54
|
+
|
|
55
|
+
const receiverDid = getWalletDid(user) || item;
|
|
56
|
+
const receiverEmail = user?.email;
|
|
57
|
+
|
|
58
|
+
if (receiverDid && walletEnabled) {
|
|
52
59
|
try {
|
|
53
60
|
await validateReceiver(receiverDid);
|
|
54
61
|
receiverDidList.push(receiverDid);
|
|
@@ -56,17 +63,21 @@ const sendToUserDid = async ({ sender, receiver: rawDid, notification, options,
|
|
|
56
63
|
/* empty */
|
|
57
64
|
}
|
|
58
65
|
}
|
|
59
|
-
if (receiverEmail) {
|
|
66
|
+
if (receiverEmail && emailEnabled) {
|
|
60
67
|
try {
|
|
61
68
|
await validateEmail(receiverEmail);
|
|
62
69
|
receiverEmailList.push({
|
|
63
70
|
email: receiverEmail,
|
|
64
|
-
locale:
|
|
71
|
+
locale: user?.locale || 'en',
|
|
65
72
|
});
|
|
66
73
|
} catch {
|
|
67
74
|
/* empty */
|
|
68
75
|
}
|
|
69
76
|
}
|
|
77
|
+
|
|
78
|
+
if (Array.isArray(webhooks)) {
|
|
79
|
+
webhookList.push(...webhooks.filter((x) => x.type && x.url));
|
|
80
|
+
}
|
|
70
81
|
});
|
|
71
82
|
|
|
72
83
|
if (receiverDidList.length === 0 && receiverEmailList.length === 0) {
|
|
@@ -85,6 +96,11 @@ const sendToUserDid = async ({ sender, receiver: rawDid, notification, options,
|
|
|
85
96
|
// parse notification
|
|
86
97
|
const notifications = parseNotification(notification, senderInfo);
|
|
87
98
|
|
|
99
|
+
// uniq receivers
|
|
100
|
+
receiverDidList = uniq(receiverDidList);
|
|
101
|
+
receiverEmailList = uniqBy(receiverEmailList, 'email');
|
|
102
|
+
webhookList = uniqBy(webhookList, 'url');
|
|
103
|
+
|
|
88
104
|
// send notification
|
|
89
105
|
notifications.forEach((data) => {
|
|
90
106
|
for (const receiverDid of receiverDidList) {
|
|
@@ -95,14 +111,29 @@ const sendToUserDid = async ({ sender, receiver: rawDid, notification, options,
|
|
|
95
111
|
}
|
|
96
112
|
});
|
|
97
113
|
}
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
}
|
|
105
|
-
|
|
114
|
+
|
|
115
|
+
// Do not send email for component activities
|
|
116
|
+
if (data.source !== 'app') {
|
|
117
|
+
// NOTICE: 目前只有 notification 的通知能够发送邮件,并且 type 可能为 undefined
|
|
118
|
+
if ([undefined, NOTIFICATION_TYPES.NOTIFICATION].includes(data.type)) {
|
|
119
|
+
for (const receiverEmail of receiverEmailList) {
|
|
120
|
+
sendEmail(receiverEmail.email, data, { teamDid: sender.appDid, node, locale: receiverEmail.locale }).catch(
|
|
121
|
+
(error) => {
|
|
122
|
+
logger.error('Failed to send email', { error });
|
|
123
|
+
}
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// send webhook
|
|
129
|
+
for (const webhook of webhookList) {
|
|
130
|
+
let webhookSender = webhookSenders.get(webhook.type);
|
|
131
|
+
if (!webhookSender) {
|
|
132
|
+
webhookSender = node.getMessageSender(webhook.type);
|
|
133
|
+
webhookSenders.set(webhook.type, webhookSender);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
webhookSender.sendNotification(webhook.url, notification);
|
|
106
137
|
}
|
|
107
138
|
}
|
|
108
139
|
});
|
package/api/socket/util.js
CHANGED
|
@@ -29,6 +29,7 @@ const parseNotification = (notification, senderInfo) => {
|
|
|
29
29
|
did: senderInfo.permanentWallet.address,
|
|
30
30
|
pk: senderInfo.permanentWallet.pk,
|
|
31
31
|
name: senderInfo.name,
|
|
32
|
+
logo: senderInfo.logo,
|
|
32
33
|
// actualDid is the did of the application that is used to decrypt the message if needed
|
|
33
34
|
actualDid: senderInfo.wallet.address,
|
|
34
35
|
actualPk: senderInfo.wallet.pk,
|