@abtnode/blocklet-services 1.16.11-beta-0ae58a71 → 1.16.11-beta-f74663ac

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.
Files changed (50) hide show
  1. package/api/index.js +11 -2
  2. package/api/libs/auth/adapters/auth0/management-client.js +1 -1
  3. package/api/libs/auth/utils.js +1 -40
  4. package/api/libs/connect/session.js +1 -1
  5. package/api/libs/jwt.js +1 -1
  6. package/api/middlewares/check-admin-permission.js +1 -1
  7. package/api/middlewares/check-running.js +23 -5
  8. package/api/middlewares/proxy-to-daemon.js +1 -1
  9. package/api/routes/blocklet.js +94 -58
  10. package/api/routes/dns-resolver.js +2 -2
  11. package/api/routes/user.js +29 -20
  12. package/api/services/auth/connect/bind-wallet.js +2 -2
  13. package/api/services/auth/connect/exchange-passport.js +1 -1
  14. package/api/services/auth/connect/login.js +1 -1
  15. package/api/services/auth/connect/pre-setup.js +1 -1
  16. package/api/services/auth/connect/receive-transfer-app-owner.js +1 -1
  17. package/api/services/auth/connect/switch-passport.js +1 -1
  18. package/api/services/auth/connect/switch-profile.js +1 -1
  19. package/api/services/auth/session.js +8 -1
  20. package/api/services/dashboard/index.js +1 -1
  21. package/api/services/notification/index.js +7 -3
  22. package/api/services/relay/index.js +1 -1
  23. package/api/services/static.js +1 -1
  24. package/api/socket/channel/app.js +2 -2
  25. package/api/socket/channel/did.js +26 -7
  26. package/api/socket/util.js +7 -0
  27. package/api/util/attach-shared-utils.js +2 -2
  28. package/api/util/index.js +1 -0
  29. package/build/asset-manifest.json +21 -21
  30. package/build/index.html +1 -1
  31. package/build/static/css/{387.bcbdad69.chunk.css → 602.df93b930.chunk.css} +1 -1
  32. package/build/static/js/158.ca656ebd.chunk.js +2 -0
  33. package/build/static/js/{189.f876739c.chunk.js → 189.97613e3f.chunk.js} +3 -3
  34. package/build/static/js/{359.c47779c2.chunk.js → 359.d6450ee2.chunk.js} +2 -2
  35. package/build/static/js/573.7ab09349.chunk.js +2 -0
  36. package/build/static/js/602.82badcd8.chunk.js +3 -0
  37. package/build/static/js/681.e2fe9e19.chunk.js +2 -0
  38. package/build/static/js/{716.0d2a2d32.chunk.js → 716.b48e64d9.chunk.js} +2 -2
  39. package/build/static/js/737.ef64812c.chunk.js +2 -0
  40. package/build/static/js/main.b00547b9.js +3 -0
  41. package/package.json +20 -21
  42. package/build/static/js/158.4e4fac79.chunk.js +0 -2
  43. package/build/static/js/387.9cec9de4.chunk.js +0 -3
  44. package/build/static/js/573.1be7f42e.chunk.js +0 -2
  45. package/build/static/js/681.77631131.chunk.js +0 -2
  46. package/build/static/js/737.38028d6a.chunk.js +0 -2
  47. package/build/static/js/main.f3fe2438.js +0 -3
  48. /package/build/static/js/{189.f876739c.chunk.js.LICENSE.txt → 189.97613e3f.chunk.js.LICENSE.txt} +0 -0
  49. /package/build/static/js/{387.9cec9de4.chunk.js.LICENSE.txt → 602.82badcd8.chunk.js.LICENSE.txt} +0 -0
  50. /package/build/static/js/{main.f3fe2438.js.LICENSE.txt → main.b00547b9.js.LICENSE.txt} +0 -0
package/api/index.js CHANGED
@@ -13,7 +13,7 @@ const minimatch = require('minimatch');
13
13
 
14
14
  const { getAccessLogStream } = require('@abtnode/logger');
15
15
  const { WELLKNOWN_SERVICE_PATH_PREFIX, NODE_SERVICES_PREFIX, EVENTS } = require('@abtnode/constant');
16
- const { BlockletEvents } = require('@blocklet/constant');
16
+ const { BlockletEvents, BlockletInternalEvents } = require('@blocklet/constant');
17
17
  const normalizePathPrefix = require('@abtnode/util/lib/normalize-path-prefix');
18
18
  const createInvite = require('@abtnode/auth/lib/invitation');
19
19
  const eventHub =
@@ -145,6 +145,13 @@ module.exports = function createServer(node, serverOptions = {}) {
145
145
  cache.del(cache.keyFns.node());
146
146
  });
147
147
 
148
+ [BlockletInternalEvents.componentsUpdated, BlockletInternalEvents.appConfigChanged].forEach((name) => {
149
+ eventHub.on(name, (data) => {
150
+ const { appDid } = data;
151
+ notificationService.sendToApp.exec({ event: name, appDid, data });
152
+ });
153
+ });
154
+
148
155
  // Http server
149
156
  const server = express();
150
157
 
@@ -308,7 +315,7 @@ module.exports = function createServer(node, serverOptions = {}) {
308
315
  // Simple websocket router like http router
309
316
  const httpServer = http.createServer(server);
310
317
  server.listen = httpServer.listen.bind(httpServer);
311
- httpServer.on('upgrade', async (req, socket, head) => {
318
+ httpServer.on('upgrade', (req, socket, head) => {
312
319
  attachSharedUtils({ node, req, options });
313
320
 
314
321
  // find matched handler registered by each service
@@ -410,6 +417,8 @@ module.exports = function createServer(node, serverOptions = {}) {
410
417
 
411
418
  BlockletEventsNotifier.init({ node, notificationService });
412
419
 
420
+ server.sendToApp = ({ event, appDid, data }) => notificationService.sendToApp.exec({ event, appDid, data });
421
+
413
422
  return server;
414
423
  };
415
424
 
@@ -12,7 +12,7 @@ class ManagementClient {
12
12
  });
13
13
  }
14
14
 
15
- async getClient(...args) {
15
+ getClient(...args) {
16
16
  return this.instance.getClient(...args);
17
17
  }
18
18
 
@@ -1,10 +1,7 @@
1
- const md5 = require('md5');
2
- const axios = require('axios');
3
- const logger = require('@abtnode/auth/lib/logger');
4
1
  const { getPassportStatusEndpoint, getApplicationInfo } = require('@abtnode/auth/lib/auth');
5
2
  const { createPassportVC } = require('@abtnode/auth/lib/passport');
6
3
  const { VC_TYPE_NODE_PASSPORT, PASSPORT_STATUS } = require('@abtnode/constant');
7
- const { parseUserAvatar } = require('@abtnode/util/lib/user');
4
+ const { parseUserAvatar, getAvatarByEmail, getAvatarByUrl } = require('@abtnode/util/lib/user');
8
5
  const { getBlockletAppIdList } = require('@blocklet/meta/lib/util');
9
6
  const pick = require('lodash/pick');
10
7
  const uniq = require('lodash/uniq');
@@ -12,41 +9,6 @@ const uniqBy = require('lodash/uniqBy');
12
9
 
13
10
  const { sendToUser } = require('../notification');
14
11
 
15
- function getEmailHash(email = '') {
16
- const cleanEmail = email.trim().toLowerCase();
17
- return md5(cleanEmail);
18
- }
19
-
20
- async function getAvatarByUrl(url, options = {}) {
21
- const { verbose = true } = options || {};
22
- try {
23
- const { data } = await axios.get(url, {
24
- responseType: 'arraybuffer',
25
- });
26
- const base64Content = Buffer.from(data, 'binary').toString('base64');
27
-
28
- return `data:image/png;base64,${base64Content}`;
29
- } catch (error) {
30
- if (verbose) {
31
- logger.error(`Fetch avatar failed: ${url}`, { error });
32
- return null;
33
- }
34
- throw error;
35
- }
36
- }
37
-
38
- async function getAvatarByEmail(email = '') {
39
- try {
40
- const emailHash = getEmailHash(email);
41
- const gravatarUrl = `https://www.gravatar.com/avatar/${emailHash}`;
42
- const avatarBase64 = await getAvatarByUrl(gravatarUrl, { verbose: false });
43
- return avatarBase64;
44
- } catch (error) {
45
- logger.error(`Fetch gravatar failed: ${email}`, { error });
46
- return null;
47
- }
48
- }
49
-
50
12
  // FIXME: @zhanghan 转移通行证目前只能颁发新的,会导致用户数据中产生多余的通行证
51
13
  async function transferPassport(fromUser, toUser, { req, teamDid, node, nodeInfo }) {
52
14
  if (!fromUser || !toUser) {
@@ -129,7 +91,6 @@ async function transferPassport(fromUser, toUser, { req, teamDid, node, nodeInfo
129
91
  }
130
92
 
131
93
  module.exports = {
132
- getEmailHash,
133
94
  getAvatarByUrl,
134
95
  getAvatarByEmail,
135
96
  transferPassport,
@@ -637,7 +637,7 @@ module.exports = {
637
637
 
638
638
  // 基本流程与 login 一致,但在创建更新用户信息的逻辑不一样
639
639
  bindWallet: {
640
- authPrincipal: async ({ email, locale, previousUserDid }) => {
640
+ authPrincipal: ({ email, locale, previousUserDid }) => {
641
641
  const user = email || previousUserDid;
642
642
 
643
643
  const message = locale === 'zh' ? `将你的 DID 钱包与账号 ${user} 绑定` : `Connect your DID Wallet with ${user}`;
package/api/libs/jwt.js CHANGED
@@ -14,7 +14,7 @@ const getUser = async (node, teamDid, userDid) => {
14
14
  const initJwt = (node, options) => {
15
15
  const ttl = options.sessionTtl || '7d';
16
16
 
17
- const createSessionToken = async (did, { role, secret, passport }) =>
17
+ const createSessionToken = (did, { role, secret, passport }) =>
18
18
  createAuthToken({
19
19
  did,
20
20
  passport,
@@ -1,6 +1,6 @@
1
1
  const { ROLES } = require('@abtnode/constant');
2
2
 
3
- module.exports = async (req, res, next) => {
3
+ module.exports = (req, res, next) => {
4
4
  if (!req.user) {
5
5
  res.status(401).json({ code: 'forbidden', error: 'not authorized' });
6
6
  return;
@@ -1,20 +1,31 @@
1
1
  const { BlockletStatus } = require('@blocklet/constant');
2
2
  const getBlockletMaintenanceTemplate = require('@abtnode/router-templates/lib/blocklet-maintenance');
3
+ const { findComponentByIdV2 } = require('@blocklet/meta/lib/util');
3
4
 
4
5
  const { shouldGotoStartPage, getRedirectUrl } = require('../util');
5
6
 
6
7
  const checkRunning = async (req, res, next) => {
7
- const blocklet = await req.getBlocklet();
8
+ const app = await req.getBlocklet();
9
+ const componentId = req.getBlockletComponentId();
10
+ let component = findComponentByIdV2(app, componentId);
11
+
12
+ // for backward compatibility
13
+ component = component || app;
14
+
8
15
  if (
9
16
  ![
10
17
  BlockletStatus.running,
11
18
  // Waiting, Downloading should be allowed because blocklet is currently being upgrading
12
19
  BlockletStatus.waiting,
13
20
  BlockletStatus.downloading,
14
- ].includes(blocklet.status)
21
+ ].includes(component.status)
15
22
  ) {
16
- if (shouldGotoStartPage(req, blocklet)) {
17
- if (blocklet.settings.initialized) {
23
+ if (shouldGotoStartPage(req, component)) {
24
+ // FIXME: @wangshijun how to validate this token or generate token here?
25
+ if (req.query.setupToken) {
26
+ res.cookie('login_token', req.query.setupToken, { maxAge: 24 * 60 * 60 * 1000 });
27
+ }
28
+ if (app.settings.initialized) {
18
29
  res.redirect(getRedirectUrl({ req, pagePath: '/start' }));
19
30
  } else {
20
31
  res.redirect(getRedirectUrl({ req, pagePath: '/setup' }));
@@ -28,11 +39,18 @@ const checkRunning = async (req, res, next) => {
28
39
  if (req.accepts(['html', 'json']) === 'json') {
29
40
  res.json({ code: 'error', error: 'blocklet is under maintenance' });
30
41
  } else {
31
- res.send(getBlockletMaintenanceTemplate(blocklet, nodeInfo));
42
+ res.send(getBlockletMaintenanceTemplate(app, nodeInfo, component));
32
43
  }
33
44
  return;
34
45
  }
35
46
 
47
+ const url = new URL(`http://localhost${req.url}`);
48
+ if (url.searchParams.get('__start__')) {
49
+ url.searchParams.delete('__start__');
50
+ res.redirect(`${url.pathname}${url.search}`);
51
+ return;
52
+ }
53
+
36
54
  next();
37
55
  };
38
56
 
@@ -37,7 +37,7 @@ const proxyToDaemon = ({ proxy, pathname, sessionSecret }) => {
37
37
  return token;
38
38
  };
39
39
 
40
- return async (req, res) => {
40
+ return (req, res) => {
41
41
  req.url = `${pathname}?${qs.stringify(req.query)}`;
42
42
  // did returned ty req.getBlockletDid() is blocklet.meta.did
43
43
  // the blocklet.meta.did is always the same as appPid in structV2 application
@@ -1,10 +1,12 @@
1
1
  /* eslint-disable no-console */
2
2
  const fs = require('fs');
3
3
  const cloneDeep = require('lodash/cloneDeep');
4
- const joinUrl = require('url-join');
5
4
  const dayjs = require('dayjs');
6
5
  const JWT = require('@arcblock/jwt');
6
+ const joinUrl = require('url-join');
7
7
  const handleInstanceInStore = require('@abtnode/core/lib/util/public-to-store');
8
+ const { getPassportStatusEndpoint } = require('@abtnode/auth/lib/auth');
9
+ const { createPassportVC, createPassport, createUserPassport } = require('@abtnode/auth/lib/passport');
8
10
 
9
11
  const formatContext = require('@abtnode/util/lib/format-context');
10
12
  const { parseUserAvatar, getAvatarFile } = require('@abtnode/util/lib/user');
@@ -19,18 +21,15 @@ const {
19
21
  cacheError,
20
22
  } = require('@abtnode/util/lib/logo-middleware');
21
23
  const formatName = require('@abtnode/util/lib/format-name');
22
- const { fixBlockletStatus, wipeSensitiveData, findComponentById } = require('@blocklet/meta/lib/util');
23
- const { WELLKNOWN_SERVICE_PATH_PREFIX, USER_AVATAR_PATH_PREFIX } = require('@abtnode/constant');
24
+ const {
25
+ fixBlockletStatus,
26
+ wipeSensitiveData,
27
+ findComponentByIdV2,
28
+ forEachComponentV2Sync,
29
+ } = require('@blocklet/meta/lib/util');
30
+ const { WELLKNOWN_SERVICE_PATH_PREFIX, USER_AVATAR_PATH_PREFIX, ROLES } = require('@abtnode/constant');
24
31
  const logger = require('@abtnode/logger')(require('../../package.json').name);
25
32
 
26
- const { getPassportStatusEndpoint } = require('@abtnode/auth/lib/auth');
27
-
28
- const {
29
- createPassportVC,
30
- createPassport,
31
- upsertToPassports,
32
- createUserPassport,
33
- } = require('@abtnode/auth/lib/passport');
34
33
  const { createDownloadLogStream } = require('@abtnode/util/lib/log');
35
34
 
36
35
  const { BlockletStatus } = require('@blocklet/constant');
@@ -94,7 +93,7 @@ module.exports = {
94
93
  onGetBlocklet: async ({ req }) => {
95
94
  const blocklet = await req.getBlocklet();
96
95
  const dids = req.url.split('?')[0].replace(`${prefix}/blocklet/logo-bundle`, '').split('/').filter(Boolean);
97
- const component = findComponentById(blocklet, [blocklet.meta.did].concat(dids));
96
+ const component = findComponentByIdV2(blocklet, [blocklet.meta.did].concat(dids));
98
97
  return component;
99
98
  },
100
99
  }),
@@ -137,7 +136,7 @@ module.exports = {
137
136
  const blocklet = await req.getBlocklet({ useCache: false });
138
137
 
139
138
  const { fromSetup } = req.body;
140
- const { did: userDid, role } = req.user;
139
+ const { did: userDid } = req.user;
141
140
 
142
141
  // eslint-disable-next-line no-unreachable
143
142
 
@@ -176,8 +175,7 @@ module.exports = {
176
175
  const user = await node.getUser({ teamDid, user: { did: userDid } });
177
176
  user.avatar = await parseUserAvatar(user.avatar, { dataDir: blocklet.env.dataDir });
178
177
 
179
- const { pk, locale = 'en', extra = {} } = user;
180
- const { baseUrl } = extra;
178
+ const { pk, locale = 'en' } = user;
181
179
 
182
180
  await node.setBlockletInitialized({ did: blocklet.meta.did, owner: { did: userDid, pk } });
183
181
 
@@ -193,52 +191,57 @@ module.exports = {
193
191
  }
194
192
  }
195
193
 
196
- // create vc
197
- const vc = createPassportVC({
198
- issuerName: name,
199
- issuerWallet: wallet,
200
- ownerDid: userDid,
201
- passport: await createPassport({
202
- name: role,
203
- node,
204
- teamDid,
205
- locale,
206
- endpoint: baseUrl,
207
- }),
208
- endpoint: getPassportStatusEndpoint({
209
- baseUrl: joinUrl(baseUrl, WELLKNOWN_SERVICE_PATH_PREFIX),
210
- userDid,
211
- teamDid,
212
- }),
213
- ownerProfile: user,
214
- preferredColor: passportColor,
215
- });
216
-
217
- // write passport to db
218
- const passport = createUserPassport(vc, { role });
219
- const result = await node.updateUser({
220
- teamDid,
221
- user: {
222
- did: userDid,
223
- pk,
224
- passports: upsertToPassports(user.passports || [], passport),
225
- },
226
- });
227
- await node.createAuditLog(
228
- {
229
- action: 'updateUser',
230
- args: { teamDid, passport, reason: 'setup blocklet' },
231
- context: formatContext(req),
232
- result,
233
- },
234
- node
235
- );
236
-
237
194
  // send notification to wallet
238
195
  const receiver = userDid;
239
196
  const token = JWT.sign(wallet.address, wallet.secretKey);
240
197
 
241
- if (!blocklet.settings.owner) {
198
+ // send passport to wallet if no passport for this user
199
+ const role = ROLES.OWNER;
200
+ const hasOwnerPassport = (user.passports || []).some((x) => x.name === role);
201
+ if (hasOwnerPassport === false) {
202
+ const appUrl = blocklet.environments.find((item) => item.key === 'BLOCKLET_APP_URL').value;
203
+
204
+ // create vc
205
+ const vc = createPassportVC({
206
+ issuerName: name,
207
+ issuerWallet: wallet,
208
+ ownerDid: userDid,
209
+ passport: await createPassport({
210
+ name: role,
211
+ node,
212
+ teamDid,
213
+ locale,
214
+ endpoint: appUrl,
215
+ }),
216
+ endpoint: getPassportStatusEndpoint({
217
+ baseUrl: joinUrl(appUrl, WELLKNOWN_SERVICE_PATH_PREFIX),
218
+ userDid,
219
+ teamDid,
220
+ }),
221
+ ownerProfile: user,
222
+ preferredColor: passportColor,
223
+ expirationDate: undefined,
224
+ });
225
+
226
+ // write passport to db
227
+ const passport = createUserPassport(vc, { role });
228
+ const result = await node.updateUser({
229
+ teamDid,
230
+ user: {
231
+ did: userDid,
232
+ passports: [passport],
233
+ },
234
+ });
235
+ await node.createAuditLog(
236
+ {
237
+ action: 'updateUser',
238
+ args: { teamDid, passport, reason: 'setup blocklet' },
239
+ context: formatContext(req),
240
+ result,
241
+ },
242
+ node
243
+ );
244
+
242
245
  // send owner vc
243
246
  const notificationText = {
244
247
  title: {
@@ -261,7 +264,7 @@ module.exports = {
261
264
  type: 'vc',
262
265
  data: {
263
266
  credential: vc,
264
- tag: role,
267
+ tag: ROLES.OWNER,
265
268
  },
266
269
  },
267
270
  ],
@@ -320,10 +323,43 @@ module.exports = {
320
323
  server.get(`${prefix}/health`, async (req, res) => {
321
324
  const blocklet = await req.getBlocklet();
322
325
 
326
+ if (!blocklet) {
327
+ return res.status(404).json({ message: 'blocklet not found' });
328
+ }
329
+
323
330
  if (blocklet.status !== BlockletStatus.running) {
324
331
  return res.status(503).json({ message: 'not running' });
325
332
  }
326
333
 
334
+ const components = {};
335
+ forEachComponentV2Sync(blocklet, (component) => {
336
+ components[component.meta.did] = {
337
+ running: component.status === BlockletStatus.running,
338
+ };
339
+ });
340
+
341
+ return res.json({ message: 'ok', components });
342
+ });
343
+
344
+ server.get(`${prefix}/health/:componentId`, async (req, res) => {
345
+ const { componentId } = req.params;
346
+
347
+ const blocklet = await req.getBlocklet();
348
+
349
+ if (!blocklet) {
350
+ return res.status(404).json({ message: 'blocklet not found' });
351
+ }
352
+
353
+ const component = findComponentByIdV2(blocklet, componentId);
354
+
355
+ if (!component) {
356
+ return res.status(404).json({ message: 'component not found' });
357
+ }
358
+
359
+ if (component.status !== BlockletStatus.running) {
360
+ return res.status(503).json({ message: 'not running' });
361
+ }
362
+
327
363
  return res.json({ message: 'ok' });
328
364
  });
329
365
 
@@ -9,7 +9,7 @@ module.exports = {
9
9
  maxAge: 5 * 60 * 1000, // cache for 5 minute
10
10
  });
11
11
 
12
- app.get(`${WELLKNOWN_SERVICE_PATH_PREFIX}/api/dns-resolve/`, async (req, res) => {
12
+ app.get(`${WELLKNOWN_SERVICE_PATH_PREFIX}/api/dns-resolve/`, (req, res) => {
13
13
  const { hostname } = req.query;
14
14
 
15
15
  if (!hostname) {
@@ -24,7 +24,7 @@ module.exports = {
24
24
  return;
25
25
  }
26
26
 
27
- dns.lookup(hostname, async (err, address) => {
27
+ dns.lookup(hostname, (err, address) => {
28
28
  if (err) {
29
29
  cache.del(hostname);
30
30
  res.json({ address: null, error: err.message });
@@ -4,6 +4,7 @@ const { fromAppDid } = require('@arcblock/did-ext');
4
4
  const { extractUserAvatar } = require('@abtnode/util/lib/user');
5
5
  const formatContext = require('@abtnode/util/lib/format-context');
6
6
  const logger = require('@abtnode/logger')('blocklet-services:user');
7
+ const { isFromPublicKey } = require('@arcblock/did');
7
8
 
8
9
  const { generateTranslate } = require('../libs/translate');
9
10
  const { isInvitedUserOnly } = require('../util');
@@ -14,6 +15,14 @@ const { loginWalletSchema, loginOAuthSchema } = require('../validators/login');
14
15
  const verifySig = require('../middlewares/verify-sig');
15
16
  const { getAvatarByEmail } = require('../libs/auth/utils');
16
17
 
18
+ const validateUser = (user) => {
19
+ try {
20
+ return user && user.did && user.pk && isFromPublicKey(user.did, user.pk);
21
+ } catch (e) {
22
+ return false;
23
+ }
24
+ };
25
+
17
26
  const PREFIX = WELLKNOWN_SERVICE_PATH_PREFIX;
18
27
 
19
28
  const prefix = `${PREFIX}/user`;
@@ -22,11 +31,13 @@ const translations = {
22
31
  needInviteToLogin: '你需要被邀请才可以登录此应用',
23
32
  notAllowed: '你没有权限登录该节点',
24
33
  needComponentId: '缺少登录参数: componentId',
34
+ userInfoError: '登录用户信息有误',
25
35
  },
26
36
  en: {
27
37
  needInviteToLogin: 'You need to be invited to sign in to this app',
28
38
  notAllowed: 'You are not allowed to login to this node',
29
39
  needComponentId: 'componentId is required when login user',
40
+ userInfoError: 'Login user info is invalid',
30
41
  },
31
42
  };
32
43
 
@@ -40,7 +51,7 @@ async function checkNeedInvite({ req, node, teamDid, componentId, locale }) {
40
51
  }
41
52
  }
42
53
 
43
- async function checkUserEnable(user, { locale }) {
54
+ function checkUserEnable(user, { locale }) {
44
55
  if (!user.approved) {
45
56
  throw new ApiError(403, t('notAllowed', locale));
46
57
  }
@@ -62,7 +73,7 @@ async function composeProfileData({ avatar, fullName, email }, { node, req, team
62
73
  }
63
74
  }
64
75
  if (avatar) {
65
- avatarLocal = await getAvatarByUrl(avatar);
76
+ avatarLocal = avatar.startsWith('data:') ? avatar : await getAvatarByUrl(avatar);
66
77
  }
67
78
  if (avatarLocal) {
68
79
  const nodeInfo = await req.getNodeInfo();
@@ -77,10 +88,7 @@ async function composeProfileData({ avatar, fullName, email }, { node, req, team
77
88
  return profile;
78
89
  }
79
90
 
80
- async function loginWallet(
81
- { did, pk, avatar, email, fullName },
82
- { node, req, locale, componentId, teamDid, blockletWallet }
83
- ) {
91
+ async function loginWallet({ did, pk, avatar, email, fullName }, { node, req, locale, componentId, teamDid }) {
84
92
  const provider = 'wallet';
85
93
  const { error } = loginWalletSchema.validate({
86
94
  provider,
@@ -101,8 +109,6 @@ async function loginWallet(
101
109
  },
102
110
  options: {
103
111
  enableConnectedAccount: true,
104
- enableNormalize: true,
105
- blockletSk: blockletWallet.secretKey,
106
112
  },
107
113
  });
108
114
 
@@ -116,17 +122,21 @@ async function loginWallet(
116
122
  }
117
123
 
118
124
  const lastLoginIp = req.get('x-real-ip');
119
- const doc = await node.loginUser({
125
+ const loginParams = {
120
126
  teamDid,
121
127
  user: {
122
- did: currentUser?.did || pk,
123
- pk: currentUser?.pk || did,
128
+ did: currentUser?.did || did,
129
+ pk: currentUser?.pk || pk,
124
130
  locale,
125
131
  lastLoginIp,
126
132
  connectedAccount: { provider, did, pk },
127
133
  ...profile,
128
134
  },
129
- });
135
+ };
136
+ if (!validateUser(loginParams.user) || !validateUser(loginParams.user.connectedAccount)) {
137
+ throw new ApiError(400, t('userInfoError', locale));
138
+ }
139
+ const doc = await node.loginUser(loginParams);
130
140
 
131
141
  // NOTICE: 使用 provider: wallet 登录的账号,其账号是由 wallet 产生的,所以不需要到链上 declare 了
132
142
  return doc;
@@ -157,8 +167,6 @@ async function loginOAuth(
157
167
  },
158
168
  options: {
159
169
  enableConnectedAccount: true,
160
- enableNormalize: true,
161
- blockletSk: blockletWallet.secretKey,
162
170
  },
163
171
  });
164
172
 
@@ -173,7 +181,7 @@ async function loginOAuth(
173
181
 
174
182
  const lastLoginIp = req.get('x-real-ip');
175
183
 
176
- const doc = await node.loginUser({
184
+ const loginParams = {
177
185
  teamDid,
178
186
  user: {
179
187
  did: currentUser?.did || userDid,
@@ -183,7 +191,11 @@ async function loginOAuth(
183
191
  connectedAccount: { provider, id, did: userDid, pk: userPk },
184
192
  ...profile,
185
193
  },
186
- });
194
+ };
195
+ if (!validateUser(loginParams.user) || !validateUser(loginParams.user.connectedAccount)) {
196
+ throw new ApiError(400, t('userInfoError', locale));
197
+ }
198
+ const doc = await node.loginUser(loginParams);
187
199
  return doc;
188
200
  }
189
201
 
@@ -198,10 +210,7 @@ async function login(req, node, options) {
198
210
 
199
211
  let doc;
200
212
  if (provider === 'wallet') {
201
- doc = await loginWallet(
202
- { did, pk, avatar, email, fullName },
203
- { node, req, locale, componentId, teamDid, blockletWallet }
204
- );
213
+ doc = await loginWallet({ did, pk, avatar, email, fullName }, { node, req, locale, componentId, teamDid });
205
214
  } else {
206
215
  doc = await loginOAuth(
207
216
  { provider, id, avatar, email, fullName },
@@ -9,11 +9,11 @@ module.exports = function createRoutes(node, authenticator, createSessionToken)
9
9
  action: 'bind-wallet',
10
10
  authPrincipal: false,
11
11
  claims: {
12
- authPrincipal: async ({ extraParams: { locale, previousUserDid, email } }) => {
12
+ authPrincipal: ({ extraParams: { locale, previousUserDid, email } }) => {
13
13
  return authPrincipal({ locale, previousUserDid, email });
14
14
  },
15
15
  },
16
- onConnect: async ({ req, userDid, extraParams: { locale, passportId = '', componentId, previousUserDid } }) => {
16
+ onConnect: ({ req, userDid, extraParams: { locale, passportId = '', componentId, previousUserDid } }) => {
17
17
  return onConnect({
18
18
  node,
19
19
  request: req,
@@ -17,7 +17,7 @@ module.exports = function createRoutes(node, authenticator, createSessionToken)
17
17
  }
18
18
  },
19
19
 
20
- onConnect: async ({ req, userDid, extraParams: { locale, passportId = '', componentId } }) => {
20
+ onConnect: ({ req, userDid, extraParams: { locale, passportId = '', componentId } }) => {
21
21
  return onConnect({ node, request: req, userDid, locale, passportId, componentId, action: 'exchangePassport' });
22
22
  },
23
23
 
@@ -7,7 +7,7 @@ const { onConnect, onApprove } = login;
7
7
  module.exports = function createRoutes(node, authenticator, createSessionToken) {
8
8
  return {
9
9
  action: 'login',
10
- onConnect: async ({ req, userDid, extraParams: { locale, passportId = '', componentId }, baseUrl }) => {
10
+ onConnect: ({ req, userDid, extraParams: { locale, passportId = '', componentId }, baseUrl }) => {
11
11
  return onConnect({ node, request: req, userDid, locale, passportId, componentId, action: 'login', baseUrl });
12
12
  },
13
13
 
@@ -9,7 +9,7 @@ module.exports = function createRoutes() {
9
9
  action: 'pre-setup',
10
10
  authPrincipal: false,
11
11
  claims: getAppDidOwnerClaims(),
12
- onAuth: async ({ claims, userDid, userPk, extraParams: { locale } }) => {
12
+ onAuth: ({ claims, userDid, userPk, extraParams: { locale } }) => {
13
13
  const claim = claims.find((x) => x.type === 'signature');
14
14
  verifySignature(claim, userDid, userPk, locale);
15
15
  logger.info('pre-setup.connect.success', { userDid });
@@ -131,7 +131,7 @@ module.exports = function createRoutes(node, _, createSessionToken) {
131
131
 
132
132
  return getKeyPairClaim(node, { title: blocklet.meta.title, declare: false })(opts);
133
133
  },
134
- profile: async ({ extraParams: { locale = 'en' } }) => {
134
+ profile: ({ extraParams: { locale = 'en' } }) => {
135
135
  return {
136
136
  fields: ['fullName', 'email', 'avatar'],
137
137
  description: messages.requestProfile[locale],
@@ -5,7 +5,7 @@ const { onConnect, onApprove } = switchPassport;
5
5
  module.exports = function createRoutes(node, authenticator, createSessionToken) {
6
6
  return {
7
7
  action: 'switch-passport',
8
- onConnect: async ({ req, userDid, baseUrl, extraParams: { locale, connectedDid } }) => {
8
+ onConnect: ({ req, userDid, baseUrl, extraParams: { locale, connectedDid } }) => {
9
9
  return onConnect({
10
10
  node,
11
11
  request: req,
@@ -15,7 +15,7 @@ module.exports = function createRoutes(node) {
15
15
  });
16
16
  },
17
17
 
18
- onAuth: async ({ req, claims, userDid, extraParams: { locale } }) => {
18
+ onAuth: ({ req, claims, userDid, extraParams: { locale } }) => {
19
19
  const profile = claims.find((x) => x.type === 'profile');
20
20
  return onApprove({
21
21
  node,