@abtnode/blocklet-services 1.16.13 → 1.16.14-beta-0c29907f

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 (101) hide show
  1. package/api/index.js +5 -0
  2. package/api/libs/auth/utils.js +6 -8
  3. package/api/libs/connect/session.js +75 -23
  4. package/api/libs/connect/shared.js +12 -1
  5. package/api/libs/connect/v1.js +32 -3
  6. package/api/libs/jwt.js +8 -3
  7. package/api/middlewares/check-federated-cors-call.js +15 -0
  8. package/api/middlewares/ensure-blocklet.js +13 -0
  9. package/api/middlewares/verify-federated-call.js +35 -0
  10. package/api/routes/blocklet.js +19 -6
  11. package/api/routes/federated.js +365 -0
  12. package/api/routes/oauth.js +8 -7
  13. package/api/services/auth/connect/invite.js +2 -1
  14. package/api/services/auth/connect/issue-passport.js +2 -8
  15. package/api/services/auth/connect/login.js +9 -1
  16. package/api/services/auth/connect/receive-transfer-app-owner.js +3 -1
  17. package/api/services/auth/session.js +3 -1
  18. package/api/services/notification/blocklet-events-notifier.js +79 -28
  19. package/api/util/blocklet-utils.js +27 -0
  20. package/api/util/federated.js +41 -0
  21. package/build/asset-manifest.json +76 -73
  22. package/build/index.html +1 -1
  23. package/build/service-worker.js +1 -1
  24. package/build/service-worker.js.map +1 -1
  25. package/build/static/css/{8730.ecc708b7.chunk.css → 2130.b9437a64.chunk.css} +1 -1
  26. package/build/static/css/4603.dce369d5.chunk.css +2 -0
  27. package/build/static/js/1162.ff3136ae.chunk.js +2 -0
  28. package/build/static/js/1237.1d0b8f1e.chunk.js +3 -0
  29. package/build/static/js/2130.59fef4ad.chunk.js +3 -0
  30. package/build/static/js/3242.5dea77f3.chunk.js +2 -0
  31. package/build/static/js/3464.12806bfe.chunk.js +2 -0
  32. package/build/static/js/{3800.a4ffc6c2.chunk.js → 3800.faac00ca.chunk.js} +2 -2
  33. package/build/static/js/{3953.b81b810f.chunk.js → 3953.e3e51520.chunk.js} +2 -2
  34. package/build/static/js/3963.2e06f9bc.chunk.js +3 -0
  35. package/build/static/js/4319.e99b5af9.chunk.js +2 -0
  36. package/build/static/js/{445.4ee5d0c9.chunk.js → 445.6da52f3e.chunk.js} +3 -3
  37. package/build/static/js/4492.97b0d4f6.chunk.js +2 -0
  38. package/build/static/js/4547.b26ceabb.chunk.js +2 -0
  39. package/build/static/js/4603.f6b8d272.chunk.js +2 -0
  40. package/build/static/js/4651.0a58cbc3.chunk.js +3 -0
  41. package/build/static/js/5050.538edf89.chunk.js +3 -0
  42. package/build/static/js/5052.91c69c75.chunk.js +2 -0
  43. package/build/static/js/5176.68fd3fde.chunk.js +2 -0
  44. package/build/static/js/5233.faabaae9.chunk.js +2 -0
  45. package/build/static/js/5430.8114614c.chunk.js +2 -0
  46. package/build/static/js/5645.a17cd258.chunk.js +2 -0
  47. package/build/static/js/5711.aa17ef28.chunk.js +2 -0
  48. package/build/static/js/6315.59e078f6.chunk.js +2 -0
  49. package/build/static/js/652.5fc00cd3.chunk.js +2 -0
  50. package/build/static/js/6737.4f37dd4a.chunk.js +2 -0
  51. package/build/static/js/6891.6c7018ab.chunk.js +2 -0
  52. package/build/static/js/7345.4b4f8941.chunk.js +3 -0
  53. package/build/static/js/7371.da3c5cc3.chunk.js +2 -0
  54. package/build/static/js/{766.724b9aea.chunk.js → 766.5dd16d47.chunk.js} +2 -2
  55. package/build/static/js/8031.d9af3d84.chunk.js +2 -0
  56. package/build/static/js/8395.e5aa519c.chunk.js +2 -0
  57. package/build/static/js/868.aa2a1c4d.chunk.js +2 -0
  58. package/build/static/js/9476.bc480cfc.chunk.js +2 -0
  59. package/build/static/js/982.11dc355f.chunk.js +3 -0
  60. package/build/static/js/main.efa52345.js +3 -0
  61. package/build/static/media/index.20414a3fa1e6c5498a67.cjs +1 -0
  62. package/package.json +36 -35
  63. package/build/static/js/1162.c3a07ee0.chunk.js +0 -2
  64. package/build/static/js/1237.03f3a68a.chunk.js +0 -3
  65. package/build/static/js/1610.13323d09.chunk.js +0 -2
  66. package/build/static/js/2793.00991f08.chunk.js +0 -2
  67. package/build/static/js/2961.8237c11f.chunk.js +0 -3
  68. package/build/static/js/3242.86fc9bc6.chunk.js +0 -2
  69. package/build/static/js/338.2f0211d7.chunk.js +0 -2
  70. package/build/static/js/4319.5363b467.chunk.js +0 -2
  71. package/build/static/js/4547.b013b965.chunk.js +0 -2
  72. package/build/static/js/4651.ea828d09.chunk.js +0 -3
  73. package/build/static/js/5050.af9b1e9c.chunk.js +0 -3
  74. package/build/static/js/5052.da35270f.chunk.js +0 -2
  75. package/build/static/js/5176.36610ff7.chunk.js +0 -2
  76. package/build/static/js/5233.1f42910b.chunk.js +0 -2
  77. package/build/static/js/5430.f101b648.chunk.js +0 -2
  78. package/build/static/js/5645.48cf12d4.chunk.js +0 -2
  79. package/build/static/js/5711.13090883.chunk.js +0 -2
  80. package/build/static/js/5782.cf7e6574.chunk.js +0 -2
  81. package/build/static/js/6315.dec40b9b.chunk.js +0 -2
  82. package/build/static/js/652.0bde51bd.chunk.js +0 -2
  83. package/build/static/js/6737.719ec631.chunk.js +0 -2
  84. package/build/static/js/7240.6bbf13e8.chunk.js +0 -3
  85. package/build/static/js/7345.fb7fa7cf.chunk.js +0 -3
  86. package/build/static/js/7371.04b43294.chunk.js +0 -2
  87. package/build/static/js/8395.c43afdf4.chunk.js +0 -2
  88. package/build/static/js/868.cd5abe55.chunk.js +0 -2
  89. package/build/static/js/8730.0ac0b4df.chunk.js +0 -3
  90. package/build/static/js/9476.302a7b02.chunk.js +0 -2
  91. package/build/static/js/9645.822c1d69.chunk.js +0 -2
  92. package/build/static/js/main.c3544e79.js +0 -3
  93. /package/build/static/js/{1237.03f3a68a.chunk.js.LICENSE.txt → 1237.1d0b8f1e.chunk.js.LICENSE.txt} +0 -0
  94. /package/build/static/js/{8730.0ac0b4df.chunk.js.LICENSE.txt → 2130.59fef4ad.chunk.js.LICENSE.txt} +0 -0
  95. /package/build/static/js/{7240.6bbf13e8.chunk.js.LICENSE.txt → 3963.2e06f9bc.chunk.js.LICENSE.txt} +0 -0
  96. /package/build/static/js/{445.4ee5d0c9.chunk.js.LICENSE.txt → 445.6da52f3e.chunk.js.LICENSE.txt} +0 -0
  97. /package/build/static/js/{4651.ea828d09.chunk.js.LICENSE.txt → 4651.0a58cbc3.chunk.js.LICENSE.txt} +0 -0
  98. /package/build/static/js/{5050.af9b1e9c.chunk.js.LICENSE.txt → 5050.538edf89.chunk.js.LICENSE.txt} +0 -0
  99. /package/build/static/js/{7345.fb7fa7cf.chunk.js.LICENSE.txt → 7345.4b4f8941.chunk.js.LICENSE.txt} +0 -0
  100. /package/build/static/js/{2961.8237c11f.chunk.js.LICENSE.txt → 982.11dc355f.chunk.js.LICENSE.txt} +0 -0
  101. /package/build/static/js/{main.c3544e79.js.LICENSE.txt → main.efa52345.js.LICENSE.txt} +0 -0
@@ -0,0 +1,365 @@
1
+ const { WELLKNOWN_SERVICE_PATH_PREFIX } = require('@abtnode/constant');
2
+ const { signV2 } = require('@arcblock/jwt');
3
+ const normalizePathPrefix = require('@abtnode/util/lib/normalize-path-prefix');
4
+ const cloneDeep = require('lodash/cloneDeep');
5
+ const get = require('lodash/get');
6
+
7
+ const cors = require('cors');
8
+ const { LOGIN_PROVIDER } = require('@blocklet/constant');
9
+ const pick = require('lodash/pick');
10
+
11
+ const { api } = require('../libs/api');
12
+ const initJwt = require('../libs/jwt');
13
+ const { createTokenFn, getDidConnectVersion } = require('../util');
14
+ const verifyFederatedCall = require('../middlewares/verify-federated-call');
15
+ const ensureBlocklet = require('../middlewares/ensure-blocklet');
16
+ const checkFederatedCorsCall = require('../middlewares/check-federated-cors-call');
17
+ const { getUserAvatarUrl } = require('../util/federated');
18
+
19
+ const PREFIX = WELLKNOWN_SERVICE_PATH_PREFIX;
20
+
21
+ const prefix = `${PREFIX}/api/federated`;
22
+
23
+ module.exports = {
24
+ preInit(server) {
25
+ server.options(`${prefix}/prelogin`, cors({ credentials: true, origin: true }));
26
+ server.options(`${prefix}/login`, cors({ credentials: true, origin: true }));
27
+ server.options(`${prefix}/logout`, cors({ credentials: true, origin: true }));
28
+ },
29
+ init(server, node, options) {
30
+ // step 1 申请加入(member 向 master 申请)
31
+ server.post(`${prefix}/join`, ensureBlocklet(), async (req, res) => {
32
+ const { blocklet } = req;
33
+ const { site } = req.body;
34
+
35
+ const federated = cloneDeep(blocklet.settings.federated || {});
36
+ let sites = federated?.sites || [];
37
+ if (sites.length === 0) {
38
+ const nodeInfo = await req.getNodeInfo();
39
+ const blockletInfo = await req.getBlockletInfo();
40
+ const masterSite = {
41
+ appId: blockletInfo.wallet.address,
42
+ appPid: blockletInfo.permanentWallet.address,
43
+ migratedFrom: blocklet.migratedFrom || [],
44
+ appName: blockletInfo.name,
45
+ appDescription: blockletInfo.description,
46
+ appUrl: blockletInfo.appUrl,
47
+ appLogo:
48
+ blocklet.environmentObj.BLOCKLET_APP_LOGO ||
49
+ normalizePathPrefix(`${WELLKNOWN_SERVICE_PATH_PREFIX}/blocklet/logo`) ||
50
+ '/',
51
+ appLogoRect: blocklet.environmentObj.BLOCKLET_APP_LOGO_RECT,
52
+ did: blockletInfo.did,
53
+ pk: blockletInfo.permanentWallet.publicKey,
54
+ serverId: nodeInfo.did,
55
+ serverVersion: nodeInfo.version,
56
+ version: blocklet.meta.version,
57
+ };
58
+ sites = [masterSite];
59
+ }
60
+
61
+ let masterConfig = federated.config;
62
+ if (!masterConfig) {
63
+ masterConfig = {
64
+ appId: blocklet.appDid,
65
+ appPid: blocklet.appPid || blocklet.appDid,
66
+ };
67
+ }
68
+ if (site) {
69
+ sites.push({
70
+ ...site,
71
+ appliedAt: new Date(),
72
+ status: 'pending',
73
+ isMaster: false,
74
+ });
75
+ }
76
+ // member 申请后,将 member 展示在列表中
77
+ // 更新的是自己
78
+ await node.setFederated({
79
+ did: blocklet.appDid,
80
+ config: {
81
+ config: masterConfig,
82
+ sites,
83
+ },
84
+ });
85
+
86
+ // 将新增的数据返回给 member
87
+ res.json({
88
+ sites,
89
+ });
90
+ });
91
+
92
+ // step 2 审批(master 申批 member)
93
+ // core/state/lib/blocklet/manager/disk.js -> auditFederatedLogin
94
+ server.post(`${prefix}/audit-res`, ensureBlocklet(), verifyFederatedCall(), async (req, res) => {
95
+ const { blocklet } = req;
96
+ const { delegation, roles, appId, status } = req.body.verifyData;
97
+ const federated = cloneDeep(blocklet.settings.federated || {});
98
+ federated.config.delegation = delegation;
99
+ if (status === 'approved') {
100
+ const trustedPassports = blocklet.trustedPassports || [];
101
+ const hasTrustedPassport = trustedPassports.find((item) => item.issuerDid === appId);
102
+ if (!hasTrustedPassport) {
103
+ await node.configTrustedPassports({
104
+ teamDid: blocklet.meta.did,
105
+ trustedPassports: [
106
+ ...trustedPassports,
107
+ {
108
+ issuerDid: appId,
109
+ remark: 'Generated on join federated login',
110
+ mappings: roles.map((item) => {
111
+ return {
112
+ from: { passport: item.name },
113
+ to: { role: 'guest' },
114
+ };
115
+ }),
116
+ },
117
+ ],
118
+ });
119
+ }
120
+ }
121
+
122
+ await node.setFederated({
123
+ did: blocklet.meta.did,
124
+ config: federated,
125
+ });
126
+ res.json(federated);
127
+ });
128
+
129
+ // step 3 同步站点群信息(master 向 member 广播站点群信息,广播请求在 manager/disk.js 文件中 class FederatedBlockletManager 发起 )
130
+ // 该路由为 member 接受响应的 api(只允许 master 调用)
131
+ server.post(`${prefix}/sync`, ensureBlocklet(), verifyFederatedCall({ isMaster: true }), async (req, res) => {
132
+ const { sites } = req.body.verifyData;
133
+ const { blocklet } = req;
134
+ const federated = cloneDeep(blocklet.settings.federated || {});
135
+ federated.sites = sites;
136
+ await node.setFederated({
137
+ did: blocklet.meta.did,
138
+ config: federated,
139
+ });
140
+ res.json(federated);
141
+ });
142
+
143
+ // step 4 检测自动登录条件(member 向 master 发起请求)
144
+ server.post(
145
+ `${prefix}/prelogin`,
146
+ cors({ credentials: true, origin: true }),
147
+ ensureBlocklet(),
148
+ checkFederatedCorsCall(),
149
+ async (req, res) => {
150
+ const { blocklet } = req;
151
+
152
+ const teamDid = blocklet.meta.did;
153
+ let user = null;
154
+ if (req.user) {
155
+ user = { ...req.user };
156
+ const currentUser = await node.getUser({
157
+ teamDid,
158
+ user: {
159
+ did: req.user.did,
160
+ },
161
+ options: {
162
+ enableConnectedAccount: true,
163
+ },
164
+ });
165
+ user.avatar = getUserAvatarUrl(currentUser.avatar, blocklet);
166
+ }
167
+ const blockletInfo = await req.getBlockletInfo();
168
+ const site = {
169
+ appId: blockletInfo.wallet.address,
170
+ appPid: blockletInfo.permanentWallet.address,
171
+ appName: blockletInfo.name,
172
+ appDescription: blockletInfo.description,
173
+ appUrl: blockletInfo.appUrl,
174
+ appLogo:
175
+ blocklet.environmentObj.BLOCKLET_APP_LOGO ||
176
+ normalizePathPrefix(`${WELLKNOWN_SERVICE_PATH_PREFIX}/blocklet/logo`) ||
177
+ '/',
178
+ appLogoRect: blocklet.environmentObj.BLOCKLET_APP_LOGO_RECT,
179
+ did: blockletInfo.did,
180
+ };
181
+ res.json({ user, site });
182
+ }
183
+ );
184
+
185
+ // step 5 完成自动登录(member 向 master 请求)
186
+ server.post(
187
+ `${prefix}/login`,
188
+ cors({ credentials: true, origin: true }),
189
+ ensureBlocklet(),
190
+ checkFederatedCorsCall(),
191
+ async (req, res) => {
192
+ if (!req.user) {
193
+ res.status(401).send('Unauthorized');
194
+ return;
195
+ }
196
+ let data;
197
+ const { onlyWriteCookie = false } = req.body;
198
+
199
+ if (!onlyWriteCookie) {
200
+ const { blocklet } = req;
201
+ const { permanentWallet } = await req.getBlockletInfo();
202
+ const teamDid = blocklet.meta.did;
203
+ const federatedSites = blocklet.settings?.federated?.sites || [];
204
+ const loginSite = federatedSites.find((item) => item.appUrl === req.headers.origin);
205
+
206
+ const currentUser = await node.getUser({
207
+ teamDid,
208
+ user: {
209
+ did: req.user.did,
210
+ },
211
+ options: {
212
+ enableConnectedAccount: true,
213
+ },
214
+ });
215
+
216
+ const user = pick(currentUser, ['did', 'pk', 'fullName', 'locale']);
217
+ user.lastLoginIp = get(req, 'headers[x-real-ip]') || '';
218
+
219
+ if (currentUser.email) {
220
+ user.email = currentUser.email;
221
+ }
222
+ if (currentUser.avatar) {
223
+ user.avatar = getUserAvatarUrl(currentUser.avatar, blocklet);
224
+ }
225
+
226
+ try {
227
+ ({ data } = await api.post(`${loginSite.appUrl}${prefix}/token`, {
228
+ signer: permanentWallet.address,
229
+ data: signV2(permanentWallet.address, permanentWallet.secretKey, {
230
+ appId: teamDid,
231
+ role: req.user.role,
232
+ user,
233
+ passport: req.user.passport,
234
+ }),
235
+ }));
236
+ } catch {
237
+ res.status(500).send('Generate token error');
238
+ }
239
+ }
240
+
241
+ res.cookie('login_token', req.token, {
242
+ // HACK: 目前 sessionToken 的有效期就是 1h
243
+ maxAge: 60 * 60 * 1000,
244
+ expires: new Date(Date.now() + 60 * 60 * 1000),
245
+ sameSite: 'none',
246
+ secure: true,
247
+ });
248
+ res.json(data);
249
+ }
250
+ );
251
+
252
+ // step 6 发放自动登录 token(master 向 member 发起生成 token 请求)
253
+ server.post(`${prefix}/token`, ensureBlocklet(), verifyFederatedCall(), async (req, res) => {
254
+ const { user, appId, role, passport } = req.body.verifyData;
255
+ const { createSessionToken } = initJwt(node, options);
256
+ const createToken = createTokenFn(createSessionToken);
257
+ const { secret } = await req.getBlockletInfo();
258
+ const { blocklet } = req;
259
+ const teamDid = blocklet.meta.did;
260
+
261
+ const sessionConfig = blocklet.settings?.session || {};
262
+ const trustedPassports = blocklet.trustedPassports || [];
263
+ const masterPassport = trustedPassports.find((item) => item.issuerDid === appId);
264
+ const findMapping = masterPassport ? masterPassport.mappings.find((item) => item.from.passport === role) : null;
265
+ const targetPassport = findMapping
266
+ ? {
267
+ role: findMapping.to?.role || 'guest',
268
+ name: findMapping.to?.role || 'Guest',
269
+ id: passport?.id || '',
270
+ }
271
+ : { role: 'guest', name: 'Guest' };
272
+ const doc = await node.loginUser({
273
+ teamDid,
274
+ user: {
275
+ ...user,
276
+ // HACK: @zhanghan 这里会将 passport 插入到当前用户的 passport 列表中,federated 登录不应该插入 passport
277
+ // passport: findMapping ? targetPassport : null,
278
+ connectedAccount: {
279
+ provider: LOGIN_PROVIDER.FEDERATED,
280
+ id: appId,
281
+ did: user.did,
282
+ pk: user.pk,
283
+ },
284
+ },
285
+ });
286
+ const { sessionToken, refreshToken } = createToken(
287
+ doc.did,
288
+ {
289
+ secret,
290
+ role: targetPassport.role,
291
+ passport: targetPassport,
292
+ fullName: doc.fullName,
293
+ provider: LOGIN_PROVIDER.FEDERATED,
294
+ },
295
+ {
296
+ ...sessionConfig,
297
+ didConnectVersion: getDidConnectVersion(req),
298
+ }
299
+ );
300
+
301
+ res.json({ sessionToken, refreshToken });
302
+ });
303
+
304
+ // member 要求 master 退出登录
305
+ server.post(
306
+ `${prefix}/logout`,
307
+ cors({ credentials: true, origin: true }),
308
+ ensureBlocklet(),
309
+ checkFederatedCorsCall(),
310
+ (req, res) => {
311
+ res.clearCookie('login_token');
312
+ res.send('');
313
+ }
314
+ );
315
+
316
+ // member 主动调起 master 登录(实现登录 member 时,自动登录 master)
317
+
318
+ server.post(`${prefix}/loginByMember`, ensureBlocklet(), verifyFederatedCall(), async (req, res) => {
319
+ const { user, appId, passport } = req.body.verifyData;
320
+ const { createSessionToken } = initJwt(node, options);
321
+ const createToken = createTokenFn(createSessionToken);
322
+ const { secret } = await req.getBlockletInfo();
323
+ const { blocklet } = req;
324
+ const teamDid = blocklet.meta.did;
325
+
326
+ const sessionConfig = blocklet.settings?.session || {};
327
+ const prevUser = await node.getUser({
328
+ teamDid,
329
+ user: { did: user.did },
330
+ options: { enableConnectedAccount: true },
331
+ });
332
+ // HACK: member 调用 master 时,将 passport 的 role 还原为 master 中原有的 role
333
+ const targetPassport = passport?.id ? (prevUser?.passports || []).find((item) => item.id === passport.id) : null;
334
+ const doc = await node.loginUser({
335
+ teamDid,
336
+ user: {
337
+ ...user,
338
+ passport: targetPassport,
339
+ connectedAccount: {
340
+ provider: LOGIN_PROVIDER.WALLET,
341
+ id: appId,
342
+ did: user.did,
343
+ pk: user.pk,
344
+ },
345
+ },
346
+ });
347
+
348
+ const { sessionToken, refreshToken } = createToken(
349
+ doc.did,
350
+ {
351
+ secret,
352
+ passport: targetPassport,
353
+ role: targetPassport?.role || 'guest',
354
+ fullName: doc.fullName,
355
+ },
356
+ {
357
+ ...sessionConfig,
358
+ didConnectVersion: getDidConnectVersion(req),
359
+ }
360
+ );
361
+
362
+ res.json({ sessionToken, refreshToken });
363
+ });
364
+ },
365
+ };
@@ -1,7 +1,7 @@
1
1
  const { handleInvitationReceive, getApplicationInfo } = require('@abtnode/auth/lib/auth');
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
- const { parseUserAvatar, extractUserAvatar } = require('@abtnode/util/lib/user');
4
+ const { extractUserAvatar, getUserAvatarUrl, getAppAvatarUrl } = require('@abtnode/util/lib/user');
5
5
  const { fromAppDid } = require('@arcblock/did-ext');
6
6
  const { getBlockletAppIdList } = require('@blocklet/meta/lib/util');
7
7
  const get = require('lodash/get');
@@ -198,11 +198,10 @@ async function invite(req, node, options) {
198
198
  });
199
199
  const { dataDir, name: applicationName } = await getApplicationInfo({ node, nodeInfo, teamDid });
200
200
  if (currentUser) {
201
- const avatar = await parseUserAvatar(currentUser.avatar, { did: teamDid, dataDir });
202
201
  profile = {
203
202
  email: currentUser.email,
204
203
  fullName: currentUser.fullName,
205
- avatar,
204
+ avatar: getUserAvatarUrl(baseUrl, currentUser.avatar),
206
205
  };
207
206
  userDid = currentUser.did;
208
207
  userPk = currentUser.pk;
@@ -213,7 +212,7 @@ async function invite(req, node, options) {
213
212
  profile = {
214
213
  email,
215
214
  fullName,
216
- avatar,
215
+ avatar: getUserAvatarUrl(baseUrl, avatar),
217
216
  };
218
217
  }
219
218
 
@@ -390,12 +389,12 @@ module.exports = {
390
389
  const userDid = req.user.did;
391
390
  const blocklet = await req.getBlockletInfo();
392
391
  const nodeInfo = await req.getNodeInfo();
393
- const { did: teamDid, wallet: blockletWallet } = blocklet;
394
- const { dataDir, passportColor } = await getApplicationInfo({ node, nodeInfo, teamDid });
392
+ const { did: teamDid, wallet: blockletWallet, appUrl } = blocklet;
393
+ const { passportColor } = await getApplicationInfo({ node, nodeInfo, teamDid });
395
394
  const issuerDidList = uniq([blockletWallet.address, ...getBlockletAppIdList(blocklet)]);
396
395
  // NOTICE: 这里获取的 did 是当前登录用户的永久 did,无需查询 connectedAccount
397
396
  const user = await node.getUser({ teamDid, user: { did: userDid } });
398
- const ownerAvatarUrl = await parseUserAvatar(user?.avatar, { dataDir });
397
+ const ownerAvatarUrl = getUserAvatarUrl(appUrl, user.avatar);
399
398
  const { passports = [] } = user || {};
400
399
  // NOTICE: 保持每一种 role 的 passport 只有一个即可
401
400
  const passportTypes = uniqBy(
@@ -417,7 +416,9 @@ module.exports = {
417
416
  title: x.title,
418
417
  issuer: x.issuer.name,
419
418
  issuerDid: x.issuer.id,
419
+ issuerAvatarUrl: getAppAvatarUrl(appUrl),
420
420
  ownerName: user?.fullName,
421
+ ownerDid: userDid,
421
422
  ownerAvatarUrl,
422
423
  isDataUrl: true,
423
424
  preferredColor: passportColor || 'auto',
@@ -36,7 +36,7 @@ module.exports = function createRoutes(node, authenticator, createSessionToken)
36
36
  };
37
37
  },
38
38
 
39
- signature: async ({ extraParams, context: { request, didwallet } }) => {
39
+ signature: async ({ extraParams, context: { request, baseUrl, didwallet } }) => {
40
40
  const { locale, inviteId } = extraParams;
41
41
  checkWalletVersion({ didwallet, locale });
42
42
  const nodeInfo = await request.getNodeInfo();
@@ -48,6 +48,7 @@ module.exports = function createRoutes(node, authenticator, createSessionToken)
48
48
  teamDid,
49
49
  inviteId,
50
50
  locale,
51
+ baseUrl,
51
52
  });
52
53
  },
53
54
  },
@@ -20,19 +20,13 @@ module.exports = function createRoutes(node) {
20
20
  },
21
21
 
22
22
  claims: {
23
- signature: async ({ extraParams, context: { request, didwallet } }) => {
23
+ signature: async ({ extraParams, context: { request, baseUrl, didwallet } }) => {
24
24
  const { id, locale } = extraParams;
25
25
  checkWalletVersion({ didwallet, locale });
26
26
  const nodeInfo = await node.getNodeInfo();
27
27
  const teamDid = request.headers['x-blocklet-did'];
28
28
 
29
- return createIssuePassportRequest({
30
- node,
31
- nodeInfo,
32
- teamDid,
33
- id,
34
- locale,
35
- });
29
+ return createIssuePassportRequest({ node, nodeInfo, teamDid, id, locale, baseUrl });
36
30
  },
37
31
  },
38
32
 
@@ -36,7 +36,15 @@ module.exports = function createRoutes(node, authenticator, createSessionToken)
36
36
  action: 'login',
37
37
  });
38
38
 
39
- await updateSession({ sessionToken: result.sessionToken, refreshToken: result.refreshToken }, true);
39
+ await updateSession(
40
+ {
41
+ sessionToken: result.sessionToken,
42
+ refreshToken: result.refreshToken,
43
+ federatedSessionToken: result.federatedSessionToken || '',
44
+ federatedRefreshToken: result.federatedRefreshToken || '',
45
+ },
46
+ true
47
+ );
40
48
 
41
49
  return result;
42
50
  } catch (err) {
@@ -203,7 +203,8 @@ module.exports = function createRoutes(node, _, createSessionToken) {
203
203
  passportColor,
204
204
  dataDir,
205
205
  secret,
206
- } = await getApplicationInfo({ node, teamDid: appPid });
206
+ logo,
207
+ } = await getApplicationInfo({ node, teamDid: appPid, baseUrl });
207
208
 
208
209
  const statusEndpointBaseUrl = baseUrl;
209
210
  const endpoint = baseUrl;
@@ -211,6 +212,7 @@ module.exports = function createRoutes(node, _, createSessionToken) {
211
212
  const vcParams = {
212
213
  issuerName,
213
214
  issuerWallet,
215
+ issuerAvatarUrl: logo,
214
216
  ownerDid: userDid,
215
217
  passport: await createPassport({
216
218
  name: ROLES.OWNER,
@@ -3,6 +3,7 @@ const joinUrl = require('url-join');
3
3
  const SealedBox = require('tweetnacl-sealedbox-js');
4
4
  const { decodeEncryptionKey } = require('@abtnode/util/lib/security');
5
5
  const { WELLKNOWN_SERVICE_PATH_PREFIX, USER_AVATAR_URL_PREFIX, USER_AVATAR_PATH_PREFIX } = require('@abtnode/constant');
6
+ const { LOGIN_PROVIDER } = require('@blocklet/constant');
6
7
 
7
8
  const { PREFIXES } = require('../../util/constants');
8
9
  const { createTokenFn, getDidConnectVersion } = require('../../util');
@@ -62,6 +63,7 @@ module.exports = {
62
63
  passport: req.user.passport,
63
64
  role: req.user.role,
64
65
  fullName: user.fullName,
66
+ provider: req.user.provider || LOGIN_PROVIDER.WALLET,
65
67
  },
66
68
  { ...sessionConfig, didConnectVersion: getDidConnectVersion(req) }
67
69
  );
@@ -77,7 +79,7 @@ module.exports = {
77
79
  }
78
80
  }
79
81
 
80
- res.json({ user, nextToken, nextRefreshToken });
82
+ res.json({ user, nextToken, nextRefreshToken, provider: req.user.provider || LOGIN_PROVIDER.WALLET });
81
83
  };
82
84
 
83
85
  const sessionApi = `${prefix}/api/did/session`;
@@ -1,4 +1,6 @@
1
1
  const get = require('lodash/get');
2
+ const uniqBy = require('lodash/uniqBy');
3
+ const uniq = require('lodash/uniq');
2
4
 
3
5
  const JWT = require('@arcblock/jwt');
4
6
 
@@ -11,57 +13,103 @@ const eventHub =
11
13
  const logger = require('@abtnode/logger')(require('../../../package.json').name);
12
14
  const cache = require('../../cache');
13
15
 
16
+ const getComponentNamesWithVersion = (app) => {
17
+ const { componentDids = [], children = [] } = app;
18
+
19
+ const componentNames = uniq(componentDids).map((x) => {
20
+ const component = children.find((y) => y.meta.did === x);
21
+ return `${component.meta.title}@${component.meta.version}`;
22
+ });
23
+
24
+ return `${componentNames}`;
25
+ };
26
+
14
27
  const messages = {
15
- [BlockletEvents.upgraded]: ({ blockletInfo }) => ({
28
+ [BlockletEvents.componentInstalled]: ({ data }) => ({
16
29
  title: {
17
- en: 'Blocklet Upgrade Success',
18
- zh: 'Blocklet 升级成功',
30
+ en: 'Component Installed Success',
31
+ zh: '组件安装成功',
19
32
  },
20
33
  body: {
21
- en: `${blockletInfo.name}@${blockletInfo.version} upgrade success.`,
22
- zh: `${blockletInfo.name}@${blockletInfo.version} 升级成功`,
34
+ en: `Component ${getComponentNamesWithVersion(data)} is installed successfully for ${data.meta.title}.`,
35
+ zh: `${data.meta.title} 中的组件 ${getComponentNamesWithVersion(data)} 安装成功`,
23
36
  },
24
37
  }),
25
- [BlockletEvents.upgradeFailed]: ({ blockletInfo, data }) => ({
38
+ [BlockletEvents.componentInstallFailed]: ({ data }) => ({
26
39
  title: {
27
- en: 'Blocklet Upgrade Failed',
28
- zh: 'Blocklet 升级失败',
40
+ en: 'Component Install Failed',
41
+ zh: '组件安装失败',
29
42
  },
30
43
  body: {
31
- en: `${blockletInfo.name}@${blockletInfo.version} upgrade failed with error: ${data?.error?.message}`,
32
- zh: `${blockletInfo.name}@${blockletInfo.version} 升级失败: ${data?.error?.message}`,
44
+ en: `${getComponentNamesWithVersion(data)} installed failed for ${data.meta.title}: ${data?.error?.message}`,
45
+ zh: `${data.meta.title} 中的组件 ${getComponentNamesWithVersion(data)} 安装失败: ${data?.error?.message}`,
33
46
  },
34
47
  }),
35
- [BlockletEvents.started]: () => ({
48
+ [BlockletEvents.componentUpgraded]: ({ data }) => ({
36
49
  title: {
37
- en: 'Blocklet Start Success',
38
- zh: 'Blocklet 启动成功',
50
+ en: 'Component Upgrade Success',
51
+ zh: '组件升级成功',
39
52
  },
40
53
  body: {
41
- en: 'Blocklet start success.',
42
- zh: 'Blocklet 启动成功',
54
+ en: `${getComponentNamesWithVersion(data)} is upgraded successfully for ${data.meta.title}.`,
55
+ zh: `${data.meta.title} 中的组件 ${getComponentNamesWithVersion(data)} 升级成功`,
43
56
  },
44
57
  }),
45
- [BlockletEvents.startFailed]: ({ data }) => ({
58
+ [BlockletEvents.componentUpgradeFailed]: ({ data }) => ({
46
59
  title: {
47
- en: 'Blocklet Start Failed',
48
- zh: 'Blocklet 启动失败',
60
+ en: 'Component Upgrade Failed',
61
+ zh: '组件升级失败',
49
62
  },
50
63
  body: {
51
- en: `Blocklet start failed: ${data.error?.message}`,
52
- zh: `Blocklet 启动失败: ${data.error?.message}`,
64
+ en: `${getComponentNamesWithVersion(data)} upgraded failed for ${data.meta.title}: ${data?.error?.message}`,
65
+ zh: `${data.meta.title} 中的组件 ${getComponentNamesWithVersion(data)} 升级失败: ${data?.error?.message}`,
53
66
  },
54
67
  }),
55
- [BlockletEvents.stopped]: () => ({
68
+ [BlockletEvents.componentRemoved]: ({ data }) => ({
69
+ title: {
70
+ en: 'Component Removed Success',
71
+ zh: '组件删除成功',
72
+ },
73
+ body: {
74
+ en: `${getComponentNamesWithVersion(data)} is removed successfully for ${data.meta.title}.`,
75
+ zh: `${data.meta.title} 中的组件 ${getComponentNamesWithVersion(data)} 删除成功`,
76
+ },
77
+ }),
78
+ [BlockletEvents.started]: ({ data = {} }) => {
79
+ return {
80
+ title: {
81
+ en: 'Component Start Success',
82
+ zh: '组件启动成功',
83
+ },
84
+ body: {
85
+ en: `${getComponentNamesWithVersion(data)} started successfully for ${data.meta.title}.`,
86
+ zh: `${data.meta.title} 中的组件 ${getComponentNamesWithVersion(data)} 启动成功`,
87
+ },
88
+ };
89
+ },
90
+
91
+ [BlockletEvents.startFailed]: ({ data }) => ({
56
92
  title: {
57
- en: 'Blocklet Stop Success',
58
- zh: 'Blocklet 已停止运行',
93
+ en: 'Component Start Failed',
94
+ zh: 'Component 启动失败',
59
95
  },
60
96
  body: {
61
- en: 'Blocklet stopped success.',
62
- zh: 'Blocklet 已停止运行',
97
+ en: `${getComponentNamesWithVersion(data)} started failed: ${data.error?.message}`,
98
+ zh: `${data.meta.title} 中的组件 ${getComponentNamesWithVersion(data)} 启动失败: ${data.error?.message}`,
63
99
  },
64
100
  }),
101
+ [BlockletEvents.stopped]: ({ data = {} }) => {
102
+ return {
103
+ title: {
104
+ en: 'Component Stop Success',
105
+ zh: '组件已停止运行',
106
+ },
107
+ body: {
108
+ en: `${getComponentNamesWithVersion(data)} stopped successfully for ${data.meta.title}.`,
109
+ zh: `${data.meta.title} 中的组件 ${getComponentNamesWithVersion(data)} 已停止运行`,
110
+ },
111
+ };
112
+ },
65
113
  [EVENTS.USER_ADDED]: ({ data }) => ({
66
114
  title: {
67
115
  en: 'New member join',
@@ -78,8 +126,11 @@ const DEFAULT_LOCALE = 'en';
78
126
 
79
127
  const init = ({ node, notificationService }) => {
80
128
  [
81
- BlockletEvents.upgraded,
82
- BlockletEvents.upgradeFailed,
129
+ BlockletEvents.componentInstalled,
130
+ BlockletEvents.componentInstallFailed,
131
+ BlockletEvents.componentUpgraded,
132
+ BlockletEvents.componentUpgradeFailed,
133
+ BlockletEvents.componentRemoved,
83
134
  BlockletEvents.started,
84
135
  BlockletEvents.startFailed,
85
136
  BlockletEvents.stopped,
@@ -115,7 +166,7 @@ const init = ({ node, notificationService }) => {
115
166
  query: { approved: true, role: ROLES.OWNER },
116
167
  });
117
168
 
118
- const users = (adminUsers || []).concat(ownerUsers).filter(Boolean);
169
+ const users = uniqBy((adminUsers || []).concat(ownerUsers).filter(Boolean), 'did');
119
170
 
120
171
  if (!users.length) {
121
172
  return;