@abtnode/core 1.17.4-beta-20251201-225048-b1682a09 → 1.17.4-beta-20251202-034514-637cd8e2

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
@@ -2121,10 +2121,6 @@ class TeamAPI extends EventEmitter {
2121
2121
  return this.teamManager.updateStoreList(teamDid, storeList);
2122
2122
  }
2123
2123
 
2124
- getNotificationReceivers(payload) {
2125
- return this.teamManager.getNotificationReceivers(payload);
2126
- }
2127
-
2128
2124
  createNotification = (payload) => {
2129
2125
  return this.teamManager.createNotification(payload);
2130
2126
  };
@@ -5148,15 +5148,6 @@ class DiskBlockletManager extends BaseBlockletManager {
5148
5148
  await forEachBlocklet(blocklet, hookFn, { parallel: true, concurrencyLimit: 4 });
5149
5149
  }
5150
5150
 
5151
- async getReceiverDids(teamDid) {
5152
- const receivers = await this.teamAPI.getNotificationReceivers({
5153
- teamDid,
5154
- roles: [ROLES.ADMIN, ROLES.OWNER],
5155
- selection: { did: 1 },
5156
- });
5157
- return receivers.map((r) => r.did);
5158
- }
5159
-
5160
5151
  // 目前:Blocklet 的安装消息都会聚合在这里。
5161
5152
  async _createNotification(did, notification, { skipGetBlocklet = false } = {}) {
5162
5153
  if (isCLI()) {
package/lib/index.js CHANGED
@@ -653,8 +653,6 @@ function ABTNode(options) {
653
653
  disconnectFromEndpoint: blockletManager.disconnectFromEndpoint.bind(blockletManager),
654
654
  publishToEndpoint: blockletManager.publishToEndpoint.bind(blockletManager),
655
655
 
656
- getNotificationReceivers: teamAPI.getNotificationReceivers.bind(teamAPI),
657
-
658
656
  // ServerBlocklet Notifications
659
657
  createNotification: teamAPI.createNotification.bind(teamAPI),
660
658
  getNotifications: teamAPI.getNotification.bind(teamAPI),
@@ -454,7 +454,7 @@ SELECT did,inviter,generation FROM UserTree`.trim();
454
454
  }
455
455
 
456
456
  // eslint-disable-next-line require-await
457
- async getUsersByDids({ dids, query }, context = {}) {
457
+ async getUsersByDids({ dids, query, paging }, context = {}) {
458
458
  const {
459
459
  approved,
460
460
  includeTags = false,
@@ -485,6 +485,20 @@ SELECT did,inviter,generation FROM UserTree`.trim();
485
485
  include.push(this.getConnectedInclude());
486
486
  }
487
487
 
488
+ // 如果提供了分页参数,使用分页查询
489
+ if (paging) {
490
+ const sorting = [['createdAt', 'DESC']];
491
+ selection.createdAt = 1;
492
+ const result = await this.paginate({ where: condition, include }, sorting, paging, selection);
493
+
494
+ if (includeFollowStatus && user?.did) {
495
+ result.list = await this._enrichWithFollowStatus(result.list, user.did, dids);
496
+ }
497
+
498
+ return result;
499
+ }
500
+
501
+ // 否则返回所有结果
488
502
  let result = await this.find({ where: condition, include }, selection);
489
503
 
490
504
  if (includeFollowStatus && user?.did) {
@@ -540,27 +554,39 @@ SELECT did,inviter,generation FROM UserTree`.trim();
540
554
  * get user by did
541
555
  * @param {string} did user's did
542
556
  */
543
- async getUser(did, { enableConnectedAccount = false, includeTags = false } = {}) {
544
- let user = await this.findOne({ where: { did }, include: includeTags ? [this.getTagInclude()] : [] });
545
- let connectedAccounts = [];
546
- let passports = [];
557
+ async getUser(
558
+ did,
559
+ {
560
+ enableConnectedAccount = false,
561
+ includeTags = false,
562
+ includePassports = true,
563
+ includeConnectedAccounts = true,
564
+ selection = {},
565
+ } = {}
566
+ ) {
567
+ let user = await this.findOne({ where: { did }, include: includeTags ? [this.getTagInclude()] : [] }, selection);
547
568
 
548
569
  // search in connected accounts
549
570
  if (!user && enableConnectedAccount) {
550
571
  const connectedAccount = await this.connectedAccount.findOne({ did });
551
572
  if (connectedAccount) {
552
- user = await this.findOne({ did: connectedAccount.userDid });
573
+ user = await this.findOne({ did: connectedAccount.userDid }, selection);
553
574
  }
554
575
  }
555
576
 
556
577
  if (user) {
557
- [connectedAccounts, passports] = await Promise.all([
558
- this.connectedAccount.find({ userDid: user.did }),
559
- this.passport.find({ userDid: user.did }),
560
- ]);
561
-
562
- user.connectedAccounts = connectedAccounts;
563
- user.passports = passports;
578
+ if (includeConnectedAccounts && includePassports) {
579
+ const [connectedAccounts, passports] = await Promise.all([
580
+ this.connectedAccount.find({ userDid: user.did }),
581
+ this.passport.find({ userDid: user.did }),
582
+ ]);
583
+ user.connectedAccounts = connectedAccounts;
584
+ user.passports = passports;
585
+ } else if (includeConnectedAccounts) {
586
+ user.connectedAccounts = await this.connectedAccount.find({ userDid: user.did });
587
+ } else if (includePassports) {
588
+ user.passports = await this.passport.find({ userDid: user.did });
589
+ }
564
590
  }
565
591
 
566
592
  return user;
@@ -224,16 +224,38 @@ class TeamManager extends EventEmitter {
224
224
  /**
225
225
  * 判断 passport 是否已经过期
226
226
  */
227
- isPassportExpired(passport) {
228
- return passport.status !== 'valid' || (passport.expirationDate && dayjs(passport.expirationDate).isBefore(dayjs()));
227
+ isPassportExpired(passport, now) {
228
+ return passport.status !== 'valid' || (passport.expirationDate && dayjs(passport.expirationDate).isBefore(now));
229
+ }
230
+
231
+ /**
232
+ * 验证用户是否有效(是否存在、passport是否过期)
233
+ * @private
234
+ */
235
+ _filterValidUsers(users) {
236
+ const now = dayjs();
237
+ return users.filter((user) => {
238
+ if (!user) {
239
+ return false;
240
+ }
241
+ const { passports = [] } = user;
242
+ // 如果用户没有 passport,或者 有一个 passport 没有过期,则返回 true
243
+ if (!passports?.length || passports.some((x) => !this.isPassportExpired(x, now))) {
244
+ return true;
245
+ }
246
+ logger.warn(`user's passports are all expired: ${user.did}`);
247
+ return false;
248
+ });
229
249
  }
230
250
 
231
251
  async getNotificationReceivers(payload) {
232
- const { teamDid, userDids = [], roles = [], selection = {}, includeConnectedAccounts = false } = payload;
252
+ const { teamDid, userDids = [], roles = [], selection = {}, includeConnectedAccounts = false, paging } = payload;
233
253
  // 会根据 teamDid 返回对应 state
234
254
  const userState = await this.getUserState(teamDid);
235
255
  if (!userDids.length && !roles.length) {
236
- return [];
256
+ return paging
257
+ ? { list: [], paging: { total: 0, pageSize: paging.size || 20, pageCount: 0, page: paging.page || 1 } }
258
+ : [];
237
259
  }
238
260
  try {
239
261
  // 这些用户信息已经是 approved 的用户,不需要再次确认
@@ -245,7 +267,7 @@ class TeamManager extends EventEmitter {
245
267
  if (userDids.length > 0) {
246
268
  let queryUsers = [];
247
269
  if (userDids.includes('*')) {
248
- queryUsers = await userState.getUsersByDids({
270
+ const result = await userState.getUsersByDids({
249
271
  dids: userDids,
250
272
  query: {
251
273
  approved: true,
@@ -253,45 +275,57 @@ class TeamManager extends EventEmitter {
253
275
  includeConnectedAccounts,
254
276
  includePassports: true,
255
277
  },
278
+ paging,
256
279
  });
280
+
281
+ // 如果是分页查询,处理分页结果
282
+ if (paging && result.list) {
283
+ const validUsers = this._filterValidUsers(result.list);
284
+ return {
285
+ list: users.concat(validUsers),
286
+ paging: result.paging,
287
+ };
288
+ }
289
+
290
+ queryUsers = result;
257
291
  } else {
258
292
  // 使用 getUser 查询的目的是为了避免传入的 receiver 不在user表中而存在于 connected_account 表中
259
293
  queryUsers = await Promise.all(
260
- userDids.map((did) => userState.getUser(did, { includePassports: true, enableConnectedAccount: true }))
294
+ userDids.map((did) =>
295
+ userState.getUser(did, {
296
+ enableConnectedAccount: true,
297
+ includeConnectedAccounts: false,
298
+ selection,
299
+ })
300
+ )
261
301
  );
262
302
  }
263
303
 
264
- const validUsers = queryUsers.filter((user, index) => {
265
- if (!user) {
266
- logger.warn('receiver is not exist: ', { userDid: userDids[index] });
267
- return false;
268
- }
269
- const { passports = [] } = user;
270
- // 如果用户没有 passport,或者 有一个 passport 没有过期,则返回 true
271
- if (!passports?.length || passports.some((x) => !this.isPassportExpired(x))) {
272
- return true;
273
- }
274
- logger.warn(`user's passports are all expired: ${user.did}`);
275
- return false;
276
- });
277
-
304
+ const validUsers = this._filterValidUsers(queryUsers);
278
305
  users = users.concat(validUsers);
279
306
  }
280
307
 
281
308
  return users;
282
309
  } catch (error) {
283
310
  logger.error('get receivers failed: ', error);
284
- return [];
311
+ return paging
312
+ ? { list: [], paging: { total: 0, pageSize: paging.size || 20, pageCount: 0, page: paging.page || 1 } }
313
+ : [];
285
314
  }
286
315
  }
287
316
 
288
317
  async getAdminReceivers({ teamDid }) {
289
- const receivers = await this.getNotificationReceivers({
290
- teamDid: teamDid ?? this.nodeDid,
291
- roles: [ROLES.ADMIN, ROLES.OWNER],
292
- selection: { did: 1 },
293
- });
294
- return receivers.map((r) => r.did);
318
+ try {
319
+ const receivers = await this.getNotificationReceivers({
320
+ teamDid: teamDid ?? this.nodeDid,
321
+ roles: [ROLES.ADMIN, ROLES.OWNER],
322
+ selection: { did: 1 },
323
+ });
324
+ return receivers.map((r) => r.did);
325
+ } catch (error) {
326
+ logger.error('get admin receivers failed', { error });
327
+ return [];
328
+ }
295
329
  }
296
330
 
297
331
  /**
@@ -351,30 +385,65 @@ class TeamManager extends EventEmitter {
351
385
  }
352
386
 
353
387
  /**
354
- * 获取接收者列表
388
+ * 获取接收者列表(支持分页)
355
389
  * @private
390
+ * @param {string} teamDid - 团队 DID
391
+ * @param {string|string[]} receiver - 接收者
392
+ * @param {boolean} isExist - 通知是否已存在
393
+ * @returns {AsyncGenerator<string[]>} 异步生成器,每次返回一页接收者的 DID 列表
356
394
  */
357
- async _getReceiverList(teamDid, receiver, isExist) {
395
+ async *_getReceiverList(teamDid, receiver, isExist) {
396
+ // 1. 如果通知已存在,直接返回规范化后的接收者
397
+ // 这种情况 receiver 一定存在。
358
398
  if (isExist) {
359
- return this._normalizeReceiver(receiver);
399
+ yield this._normalizeReceiver(receiver);
400
+ return;
360
401
  }
361
402
 
362
403
  const receivers = this._normalizeReceiver(receiver);
363
- if (receivers) {
404
+
405
+ // 2. 如果传入的是具体的 DID 数组(不包含 *),不需要分页查询,直接拿来用
406
+ if (receivers && !receivers.includes('*')) {
407
+ yield receivers;
408
+ return;
409
+ }
410
+
411
+ // 3. 如果传入 receiver 包含 * 需要分页查询,返回一页的数据,开始执行一页
412
+ if (receivers && receivers.includes('*')) {
364
413
  try {
365
- const users = await this.getNotificationReceivers({
366
- teamDid,
367
- userDids: receivers,
368
- includeConnectedAccounts: true,
369
- });
370
- return users.map((u) => u.did);
414
+ const pageSize = 100; // 每页查询 100 个用户
415
+ let currentPage = 1;
416
+ let hasMore = true;
417
+
418
+ // eslint-disable-next-line no-await-in-loop
419
+ while (hasMore) {
420
+ // eslint-disable-next-line no-await-in-loop
421
+ const result = await this.getNotificationReceivers({
422
+ teamDid,
423
+ userDids: receivers,
424
+ selection: { did: 1 },
425
+ paging: { page: currentPage, pageSize },
426
+ });
427
+
428
+ const userDids = result.list.map((u) => u.did);
429
+
430
+ if (userDids.length > 0) {
431
+ yield userDids;
432
+ }
433
+
434
+ hasMore = currentPage < result.paging.pageCount;
435
+ currentPage += 1;
436
+ }
371
437
  } catch (error) {
372
- logger.error('get receivers failed', { error });
373
- return [];
438
+ logger.error('get receivers failed with pagination', { error });
439
+ yield [];
374
440
  }
441
+ return;
375
442
  }
376
443
 
377
- return this.getAdminReceivers({ teamDid });
444
+ // 4. 如果没有传入 receiver,需要查询站点的 admin 相关的用户
445
+ const adminReceivers = await this.getAdminReceivers({ teamDid });
446
+ yield adminReceivers;
378
447
  }
379
448
 
380
449
  /**
@@ -432,11 +501,10 @@ class TeamManager extends EventEmitter {
432
501
  * 创建通知文档
433
502
  * @private
434
503
  */
435
- async _createNotificationDoc(notificationState, payload, receivers, source, isServices, teamDid) {
504
+ async _createNotificationDoc(notificationState, payload, source, isServices, teamDid) {
436
505
  try {
437
506
  return await notificationState.create({
438
507
  ...payload,
439
- receiver: receivers,
440
508
  source,
441
509
  ...(!isServices ? {} : { teamDid }),
442
510
  });
@@ -539,18 +607,16 @@ class TeamManager extends EventEmitter {
539
607
 
540
608
  // 检查通知是否已存在
541
609
  const isExist = await this._checkExistingNotification(notificationState, id || notification?.id);
542
- // 获取接收者列表
543
- const receivers = await this._getReceiverList(teamDid, receiver, isExist);
544
610
 
545
- if (!receivers?.length && process.env.NODE_ENV !== 'test') {
546
- logger.warn('No valid receivers', {
547
- teamDid,
548
- receiver,
549
- notification: JSON.stringify(notification || payload || {}),
550
- });
551
- return undefined;
611
+ let doc = null;
612
+ const source = this._determineSource(teamDid, payload);
613
+ const isServices = teamDid && !this.isNodeTeam(teamDid);
614
+ if (!isExist && !payload.pushOnly) {
615
+ doc = await this._createNotificationDoc(notificationState, payload, source, isServices, teamDid);
616
+ logger.info('notification created successfully', { teamDid, notificationId: doc.id });
552
617
  }
553
618
 
619
+ // 获取 actor 信息
554
620
  const notificationActor = notification?.activity?.actor || payload.activity?.actor;
555
621
  let actorInfo = null;
556
622
  if (notificationActor) {
@@ -559,32 +625,53 @@ class TeamManager extends EventEmitter {
559
625
  actorInfo = pick(user, ['did', 'fullName', 'avatar']);
560
626
  }
561
627
 
562
- if (payload.pushOnly || isExist) {
563
- this.emit(EVENTS.NOTIFICATION_CREATE_QUEUED, {
564
- channels: payload.channels || [NOTIFICATION_SEND_CHANNEL.WALLET],
565
- notification: {
566
- ...(notification || payload),
567
- },
568
- receivers: receivers ?? receiver,
569
- teamDid: teamDid || this.nodeDid,
570
- sender: payload.sender,
571
- pushOnly: true,
572
- isExist,
573
- ...(actorInfo ? { actorInfo } : {}),
574
- options: typeof payload.options === 'object' && payload.options !== null ? payload.options : {},
575
- });
576
- return undefined;
577
- }
628
+ // 获取接收者列表(使用异步生成器逐页处理)
629
+ const receiverGenerator = this._getReceiverList(teamDid, receiver, isExist);
630
+ let hasReceivers = false;
578
631
 
579
- const source = this._determineSource(teamDid, payload);
580
- const isServices = teamDid && !this.isNodeTeam(teamDid);
632
+ for await (const receiversPage of receiverGenerator) {
633
+ if (!receiversPage?.length) {
634
+ continue;
635
+ }
581
636
 
582
- const doc = await this._createNotificationDoc(notificationState, payload, receivers, source, isServices, teamDid);
637
+ hasReceivers = true;
583
638
 
584
- logger.info('notification created successfully', { teamDid, notificationId: doc.id });
639
+ if (payload.pushOnly || isExist) {
640
+ this.emit(EVENTS.NOTIFICATION_CREATE_QUEUED, {
641
+ channels: payload.channels || [NOTIFICATION_SEND_CHANNEL.WALLET],
642
+ notification: {
643
+ ...(notification || payload),
644
+ },
645
+ receivers: receiversPage,
646
+ teamDid: teamDid || this.nodeDid,
647
+ sender: payload.sender,
648
+ pushOnly: true,
649
+ isExist,
650
+ ...(actorInfo ? { actorInfo } : {}),
651
+ options: typeof payload.options === 'object' && payload.options !== null ? payload.options : {},
652
+ });
653
+ } else {
654
+ const defaultChannel = this._getDefaultChannels(isServices, source);
655
+ await this._emitNotificationEvents(
656
+ doc,
657
+ payload,
658
+ receiversPage,
659
+ teamDid,
660
+ isServices,
661
+ defaultChannel,
662
+ actorInfo
663
+ );
664
+ }
665
+ }
585
666
 
586
- const defaultChannel = this._getDefaultChannels(isServices, source);
587
- await this._emitNotificationEvents(doc, payload, receivers, teamDid, isServices, defaultChannel, actorInfo);
667
+ if (!hasReceivers && process.env.NODE_ENV !== 'test') {
668
+ logger.warn('No valid receivers', {
669
+ teamDid,
670
+ receiver,
671
+ notification: JSON.stringify(notification || payload || {}),
672
+ });
673
+ return undefined;
674
+ }
588
675
 
589
676
  return doc;
590
677
  } catch (error) {
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "1.17.4-beta-20251201-225048-b1682a09",
6
+ "version": "1.17.4-beta-20251202-034514-637cd8e2",
7
7
  "description": "",
8
8
  "main": "lib/index.js",
9
9
  "files": [
@@ -17,21 +17,21 @@
17
17
  "author": "wangshijun <wangshijun2010@gmail.com> (http://github.com/wangshijun)",
18
18
  "license": "Apache-2.0",
19
19
  "dependencies": {
20
- "@abtnode/analytics": "1.17.4-beta-20251201-225048-b1682a09",
21
- "@abtnode/auth": "1.17.4-beta-20251201-225048-b1682a09",
22
- "@abtnode/certificate-manager": "1.17.4-beta-20251201-225048-b1682a09",
23
- "@abtnode/constant": "1.17.4-beta-20251201-225048-b1682a09",
24
- "@abtnode/cron": "1.17.4-beta-20251201-225048-b1682a09",
25
- "@abtnode/db-cache": "1.17.4-beta-20251201-225048-b1682a09",
26
- "@abtnode/docker-utils": "1.17.4-beta-20251201-225048-b1682a09",
27
- "@abtnode/logger": "1.17.4-beta-20251201-225048-b1682a09",
28
- "@abtnode/models": "1.17.4-beta-20251201-225048-b1682a09",
29
- "@abtnode/queue": "1.17.4-beta-20251201-225048-b1682a09",
30
- "@abtnode/rbac": "1.17.4-beta-20251201-225048-b1682a09",
31
- "@abtnode/router-provider": "1.17.4-beta-20251201-225048-b1682a09",
32
- "@abtnode/static-server": "1.17.4-beta-20251201-225048-b1682a09",
33
- "@abtnode/timemachine": "1.17.4-beta-20251201-225048-b1682a09",
34
- "@abtnode/util": "1.17.4-beta-20251201-225048-b1682a09",
20
+ "@abtnode/analytics": "1.17.4-beta-20251202-034514-637cd8e2",
21
+ "@abtnode/auth": "1.17.4-beta-20251202-034514-637cd8e2",
22
+ "@abtnode/certificate-manager": "1.17.4-beta-20251202-034514-637cd8e2",
23
+ "@abtnode/constant": "1.17.4-beta-20251202-034514-637cd8e2",
24
+ "@abtnode/cron": "1.17.4-beta-20251202-034514-637cd8e2",
25
+ "@abtnode/db-cache": "1.17.4-beta-20251202-034514-637cd8e2",
26
+ "@abtnode/docker-utils": "1.17.4-beta-20251202-034514-637cd8e2",
27
+ "@abtnode/logger": "1.17.4-beta-20251202-034514-637cd8e2",
28
+ "@abtnode/models": "1.17.4-beta-20251202-034514-637cd8e2",
29
+ "@abtnode/queue": "1.17.4-beta-20251202-034514-637cd8e2",
30
+ "@abtnode/rbac": "1.17.4-beta-20251202-034514-637cd8e2",
31
+ "@abtnode/router-provider": "1.17.4-beta-20251202-034514-637cd8e2",
32
+ "@abtnode/static-server": "1.17.4-beta-20251202-034514-637cd8e2",
33
+ "@abtnode/timemachine": "1.17.4-beta-20251202-034514-637cd8e2",
34
+ "@abtnode/util": "1.17.4-beta-20251202-034514-637cd8e2",
35
35
  "@aigne/aigne-hub": "^0.10.10",
36
36
  "@arcblock/did": "^1.27.12",
37
37
  "@arcblock/did-connect-js": "^1.27.12",
@@ -43,15 +43,15 @@
43
43
  "@arcblock/pm2-events": "^0.0.5",
44
44
  "@arcblock/validator": "^1.27.12",
45
45
  "@arcblock/vc": "^1.27.12",
46
- "@blocklet/constant": "1.17.4-beta-20251201-225048-b1682a09",
46
+ "@blocklet/constant": "1.17.4-beta-20251202-034514-637cd8e2",
47
47
  "@blocklet/did-space-js": "^1.2.6",
48
- "@blocklet/env": "1.17.4-beta-20251201-225048-b1682a09",
48
+ "@blocklet/env": "1.17.4-beta-20251202-034514-637cd8e2",
49
49
  "@blocklet/error": "^0.3.3",
50
- "@blocklet/meta": "1.17.4-beta-20251201-225048-b1682a09",
51
- "@blocklet/resolver": "1.17.4-beta-20251201-225048-b1682a09",
52
- "@blocklet/sdk": "1.17.4-beta-20251201-225048-b1682a09",
53
- "@blocklet/server-js": "1.17.4-beta-20251201-225048-b1682a09",
54
- "@blocklet/store": "1.17.4-beta-20251201-225048-b1682a09",
50
+ "@blocklet/meta": "1.17.4-beta-20251202-034514-637cd8e2",
51
+ "@blocklet/resolver": "1.17.4-beta-20251202-034514-637cd8e2",
52
+ "@blocklet/sdk": "1.17.4-beta-20251202-034514-637cd8e2",
53
+ "@blocklet/server-js": "1.17.4-beta-20251202-034514-637cd8e2",
54
+ "@blocklet/store": "1.17.4-beta-20251202-034514-637cd8e2",
55
55
  "@blocklet/theme": "^3.2.11",
56
56
  "@fidm/x509": "^1.2.1",
57
57
  "@ocap/mcrypto": "^1.27.12",
@@ -116,5 +116,5 @@
116
116
  "express": "^4.18.2",
117
117
  "unzipper": "^0.10.11"
118
118
  },
119
- "gitHead": "150c52d552b8b2d8c3a5bae57818aabf90dfd115"
119
+ "gitHead": "d0e748c5cae1a08c63cfc5bb59a6d471c3120e7a"
120
120
  }