@abtnode/blocklet-services 1.16.18-beta-aa01bd8e → 1.16.18-beta-f4777312
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/libs/image.js +19 -12
- package/api/routes/env.js +4 -2
- package/api/routes/federated.js +4 -1
- package/api/routes/oauth.js +11 -2
- package/api/routes/user.js +4 -4
- 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 +107 -95
- 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/css/{5547.5f9be15f.chunk.css → 5547.e016de4c.chunk.css} +3 -3
- 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.f5369a1d.chunk.js +2 -0
- package/build/static/js/4160.e45b5ba1.chunk.js +2 -0
- package/build/static/js/{4461.26253c6a.chunk.js → 4461.3cd698bf.chunk.js} +2 -2
- 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.570ded36.chunk.js +3 -0
- package/build/static/js/{5547.8447edf9.chunk.js.LICENSE.txt → 5547.570ded36.chunk.js.LICENSE.txt} +2 -3
- package/build/static/js/556.e1875260.chunk.js +3 -0
- package/build/static/js/{5569.a7e151fc.chunk.js → 5569.e4bfe697.chunk.js} +2 -2
- 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.4f3036a7.chunk.js +2 -0
- package/build/static/js/{6629.376863d2.chunk.js → 6629.a4a3fb70.chunk.js} +3 -3
- 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.6f9907fb.js +3 -0
- package/build/static/js/{main.63103d24.js.LICENSE.txt → main.6f9907fb.js.LICENSE.txt} +1 -1
- 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/{556.4d5cc702.chunk.js.LICENSE.txt → 556.e1875260.chunk.js.LICENSE.txt} +0 -0
- /package/build/static/js/{6629.376863d2.chunk.js.LICENSE.txt → 6629.a4a3fb70.chunk.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/libs/image.js
CHANGED
|
@@ -128,6 +128,8 @@ const isImageRequest = (req) => {
|
|
|
128
128
|
return true;
|
|
129
129
|
};
|
|
130
130
|
|
|
131
|
+
const getImageContentType = (extension) => (extension === 'svg' ? 'image/svg+xml' : `image/${extension}`);
|
|
132
|
+
|
|
131
133
|
const tasks = {};
|
|
132
134
|
const processAndRespond = (req, res, cacheDir, getSrc, ext, sendOptions = { maxAge: '356d', immutable: true }) => {
|
|
133
135
|
if (fs.existsSync(cacheDir) === false) {
|
|
@@ -159,7 +161,7 @@ const processAndRespond = (req, res, cacheDir, getSrc, ext, sendOptions = { maxA
|
|
|
159
161
|
const cacheKey = md5(stringify({ target: req.target, path: req.originalUrl, params }));
|
|
160
162
|
const destPath = getCacheFilePath(cacheDir, `${cacheKey}.${params.f || extension}`);
|
|
161
163
|
if (fs.existsSync(destPath)) {
|
|
162
|
-
res.header('Content-Type',
|
|
164
|
+
res.header('Content-Type', getImageContentType(params.f || extension));
|
|
163
165
|
res.sendFile(destPath, sendOptions);
|
|
164
166
|
return;
|
|
165
167
|
}
|
|
@@ -176,7 +178,7 @@ const processAndRespond = (req, res, cacheDir, getSrc, ext, sendOptions = { maxA
|
|
|
176
178
|
tasks[cacheKey]
|
|
177
179
|
.then(() => {
|
|
178
180
|
logger.info('image filter succeed', { params, url: req.originalUrl, destPath });
|
|
179
|
-
res.header('Content-Type',
|
|
181
|
+
res.header('Content-Type', getImageContentType(params.f || extension));
|
|
180
182
|
res.sendFile(destPath, sendOptions);
|
|
181
183
|
})
|
|
182
184
|
.catch((err) => {
|
|
@@ -192,6 +194,21 @@ const processAndRespond = (req, res, cacheDir, getSrc, ext, sendOptions = { maxA
|
|
|
192
194
|
|
|
193
195
|
const processImage = (src, extension, dest, params) => {
|
|
194
196
|
return new Promise((resolve, reject) => {
|
|
197
|
+
// output stream
|
|
198
|
+
const out = fs.createWriteStream(dest);
|
|
199
|
+
out.on('close', () => {
|
|
200
|
+
resolve(dest);
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
out.on('error', (err) => {
|
|
204
|
+
reject(err);
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
if (extension === 'svg') {
|
|
208
|
+
src.pipe(out);
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
|
|
195
212
|
const {
|
|
196
213
|
imageFilter,
|
|
197
214
|
w: width,
|
|
@@ -251,16 +268,6 @@ const processImage = (src, extension, dest, params) => {
|
|
|
251
268
|
pipeline.negate();
|
|
252
269
|
}
|
|
253
270
|
|
|
254
|
-
// output stream
|
|
255
|
-
const out = fs.createWriteStream(dest);
|
|
256
|
-
out.on('close', () => {
|
|
257
|
-
resolve(dest);
|
|
258
|
-
});
|
|
259
|
-
|
|
260
|
-
out.on('error', (err) => {
|
|
261
|
-
reject(err);
|
|
262
|
-
});
|
|
263
|
-
|
|
264
271
|
pipeline[format || EXTENSIONS[extension]]({ quality, progressive: !!progressive, force: true });
|
|
265
272
|
|
|
266
273
|
pipeline.on('error', (err) => {
|
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
|
},
|
package/api/routes/federated.js
CHANGED
|
@@ -339,7 +339,10 @@ module.exports = {
|
|
|
339
339
|
pendingUserList.push(
|
|
340
340
|
limitSync(async () => {
|
|
341
341
|
await syncFnMaps[user.action]?.(
|
|
342
|
-
{
|
|
342
|
+
{
|
|
343
|
+
...user,
|
|
344
|
+
sourceAppPid: user.sourceAppPid === teamDid ? null : user.sourceAppPid,
|
|
345
|
+
},
|
|
343
346
|
{ node, teamDid, dataDir }
|
|
344
347
|
);
|
|
345
348
|
})
|
package/api/routes/oauth.js
CHANGED
|
@@ -64,7 +64,7 @@ function getAuthClient(blocklet, provider) {
|
|
|
64
64
|
|
|
65
65
|
async function login(req, node, options) {
|
|
66
66
|
const blocklet = await req.getBlocklet();
|
|
67
|
-
const { token, locale = 'en', provider, componentId, sourceAppPid, visitorId } = req.body;
|
|
67
|
+
const { token, locale = 'en', provider, componentId, sourceAppPid = null, visitorId } = req.body;
|
|
68
68
|
|
|
69
69
|
if (!blocklet.settings?.owner) {
|
|
70
70
|
throw new ApiError(400, t('oauthCantBeOwner', locale));
|
|
@@ -264,7 +264,15 @@ async function login(req, node, options) {
|
|
|
264
264
|
}
|
|
265
265
|
|
|
266
266
|
async function invite(req, node, options) {
|
|
267
|
-
const {
|
|
267
|
+
const {
|
|
268
|
+
locale,
|
|
269
|
+
inviteId,
|
|
270
|
+
token,
|
|
271
|
+
baseUrl,
|
|
272
|
+
provider = LOGIN_PROVIDER.AUTH0,
|
|
273
|
+
sourceAppPid = null,
|
|
274
|
+
visitorId,
|
|
275
|
+
} = req.body;
|
|
268
276
|
const blocklet = await req.getBlocklet();
|
|
269
277
|
let userWallet;
|
|
270
278
|
let oauthInfo;
|
|
@@ -390,6 +398,7 @@ async function invite(req, node, options) {
|
|
|
390
398
|
syncData.users.push({
|
|
391
399
|
...syncUserData,
|
|
392
400
|
action: 'connectAccount',
|
|
401
|
+
// HACK: @zhanghan 这里会造成 master 中的用户也增加 sourceAppPid 字段,需要在 sync 接收端处理
|
|
393
402
|
sourceAppPid: sourceAppPid || masterSite?.appPid,
|
|
394
403
|
});
|
|
395
404
|
}
|
package/api/routes/user.js
CHANGED
|
@@ -102,7 +102,7 @@ async function composeProfileData({ avatar, fullName, email }, { node, req, team
|
|
|
102
102
|
|
|
103
103
|
async function loginWallet(
|
|
104
104
|
{ did, pk, avatar, email, fullName },
|
|
105
|
-
{ node, req, locale, componentId, teamDid, updateInfo = true, sourceAppPid }
|
|
105
|
+
{ node, req, locale, componentId, teamDid, updateInfo = true, sourceAppPid = null }
|
|
106
106
|
) {
|
|
107
107
|
const provider = LOGIN_PROVIDER.WALLET;
|
|
108
108
|
const { error } = loginWalletSchema.validate({
|
|
@@ -162,7 +162,7 @@ async function loginWallet(
|
|
|
162
162
|
|
|
163
163
|
async function loginOAuth(
|
|
164
164
|
{ provider, id, avatar, email, fullName },
|
|
165
|
-
{ node, req, locale, componentId, teamDid, blockletWallet, updateInfo = true, sourceAppPid }
|
|
165
|
+
{ node, req, locale, componentId, teamDid, blockletWallet, updateInfo = true, sourceAppPid = null }
|
|
166
166
|
) {
|
|
167
167
|
const { error } = loginOAuthSchema.validate({
|
|
168
168
|
provider,
|
|
@@ -232,7 +232,7 @@ async function login(req, node, options) {
|
|
|
232
232
|
locale = 'en',
|
|
233
233
|
updateInfo = true,
|
|
234
234
|
visitorId,
|
|
235
|
-
sourceAppPid,
|
|
235
|
+
sourceAppPid = null,
|
|
236
236
|
} = req.body;
|
|
237
237
|
|
|
238
238
|
const componentId = req.get('x-blocklet-component-id');
|
|
@@ -356,7 +356,7 @@ module.exports = {
|
|
|
356
356
|
* @summary 暂时不允许用户注册,只允许登录
|
|
357
357
|
*/
|
|
358
358
|
server.post(`${prefixApi}/loginByWallet`, async (req, res) => {
|
|
359
|
-
const { userDid, signature, walletOS, nonce, visitorId, passportId, sourceAppPid, locale } = req.body;
|
|
359
|
+
const { userDid, signature, walletOS, nonce, visitorId, passportId, sourceAppPid = null, locale } = req.body;
|
|
360
360
|
const { error } = loginUserWalletSchema.validate({
|
|
361
361
|
userDid,
|
|
362
362
|
signature,
|
|
@@ -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,
|