@abtnode/core 1.16.17-beta-2679e686 → 1.16.17-beta-6f0c7674

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/lib/api/team.js CHANGED
@@ -1,5 +1,7 @@
1
1
  const { EventEmitter } = require('events');
2
2
  const pick = require('lodash/pick');
3
+ const defaults = require('lodash/defaults');
4
+ const cloneDeep = require('lodash/cloneDeep');
3
5
  const joinUrl = require('url-join');
4
6
 
5
7
  const logger = require('@abtnode/logger')('@abtnode/core:api:team');
@@ -40,6 +42,7 @@ const { validateCreatePermission, validateUpdatePermission } = require('../valid
40
42
  const { getBlocklet } = require('../util/blocklet');
41
43
  const StoreUtil = require('../util/store');
42
44
  const { profileSchema } = require('../validators/user');
45
+ const { callFederated } = require('../util/federated');
43
46
 
44
47
  const sanitizeUrl = (url) => {
45
48
  if (!url) {
@@ -1000,6 +1003,7 @@ class TeamAPI extends EventEmitter {
1000
1003
  async getPassportIssuance({ teamDid, sessionId }) {
1001
1004
  const state = await this.getSessionState(teamDid);
1002
1005
  const doc = await state.read(sessionId);
1006
+ // FIXME: @zhanghan 指定被邀请者的邀请需要查询被邀请者的详细信息
1003
1007
  return doc;
1004
1008
  }
1005
1009
 
@@ -1315,6 +1319,10 @@ class TeamAPI extends EventEmitter {
1315
1319
  return this.teamManager.getUserState(did);
1316
1320
  }
1317
1321
 
1322
+ getUserSessionState(did) {
1323
+ return this.teamManager.getUserSessionState(did);
1324
+ }
1325
+
1318
1326
  getTagState(did) {
1319
1327
  return this.teamManager.getTagState(did);
1320
1328
  }
@@ -1329,6 +1337,148 @@ class TeamAPI extends EventEmitter {
1329
1337
  setInviteExpireTime(ms) {
1330
1338
  this.memberInviteExpireTime = ms;
1331
1339
  }
1340
+
1341
+ // user-session management
1342
+ async getUserSession({ teamDid, userDid, visitorId }) {
1343
+ const state = await this.getUserSessionState(teamDid);
1344
+ const userState = await this.getUserState(teamDid);
1345
+
1346
+ const where = {
1347
+ status: 'online',
1348
+ };
1349
+ if (userDid) where.userDid = userDid;
1350
+ if (visitorId) where.visitorId = visitorId;
1351
+
1352
+ const userSessions = await state.model.findAll({
1353
+ where,
1354
+ attributes: ['id', 'appPid', 'userDid', 'visitorId', 'passportId'],
1355
+ include: {
1356
+ model: userState.model,
1357
+ as: 'user',
1358
+ attributes: ['did', 'sourceProvider', 'fullName', 'email', 'avatar', 'remark', 'sourceAppPid'],
1359
+ },
1360
+ });
1361
+ return userSessions.map((x) => x.toJSON());
1362
+ }
1363
+
1364
+ async getPassportById({ teamDid, passportId }) {
1365
+ const userState = await this.getUserState(teamDid);
1366
+ const passport = await userState.getPassportById(passportId);
1367
+ return passport;
1368
+ }
1369
+
1370
+ async getPassportFromFederated({ site, passportId, teamDid }) {
1371
+ const blocklet = await getBlocklet({ did: teamDid, states: this.states, dataDirs: this.dataDirs });
1372
+ const nodeInfo = await this.node.read();
1373
+ const { permanentWallet } = getBlockletInfo(blocklet, nodeInfo.sk);
1374
+
1375
+ const result = await callFederated({
1376
+ action: 'getPassport',
1377
+ data: {
1378
+ passportId,
1379
+ },
1380
+ permanentWallet,
1381
+ site,
1382
+ });
1383
+
1384
+ return result;
1385
+ }
1386
+
1387
+ async upsertUserSession({ teamDid, appPid, userDid, visitorId, ua, lastLoginIp, sourceAppPid, passportId, status }) {
1388
+ if (!userDid) {
1389
+ throw new Error('userDid are required');
1390
+ }
1391
+
1392
+ const state = await this.getUserSessionState(teamDid);
1393
+ let data;
1394
+ if (!visitorId) {
1395
+ data = await state.insert({
1396
+ userDid,
1397
+ ua,
1398
+ lastLoginIp,
1399
+ appPid,
1400
+ passportId,
1401
+ });
1402
+ } else {
1403
+ const exist = await state.findOne({ userDid, visitorId, appPid });
1404
+ if (exist) {
1405
+ [, [data]] = await state.update(exist.id, { ua, lastLoginIp, passportId, status });
1406
+ } else {
1407
+ data = await state.insert({
1408
+ visitorId,
1409
+ userDid,
1410
+ ua,
1411
+ lastLoginIp,
1412
+ appPid,
1413
+ passportId,
1414
+ });
1415
+ }
1416
+ }
1417
+
1418
+ if (sourceAppPid) {
1419
+ const blocklet = await getBlocklet({ did: teamDid, states: this.states, dataDirs: this.dataDirs });
1420
+ const nodeInfo = await this.node.read();
1421
+ const { permanentWallet } = getBlockletInfo(blocklet, nodeInfo.sk);
1422
+ const federated = defaults(cloneDeep(blocklet.settings.federated || {}), {
1423
+ config: {},
1424
+ sites: [],
1425
+ });
1426
+ const masterSite = federated.sites[0];
1427
+ if (masterSite && masterSite.isMaster !== false && masterSite.appPid === sourceAppPid) {
1428
+ await callFederated({
1429
+ action: 'sync',
1430
+ permanentWallet,
1431
+ site: masterSite,
1432
+ data: {
1433
+ userSessions: [
1434
+ {
1435
+ action: 'login',
1436
+ userDid,
1437
+ visitorId: data.visitorId,
1438
+ ua,
1439
+ lastLoginIp,
1440
+ appPid: teamDid,
1441
+ passportId,
1442
+ },
1443
+ ],
1444
+ },
1445
+ });
1446
+ }
1447
+ }
1448
+
1449
+ return data;
1450
+ }
1451
+
1452
+ async logoutUser({ teamDid, userDid, visitorId, appPid }) {
1453
+ const state = await this.getUserSessionState(teamDid);
1454
+ const data = await state.update({ userDid, visitorId, appPid }, { status: 'offline' });
1455
+ const nodeInfo = await this.node.read();
1456
+ const blocklet = await getBlocklet({ did: teamDid, states: this.states, dataDirs: this.dataDirs });
1457
+ const { permanentWallet } = getBlockletInfo(blocklet, nodeInfo.sk);
1458
+ const federated = defaults(cloneDeep(blocklet.settings.federated || {}), {
1459
+ config: {},
1460
+ sites: [],
1461
+ });
1462
+ const masterSite = federated.sites[0];
1463
+ if (masterSite && masterSite.isMaster !== false && masterSite.appPid !== teamDid) {
1464
+ callFederated({
1465
+ action: 'sync',
1466
+ permanentWallet,
1467
+ site: masterSite,
1468
+ data: {
1469
+ userSessions: [
1470
+ {
1471
+ action: 'logout',
1472
+ userDid,
1473
+ visitorId,
1474
+ appPid: teamDid,
1475
+ },
1476
+ ],
1477
+ },
1478
+ });
1479
+ }
1480
+ return data;
1481
+ }
1332
1482
  }
1333
1483
 
1334
1484
  module.exports = TeamAPI;
@@ -1,5 +1,4 @@
1
1
  /* eslint-disable max-classes-per-file */
2
- /* eslint-disable no-underscore-dangle */
3
2
  /* eslint-disable no-await-in-loop */
4
3
  const fs = require('fs-extra');
5
4
  const path = require('path');
@@ -182,6 +181,7 @@ const {
182
181
  getSelectedResources,
183
182
  updateSelectedResources,
184
183
  } = require('../project');
184
+ const { callFederated } = require('../../util/federated');
185
185
 
186
186
  const { formatEnvironments, getBlockletMeta, validateOwner, isCLI } = util;
187
187
 
@@ -3744,7 +3744,7 @@ class FederatedBlockletManager extends DiskBlockletManager {
3744
3744
  * @param {string} options.appUrl - 申请加入统一登录的 master appUrl
3745
3745
  * @returns {Promise<any>} 更新后的 blocklet 数据
3746
3746
  */
3747
- async joinFederatedLogin({ appUrl, did }) {
3747
+ async joinFederatedLogin({ appUrl, did }, context) {
3748
3748
  const url = new URL(appUrl);
3749
3749
  // master service api 的地址
3750
3750
  url.pathname = joinUrl(WELLKNOWN_SERVICE_PATH_PREFIX, '/api/federated/join');
@@ -3818,7 +3818,7 @@ class FederatedBlockletManager extends DiskBlockletManager {
3818
3818
  * @param {string} options.did - blocklet pid
3819
3819
  * @returns {Promise<any>} 更新后的 blocklet 数据
3820
3820
  */
3821
- async quitFederatedLogin({ did }) {
3821
+ async quitFederatedLogin({ did }, context) {
3822
3822
  const blocklet = await this.getBlocklet(did);
3823
3823
  const federated = defaults(cloneDeep(blocklet.settings.federated || {}), {
3824
3824
  config: {},
@@ -3839,21 +3839,17 @@ class FederatedBlockletManager extends DiskBlockletManager {
3839
3839
  },
3840
3840
  masterAppUrl: masterSite.appUrl,
3841
3841
  });
3842
- const url = new URL(masterSite.appUrl);
3843
- url.pathname = joinUrl(WELLKNOWN_SERVICE_PATH_PREFIX, '/api/federated/quit');
3844
3842
  try {
3845
- await pRetry(
3846
- () =>
3847
- request.post(url.href, {
3848
- signer: permanentWallet.address,
3849
- data: signV2(permanentWallet.address, permanentWallet.secretKey, {
3850
- memberPid: blocklet.appPid,
3851
- }),
3852
- }),
3853
- { retries: 3 }
3854
- );
3843
+ await callFederated({
3844
+ action: 'quit',
3845
+ site: masterSite,
3846
+ permanentWallet,
3847
+ data: {
3848
+ memberPid: blocklet.appPid,
3849
+ },
3850
+ });
3855
3851
  } catch (error) {
3856
- logger.error('Failed to quit blocklet, will still quit federated by itself', { error, did, url: url.href });
3852
+ logger.error('Failed to quit blocklet, will still quit federated by itself', { error, did });
3857
3853
  }
3858
3854
  }
3859
3855
 
@@ -3872,7 +3868,7 @@ class FederatedBlockletManager extends DiskBlockletManager {
3872
3868
  * @param {string} options.did - blocklet pid
3873
3869
  * @returns {Promise<any>} 更新后的 blocklet 数据
3874
3870
  */
3875
- async disbandFederatedLogin({ did }) {
3871
+ async disbandFederatedLogin({ did }, context) {
3876
3872
  const blocklet = await this.getBlocklet(did);
3877
3873
  const federated = defaults(cloneDeep(blocklet.settings.federated || {}), {
3878
3874
  config: {},
@@ -3899,22 +3895,17 @@ class FederatedBlockletManager extends DiskBlockletManager {
3899
3895
  .filter((item) => item.appPid !== did)
3900
3896
  .map((item) => {
3901
3897
  return limitSync(async () => {
3902
- const url = new URL(item.appUrl);
3903
- url.pathname = joinUrl(WELLKNOWN_SERVICE_PATH_PREFIX, '/api/federated/disband');
3904
3898
  try {
3905
- await pRetry(
3906
- () =>
3907
- request.post(url.href, {
3908
- signer: permanentWallet.address,
3909
- data: signV2(permanentWallet.address, permanentWallet.secretKey, {}),
3910
- }),
3911
- { retries: 1 }
3912
- );
3899
+ await callFederated({
3900
+ action: 'disband',
3901
+ permanentWallet,
3902
+ site: item,
3903
+ data: {},
3904
+ });
3913
3905
  } catch (error) {
3914
3906
  logger.error('Failed to disband blocklet, will still disband federated by itself', {
3915
3907
  error,
3916
3908
  did,
3917
- url: url.href,
3918
3909
  });
3919
3910
  }
3920
3911
  });
@@ -3937,7 +3928,7 @@ class FederatedBlockletManager extends DiskBlockletManager {
3937
3928
  * @param {object} param.config federated 配置内容
3938
3929
  * @returns {Promise<any>} 更新后的 blocklet 数据
3939
3930
  */
3940
- async setFederated({ did, config }) {
3931
+ async setFederated({ did, config }, context) {
3941
3932
  await states.blockletExtras.setSettings(did, { federated: config });
3942
3933
 
3943
3934
  const newState = await this.getBlocklet(did);
@@ -3953,7 +3944,8 @@ class FederatedBlockletManager extends DiskBlockletManager {
3953
3944
  * @param {boolean} param.autoLogin 是否自动登录
3954
3945
  * @returns {Promise<any>} 更新后的 blocklet 数据
3955
3946
  */
3956
- async configFederated({ did, autoLogin = false }) {
3947
+ // FIXME: @zhanghan deprecated 不再需要配置自动登录
3948
+ async configFederated({ did, autoLogin = false }, context) {
3957
3949
  const blocklet = await this.getBlocklet(did);
3958
3950
  const federated = cloneDeep(blocklet.settings.federated || {});
3959
3951
  federated.config.autoLogin = autoLogin;
@@ -3970,7 +3962,6 @@ class FederatedBlockletManager extends DiskBlockletManager {
3970
3962
  * @param {object} param
3971
3963
  * @param {string} param.did master blocklet pid
3972
3964
  * @param {string} param.memberPid member blocklet pid
3973
- * @param {boolean} param.autoLogin 是否自动登录
3974
3965
  * @returns {Promise<any>} 更新后的 blocklet 数据
3975
3966
  */
3976
3967
  async auditFederatedLogin({ memberPid, did, status }) {
@@ -4028,31 +4019,24 @@ class FederatedBlockletManager extends DiskBlockletManager {
4028
4019
  roles = await this.teamManager.getRoles(blocklet.appPid);
4029
4020
  }
4030
4021
 
4031
- const postUrl = joinUrl(memberSite.appUrl, WELLKNOWN_SERVICE_PATH_PREFIX, '/api/federated/audit-res');
4032
-
4033
4022
  logger.info('Audit member join federated login', {
4034
4023
  status,
4035
- postUrl,
4036
4024
  });
4037
4025
  try {
4038
4026
  const roleList = roles.map((item) => pick(item, ['name', 'title', 'description']));
4039
- await pRetry(
4040
- () =>
4041
- request.post(postUrl, {
4042
- signer: permanentWallet.address,
4043
- data: signV2(permanentWallet.address, permanentWallet.secretKey, {
4044
- masterPid: blocklet.appPid,
4045
- status,
4046
- delegation,
4047
- roles: roleList,
4048
- }),
4049
- }),
4050
- {
4051
- retries: 3,
4052
- }
4053
- );
4027
+ await callFederated({
4028
+ action: 'audit-res',
4029
+ permanentWallet,
4030
+ site: memberSite,
4031
+ data: {
4032
+ masterPid: blocklet.appPid,
4033
+ status,
4034
+ delegation,
4035
+ roles: roleList,
4036
+ },
4037
+ });
4054
4038
  } catch (error) {
4055
- logger.error('Failed to post audit res to member-site', { error, did, url: postUrl });
4039
+ logger.error('Failed to post audit res to member-site', { error, did });
4056
4040
  throw error;
4057
4041
  }
4058
4042
  await this.syncFederated({
@@ -4119,24 +4103,34 @@ class FederatedBlockletManager extends DiskBlockletManager {
4119
4103
  .filter((item) => item.appId !== federated.config.appId)
4120
4104
  .map((item) => {
4121
4105
  return limitSync(async () => {
4122
- const url = joinUrl(item.appUrl, WELLKNOWN_SERVICE_PATH_PREFIX, '/api/federated/sync');
4123
4106
  try {
4124
4107
  // NOTICE: 即使通知某个站点失败了,也不影响其他站点接收同步结果
4125
- await pRetry(
4126
- () =>
4127
- request.post(url, {
4128
- signer: permanentWallet.address,
4129
- data: signV2(permanentWallet.address, permanentWallet.secretKey, safeData),
4130
- }),
4131
- { retries: 3 }
4132
- );
4108
+ await callFederated({
4109
+ action: 'sync',
4110
+ permanentWallet,
4111
+ site: item,
4112
+ data: safeData,
4113
+ });
4133
4114
  } catch (error) {
4134
- logger.warn('Failed to sync federated sites', { error, did, url, action: 'sync' });
4115
+ logger.warn('Failed to sync federated sites', { error, did, action: 'sync' });
4135
4116
  }
4136
4117
  });
4137
4118
  });
4138
4119
  await Promise.all(waitingList);
4139
4120
  }
4121
+
4122
+ async loginFederated({ did, site, data }) {
4123
+ const blocklet = await this.getBlocklet(did);
4124
+ const nodeInfo = await states.node.read();
4125
+ const { permanentWallet } = getBlockletInfo(blocklet, nodeInfo.sk);
4126
+ const result = await callFederated({
4127
+ action: 'loginByMaster',
4128
+ data,
4129
+ permanentWallet,
4130
+ site,
4131
+ });
4132
+ return result;
4133
+ }
4140
4134
  }
4141
4135
 
4142
4136
  class BlockletManager extends FederatedBlockletManager {}
package/lib/index.js CHANGED
@@ -269,6 +269,7 @@ function ABTNode(options) {
269
269
  configFederated: blockletManager.configFederated.bind(blockletManager),
270
270
  setFederated: blockletManager.setFederated.bind(blockletManager),
271
271
  syncFederated: blockletManager.syncFederated.bind(blockletManager),
272
+ loginFederated: blockletManager.loginFederated.bind(blockletManager),
272
273
  configNotification: blockletManager.configNotification.bind(blockletManager),
273
274
  updateWhoCanAccess: blockletManager.updateWhoCanAccess.bind(blockletManager),
274
275
  updateAppSessionConfig: blockletManager.updateAppSessionConfig.bind(blockletManager),
@@ -381,6 +382,13 @@ function ABTNode(options) {
381
382
  isConnectedAccount: teamAPI.isConnectedAccount.bind(teamAPI),
382
383
  switchProfile: teamAPI.switchProfile.bind(teamAPI),
383
384
 
385
+ // user-session
386
+ getUserSession: teamAPI.getUserSession.bind(teamAPI),
387
+ upsertUserSession: teamAPI.upsertUserSession.bind(teamAPI),
388
+ logoutUser: teamAPI.logoutUser.bind(teamAPI),
389
+ getPassportById: teamAPI.getPassportById.bind(teamAPI),
390
+ getPassportFromFederated: teamAPI.getPassportFromFederated.bind(teamAPI),
391
+
384
392
  // Tagging
385
393
  createTag: teamAPI.createTag.bind(teamAPI),
386
394
  updateTag: teamAPI.updateTag.bind(teamAPI),
@@ -18,6 +18,7 @@ const normalizePathPrefix = require('@abtnode/util/lib/normalize-path-prefix');
18
18
  const logger = require('@abtnode/logger')('@abtnode/core:router:manager');
19
19
  const { getProvider } = require('@abtnode/router-provider');
20
20
  const checkDomainMatch = require('@abtnode/util/lib/check-domain-match');
21
+ const { isDidDomain } = require('@abtnode/util/lib/url-evaluation');
21
22
  const {
22
23
  DOMAIN_FOR_IP_SITE,
23
24
  DOMAIN_FOR_DEFAULT_SITE,
@@ -44,6 +45,7 @@ const { findWebInterface } = require('../util/blocklet');
44
45
  const { attachRuntimeDomainAliases, ensureLatestInfo } = require('./helper');
45
46
  const Router = require('./index');
46
47
  const states = require('../states');
48
+ const { getDidFromDomainGroupName, updateNFTDomainRecord, revokeAndDeleteNFTDomainRecord } = require('../util/router');
47
49
 
48
50
  const checkPathPrefixInBlackList = (pathPrefix, extraBlackList = []) => {
49
51
  const blacklist = [
@@ -184,7 +186,7 @@ class RouterManager extends EventEmitter {
184
186
  return dbSite;
185
187
  }
186
188
 
187
- async addDomainAlias({ id, domainAlias: tmpAlias, force }, context = {}) {
189
+ async addDomainAlias({ id, domainAlias: tmpAlias, force, type, nftDid, chainHost }, context = {}) {
188
190
  const domainAlias = await validateAddDomainAlias(tmpAlias, context);
189
191
  const dbSite = await states.site.findOne({ id });
190
192
  if (!dbSite) {
@@ -215,12 +217,26 @@ class RouterManager extends EventEmitter {
215
217
  }
216
218
 
217
219
  const doc = await states.site.findOne({ id });
218
- const updateResult = await states.site.update(
219
- { id },
220
- { $set: { domainAliases: [...doc.domainAliases, { value: domainAlias, isProtected: false }] } }
221
- );
220
+ const item = { value: domainAlias, isProtected: false };
221
+ if (type === 'nft-domain') {
222
+ item.type = type;
223
+ item.nftDid = nftDid;
224
+ item.chainHost = chainHost;
225
+ }
226
+
227
+ const updateResult = await states.site.update({ id }, { $set: { domainAliases: [...doc.domainAliases, item] } });
222
228
  logger.debug('add domain alias update result', { id, updateResult, domainAlias });
223
229
 
230
+ if (type === 'nft-domain') {
231
+ const did = getDidFromDomainGroupName(doc.domain); // TODO: 是不是可靠?
232
+ const didDomain = doc.domainAliases.find((x) => isDidDomain(x.value));
233
+ const blocklet = await states.blocklet.getBlocklet(did);
234
+ const nodeInfo = await states.node.read();
235
+
236
+ await updateNFTDomainRecord({ name: domainAlias, value: didDomain.value, blocklet, nodeInfo });
237
+ logger.info('update nft domain record', { domain: domainAlias, didDomain: '', nftDid, id });
238
+ }
239
+
224
240
  const newSite = await states.site.findOne({ id });
225
241
  await attachRuntimeDomainAliases({ sites: newSite, context, node: states.node });
226
242
 
@@ -228,12 +244,16 @@ class RouterManager extends EventEmitter {
228
244
  }
229
245
 
230
246
  async deleteDomainAlias({ id, domainAlias: tmpAlias }, context = {}) {
231
- const domainAlias = await validateAddDomainAlias(tmpAlias, context);
247
+ let domainAlias = await validateAddDomainAlias(tmpAlias, context);
248
+ domainAlias = toLower(domainAlias);
249
+
232
250
  const dbSite = await states.site.findOne({ id });
233
251
  if (!dbSite) {
234
252
  throw new Error(`site ${id} does not exist`);
235
253
  }
236
254
 
255
+ const toDelete = dbSite.domainAliases.find((x) => toLower(x.value) === domainAlias);
256
+
237
257
  dbSite.domainAliases = dbSite.domainAliases.filter((x) => {
238
258
  if (typeof x === 'string') {
239
259
  return toLower(x) !== domainAlias;
@@ -247,6 +267,18 @@ class RouterManager extends EventEmitter {
247
267
 
248
268
  await attachRuntimeDomainAliases({ sites: dbSite, context, node: states.node });
249
269
 
270
+ if (toDelete?.type === 'nft-domain') {
271
+ const blockletDid = getDidFromDomainGroupName(dbSite.domain); // TODO: 是不是可靠?
272
+ const blocklet = await states.blocklet.getBlocklet(blockletDid);
273
+ const nodeInfo = await states.node.read();
274
+
275
+ revokeAndDeleteNFTDomainRecord({ name: domainAlias, blocklet, nodeInfo })
276
+ .then(() => logger.info('revoke and delete nft domain record', { domain: domainAlias, blockletDid, id }))
277
+ .catch((error) =>
278
+ logger.error('revoke and delete nft domain record failed', { error, domain: domainAlias, blockletDid, id })
279
+ );
280
+ }
281
+
250
282
  return dbSite;
251
283
  }
252
284
 
@@ -125,6 +125,7 @@ class NodeState extends BaseState {
125
125
  trustedPassports,
126
126
  customBlockletNumber: 0,
127
127
  webWalletUrl,
128
+ nftDomainUrl: '', // TODO: Default domain url
128
129
  });
129
130
 
130
131
  if (dek) {
@@ -0,0 +1,8 @@
1
+ const ExtendBase = require('./base');
2
+
3
+ /**
4
+ * @extends BaseState<import('@abtnode/models').UserSession>
5
+ */
6
+ class UserSession extends ExtendBase {}
7
+
8
+ module.exports = UserSession;
@@ -423,6 +423,16 @@ class User extends ExtendBase {
423
423
 
424
424
  return result;
425
425
  }
426
+
427
+ async getPassports(did) {
428
+ const passports = await this.passport.find({ userDid: did });
429
+ return passports;
430
+ }
431
+
432
+ async getPassportById(passportId) {
433
+ const passport = await this.passport.findOne({ id: passportId });
434
+ return passport;
435
+ }
426
436
  }
427
437
 
428
438
  module.exports = User;
@@ -28,6 +28,7 @@ const rbacCreationLock = new Lock('rbac-creation-lock');
28
28
 
29
29
  const States = {
30
30
  User: require('../states/user'),
31
+ UserSession: require('../states/user-session'),
31
32
  Passport: require('../states/passport'),
32
33
  ConnectedAccount: require('../states/connect-account'),
33
34
  Session: require('../states/session'),
@@ -96,6 +97,7 @@ class TeamManager extends EventEmitter {
96
97
  this.cache[this.nodeDid] = {
97
98
  rbac: await createRBAC({ storage: new MemoryStorage(), data: RBAC_CONFIG }),
98
99
  user: await this.createState(this.nodeDid, 'User'),
100
+ userSession: await this.createState(this.nodeDid, 'UserSession'),
99
101
  tag: await this.createState(this.nodeDid, 'Tag'),
100
102
  passport: await this.createState(this.nodeDid, 'Passport'),
101
103
  connectedAccount: await this.createState(this.nodeDid, 'ConnectedAccount'),
@@ -107,6 +109,10 @@ class TeamManager extends EventEmitter {
107
109
  return this.getState(teamDid, 'user');
108
110
  }
109
111
 
112
+ getUserSessionState(teamDid) {
113
+ return this.getState(teamDid, 'userSession');
114
+ }
115
+
110
116
  getTagState(teamDid) {
111
117
  return this.getState(teamDid, 'tag');
112
118
  }
@@ -339,10 +345,12 @@ class TeamManager extends EventEmitter {
339
345
  const passport = await this.createState(did, 'Passport');
340
346
  const connectedAccount = await this.createState(did, 'ConnectedAccount');
341
347
  const session = await this.createState(did, 'Session');
348
+ const userSession = await this.createState(did, 'UserSession');
342
349
 
343
350
  this.cache[did] = {
344
351
  rbac,
345
352
  user,
353
+ userSession,
346
354
  tag,
347
355
  session,
348
356
  passport,
@@ -274,6 +274,7 @@ const getAppSystemEnvironments = (blocklet, nodeInfo, dataDirs) => {
274
274
 
275
275
  const { wallet } = result;
276
276
  const appSk = toHex(wallet.secretKey);
277
+ const appPk = toHex(wallet.publicKey);
277
278
  const appId = wallet.address;
278
279
  const appName = title || name || result.name;
279
280
  const appDescription = description || result.description;
@@ -302,6 +303,7 @@ const getAppSystemEnvironments = (blocklet, nodeInfo, dataDirs) => {
302
303
 
303
304
  return {
304
305
  BLOCKLET_DID: did, // BLOCKLET_DID is always same as BLOCKLET_APP_PID in structV2 application
306
+ BLOCKLET_APP_PK: appPk,
305
307
  BLOCKLET_APP_SK: appSk,
306
308
  BLOCKLET_APP_ID: appId,
307
309
  BLOCKLET_APP_PSK: appPsk, // permanent sk even the blocklet has been migrated
@@ -0,0 +1,24 @@
1
+ const { WELLKNOWN_SERVICE_PATH_PREFIX } = require('@abtnode/constant');
2
+ const pRetry = require('p-retry');
3
+ const { signV2 } = require('@arcblock/jwt');
4
+ const joinUrl = require('url-join');
5
+
6
+ const request = require('./request');
7
+
8
+ async function callFederated({ site, permanentWallet, data, action }) {
9
+ const url = new URL(site.appUrl);
10
+ url.pathname = joinUrl(WELLKNOWN_SERVICE_PATH_PREFIX, `/api/federated/${action}`);
11
+ const result = await pRetry(
12
+ () =>
13
+ request.post(url.href, {
14
+ signer: permanentWallet.address,
15
+ data: signV2(permanentWallet.address, permanentWallet.secretKey, data),
16
+ }),
17
+ { retries: 3 }
18
+ );
19
+ return result.data;
20
+ }
21
+
22
+ module.exports = {
23
+ callFederated,
24
+ };
@@ -1,4 +1,10 @@
1
1
  const { BLOCKLET_SITE_GROUP_SUFFIX } = require('@abtnode/constant');
2
+ const axios = require('@abtnode/util/lib/axios');
3
+ const getBlockletInfo = require('@blocklet/meta/lib/info');
4
+ const { stableStringify } = require('@arcblock/vc');
5
+ const { toBase58 } = require('@ocap/util');
6
+ const joinURL = require('url-join');
7
+ const logger = require('@abtnode/logger')('@abtnode/core:router:util');
2
8
 
3
9
  const getBlockletDomainGroupName = (did) => `${did}${BLOCKLET_SITE_GROUP_SUFFIX}`;
4
10
 
@@ -7,7 +13,74 @@ const getDidFromDomainGroupName = (name) => {
7
13
  return did;
8
14
  };
9
15
 
16
+ const getNFTDomainHeaders = ({ wallet, payload }) => ({
17
+ 'x-blocklet-sig': toBase58(wallet.sign(stableStringify(payload))),
18
+ });
19
+
20
+ const updateNFTDomainRecord = async ({ name, value, blocklet, nodeInfo }) => {
21
+ const { wallet } = getBlockletInfo(blocklet, nodeInfo.sk);
22
+
23
+ const payload = {
24
+ delegatee: blocklet.appPid,
25
+ domain: name,
26
+ record: {
27
+ name,
28
+ type: 'CNAME',
29
+ value,
30
+ },
31
+ };
32
+ try {
33
+ const { data } = await axios({
34
+ method: 'POST',
35
+ url: joinURL(nodeInfo.nftDomainUrl, '/api/domains'), // TODO: 替换为真实地址
36
+ data: payload,
37
+ headers: getNFTDomainHeaders({ wallet, payload }),
38
+ });
39
+
40
+ return data;
41
+ } catch (error) {
42
+ logger.error('updateNFTDomainRecord error', {
43
+ error,
44
+ name,
45
+ value,
46
+ delegatee: blocklet.appPid,
47
+ appPid: blocklet.appPid,
48
+ resp: error.response?.data,
49
+ });
50
+ throw new Error('update nft domain record failed');
51
+ }
52
+ };
53
+
54
+ const revokeAndDeleteNFTDomainRecord = async ({ name, blocklet, nodeInfo }) => {
55
+ const { wallet } = getBlockletInfo(blocklet, nodeInfo.sk);
56
+
57
+ const payload = {
58
+ delegatee: wallet.address,
59
+ domain: name,
60
+ };
61
+ try {
62
+ const { data } = await axios({
63
+ method: 'DELETE',
64
+ url: joinURL(nodeInfo.nftDomainUrl, '/api/domains'),
65
+ data: payload,
66
+ headers: getNFTDomainHeaders({ wallet, payload }),
67
+ });
68
+
69
+ return data;
70
+ } catch (error) {
71
+ logger.error('revokeAndDeleteNFTDomainRecord error', {
72
+ error,
73
+ name,
74
+ appPid: blocklet.appPid,
75
+ resp: error.response?.data,
76
+ });
77
+ throw new Error('revoke nft domain record failed');
78
+ }
79
+ };
80
+
10
81
  module.exports = {
11
82
  getBlockletDomainGroupName,
12
83
  getDidFromDomainGroupName,
84
+ updateNFTDomainRecord,
85
+ revokeAndDeleteNFTDomainRecord,
13
86
  };
@@ -28,6 +28,15 @@ const nodeInfoSchema = Joi.object({
28
28
  zh: { 'string.uriCustomScheme': 'Web Wallet 必须是合法的 URL' },
29
29
  en: { 'string.uriCustomScheme': 'Web Wallet must be a valid URL' },
30
30
  }),
31
+ nftDomainUrl: Joi.string()
32
+ .uri({ scheme: [/https?/] })
33
+ .label('web wallet url')
34
+ .allow('')
35
+ .optional()
36
+ .messages({
37
+ zh: { 'string.uriCustomScheme': 'NFT Domain 必须是合法的 URL' },
38
+ en: { 'string.uriCustomScheme': 'NFT Domain must be a valid URL' },
39
+ }),
31
40
  autoUpgrade: Joi.boolean(),
32
41
  enableWelcomePage: Joi.boolean(),
33
42
  diskAlertThreshold: Joi.number()
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "1.16.17-beta-2679e686",
6
+ "version": "1.16.17-beta-6f0c7674",
7
7
  "description": "",
8
8
  "main": "lib/index.js",
9
9
  "files": [
@@ -19,19 +19,19 @@
19
19
  "author": "wangshijun <wangshijun2010@gmail.com> (http://github.com/wangshijun)",
20
20
  "license": "Apache-2.0",
21
21
  "dependencies": {
22
- "@abtnode/analytics": "1.16.17-beta-2679e686",
23
- "@abtnode/auth": "1.16.17-beta-2679e686",
24
- "@abtnode/certificate-manager": "1.16.17-beta-2679e686",
25
- "@abtnode/constant": "1.16.17-beta-2679e686",
26
- "@abtnode/cron": "1.16.17-beta-2679e686",
27
- "@abtnode/logger": "1.16.17-beta-2679e686",
28
- "@abtnode/models": "1.16.17-beta-2679e686",
29
- "@abtnode/queue": "1.16.17-beta-2679e686",
30
- "@abtnode/rbac": "1.16.17-beta-2679e686",
31
- "@abtnode/router-provider": "1.16.17-beta-2679e686",
32
- "@abtnode/static-server": "1.16.17-beta-2679e686",
33
- "@abtnode/timemachine": "1.16.17-beta-2679e686",
34
- "@abtnode/util": "1.16.17-beta-2679e686",
22
+ "@abtnode/analytics": "1.16.17-beta-6f0c7674",
23
+ "@abtnode/auth": "1.16.17-beta-6f0c7674",
24
+ "@abtnode/certificate-manager": "1.16.17-beta-6f0c7674",
25
+ "@abtnode/constant": "1.16.17-beta-6f0c7674",
26
+ "@abtnode/cron": "1.16.17-beta-6f0c7674",
27
+ "@abtnode/logger": "1.16.17-beta-6f0c7674",
28
+ "@abtnode/models": "1.16.17-beta-6f0c7674",
29
+ "@abtnode/queue": "1.16.17-beta-6f0c7674",
30
+ "@abtnode/rbac": "1.16.17-beta-6f0c7674",
31
+ "@abtnode/router-provider": "1.16.17-beta-6f0c7674",
32
+ "@abtnode/static-server": "1.16.17-beta-6f0c7674",
33
+ "@abtnode/timemachine": "1.16.17-beta-6f0c7674",
34
+ "@abtnode/util": "1.16.17-beta-6f0c7674",
35
35
  "@arcblock/did": "1.18.92",
36
36
  "@arcblock/did-auth": "1.18.92",
37
37
  "@arcblock/did-ext": "^1.18.92",
@@ -42,11 +42,11 @@
42
42
  "@arcblock/pm2-events": "^0.0.5",
43
43
  "@arcblock/validator": "^1.18.92",
44
44
  "@arcblock/vc": "1.18.92",
45
- "@blocklet/constant": "1.16.17-beta-2679e686",
46
- "@blocklet/env": "1.16.17-beta-2679e686",
47
- "@blocklet/meta": "1.16.17-beta-2679e686",
48
- "@blocklet/resolver": "1.16.17-beta-2679e686",
49
- "@blocklet/sdk": "1.16.17-beta-2679e686",
45
+ "@blocklet/constant": "1.16.17-beta-6f0c7674",
46
+ "@blocklet/env": "1.16.17-beta-6f0c7674",
47
+ "@blocklet/meta": "1.16.17-beta-6f0c7674",
48
+ "@blocklet/resolver": "1.16.17-beta-6f0c7674",
49
+ "@blocklet/sdk": "1.16.17-beta-6f0c7674",
50
50
  "@did-space/client": "^0.3.11",
51
51
  "@fidm/x509": "^1.2.1",
52
52
  "@ocap/mcrypto": "1.18.92",
@@ -101,5 +101,5 @@
101
101
  "jest": "^27.5.1",
102
102
  "unzipper": "^0.10.11"
103
103
  },
104
- "gitHead": "abbe4a479b379552cd9ebd8c27732b360844cb73"
104
+ "gitHead": "8123f54b0f572c47f1ea704607aae113576c2a76"
105
105
  }