@abtnode/core 1.16.31-beta-52250475 → 1.16.31-beta-4246ab25

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
@@ -282,7 +282,7 @@ class TeamAPI extends EventEmitter {
282
282
  const now = Date.now();
283
283
  let sessionTtl = SESSION_TTL;
284
284
  let blocklet;
285
- if (teamDid !== nodeInfo.did) {
285
+ if (teamDid !== nodeInfo.did && query?.includeUserSessions) {
286
286
  blocklet = await getBlocklet({ did: teamDid, states: this.states, dataDirs: this.dataDirs });
287
287
  sessionTtl = blocklet.settings?.session?.ttl || SESSION_TTL;
288
288
  }
@@ -310,6 +310,8 @@ class TeamAPI extends EventEmitter {
310
310
  'locale',
311
311
  'tags',
312
312
  'url',
313
+ 'inviter',
314
+ 'generation',
313
315
  'userSessions',
314
316
  // oauth relate fields
315
317
  'sourceProvider',
@@ -2372,6 +2372,20 @@ class DiskBlockletManager extends BaseBlockletManager {
2372
2372
  return states.backup.getBlockletBackups({ did });
2373
2373
  }
2374
2374
 
2375
+ async updateInviteSettings({ did, invite }, context) {
2376
+ await states.blockletExtras.setSettings(did, { invite });
2377
+
2378
+ const newState = await this.getBlocklet(did);
2379
+ this.emit(BlockletInternalEvents.appSettingChanged, { appDid: did });
2380
+ this.emit(BlockletEvents.updated, { ...newState, context });
2381
+
2382
+ return newState;
2383
+ }
2384
+
2385
+ getInviteSettings({ did }) {
2386
+ return states.blockletExtras.getSettings(did, 'invite', { enabled: false });
2387
+ }
2388
+
2375
2389
  deleteCache(did) {
2376
2390
  const cache = this.cachedBlocklets.get(did);
2377
2391
  if (cache) {
@@ -4854,6 +4868,8 @@ class FederatedBlockletManager extends DiskBlockletManager {
4854
4868
  'disconnectedAccount',
4855
4869
  'action',
4856
4870
  'sourceAppPid',
4871
+ 'inviter',
4872
+ 'generation',
4857
4873
  ])
4858
4874
  );
4859
4875
  }
package/lib/index.js CHANGED
@@ -377,15 +377,15 @@ function ABTNode(options) {
377
377
  updateBlockletSpaceGateway: blockletManager.updateBlockletSpaceGateway.bind(blockletManager),
378
378
  getBlockletSpaceGateways: blockletManager.getBlockletSpaceGateways.bind(blockletManager),
379
379
 
380
- // auto backup
380
+ // auto backup related
381
381
  updateAutoBackup: blockletManager.updateAutoBackup.bind(blockletManager),
382
-
383
- // blocklet backup record
384
382
  getBlockletBackups: blockletManager.getBlockletBackups.bind(blockletManager),
385
383
 
386
384
  // check update
387
385
  updateAutoCheckUpdate: blockletManager.updateAutoCheckUpdate.bind(blockletManager),
388
386
 
387
+ updateInviteSettings: blockletManager.updateInviteSettings.bind(blockletManager),
388
+
389
389
  // Store
390
390
  getBlockletMeta: StoreUtil.getBlockletMeta,
391
391
  getStoreMeta: StoreUtil.getStoreMeta,
@@ -3,11 +3,12 @@ const get = require('lodash/get');
3
3
  const pick = require('lodash/pick');
4
4
  const uniq = require('lodash/uniq');
5
5
  const { isValid, toAddress } = require('@arcblock/did');
6
- const { PASSPORT_STATUS } = require('@abtnode/constant');
6
+ const { PASSPORT_STATUS, USER_MAX_INVITE_DEPTH } = require('@abtnode/constant');
7
7
  const { BaseState } = require('@abtnode/models');
8
8
  const { Sequelize, Op } = require('sequelize');
9
9
  const { updateConnectedAccount } = require('@abtnode/util/lib/user');
10
10
  const { LOGIN_PROVIDER } = require('@blocklet/constant');
11
+ const logger = require('@abtnode/logger')('@abtnode/core:states:user');
11
12
 
12
13
  const { validateOwner } = require('../util');
13
14
  const { loginSchema, disconnectAccountSchema } = require('../validators/user');
@@ -72,6 +73,7 @@ class User extends ExtendBase {
72
73
  // create user
73
74
  await this.insert({
74
75
  ...user,
76
+ ...(await this._prepareInviteInfo(user)),
75
77
  sourceProvider: user.sourceProvider || LOGIN_PROVIDER.WALLET,
76
78
  approved: !!user.approved,
77
79
  });
@@ -188,7 +190,18 @@ class User extends ExtendBase {
188
190
  // eslint-disable-next-line require-await
189
191
  async getUsers({ query, sort, paging } = {}) {
190
192
  const where = {};
191
- const { approved, role, search, tags, includeTags, includePassports, includeUserSessions } = query || {};
193
+ const {
194
+ approved,
195
+ role,
196
+ search,
197
+ tags,
198
+ invitee,
199
+ inviter,
200
+ generation, // 0 - unlimited, 1 - invited by inviter, 2 - invited by another
201
+ includeTags,
202
+ includePassports,
203
+ includeUserSessions,
204
+ } = query || {};
192
205
  const shouldIncludeTag = (tags && tags.length) || includeTags;
193
206
 
194
207
  if (isNullOrUndefined(approved) === false) {
@@ -206,6 +219,68 @@ class User extends ExtendBase {
206
219
  }
207
220
  }
208
221
 
222
+ if (inviter && invitee) {
223
+ throw new Error('You can not query by inviter and invitee at the same time');
224
+ }
225
+
226
+ // handle descendant query
227
+ if (inviter) {
228
+ if (isValid(inviter) === false) {
229
+ throw new Error('inviter did invalid');
230
+ }
231
+ const exist = await this.model.findByPk(toAddress(inviter), { attributes: ['did', 'generation'] });
232
+ if (!exist) {
233
+ throw new Error(`inviter not found: ${inviter}`);
234
+ }
235
+
236
+ try {
237
+ const subQuery = `
238
+ WITH RECURSIVE UserTree(did,inviter,generation) AS (
239
+ SELECT did,inviter,generation FROM users WHERE inviter="${exist.did}"
240
+ UNION ALL
241
+ SELECT child.did,child.inviter,child.generation FROM users AS child INNER JOIN UserTree AS parent ON (child.inviter=parent.did)
242
+ )
243
+ SELECT did,inviter,generation FROM UserTree ${generation > 0 ? `WHERE generation=${(exist.generation > 0 ? exist.generation : 0) + generation}` : ''} LIMIT ${USER_MAX_INVITE_DEPTH}`.trim();
244
+ const children = await this.query(subQuery);
245
+ where.did = children.map((x) => x.did);
246
+ } catch (err) {
247
+ console.error('Failed to get descendants', err);
248
+ where.did = [];
249
+ }
250
+ }
251
+
252
+ // handle ancestor query
253
+ if (invitee) {
254
+ if (isValid(invitee) === false) {
255
+ throw new Error('invitee did invalid');
256
+ }
257
+ const exist = await this.model.findByPk(toAddress(invitee), { attributes: ['did', 'generation'] });
258
+ if (!exist) {
259
+ throw new Error(`invitee not found: ${invitee}`);
260
+ }
261
+
262
+ try {
263
+ const subQuery = `
264
+ WITH RECURSIVE UserTree(did,inviter,generation) AS (
265
+ SELECT did,inviter,generation FROM users WHERE did="${exist.did}"
266
+ UNION ALL
267
+ SELECT
268
+ inviter,
269
+ (SELECT inviter FROM users AS parent WHERE parent.did=child.inviter),
270
+ (SELECT generation FROM users AS parent WHERE parent.did=child.inviter)
271
+ FROM UserTree AS child
272
+ WHERE inviter IS NOT NULL
273
+ LIMIT ${USER_MAX_INVITE_DEPTH}
274
+ )
275
+ SELECT did,inviter,generation FROM UserTree`.trim();
276
+ const children = await this.query(subQuery);
277
+ where.did = children.map((x) => x.did).filter((x) => x !== exist.did);
278
+ } catch (err) {
279
+ console.error('Failed to get ancestors', err);
280
+ where.did = [];
281
+ }
282
+ }
283
+
209
284
  const replacements = {};
210
285
 
211
286
  if (role && role !== '$all' && !where.did) {
@@ -371,10 +446,10 @@ class User extends ExtendBase {
371
446
  * @param {string} user.did
372
447
  * @param {string} user.pk
373
448
  * @param {ConnectedAccount} user.connectedAccount
374
- * @param {passport} user.passport
375
449
  * @param {string} user.fullName - user profile's name
376
450
  * @param {string} user.avatar - url of user's avatar, eg: bn://avatar/7f8848569405f8cdf8b1b2788ebf7d0f.jpg
377
451
  * @param {string} user.locale - locale
452
+ * @param {string} user.inviter - inviter
378
453
  * @param {Object} [user.extra] - extra data of user
379
454
  * @param {string} user.lastLoginIp - lastLoginIp
380
455
  * @param {('owner'|'admin'|'member'|'guest'|string)} user.role - deprecated user's role
@@ -402,6 +477,8 @@ class User extends ExtendBase {
402
477
  'extra',
403
478
  'lastLoginIp',
404
479
  'remark',
480
+ 'inviter',
481
+ 'generation',
405
482
  'sourceAppPid',
406
483
  ]),
407
484
  lastLoginAt: now,
@@ -413,11 +490,17 @@ class User extends ExtendBase {
413
490
  updates.sourceAppPid = null;
414
491
  }
415
492
 
493
+ Object.assign(updates, await this._prepareInviteInfo(raw));
494
+
416
495
  if (exist) {
417
- // HACK: sourceAppPid 不能更新
496
+ // immutable
418
497
  if (updates.sourceAppPid) {
419
498
  delete updates.sourceAppPid;
420
499
  }
500
+ if (updates.inviter) {
501
+ delete updates.inviter;
502
+ delete updates.generation;
503
+ }
421
504
  // 登录不再更新 locale
422
505
  delete updates.locale;
423
506
  // update user, connectedAccount, passport
@@ -437,6 +520,42 @@ class User extends ExtendBase {
437
520
  return { ...updated, _action: exist ? 'update' : 'add' };
438
521
  }
439
522
 
523
+ async _prepareInviteInfo(raw) {
524
+ const info = {};
525
+
526
+ // set inviter and generation
527
+ if (raw.inviter) {
528
+ // sybil-attack
529
+ if (isValid(raw.inviter)) {
530
+ const inviterId = toAddress(raw.inviter);
531
+ const inviter = await this.model.findByPk(inviterId, { attributes: ['did', 'generation'] });
532
+ if (inviter) {
533
+ info.inviter = inviterId;
534
+ info.generation = inviter.generation + 1;
535
+ } else {
536
+ logger.warn('Set inviter to non-exist user is not allowed', raw);
537
+ }
538
+ } else {
539
+ logger.warn('Set inviter to invalid did is not allowed', raw);
540
+ info.inviter = null;
541
+ }
542
+
543
+ // anti-land-attack
544
+ if (info.inviter === raw.did) {
545
+ logger.warn('Set inviter to self is not allowed', raw);
546
+ info.inviter = null;
547
+ }
548
+ }
549
+ if (!info.inviter) {
550
+ info.generation = 0;
551
+ }
552
+ if (info.generation > USER_MAX_INVITE_DEPTH) {
553
+ throw new Error('You have exceeded max user invite chain length');
554
+ }
555
+
556
+ return info;
557
+ }
558
+
440
559
  async disconnectUserAccount(raw) {
441
560
  const { error, value: connectedAccount } = disconnectAccountSchema.validate(raw);
442
561
  if (error) {
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "1.16.31-beta-52250475",
6
+ "version": "1.16.31-beta-4246ab25",
7
7
  "description": "",
8
8
  "main": "lib/index.js",
9
9
  "files": [
@@ -19,43 +19,43 @@
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.31-beta-52250475",
23
- "@abtnode/auth": "1.16.31-beta-52250475",
24
- "@abtnode/certificate-manager": "1.16.31-beta-52250475",
25
- "@abtnode/constant": "1.16.31-beta-52250475",
26
- "@abtnode/cron": "1.16.31-beta-52250475",
27
- "@abtnode/logger": "1.16.31-beta-52250475",
28
- "@abtnode/models": "1.16.31-beta-52250475",
29
- "@abtnode/queue": "1.16.31-beta-52250475",
30
- "@abtnode/rbac": "1.16.31-beta-52250475",
31
- "@abtnode/router-provider": "1.16.31-beta-52250475",
32
- "@abtnode/static-server": "1.16.31-beta-52250475",
33
- "@abtnode/timemachine": "1.16.31-beta-52250475",
34
- "@abtnode/util": "1.16.31-beta-52250475",
35
- "@arcblock/did": "1.18.132",
36
- "@arcblock/did-auth": "1.18.132",
37
- "@arcblock/did-ext": "^1.18.132",
22
+ "@abtnode/analytics": "1.16.31-beta-4246ab25",
23
+ "@abtnode/auth": "1.16.31-beta-4246ab25",
24
+ "@abtnode/certificate-manager": "1.16.31-beta-4246ab25",
25
+ "@abtnode/constant": "1.16.31-beta-4246ab25",
26
+ "@abtnode/cron": "1.16.31-beta-4246ab25",
27
+ "@abtnode/logger": "1.16.31-beta-4246ab25",
28
+ "@abtnode/models": "1.16.31-beta-4246ab25",
29
+ "@abtnode/queue": "1.16.31-beta-4246ab25",
30
+ "@abtnode/rbac": "1.16.31-beta-4246ab25",
31
+ "@abtnode/router-provider": "1.16.31-beta-4246ab25",
32
+ "@abtnode/static-server": "1.16.31-beta-4246ab25",
33
+ "@abtnode/timemachine": "1.16.31-beta-4246ab25",
34
+ "@abtnode/util": "1.16.31-beta-4246ab25",
35
+ "@arcblock/did": "1.18.135",
36
+ "@arcblock/did-auth": "1.18.135",
37
+ "@arcblock/did-ext": "^1.18.135",
38
38
  "@arcblock/did-motif": "^1.1.13",
39
- "@arcblock/did-util": "1.18.132",
40
- "@arcblock/event-hub": "1.18.132",
41
- "@arcblock/jwt": "^1.18.132",
39
+ "@arcblock/did-util": "1.18.135",
40
+ "@arcblock/event-hub": "1.18.135",
41
+ "@arcblock/jwt": "^1.18.135",
42
42
  "@arcblock/pm2-events": "^0.0.5",
43
- "@arcblock/validator": "^1.18.132",
44
- "@arcblock/vc": "1.18.132",
45
- "@blocklet/constant": "1.16.31-beta-52250475",
46
- "@blocklet/env": "1.16.31-beta-52250475",
47
- "@blocklet/meta": "1.16.31-beta-52250475",
48
- "@blocklet/resolver": "1.16.31-beta-52250475",
49
- "@blocklet/sdk": "1.16.31-beta-52250475",
50
- "@blocklet/store": "1.16.31-beta-52250475",
51
- "@did-space/client": "^0.5.28",
43
+ "@arcblock/validator": "^1.18.135",
44
+ "@arcblock/vc": "1.18.135",
45
+ "@blocklet/constant": "1.16.31-beta-4246ab25",
46
+ "@blocklet/env": "1.16.31-beta-4246ab25",
47
+ "@blocklet/meta": "1.16.31-beta-4246ab25",
48
+ "@blocklet/resolver": "1.16.31-beta-4246ab25",
49
+ "@blocklet/sdk": "1.16.31-beta-4246ab25",
50
+ "@blocklet/store": "1.16.31-beta-4246ab25",
51
+ "@did-space/client": "^0.5.31",
52
52
  "@fidm/x509": "^1.2.1",
53
- "@ocap/mcrypto": "1.18.132",
54
- "@ocap/util": "1.18.132",
55
- "@ocap/wallet": "1.18.132",
53
+ "@ocap/mcrypto": "1.18.135",
54
+ "@ocap/util": "1.18.135",
55
+ "@ocap/wallet": "1.18.135",
56
56
  "@slack/webhook": "^5.0.4",
57
57
  "archiver": "^7.0.1",
58
- "axios": "^1.7.2",
58
+ "axios": "^1.7.5",
59
59
  "axon": "^2.0.3",
60
60
  "chalk": "^4.1.2",
61
61
  "cross-spawn": "^7.0.3",
@@ -81,7 +81,7 @@
81
81
  "p-limit": "^3.1.0",
82
82
  "p-retry": "4.6.1",
83
83
  "read-last-lines": "^1.8.0",
84
- "semver": "^7.3.8",
84
+ "semver": "^7.6.3",
85
85
  "sequelize": "^6.35.0",
86
86
  "shelljs": "^0.8.5",
87
87
  "ssri": "^8.0.1",
@@ -103,5 +103,5 @@
103
103
  "jest": "^29.7.0",
104
104
  "unzipper": "^0.10.11"
105
105
  },
106
- "gitHead": "26155b86f103f9e64fd8a23b7fffdb279c71209c"
106
+ "gitHead": "ef93705b89033e4cd5fed458b64521d0a994a1d0"
107
107
  }