@abtnode/core 1.7.20 → 1.7.21

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/node.js CHANGED
@@ -6,6 +6,7 @@ const isDocker = require('@abtnode/util/lib/is-docker');
6
6
  const isGitpod = require('@abtnode/util/lib/is-gitpod');
7
7
  const getFolderSize = require('@abtnode/util/lib/get-folder-size');
8
8
  const canPackageReadWrite = require('@abtnode/util/lib/can-pkg-rw');
9
+ const { toDelegateAddress } = require('@arcblock/did-util');
9
10
 
10
11
  const logger = require('@abtnode/logger')('@abtnode/core:api:node');
11
12
 
@@ -13,6 +14,7 @@ const IP = require('../util/ip');
13
14
  const { validateNodeInfo, validateUpdateGateway } = require('../validators/node');
14
15
  const BlockletRegistry = require('../blocklet/registry');
15
16
  const { getAll } = require('../blocklet/manager/engine');
17
+ const { getDelegateState } = require('../util');
16
18
 
17
19
  const sanitizeUrl = (url) => {
18
20
  if (!url) {
@@ -160,6 +162,24 @@ class NodeAPI {
160
162
 
161
163
  return this.state.updateGateway(data);
162
164
  }
165
+
166
+ async getDelegationState() {
167
+ const info = await this.state.read();
168
+
169
+ const { ownerNft } = info;
170
+ if (!ownerNft.did) {
171
+ throw new Error('Invalid owner NFT');
172
+ }
173
+
174
+ const address = toDelegateAddress(ownerNft.holder, info.did);
175
+ const state = await getDelegateState(info.launcher.chainHost, address);
176
+ if (!state) {
177
+ return { delegated: false };
178
+ }
179
+
180
+ const transferV2Delegation = (state.ops || []).find(({ key }) => key === 'fg:t:transfer_v2');
181
+ return { delegated: !!transferV2Delegation };
182
+ }
163
183
  }
164
184
 
165
185
  module.exports = NodeAPI;
package/lib/api/team.js CHANGED
@@ -25,7 +25,8 @@ class TeamAPI extends EventEmitter {
25
25
 
26
26
  this.notification = states.notification;
27
27
  this.node = states.node;
28
- this.inviteExpireTime = 1000 * 3600 * 24 * 30; // 30 days
28
+ this.memberInviteExpireTime = 1000 * 3600 * 24 * 30; // 30 days
29
+ this.serverInviteExpireTime = 1000 * 3600; // 1 hour
29
30
  this.teamManager = teamManager;
30
31
  }
31
32
 
@@ -224,9 +225,13 @@ class TeamAPI extends EventEmitter {
224
225
 
225
226
  // Invite member
226
227
 
227
- async createInvitation({ teamDid, role, remark }, context) {
228
+ async createMemberInvitation({ teamDid, role, expireTime, remark }, context) {
228
229
  await this.teamManager.checkEnablePassportIssuance(teamDid);
229
230
 
231
+ if (expireTime && expireTime <= 0) {
232
+ throw new Error('Expire time must be greater than 0');
233
+ }
234
+
230
235
  if (!role) {
231
236
  throw new Error('Role cannot be empty');
232
237
  }
@@ -246,7 +251,7 @@ class TeamAPI extends EventEmitter {
246
251
  throw new Error('Inviter does not exist');
247
252
  }
248
253
 
249
- const expireDate = Date.now() + this.inviteExpireTime;
254
+ const expireDate = Date.now() + (expireTime || this.memberInviteExpireTime);
250
255
  const state = await this.getSessionState(teamDid);
251
256
  const { id: inviteId } = await state.start({
252
257
  type: 'invite',
@@ -269,6 +274,36 @@ class TeamAPI extends EventEmitter {
269
274
  };
270
275
  }
271
276
 
277
+ async createTransferInvitation({ teamDid, remark }, context) {
278
+ return this.createMemberInvitation(
279
+ { teamDid, expireTime: this.serverInviteExpireTime, remark, role: 'owner' },
280
+ context
281
+ );
282
+ }
283
+
284
+ async getInvitation({ teamDid, inviteId }) {
285
+ if (!teamDid || !inviteId) {
286
+ return null;
287
+ }
288
+
289
+ const state = await this.getSessionState(teamDid);
290
+
291
+ const invitation = await state.findOne({ _id: inviteId, type: 'invite' });
292
+ if (!invitation) {
293
+ return null;
294
+ }
295
+
296
+ return {
297
+ // eslint-disable-next-line no-underscore-dangle
298
+ inviteId: invitation._id,
299
+ role: invitation.role,
300
+ remark: invitation.remark,
301
+ expireDate: new Date(invitation.expireDate).toString(),
302
+ inviter: invitation.inviter,
303
+ teamDid: invitation.teamDid,
304
+ };
305
+ }
306
+
272
307
  async getInvitations({ teamDid }) {
273
308
  const state = await this.getSessionState(teamDid);
274
309
 
@@ -350,7 +385,7 @@ class TeamAPI extends EventEmitter {
350
385
  throw new Error(`Passport does not exist: ${name}`);
351
386
  }
352
387
 
353
- const expireDate = Date.now() + this.inviteExpireTime;
388
+ const expireDate = Date.now() + this.memberInviteExpireTime;
354
389
 
355
390
  const state = await this.getSessionState(teamDid);
356
391
  const { id } = await state.start({
@@ -668,7 +703,7 @@ class TeamAPI extends EventEmitter {
668
703
  // Just for test
669
704
  // =============
670
705
  setInviteExpireTime(ms) {
671
- this.inviteExpireTime = ms;
706
+ this.memberInviteExpireTime = ms;
672
707
  }
673
708
  }
674
709
 
package/lib/index.js CHANGED
@@ -247,9 +247,10 @@ function ABTNode(options) {
247
247
  addBlockletStore: nodeAPI.addRegistry.bind(nodeAPI),
248
248
  deleteBlockletStore: nodeAPI.deleteRegistry.bind(nodeAPI),
249
249
  selectBlockletStore: nodeAPI.selectRegistry.bind(nodeAPI),
250
+ getDelegationState: nodeAPI.getDelegationState.bind(nodeAPI),
250
251
  cleanupDirtyUpgradeState: states.node.cleanupDirtyUpgradeState.bind(states.node),
251
- addNodeOwner: states.node.addNodeOwner.bind(states.node),
252
252
  updateNodeOwner: states.node.updateNodeOwner.bind(states.node),
253
+ updateNftHolder: states.node.updateNftHolder.bind(states.node),
253
254
  updateNodeRouting,
254
255
  isInitialized,
255
256
  resetNode: (params, context) =>
@@ -261,7 +262,9 @@ function ABTNode(options) {
261
262
  // Team && Access control
262
263
 
263
264
  // Invitation
264
- createInvitation: teamAPI.createInvitation.bind(teamAPI),
265
+ createMemberInvitation: teamAPI.createMemberInvitation.bind(teamAPI),
266
+ createTransferInvitation: teamAPI.createTransferInvitation.bind(teamAPI),
267
+ getInvitation: teamAPI.getInvitation.bind(teamAPI),
265
268
  getInvitations: teamAPI.getInvitations.bind(teamAPI),
266
269
  processInvitation: teamAPI.processInvitation.bind(teamAPI),
267
270
  deleteInvitation: teamAPI.deleteInvitation.bind(teamAPI),
@@ -154,7 +154,7 @@ const getLogContent = async (action, args, context, result, info, node) => {
154
154
  return `${args.user.approved ? 'enabled' : 'disabled'} user ${user} for ${team}`;
155
155
  case 'deletePassportIssuance':
156
156
  return `removed passport issuance ${args.sessionId} from ${team}`;
157
- case 'createInvitation':
157
+ case 'createMemberInvitation':
158
158
  return `created member invitation(${result.inviteId}: ${args.remark}) with **${args.role}** passport for ${team}`; // prettier-ignore
159
159
  case 'deleteInvitation':
160
160
  return `removed unused member invitation(${args.inviteId}) from ${team}`;
@@ -169,6 +169,8 @@ const getLogContent = async (action, args, context, result, info, node) => {
169
169
  return `removed all trusted passport issuers for ${team}`;
170
170
  }
171
171
  return `updated trusted passport issuers to following for ${team}: \n${args.trustedPassports.map(x => `- ${x.remark}: ${x.issuerDid}`).join('\n')}`; // prettier-ignore
172
+ case 'delegateTransferNFT':
173
+ return `${args.owner} ${args.reason}`;
172
174
 
173
175
  // accessKeys
174
176
  case 'createAccessKey':
@@ -258,12 +260,13 @@ const getLogCategory = (action) => {
258
260
  case 'revokeUserPassport':
259
261
  case 'enableUserPassport':
260
262
  case 'updateUserApproval':
261
- case 'createInvitation':
263
+ case 'createMemberInvitation':
262
264
  case 'deleteInvitation':
263
265
  case 'createRole':
264
266
  case 'updateRole':
265
267
  case 'updatePermissionsForRole':
266
268
  case 'configTrustedPassports':
269
+ case 'delegateTransferNFT':
267
270
  return 'team';
268
271
 
269
272
  // accessKeys
@@ -299,6 +302,25 @@ const getLogCategory = (action) => {
299
302
  }
300
303
  };
301
304
 
305
+ const getScope = (args = {}) => {
306
+ // this param usually means mutating an application (server or blocklet)
307
+ if (args.teamDid) {
308
+ return args.teamDid;
309
+ }
310
+
311
+ // this param usually means mutating a child component
312
+ if (args.rootDid) {
313
+ return args.rootDid;
314
+ }
315
+
316
+ // this param usually means mutating a nested child component
317
+ if (Array.isArray(args.did)) {
318
+ return args.did[0];
319
+ }
320
+
321
+ return null;
322
+ };
323
+
302
324
  class AuditLogState extends BaseState {
303
325
  constructor(baseDir, options = {}) {
304
326
  super(baseDir, { filename: 'audit-log.db', ...options });
@@ -355,7 +377,7 @@ class AuditLogState extends BaseState {
355
377
  const [info, geoInfo, uaInfo] = await Promise.all([node.states.node.read(), lookup(ip), parse(ua)]);
356
378
 
357
379
  const data = await this.asyncDB.insert({
358
- scope: args.teamDid || info.did, // server or blocklet did
380
+ scope: getScope(args) || info.did, // server or blocklet did
359
381
  action,
360
382
  category: await getLogCategory(action, args, context, result, info, node),
361
383
  content: (await getLogContent(action, args, context, result, info, node)).trim(),
@@ -1,7 +1,6 @@
1
1
  /* eslint-disable no-underscore-dangle */
2
2
  const semver = require('semver');
3
3
  const omit = require('lodash/omit');
4
- const isEqual = require('lodash/isEqual');
5
4
  const isEmpty = require('lodash/isEmpty');
6
5
  const security = require('@abtnode/util/lib/security');
7
6
  const { isFromPublicKey } = require('@arcblock/did');
@@ -215,32 +214,43 @@ class NodeState extends BaseState {
215
214
  return updateResult;
216
215
  }
217
216
 
218
- async addNodeOwner(owner) {
219
- if (!validateOwner(owner)) {
217
+ async updateNodeOwner({ nodeOwner, ownerNft }) {
218
+ if (!validateOwner(nodeOwner)) {
220
219
  throw new Error('Node owner is invalid');
221
220
  }
222
221
 
223
222
  const doc = await this.read();
224
223
 
225
- if (doc.nodeOwner) {
226
- if (isEqual(owner, doc.nodeOwner)) {
227
- return doc;
228
- }
224
+ const initialized = this.isInitialized({ nodeOwner });
225
+
226
+ if (this.notification && typeof this.notification.setDefaultReceiver === 'function') {
227
+ this.notification.setDefaultReceiver(nodeOwner.did);
228
+ }
229
229
 
230
- throw new Error('Cannot set owner because owner already exists');
230
+ const entities = { nodeOwner, initialized, initializedAt: initialized ? new Date() : null };
231
+ if (ownerNft) {
232
+ entities.ownerNft = ownerNft;
231
233
  }
232
234
 
233
- return this.addOwner(owner, doc);
235
+ const updateResult = await this.update(doc._id, {
236
+ $set: entities,
237
+ });
238
+
239
+ this.emit(EVENTS.NODE_ADDED_OWNER, updateResult);
240
+
241
+ return updateResult;
234
242
  }
235
243
 
236
- async updateNodeOwner(owner) {
237
- if (!validateOwner(owner)) {
238
- throw new Error('Node owner is invalid');
244
+ async updateNftHolder(holder) {
245
+ if (!holder) {
246
+ throw new Error('NFT holder can not be empty');
239
247
  }
240
248
 
241
249
  const doc = await this.read();
242
250
 
243
- return this.addOwner(owner, doc);
251
+ return this.update(doc._id, {
252
+ $set: { 'ownerNft.holder': holder },
253
+ });
244
254
  }
245
255
 
246
256
  async enterMode(mode) {
package/lib/util/index.js CHANGED
@@ -14,6 +14,7 @@ const { Certificate } = require('@fidm/x509');
14
14
  const getPortLib = require('get-port');
15
15
  const v8 = require('v8');
16
16
  const normalizePathPrefix = require('@abtnode/util/lib/normalize-path-prefix');
17
+ const axios = require('@abtnode/util/lib/axios');
17
18
  const parseBlockletMeta = require('@blocklet/meta/lib/parse');
18
19
  const { validateMeta, fixAndValidateService } = require('@blocklet/meta/lib/validate');
19
20
  const { BlockletStatus, BLOCKLET_INTERFACE_WELLKNOWN } = require('@blocklet/meta/lib/constants');
@@ -503,6 +504,33 @@ const getStateCrons = (states) => [
503
504
  },
504
505
  ];
505
506
 
507
+ const getDelegateState = async (chainHost, address) => {
508
+ const result = await axios.post(
509
+ joinUrl(chainHost, '/gql/'),
510
+ JSON.stringify({
511
+ query: `{
512
+ getDelegateState(address: "${address}") {
513
+ state {
514
+ address
515
+ ops {
516
+ key
517
+ }
518
+ }
519
+ }
520
+ }`,
521
+ }),
522
+ {
523
+ headers: {
524
+ 'Content-Type': 'application/json',
525
+ Accept: 'application/json',
526
+ },
527
+ timeout: 60 * 1000,
528
+ }
529
+ );
530
+
531
+ return get(result.data, 'data.getDelegateState.state');
532
+ };
533
+
506
534
  const lib = {
507
535
  validateOwner,
508
536
  getProviderFromNodeInfo,
@@ -541,6 +569,7 @@ const lib = {
541
569
  getSafeEnv,
542
570
  memoizeAsync,
543
571
  getStateCrons,
572
+ getDelegateState,
544
573
  };
545
574
 
546
575
  module.exports = lib;
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "1.7.20",
6
+ "version": "1.7.21",
7
7
  "description": "",
8
8
  "main": "lib/index.js",
9
9
  "files": [
@@ -19,23 +19,24 @@
19
19
  "author": "wangshijun <wangshijun2010@gmail.com> (http://github.com/wangshijun)",
20
20
  "license": "MIT",
21
21
  "dependencies": {
22
- "@abtnode/certificate-manager": "1.7.20",
23
- "@abtnode/constant": "1.7.20",
24
- "@abtnode/cron": "1.7.20",
25
- "@abtnode/db": "1.7.20",
26
- "@abtnode/logger": "1.7.20",
27
- "@abtnode/queue": "1.7.20",
28
- "@abtnode/rbac": "1.7.20",
29
- "@abtnode/router-provider": "1.7.20",
30
- "@abtnode/static-server": "1.7.20",
31
- "@abtnode/timemachine": "1.7.20",
32
- "@abtnode/util": "1.7.20",
22
+ "@abtnode/certificate-manager": "1.7.21",
23
+ "@abtnode/constant": "1.7.21",
24
+ "@abtnode/cron": "1.7.21",
25
+ "@abtnode/db": "1.7.21",
26
+ "@abtnode/logger": "1.7.21",
27
+ "@abtnode/queue": "1.7.21",
28
+ "@abtnode/rbac": "1.7.21",
29
+ "@abtnode/router-provider": "1.7.21",
30
+ "@abtnode/static-server": "1.7.21",
31
+ "@abtnode/timemachine": "1.7.21",
32
+ "@abtnode/util": "1.7.21",
33
33
  "@arcblock/did": "^1.16.15",
34
34
  "@arcblock/did-motif": "^1.1.9",
35
+ "@arcblock/did-util": "^1.16.15",
35
36
  "@arcblock/event-hub": "1.16.15",
36
37
  "@arcblock/pm2-events": "^0.0.5",
37
38
  "@arcblock/vc": "^1.16.15",
38
- "@blocklet/meta": "1.7.20",
39
+ "@blocklet/meta": "1.7.21",
39
40
  "@fidm/x509": "^1.2.1",
40
41
  "@nedb/core": "^1.2.2",
41
42
  "@nedb/multi": "^1.2.2",
@@ -79,5 +80,5 @@
79
80
  "express": "^4.17.1",
80
81
  "jest": "^27.4.5"
81
82
  },
82
- "gitHead": "7e579415aa56cc422f3e26d902cf029fbc92e47a"
83
+ "gitHead": "e7b99d9b3f878cc47e54ddfead2ac5284145b6ae"
83
84
  }