@abtnode/blocklet-services 1.16.7 → 1.16.8-beta-ca58a421

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 CHANGED
@@ -34,6 +34,7 @@ const StaticService = require('./services/static');
34
34
  const StudioService = require('./services/studio');
35
35
  const createEnvRoutes = require('./routes/env');
36
36
  const createOAuthRoutes = require('./routes/oauth');
37
+ const createUserRoutes = require('./routes/user');
37
38
  const createBlockletRoutes = require('./routes/blocklet');
38
39
  const createConnectRelayRoutes = require('./routes/connect/relay');
39
40
  const createConnectSessionRoutes = require('./routes/connect/session');
@@ -223,6 +224,7 @@ module.exports = function createServer(node, serverOptions = {}) {
223
224
 
224
225
  // API: auth
225
226
  createOAuthRoutes.init(server, node, options);
227
+ createUserRoutes.init(server, node, options);
226
228
  createEnvRoutes.init(server, node, options);
227
229
  createBlockletRoutes.init(server, node);
228
230
  createConnectSessionRoutes.init(server, node, options);
@@ -17,16 +17,30 @@ function getEmailHash(email = '') {
17
17
  return md5(cleanEmail);
18
18
  }
19
19
 
20
- async function getAvatarByEmail(email = '') {
20
+ async function getAvatarByUrl(url, options = {}) {
21
+ const { verbose = true } = options || {};
21
22
  try {
22
- const emailHash = getEmailHash(email);
23
- const gravatarUrl = `https://www.gravatar.com/avatar/${emailHash}`;
24
- const { data } = await axios.get(gravatarUrl, {
23
+ const { data } = await axios.get(url, {
25
24
  responseType: 'arraybuffer',
26
25
  });
27
26
  const base64Content = Buffer.from(data, 'binary').toString('base64');
28
27
 
29
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;
30
44
  } catch (error) {
31
45
  logger.error(`Fetch gravatar failed: ${email}`, { error });
32
46
  return null;
@@ -116,6 +130,7 @@ async function transferPassport(fromUser, toUser, { req, teamDid, node, nodeInfo
116
130
 
117
131
  module.exports = {
118
132
  getEmailHash,
133
+ getAvatarByUrl,
119
134
  getAvatarByEmail,
120
135
  transferPassport,
121
136
  };
@@ -33,7 +33,6 @@ const {
33
33
  } = require('@abtnode/auth/lib/passport');
34
34
  const { getKeyPairClaim, getAuthPrincipalForMigrateAppToV2 } = require('@abtnode/auth/lib/server');
35
35
  const merge = require('lodash/merge');
36
- const { types } = require('@arcblock/did');
37
36
  const { fromAppDid } = require('@arcblock/did-ext');
38
37
 
39
38
  const { getRolesFromAuthConfig, getBlockletAppIdList } = require('@blocklet/meta/lib/util');
@@ -43,7 +42,7 @@ const logger = require('@abtnode/logger')(require('../../../package.json').name)
43
42
  const { isInvitedUserOnly } = require('../../util');
44
43
  const { transferPassport } = require('../auth/utils');
45
44
  const { generateTranslate } = require('../translate');
46
- const { mergeUserData, migrateAccount } = require('../../services/oauth');
45
+ const { mergeUserData, migrateAccount, declareAccount } = require('../../services/oauth');
47
46
 
48
47
  const vcTypes = [VC_TYPE_GENERAL_PASSPORT, VC_TYPE_NODE_PASSPORT];
49
48
 
@@ -468,7 +467,7 @@ module.exports = {
468
467
  },
469
468
  onApprove: async ({ node, request, locale, profile, userDid }) => {
470
469
  const blocklet = await request.getBlocklet();
471
- const { did: teamDid, blocklet: blockletWallet } = await request.getBlockletInfo();
470
+ const { did: teamDid, wallet: blockletWallet } = await request.getBlockletInfo();
472
471
 
473
472
  // check user approved
474
473
  const user = await node.getUser({
@@ -843,7 +842,8 @@ module.exports = {
843
842
  const connectedAccounts = oauthUser?.extraConfigs?.connectedAccounts || [];
844
843
  const sourceProvider = oauthUser?.extraConfigs?.sourceProvider;
845
844
  const oauthAccount = connectedAccounts.find((item) => item.provider === sourceProvider);
846
- const userWallet = fromAppDid(oauthAccount.id, blockletWallet.secretKey, types.RoleType.ROLE_ACCOUNT);
845
+ const userWallet = fromAppDid(oauthAccount.id, blockletWallet.secretKey);
846
+ await declareAccount({ wallet: userWallet, blocklet });
847
847
  await migrateAccount({ wallet: userWallet, blocklet, user: bindUser });
848
848
  await node.createAuditLog(
849
849
  {
@@ -0,0 +1,25 @@
1
+ const { verify } = require('@blocklet/sdk/lib/util/verify-sign');
2
+
3
+ const verifySig = async (req, res, next) => {
4
+ try {
5
+ const blocklet = await req.getBlocklet();
6
+ const sig = req.get('x-blocklet-sig');
7
+ const data = typeof req.body === 'undefined' ? {} : req.body;
8
+ const params = typeof req.query === 'undefined' ? {} : req.query;
9
+ const verified = verify({ data, params }, sig, {
10
+ // NOTICE: blocklet-service 的运行环境中不包含以下环境变量,必须从 blocklet 信息中去获取并传递
11
+ type: blocklet.environmentObj.BLOCKLET_APP_CHAIN_TYPE,
12
+ appSk: blocklet.environmentObj.BLOCKLET_APP_SK,
13
+ });
14
+ if (!verified) {
15
+ res.status(401).json('verify sig failed');
16
+ return;
17
+ }
18
+ } catch (error) {
19
+ res.status(401).json('verify sig failed');
20
+ return;
21
+ }
22
+ next();
23
+ };
24
+
25
+ module.exports = verifySig;
@@ -2,7 +2,6 @@ const { handleInvitationReceive, getApplicationInfo } = require('@abtnode/auth/l
2
2
  const { upsertToPassports, createPassportSvg } = require('@abtnode/auth/lib/passport');
3
3
  const { WELLKNOWN_SERVICE_PATH_PREFIX, NODE_SERVICES, PASSPORT_STATUS } = require('@abtnode/constant');
4
4
  const { parseUserAvatar, extractUserAvatar } = require('@abtnode/util/lib/user-avatar');
5
- const { types } = require('@arcblock/did');
6
5
  const { fromAppDid } = require('@arcblock/did-ext');
7
6
  const { getBlockletAppIdList } = require('@blocklet/meta/lib/util');
8
7
  const get = require('lodash/get');
@@ -14,14 +13,16 @@ const sortBy = require('lodash/sortBy');
14
13
  const joinUrl = require('url-join');
15
14
  const { getWalletDid } = require('@blocklet/meta/lib/did-utils');
16
15
  const formatContext = require('@abtnode/util/lib/format-context');
16
+ const logger = require('@abtnode/logger')('blocklet-services:oauth');
17
17
 
18
18
  const { AuthenticationClient } = require('../libs/auth/adapters/auth0');
19
- const { getAvatarByEmail, transferPassport } = require('../libs/auth/utils');
19
+ const { getAvatarByEmail, transferPassport, getAvatarByUrl } = require('../libs/auth/utils');
20
20
  const initJwt = require('../libs/jwt');
21
21
  const { sendToUser } = require('../libs/notification');
22
22
  const { generateTranslate } = require('../libs/translate');
23
- const { mergeUserData, declareAccount, migrateAccount } = require('../services/oauth');
23
+ const { mergeUserData, migrateAccount, declareAccount } = require('../services/oauth');
24
24
  const { isInvitedUserOnly } = require('../util');
25
+ const { ApiError } = require('../util/error');
25
26
 
26
27
  const PREFIX = WELLKNOWN_SERVICE_PATH_PREFIX;
27
28
 
@@ -48,7 +49,7 @@ function getAuthClient(blocklet, provider) {
48
49
  const oauthConfig = blocklet?.settings?.oauth || {};
49
50
  const providerConfig = oauthConfig?.[provider];
50
51
  if (!providerConfig) {
51
- throw new Error(`Provider ${provider} is not valid`);
52
+ throw new ApiError(400, `Provider ${provider} is not valid`);
52
53
  }
53
54
  const authClient = new AuthenticationClient({
54
55
  domain: providerConfig.domain,
@@ -61,7 +62,7 @@ async function login(req, node, options) {
61
62
  const { token, locale = 'en', provider, componentId } = req.body;
62
63
 
63
64
  if (!blocklet.settings?.owner) {
64
- throw new Error(t('oauthCantBeOwner', locale));
65
+ throw new ApiError(400, t('oauthCantBeOwner', locale));
65
66
  }
66
67
  const authClient = getAuthClient(blocklet, provider);
67
68
 
@@ -71,7 +72,8 @@ async function login(req, node, options) {
71
72
  const { dataDir } = await getApplicationInfo({ node, nodeInfo, teamDid });
72
73
  const [invitedUserOnly] = await isInvitedUserOnly(config, node, teamDid);
73
74
  const oauthInfo = await authClient.getProfile(token);
74
- const userWallet = fromAppDid(oauthInfo.sub, blockletWallet.secretKey, types.RoleType.ROLE_ACCOUNT);
75
+
76
+ const userWallet = fromAppDid(oauthInfo.sub, blockletWallet.secretKey);
75
77
  const userDid = userWallet.address;
76
78
  const userPk = userWallet.publicKey;
77
79
  const lastLoginIp = get(req, 'headers[x-real-ip]') || '';
@@ -114,11 +116,11 @@ async function login(req, node, options) {
114
116
  did: userWallet.address,
115
117
  pk: userWallet.publicKey,
116
118
  };
117
- let avatar = await getAvatarByEmail(oauthInfo.email);
119
+ let avatar = oauthInfo.picture ? await getAvatarByUrl(oauthInfo.picture) : await getAvatarByEmail(oauthInfo.email);
118
120
  avatar = await extractUserAvatar(avatar, { dataDir });
119
121
  // 当前 oauth 账户不存在,自动添加一个新用户
120
122
  if (invitedUserOnly) {
121
- throw new Error(t('needInviteToLogin', locale));
123
+ throw new ApiError(403, t('needInviteToLogin', locale));
122
124
  }
123
125
  passportForLog = { name: 'Guest', role: 'guest' };
124
126
  doc = await node.loginUser({
@@ -139,7 +141,6 @@ async function login(req, node, options) {
139
141
  lastLoginIp,
140
142
  },
141
143
  });
142
- await declareAccount({ wallet: userWallet, moniker: oauthInfo.nickname, blocklet });
143
144
  }
144
145
 
145
146
  await node.createAuditLog(
@@ -170,7 +171,7 @@ async function invite(req, node, options) {
170
171
  const { did: teamDid, wallet: blockletWallet, secret } = await req.getBlockletInfo();
171
172
  const nodeInfo = await req.getNodeInfo();
172
173
  const oauthInfo = await authClient.getProfile(token);
173
- const userWallet = fromAppDid(oauthInfo.sub, blockletWallet.secretKey, types.RoleType.ROLE_ACCOUNT);
174
+ const userWallet = fromAppDid(oauthInfo.sub, blockletWallet.secretKey);
174
175
  let userDid = userWallet.address;
175
176
  let userPk = userWallet.publicKey;
176
177
 
@@ -197,8 +198,8 @@ async function invite(req, node, options) {
197
198
  userDid = currentUser.did;
198
199
  userPk = currentUser.pk;
199
200
  } else {
200
- const { email, nickname: fullName } = oauthInfo;
201
- let avatar = await getAvatarByEmail(email);
201
+ const { picture, email, nickname: fullName } = oauthInfo;
202
+ let avatar = picture ? await getAvatarByUrl(picture) : await getAvatarByEmail(email);
202
203
  avatar = await extractUserAvatar(avatar, { dataDir });
203
204
  profile = {
204
205
  email,
@@ -261,7 +262,6 @@ async function invite(req, node, options) {
261
262
  },
262
263
  },
263
264
  });
264
- await declareAccount({ wallet: userWallet, moniker: oauthInfo.nickname, blocklet });
265
265
  }
266
266
 
267
267
  const { createSessionToken } = initJwt(node, options);
@@ -279,7 +279,7 @@ async function bind(req, node, options) {
279
279
  const userDid = req.user.did;
280
280
  const userInfo = await authClient.getProfile(token);
281
281
  const { did: teamDid, wallet: blockletWallet } = await req.getBlockletInfo();
282
- const userWallet = fromAppDid(userInfo.sub, blockletWallet.secretKey, types.RoleType.ROLE_ACCOUNT);
282
+ const userWallet = fromAppDid(userInfo.sub, blockletWallet.secretKey);
283
283
  let oauthUser = await node.getUser({
284
284
  teamDid,
285
285
  user: { did: userWallet.address },
@@ -288,14 +288,14 @@ async function bind(req, node, options) {
288
288
  },
289
289
  });
290
290
  if (oauthUser) {
291
- throw new Error(t('alreadyMainAccount', locale));
291
+ throw new ApiError(400, t('alreadyMainAccount', locale));
292
292
  }
293
293
 
294
294
  // NOTICE: 这里获得的 did 是当前登录用户的永久 did,无需再去查询 connectedAccount
295
295
  const bindUser = await node.getUser({ teamDid, user: { did: userDid } });
296
296
 
297
297
  if (bindUser.extraConfigs?.sourceProvider !== 'wallet') {
298
- throw new Error('oauthCantBindOauth', locale);
298
+ throw new ApiError(400, t('oauthCantBindOauth', locale));
299
299
  }
300
300
 
301
301
  const mergePassport = (oauthUser?.passports || []).reduce((sum, cur) => {
@@ -359,6 +359,7 @@ async function bind(req, node, options) {
359
359
  },
360
360
  },
361
361
  });
362
+ await declareAccount({ wallet: userWallet, blocklet });
362
363
  await migrateAccount({ wallet: userWallet, blocklet, user: bindUser });
363
364
  } else {
364
365
  oauthUser = {
@@ -428,8 +429,17 @@ module.exports = {
428
429
  });
429
430
 
430
431
  server.post(`${prefix}/bind`, async (req, res) => {
431
- await bind(req, node, options);
432
- res.status(200).send('');
432
+ try {
433
+ await bind(req, node, options);
434
+ res.status(200).send('');
435
+ } catch (err) {
436
+ logger.error('Failed bind oauth', { error: err });
437
+ if (err instanceof ApiError) {
438
+ res.status(err.code).send(err.message);
439
+ return;
440
+ }
441
+ throw err;
442
+ }
433
443
  });
434
444
 
435
445
  server.post(`${prefix}/switch`, async (req, res) => {
@@ -469,7 +479,12 @@ module.exports = {
469
479
  const result = await actionMap[action](req, node, options);
470
480
  res.send(result);
471
481
  } catch (err) {
472
- res.status(400).send(err.message);
482
+ logger.error('Failed login oauth', { error: err });
483
+ if (err instanceof ApiError) {
484
+ res.status(err.code).send(err.message);
485
+ return;
486
+ }
487
+ throw err;
473
488
  }
474
489
  });
475
490
  },
@@ -0,0 +1,235 @@
1
+ const { NODE_SERVICES, WELLKNOWN_SERVICE_PATH_PREFIX } = require('@abtnode/constant');
2
+ const { getApplicationInfo } = require('@abtnode/auth/lib/auth');
3
+ const { fromAppDid } = require('@arcblock/did-ext');
4
+ const { extractUserAvatar } = require('@abtnode/util/lib/user-avatar');
5
+ const formatContext = require('@abtnode/util/lib/format-context');
6
+ const logger = require('@abtnode/logger')('blocklet-services:user');
7
+
8
+ const { generateTranslate } = require('../libs/translate');
9
+ const { isInvitedUserOnly } = require('../util');
10
+ const initJwt = require('../libs/jwt');
11
+ const { getAvatarByUrl } = require('../libs/auth/utils');
12
+ const { ApiError } = require('../util/error');
13
+ const { loginWalletSchema, loginOAuthSchema } = require('../validators/login');
14
+ const verifySig = require('../middlewares/verify-sig');
15
+
16
+ const PREFIX = WELLKNOWN_SERVICE_PATH_PREFIX;
17
+
18
+ const prefix = `${PREFIX}/user`;
19
+ const translations = {
20
+ zh: {
21
+ needInviteToLogin: '你需要被邀请才可以登录此应用',
22
+ notAllowed: '你没有权限登录该节点',
23
+ needComponentId: '缺少登录参数: componentId',
24
+ },
25
+ en: {
26
+ needInviteToLogin: 'You need to be invited to sign in to this app',
27
+ notAllowed: 'You are not allowed to login to this node',
28
+ needComponentId: 'componentId is required when login user',
29
+ },
30
+ };
31
+
32
+ const t = generateTranslate({ translations });
33
+
34
+ async function checkNeedInvite({ req, node, teamDid, componentId, locale }) {
35
+ const config = await req.getServiceConfig(NODE_SERVICES.AUTH, { componentId });
36
+ const [invitedUserOnly] = await isInvitedUserOnly(config, node, teamDid);
37
+ if (invitedUserOnly) {
38
+ throw new ApiError(403, t('needInviteToLogin', locale));
39
+ }
40
+ }
41
+
42
+ async function checkUserEnable(user, { locale }) {
43
+ if (!user.approved) {
44
+ throw new ApiError(403, t('notAllowed', locale));
45
+ }
46
+ }
47
+
48
+ async function composeProfileData({ avatar, fullName, email }, { node, req, teamDid }) {
49
+ const profile = {};
50
+ if (avatar) {
51
+ let avatarLocal = await getAvatarByUrl(avatar);
52
+ const nodeInfo = await req.getNodeInfo();
53
+ const { dataDir } = await getApplicationInfo({ node, nodeInfo, teamDid });
54
+ avatarLocal = await extractUserAvatar(avatar, { dataDir });
55
+ profile.avatar = avatarLocal;
56
+ }
57
+ if (email) {
58
+ profile.email = email;
59
+ }
60
+ if (fullName) {
61
+ profile.fullName = fullName;
62
+ }
63
+ return profile;
64
+ }
65
+
66
+ async function loginWallet(
67
+ { did, pk, avatar, email, fullName },
68
+ { node, req, locale, componentId, teamDid, blockletWallet }
69
+ ) {
70
+ const provider = 'wallet';
71
+ const { error } = loginWalletSchema.validate({
72
+ provider,
73
+ did,
74
+ pk,
75
+ avatar,
76
+ email,
77
+ fullName,
78
+ });
79
+ if (error) {
80
+ throw new ApiError(400, error.message);
81
+ }
82
+
83
+ const currentUser = await node.getUser({
84
+ teamDid,
85
+ user: {
86
+ did,
87
+ },
88
+ options: {
89
+ enableConnectedAccount: true,
90
+ enableNormalize: true,
91
+ blockletSk: blockletWallet.secretKey,
92
+ },
93
+ });
94
+
95
+ if (currentUser) {
96
+ await checkUserEnable(currentUser, { locale });
97
+ } else {
98
+ await checkNeedInvite(req, node, teamDid, componentId, locale);
99
+ }
100
+
101
+ const profile = await composeProfileData({ avatar, email, fullName }, { node, req, teamDid });
102
+
103
+ const lastLoginIp = req.get('x-real-ip');
104
+ const doc = await node.loginUser({
105
+ teamDid,
106
+ user: {
107
+ did: currentUser.did,
108
+ pk: currentUser.pk,
109
+ locale,
110
+ lastLoginIp,
111
+ connectedAccount: { provider, did, pk },
112
+ ...profile,
113
+ },
114
+ });
115
+
116
+ // NOTICE: 使用 provider: wallet 登录的账号,其账号是由 wallet 产生的,所以不需要到链上 declare 了
117
+
118
+ return doc;
119
+ }
120
+
121
+ async function loginOAuth(
122
+ { provider, id, avatar, email, fullName },
123
+ { node, req, locale, componentId, teamDid, blockletWallet }
124
+ ) {
125
+ const { error } = loginOAuthSchema.validate({
126
+ provider,
127
+ id,
128
+ avatar,
129
+ email,
130
+ fullName,
131
+ });
132
+ if (error) {
133
+ throw new ApiError(400, error.message);
134
+ }
135
+
136
+ const userWallet = fromAppDid(id, blockletWallet.secretKey);
137
+ const userDid = userWallet.address;
138
+ const userPk = userWallet.publicKey;
139
+ const currentUser = await node.getUser({
140
+ teamDid,
141
+ user: {
142
+ did: userDid,
143
+ },
144
+ options: {
145
+ enableConnectedAccount: true,
146
+ enableNormalize: true,
147
+ blockletSk: blockletWallet.secretKey,
148
+ },
149
+ });
150
+
151
+ if (currentUser) {
152
+ await checkUserEnable(currentUser, { locale });
153
+ } else {
154
+ await checkNeedInvite(req, node, teamDid, componentId, locale);
155
+ }
156
+ const profile = await composeProfileData({ avatar, email, fullName }, { node, req, teamDid });
157
+
158
+ const lastLoginIp = req.get('x-real-ip');
159
+
160
+ const doc = await node.loginUser({
161
+ teamDid,
162
+ user: {
163
+ did: currentUser.did,
164
+ pk: currentUser.pk,
165
+ locale,
166
+ lastLoginIp,
167
+ connectedAccount: { provider, id, did: userDid, pk: userPk },
168
+ ...profile,
169
+ },
170
+ });
171
+ return doc;
172
+ }
173
+
174
+ async function login(req, node, options) {
175
+ const { provider = 'wallet', did, pk, avatar, email, fullName, id, locale = 'en' } = req.body;
176
+
177
+ const componentId = req.get('x-blocklet-component-id');
178
+ if (!componentId) {
179
+ throw new ApiError(400, t('needComponentId', locale));
180
+ }
181
+ const { did: teamDid, wallet: blockletWallet, secret } = await req.getBlockletInfo();
182
+
183
+ let doc;
184
+ if (provider === 'wallet') {
185
+ doc = await loginWallet(
186
+ { did, pk, avatar, email, fullName },
187
+ { node, req, locale, componentId, teamDid, blockletWallet }
188
+ );
189
+ } else {
190
+ doc = await loginOAuth(
191
+ { provider, id, avatar, email, fullName },
192
+ { node, req, locale, componentId, teamDid, blockletWallet }
193
+ );
194
+ }
195
+
196
+ // NOTICE: 这种方式限制登录的角色为 guest,后续需要登录其它角色可以通过传入 passportId 或者 role name 来实现
197
+ const passport = { name: 'Guest', role: 'guest' };
198
+
199
+ await node.createAuditLog(
200
+ {
201
+ action: 'login',
202
+ args: { teamDid, userDid: doc.did, passport, provider },
203
+ context: formatContext(Object.assign(req, { user: doc })),
204
+ result: doc,
205
+ },
206
+ node
207
+ );
208
+
209
+ const { createSessionToken } = initJwt(node, options);
210
+
211
+ const sessionToken = await createSessionToken(doc.did, {
212
+ secret,
213
+ passport,
214
+ role: passport.role,
215
+ });
216
+ return { user: doc, token: sessionToken };
217
+ }
218
+
219
+ module.exports = {
220
+ init(server, node, options) {
221
+ server.post(`${prefix}/login`, verifySig, async (req, res) => {
222
+ try {
223
+ const data = await login(req, node, options);
224
+ res.status(200).json(data);
225
+ } catch (err) {
226
+ logger.error('Failed login', { error: err });
227
+ if (err instanceof ApiError) {
228
+ res.status(err.code).json(err.message);
229
+ return;
230
+ }
231
+ throw err;
232
+ }
233
+ });
234
+ },
235
+ };
@@ -6,7 +6,6 @@ const { slugify } = require('transliteration');
6
6
  const { MAIN_CHAIN_ENDPOINT } = require('@abtnode/constant');
7
7
  const { getBlockletChainInfo } = require('@blocklet/meta/lib/util');
8
8
  const { fromAppDid } = require('@arcblock/did-ext');
9
- const { types } = require('@arcblock/did');
10
9
  const logger = require('@abtnode/logger')('blocklet-services:oauth');
11
10
 
12
11
  function mergeUserData(oldUser, updateData) {
@@ -88,7 +87,11 @@ async function declareAccount({ wallet, moniker, blocklet }) {
88
87
  chainHostList.push(chainHost);
89
88
  }
90
89
  const waitingList = [...new Set(chainHostList)].map((item) =>
91
- declareAccountByChain({ chainHost: item, wallet, moniker })
90
+ declareAccountByChain({
91
+ chainHost: item,
92
+ wallet,
93
+ moniker: moniker || wallet.address.slice(0, 4) + wallet.address.slice(-4),
94
+ })
92
95
  );
93
96
  await Promise.all(waitingList);
94
97
  }
@@ -191,7 +194,7 @@ async function normalizeUser(teamDid, user, { updateUser, getBlockletInfo }) {
191
194
  const { wallet: blockletWallet } = await getBlockletInfo();
192
195
  connectedAccounts.forEach((account) => {
193
196
  if (account.id) {
194
- const accountWallet = fromAppDid(account.id, blockletWallet.secretKey, types.RoleType.ROLE_ACCOUNT);
197
+ const accountWallet = fromAppDid(account.id, blockletWallet.secretKey);
195
198
  account.did = accountWallet.address;
196
199
  account.pk = accountWallet.publicKey;
197
200
  account.firstLoginAt = account.firstLoginAt || new Date().toISOString();
@@ -0,0 +1,8 @@
1
+ class ApiError extends Error {
2
+ constructor(code, ...args) {
3
+ super(...args);
4
+ this.code = code;
5
+ }
6
+ }
7
+
8
+ exports.ApiError = ApiError;
@@ -0,0 +1,21 @@
1
+ const { Joi } = require('@arcblock/validator');
2
+
3
+ const loginWalletSchema = Joi.object({
4
+ provider: 'wallet',
5
+ did: Joi.DID().trim().required(),
6
+ pk: Joi.string().required(),
7
+ avatar: Joi.string().optional(),
8
+ email: Joi.string().optional(),
9
+ fullName: Joi.string().optional(),
10
+ }).empty(null);
11
+
12
+ const loginOAuthSchema = Joi.object({
13
+ provider: Joi.string().allow('auth0'),
14
+ id: Joi.string().required(),
15
+ avatar: Joi.string().required(),
16
+ email: Joi.string().required(),
17
+ fullName: Joi.string().required(),
18
+ }).empty(null);
19
+
20
+ exports.loginWalletSchema = loginWalletSchema;
21
+ exports.loginOAuthSchema = loginOAuthSchema;
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "files": {
3
3
  "main.css": "/.blocklet/proxy/blocklet-service/static/css/main.632501d5.css",
4
- "main.js": "/.blocklet/proxy/blocklet-service/static/js/main.a741fa5b.js",
4
+ "main.js": "/.blocklet/proxy/blocklet-service/static/js/main.255c0179.js",
5
5
  "static/js/716.0d2a2d32.chunk.js": "/.blocklet/proxy/blocklet-service/static/js/716.0d2a2d32.chunk.js",
6
6
  "static/js/359.c47779c2.chunk.js": "/.blocklet/proxy/blocklet-service/static/js/359.c47779c2.chunk.js",
7
7
  "static/js/255.279b1bca.chunk.js": "/.blocklet/proxy/blocklet-service/static/js/255.279b1bca.chunk.js",
@@ -10,7 +10,7 @@
10
10
  "static/js/460.4763afe0.chunk.js": "/.blocklet/proxy/blocklet-service/static/js/460.4763afe0.chunk.js",
11
11
  "static/js/868.ac8df3a0.chunk.js": "/.blocklet/proxy/blocklet-service/static/js/868.ac8df3a0.chunk.js",
12
12
  "static/js/547.03d5d719.chunk.js": "/.blocklet/proxy/blocklet-service/static/js/547.03d5d719.chunk.js",
13
- "static/js/343.b2beb5b2.chunk.js": "/.blocklet/proxy/blocklet-service/static/js/343.b2beb5b2.chunk.js",
13
+ "static/js/343.9ca4b069.chunk.js": "/.blocklet/proxy/blocklet-service/static/js/343.9ca4b069.chunk.js",
14
14
  "static/js/682.a8bf723a.chunk.js": "/.blocklet/proxy/blocklet-service/static/js/682.a8bf723a.chunk.js",
15
15
  "static/js/711.56427a24.chunk.js": "/.blocklet/proxy/blocklet-service/static/js/711.56427a24.chunk.js",
16
16
  "static/js/437.075e8453.chunk.js": "/.blocklet/proxy/blocklet-service/static/js/437.075e8453.chunk.js",
@@ -19,8 +19,8 @@
19
19
  "static/js/189.70043381.chunk.js": "/.blocklet/proxy/blocklet-service/static/js/189.70043381.chunk.js",
20
20
  "static/js/162.8c29e450.chunk.js": "/.blocklet/proxy/blocklet-service/static/js/162.8c29e450.chunk.js",
21
21
  "static/js/610.40349d57.chunk.js": "/.blocklet/proxy/blocklet-service/static/js/610.40349d57.chunk.js",
22
- "static/css/702.f684be9e.chunk.css": "/.blocklet/proxy/blocklet-service/static/css/702.f684be9e.chunk.css",
23
- "static/js/702.8320bb4a.chunk.js": "/.blocklet/proxy/blocklet-service/static/js/702.8320bb4a.chunk.js",
22
+ "static/css/286.97b3dab6.chunk.css": "/.blocklet/proxy/blocklet-service/static/css/286.97b3dab6.chunk.css",
23
+ "static/js/286.af946efa.chunk.js": "/.blocklet/proxy/blocklet-service/static/js/286.af946efa.chunk.js",
24
24
  "static/js/199.3d76c24b.chunk.js": "/.blocklet/proxy/blocklet-service/static/js/199.3d76c24b.chunk.js",
25
25
  "static/js/573.b071429e.chunk.js": "/.blocklet/proxy/blocklet-service/static/js/573.b071429e.chunk.js",
26
26
  "static/media/ubuntu-mono-all-400-normal.woff": "/.blocklet/proxy/blocklet-service/static/media/ubuntu-mono-all-400-normal.c879328bc62e9c68268f.woff",
@@ -45,7 +45,7 @@
45
45
  "router-template-styles/styles.css": "/.blocklet/proxy/blocklet-service/router-template-styles/styles.css",
46
46
  "index.html": "/.blocklet/proxy/blocklet-service/index.html",
47
47
  "main.632501d5.css.map": "/.blocklet/proxy/blocklet-service/static/css/main.632501d5.css.map",
48
- "main.a741fa5b.js.map": "/.blocklet/proxy/blocklet-service/static/js/main.a741fa5b.js.map",
48
+ "main.255c0179.js.map": "/.blocklet/proxy/blocklet-service/static/js/main.255c0179.js.map",
49
49
  "716.0d2a2d32.chunk.js.map": "/.blocklet/proxy/blocklet-service/static/js/716.0d2a2d32.chunk.js.map",
50
50
  "359.c47779c2.chunk.js.map": "/.blocklet/proxy/blocklet-service/static/js/359.c47779c2.chunk.js.map",
51
51
  "255.279b1bca.chunk.js.map": "/.blocklet/proxy/blocklet-service/static/js/255.279b1bca.chunk.js.map",
@@ -54,7 +54,7 @@
54
54
  "460.4763afe0.chunk.js.map": "/.blocklet/proxy/blocklet-service/static/js/460.4763afe0.chunk.js.map",
55
55
  "868.ac8df3a0.chunk.js.map": "/.blocklet/proxy/blocklet-service/static/js/868.ac8df3a0.chunk.js.map",
56
56
  "547.03d5d719.chunk.js.map": "/.blocklet/proxy/blocklet-service/static/js/547.03d5d719.chunk.js.map",
57
- "343.b2beb5b2.chunk.js.map": "/.blocklet/proxy/blocklet-service/static/js/343.b2beb5b2.chunk.js.map",
57
+ "343.9ca4b069.chunk.js.map": "/.blocklet/proxy/blocklet-service/static/js/343.9ca4b069.chunk.js.map",
58
58
  "682.a8bf723a.chunk.js.map": "/.blocklet/proxy/blocklet-service/static/js/682.a8bf723a.chunk.js.map",
59
59
  "711.56427a24.chunk.js.map": "/.blocklet/proxy/blocklet-service/static/js/711.56427a24.chunk.js.map",
60
60
  "437.075e8453.chunk.js.map": "/.blocklet/proxy/blocklet-service/static/js/437.075e8453.chunk.js.map",
@@ -63,13 +63,13 @@
63
63
  "189.70043381.chunk.js.map": "/.blocklet/proxy/blocklet-service/static/js/189.70043381.chunk.js.map",
64
64
  "162.8c29e450.chunk.js.map": "/.blocklet/proxy/blocklet-service/static/js/162.8c29e450.chunk.js.map",
65
65
  "610.40349d57.chunk.js.map": "/.blocklet/proxy/blocklet-service/static/js/610.40349d57.chunk.js.map",
66
- "702.f684be9e.chunk.css.map": "/.blocklet/proxy/blocklet-service/static/css/702.f684be9e.chunk.css.map",
67
- "702.8320bb4a.chunk.js.map": "/.blocklet/proxy/blocklet-service/static/js/702.8320bb4a.chunk.js.map",
66
+ "286.97b3dab6.chunk.css.map": "/.blocklet/proxy/blocklet-service/static/css/286.97b3dab6.chunk.css.map",
67
+ "286.af946efa.chunk.js.map": "/.blocklet/proxy/blocklet-service/static/js/286.af946efa.chunk.js.map",
68
68
  "199.3d76c24b.chunk.js.map": "/.blocklet/proxy/blocklet-service/static/js/199.3d76c24b.chunk.js.map",
69
69
  "573.b071429e.chunk.js.map": "/.blocklet/proxy/blocklet-service/static/js/573.b071429e.chunk.js.map"
70
70
  },
71
71
  "entrypoints": [
72
72
  "static/css/main.632501d5.css",
73
- "static/js/main.a741fa5b.js"
73
+ "static/js/main.255c0179.js"
74
74
  ]
75
75
  }
package/build/index.html CHANGED
@@ -1 +1 @@
1
- <!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="icon" href="/favicon.ico"/><meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=0"/><meta name="theme-color" content="#000000"/><title>Blocklet Service</title><script src=".well-known/service/api/env"></script><script src="/__blocklet__.js"></script><script defer="defer" src="/.blocklet/proxy/blocklet-service/static/js/main.a741fa5b.js"></script><link href="/.blocklet/proxy/blocklet-service/static/css/main.632501d5.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div></body></html>
1
+ <!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="icon" href="/favicon.ico"/><meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=0"/><meta name="theme-color" content="#000000"/><title>Blocklet Service</title><script src=".well-known/service/api/env"></script><script src="/__blocklet__.js"></script><script defer="defer" src="/.blocklet/proxy/blocklet-service/static/js/main.255c0179.js"></script><link href="/.blocklet/proxy/blocklet-service/static/css/main.632501d5.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div></body></html>