@abtnode/blocklet-services 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.
@@ -17,9 +17,11 @@ const {
17
17
  NOTIFICATION_SEND_STATUS,
18
18
  NOTIFICATION_SEND_FAILED_REASON,
19
19
  } = require('@abtnode/constant');
20
+ const sleep = require('@abtnode/util/lib/sleep');
20
21
  const { nanoid } = require('@blocklet/meta/lib/util');
21
22
  const get = require('lodash/get');
22
23
  const uniqBy = require('lodash/uniqBy');
24
+ const { getQueueConcurrencyByMem } = require('@abtnode/core/lib/util');
23
25
  const { getBlockletInfo } = require('../../cache');
24
26
  const { updateNotificationSendStatus } = require('../../socket/channel/did');
25
27
  const eventHub =
@@ -29,6 +31,8 @@ const logger = require('../../libs/logger')('blocklet-services:notification-queu
29
31
 
30
32
  const emailSchema = Joi.string().email().required();
31
33
 
34
+ const concurrency = getQueueConcurrencyByMem();
35
+
32
36
  /**
33
37
  *
34
38
  * 校验是否是有效的 passthrough 消息
@@ -58,10 +62,14 @@ const createNotificationQueue = (name, options, handler) => {
58
62
  maxRetries: 3,
59
63
  retryDelay: 10 * 1000,
60
64
  maxTimeout: 60 * 1000, // throw timeout error after 1 minutes
65
+ concurrency,
61
66
  ...(options ?? {}),
62
67
  },
63
68
  onJob: async (job) => {
64
69
  await handler(job);
70
+ if (options.delay) {
71
+ await sleep(options.delay * 1000);
72
+ }
65
73
  },
66
74
  });
67
75
  };
@@ -85,6 +93,7 @@ const init = ({ node, notificationService }) => {
85
93
  {
86
94
  maxRetries: 1,
87
95
  retryDelay: 0,
96
+ enableScheduledJob: true,
88
97
  },
89
98
  async (job) => {
90
99
  try {
@@ -111,28 +120,42 @@ const init = ({ node, notificationService }) => {
111
120
  /**
112
121
  * Push Kit 推送队列
113
122
  */
114
- const pushKitPushQueue = createNotificationQueue('send-notification-push', {}, async (job) => {
115
- try {
116
- const { notification, receiver, sender } = job;
123
+ const pushKitPushQueue = createNotificationQueue(
124
+ 'send-notification-push',
125
+ {
126
+ enableScheduledJob: true,
127
+ },
128
+ async (job) => {
129
+ try {
130
+ const { notification, receiver, sender } = job;
117
131
 
118
- if (!receiver) {
119
- throw new Error('Invalid receiver');
120
- }
132
+ if (!receiver) {
133
+ throw new Error('Invalid receiver');
134
+ }
121
135
 
122
- await notificationService.sendToPush.exec({
123
- sender,
124
- receiver,
125
- notification,
126
- pushOnly: job.pushOnly,
127
- });
128
- } catch (error) {
129
- if (error.logLevel === 'debug') {
130
- logger.debug('Failed to send to push', { notificationId: job.notification.id, receiver: job.receiver, error });
131
- } else {
132
- logger.error('Failed to send to push', { notificationId: job.notification.id, receiver: job.receiver, error });
136
+ await notificationService.sendToPush.exec({
137
+ sender,
138
+ receiver,
139
+ notification,
140
+ pushOnly: job.pushOnly,
141
+ });
142
+ } catch (error) {
143
+ if (error.logLevel === 'debug') {
144
+ logger.debug('Failed to send to push', {
145
+ notificationId: job.notification.id,
146
+ receiver: job.receiver,
147
+ error,
148
+ });
149
+ } else {
150
+ logger.error('Failed to send to push', {
151
+ notificationId: job.notification.id,
152
+ receiver: job.receiver,
153
+ error,
154
+ });
155
+ }
133
156
  }
134
157
  }
135
- });
158
+ );
136
159
 
137
160
  /**
138
161
  * email 推送队列
@@ -184,7 +207,6 @@ const init = ({ node, notificationService }) => {
184
207
  : null;
185
208
 
186
209
  logger.info('Start send to email', {
187
- email,
188
210
  notificationId: job.notificationId,
189
211
  });
190
212
  await notificationService.sendToMail.exec({
@@ -206,7 +228,6 @@ const init = ({ node, notificationService }) => {
206
228
  options,
207
229
  });
208
230
  logger.info('End send to email', {
209
- email,
210
231
  notificationId: job.notificationId,
211
232
  });
212
233
  } catch (error) {
@@ -381,7 +402,7 @@ const init = ({ node, notificationService }) => {
381
402
  options,
382
403
  },
383
404
  },
384
- delay: 5,
405
+ delay: 8,
385
406
  });
386
407
  } else {
387
408
  // eslint-disable-next-line no-lonely-if
@@ -516,12 +537,15 @@ const init = ({ node, notificationService }) => {
516
537
  receiver: receiverDid,
517
538
  });
518
539
  pushKitPushQueue.push({
519
- notification,
520
- sender,
521
- options,
522
- receiver: receiverDid,
523
- isResend,
524
- pushOnly: props.pushOnly && !isResend,
540
+ job: {
541
+ notification,
542
+ sender,
543
+ options,
544
+ receiver: receiverDid,
545
+ isResend,
546
+ pushOnly: props.pushOnly && !isResend,
547
+ },
548
+ delay: 10,
525
549
  });
526
550
  } else {
527
551
  // eslint-disable-next-line no-lonely-if
@@ -557,13 +581,16 @@ const init = ({ node, notificationService }) => {
557
581
  receiver: receiverDid,
558
582
  });
559
583
  walletPushQueue.push({
560
- notification,
561
- sender,
562
- options,
563
- receiver: receiverDid,
564
- isResend,
565
- pushOnly: props.pushOnly && !isResend,
566
- source,
584
+ job: {
585
+ notification,
586
+ sender,
587
+ options,
588
+ receiver: receiverDid,
589
+ isResend,
590
+ pushOnly: props.pushOnly && !isResend,
591
+ source,
592
+ },
593
+ delay: 5,
567
594
  });
568
595
  } else {
569
596
  // 如果是重发的消息,只需要更新推送状态,不需要更新 channel状态
@@ -628,7 +655,7 @@ const init = ({ node, notificationService }) => {
628
655
  * }
629
656
  */
630
657
  const queue = createNotificationQueue('notification-receivers', {}, async (job) => {
631
- const { teamDid, channels, receiver, sender, notification, nodeInfo, userInfo, ...rest } = job;
658
+ const { teamDid, channels, receiver, sender, notification, nodeInfo, ...rest } = job;
632
659
 
633
660
  logger.info('notification start insert to queue', {
634
661
  teamDid,
@@ -636,6 +663,17 @@ const init = ({ node, notificationService }) => {
636
663
  receiver,
637
664
  });
638
665
  try {
666
+ const selection = {
667
+ did: 1,
668
+ fullName: 1,
669
+ email: 1,
670
+ extra: 1,
671
+ };
672
+ const userInfo = await node.getUser({
673
+ teamDid,
674
+ user: { did: receiver },
675
+ options: { enableConnectedAccount: true, selection, includePassports: false, includeConnectedAccounts: false },
676
+ });
639
677
  if (!userInfo) {
640
678
  throw new Error(`Invalid receiver user: ${receiver}`);
641
679
  }
@@ -654,26 +692,22 @@ const init = ({ node, notificationService }) => {
654
692
  });
655
693
  // websocket 通知
656
694
  const receiverDid = userInfo.did;
657
- try {
658
- websocketQueue.push({
659
- input: {
660
- notification: {
661
- ...notification,
662
- entityType: rest.entityType,
663
- entityId: rest.entityId,
664
- componentDid: rest.componentDid,
665
- source: rest.source,
666
- createdAt: rest.createdAt,
667
- ...(rest.actorInfo ? { actorInfo: rest.actorInfo } : {}),
668
- },
669
- receiver: receiverDid,
670
- teamDid,
671
- isServices: rest.isServices,
695
+ websocketQueue.push({
696
+ input: {
697
+ notification: {
698
+ ...notification,
699
+ entityType: rest.entityType,
700
+ entityId: rest.entityId,
701
+ componentDid: rest.componentDid,
702
+ source: rest.source,
703
+ createdAt: rest.createdAt,
704
+ ...(rest.actorInfo ? { actorInfo: rest.actorInfo } : {}),
672
705
  },
673
- });
674
- } catch (error) {
675
- logger.error('Failed to insert to websocket push queue', { error });
676
- }
706
+ receiver: receiverDid,
707
+ teamDid,
708
+ isServices: rest.isServices,
709
+ },
710
+ });
677
711
  }
678
712
 
679
713
  notification.type = notification.type || 'notification';
@@ -727,7 +761,6 @@ const init = ({ node, notificationService }) => {
727
761
  logger.info('notification start insert to queue:', {
728
762
  teamDid: data?.teamDid,
729
763
  notificationId: data?.notification?.id,
730
- receivers: data?.receivers?.join(','),
731
764
  });
732
765
  if (isInstanceWorker()) {
733
766
  return;
@@ -753,13 +786,7 @@ const init = ({ node, notificationService }) => {
753
786
  type: 'server',
754
787
  });
755
788
 
756
- const users = await node.getNotificationReceivers({
757
- teamDid,
758
- userDids: receivers,
759
- includeConnectedAccounts: true,
760
- });
761
-
762
- if (users.length === 0) {
789
+ if (receivers.length === 0) {
763
790
  throw new Error('No users found');
764
791
  }
765
792
  // 如果 notification 没有 id,则生成一个, wallet 要基于这唯一个ID进行处理
@@ -771,8 +798,7 @@ const init = ({ node, notificationService }) => {
771
798
  notification.id = `NOTIF-${nanoid()}`;
772
799
  }
773
800
 
774
- users.forEach((userInfo) => {
775
- const receiverDid = userInfo.did;
801
+ receivers.forEach((receiverDid) => {
776
802
  queue.push({
777
803
  ...rest,
778
804
  notification,
@@ -781,7 +807,6 @@ const init = ({ node, notificationService }) => {
781
807
  receiver: receiverDid,
782
808
  sender,
783
809
  nodeInfo,
784
- userInfo,
785
810
  });
786
811
  });
787
812
  } catch (error) {
@@ -164,40 +164,38 @@ const sendToUserDid = async ({ sender, receiver: rawDid, notification, options,
164
164
 
165
165
  const isServer = !teamDid || teamDid === nodeInfo.did;
166
166
 
167
- try {
168
- const result = await Promise.all(
169
- notifications.map((_notification) => {
170
- // 如果类型不存在或者是 notification 类型是才进行保存数据库,其他类型只需要通知到用户即可
171
- // eg: type = 'passthrough', 'hi', 'connect', 'feed', 默认只需要通知 wallet
172
- const pushOnly = _notification.type && _notification.type.toLowerCase() !== NOTIFICATION_TYPES.NOTIFICATION;
173
-
174
- // 通知渠道的确定有两种方式 1. 用户传入的 channels 2. 根据 notification 类型确定
175
- // 如果是 hi connect, 只需要通知 wallet
176
- // passthrough 类型,用于 discuss kit 的 chat channel
177
- // 1. push kit: native 通知快速跳转到 chat channel
178
- // 2. app: 钱包消息,可以查看到接收到消息
179
-
180
- return node.createNotification({
181
- teamDid,
182
- ...(pushOnly ? { notification: _notification } : { ..._notification }),
183
- receiver: rawDid,
184
- componentDid: sender.componentDid,
185
- // 这两个字段用于 socket 通知
186
- entityType: _notification.entityType || 'blocklet',
187
- entityId: _notification.entityId || sender.appDid,
188
- source: isServer ? 'system' : _notification.source || 'component',
189
- channels: CHANNEL_MAP[_notification.type] || channels,
190
- sender,
191
- options: { ...rest },
192
- pushOnly,
193
- });
194
- })
195
- );
196
- return result;
197
- } catch (error) {
198
- logger.error('Failed to create notification', { error });
199
- return undefined;
200
- }
167
+ // 开始执行 service 消息的推送,不需要同步执行
168
+ Promise.all(
169
+ notifications.map((_notification) => {
170
+ // 如果类型不存在或者是 notification 类型是才进行保存数据库,其他类型只需要通知到用户即可
171
+ // eg: type = 'passthrough', 'hi', 'connect', 'feed', 默认只需要通知 wallet
172
+ const pushOnly = _notification.type && _notification.type.toLowerCase() !== NOTIFICATION_TYPES.NOTIFICATION;
173
+
174
+ // 通知渠道的确定有两种方式 1. 用户传入的 channels 2. 根据 notification 类型确定
175
+ // 如果是 hi connect, 只需要通知 wallet
176
+ // passthrough 类型,用于 discuss kit 的 chat channel
177
+ // 1. push kit: native 通知快速跳转到 chat channel
178
+ // 2. app: 钱包消息,可以查看到接收到消息
179
+
180
+ return node.createNotification({
181
+ teamDid,
182
+ ...(pushOnly ? { notification: _notification } : { ..._notification }),
183
+ receiver: rawDid,
184
+ componentDid: sender.componentDid,
185
+ // 这两个字段用于 socket 通知
186
+ entityType: _notification.entityType || 'blocklet',
187
+ entityId: _notification.entityId || sender.appDid,
188
+ source: isServer ? 'system' : _notification.source || 'component',
189
+ channels: CHANNEL_MAP[_notification.type] || channels,
190
+ sender,
191
+ options: { ...rest },
192
+ pushOnly,
193
+ });
194
+ })
195
+ ).catch((error) => {
196
+ logger.error('Failed to send notification', { error });
197
+ });
198
+ return true;
201
199
  };
202
200
 
203
201
  // server send notification to app
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": "Provide unified services for every blocklet",
8
8
  "main": "api/index.js",
9
9
  "files": [
@@ -32,17 +32,17 @@
32
32
  "author": "wangshijun <wangshijun2010@gmail.com> (http://github.com/wangshijun)",
33
33
  "license": "Apache-2.0",
34
34
  "dependencies": {
35
- "@abtnode/analytics": "1.17.4-beta-20251201-225048-b1682a09",
36
- "@abtnode/auth": "1.17.4-beta-20251201-225048-b1682a09",
37
- "@abtnode/connect-storage": "1.17.4-beta-20251201-225048-b1682a09",
38
- "@abtnode/constant": "1.17.4-beta-20251201-225048-b1682a09",
39
- "@abtnode/core": "1.17.4-beta-20251201-225048-b1682a09",
40
- "@abtnode/cron": "1.17.4-beta-20251201-225048-b1682a09",
41
- "@abtnode/db-cache": "1.17.4-beta-20251201-225048-b1682a09",
42
- "@abtnode/logger": "1.17.4-beta-20251201-225048-b1682a09",
43
- "@abtnode/models": "1.17.4-beta-20251201-225048-b1682a09",
44
- "@abtnode/router-templates": "1.17.4-beta-20251201-225048-b1682a09",
45
- "@abtnode/util": "1.17.4-beta-20251201-225048-b1682a09",
35
+ "@abtnode/analytics": "1.17.4-beta-20251202-034514-637cd8e2",
36
+ "@abtnode/auth": "1.17.4-beta-20251202-034514-637cd8e2",
37
+ "@abtnode/connect-storage": "1.17.4-beta-20251202-034514-637cd8e2",
38
+ "@abtnode/constant": "1.17.4-beta-20251202-034514-637cd8e2",
39
+ "@abtnode/core": "1.17.4-beta-20251202-034514-637cd8e2",
40
+ "@abtnode/cron": "1.17.4-beta-20251202-034514-637cd8e2",
41
+ "@abtnode/db-cache": "1.17.4-beta-20251202-034514-637cd8e2",
42
+ "@abtnode/logger": "1.17.4-beta-20251202-034514-637cd8e2",
43
+ "@abtnode/models": "1.17.4-beta-20251202-034514-637cd8e2",
44
+ "@abtnode/router-templates": "1.17.4-beta-20251202-034514-637cd8e2",
45
+ "@abtnode/util": "1.17.4-beta-20251202-034514-637cd8e2",
46
46
  "@arcblock/did": "^1.27.12",
47
47
  "@arcblock/did-connect-js": "^1.27.12",
48
48
  "@arcblock/did-ext": "^1.27.12",
@@ -52,18 +52,18 @@
52
52
  "@arcblock/jwt": "^1.27.12",
53
53
  "@arcblock/validator": "^1.27.12",
54
54
  "@arcblock/ws": "^1.27.12",
55
- "@blocklet/constant": "1.17.4-beta-20251201-225048-b1682a09",
55
+ "@blocklet/constant": "1.17.4-beta-20251202-034514-637cd8e2",
56
56
  "@blocklet/dbhub": "^0.2.9",
57
- "@blocklet/env": "1.17.4-beta-20251201-225048-b1682a09",
57
+ "@blocklet/env": "1.17.4-beta-20251202-034514-637cd8e2",
58
58
  "@blocklet/error": "^0.3.3",
59
59
  "@blocklet/form-builder": "^0.1.12",
60
60
  "@blocklet/form-collector": "^0.1.8",
61
- "@blocklet/images": "1.17.4-beta-20251201-225048-b1682a09",
62
- "@blocklet/js-sdk": "1.17.4-beta-20251201-225048-b1682a09",
63
- "@blocklet/meta": "1.17.4-beta-20251201-225048-b1682a09",
64
- "@blocklet/rate-limit": "1.17.4-beta-20251201-225048-b1682a09",
65
- "@blocklet/sdk": "1.17.4-beta-20251201-225048-b1682a09",
66
- "@blocklet/server-js": "1.17.4-beta-20251201-225048-b1682a09",
61
+ "@blocklet/images": "1.17.4-beta-20251202-034514-637cd8e2",
62
+ "@blocklet/js-sdk": "1.17.4-beta-20251202-034514-637cd8e2",
63
+ "@blocklet/meta": "1.17.4-beta-20251202-034514-637cd8e2",
64
+ "@blocklet/rate-limit": "1.17.4-beta-20251202-034514-637cd8e2",
65
+ "@blocklet/sdk": "1.17.4-beta-20251202-034514-637cd8e2",
66
+ "@blocklet/server-js": "1.17.4-beta-20251202-034514-637cd8e2",
67
67
  "@blocklet/theme": "^3.2.11",
68
68
  "@blocklet/theme-builder": "0.4.8",
69
69
  "@blocklet/uploader-server": "^0.3.12",
@@ -123,7 +123,7 @@
123
123
  "whatwg-url": "14.0.0"
124
124
  },
125
125
  "devDependencies": {
126
- "@abtnode/ux": "1.17.4-beta-20251201-225048-b1682a09",
126
+ "@abtnode/ux": "1.17.4-beta-20251202-034514-637cd8e2",
127
127
  "@arcblock/bridge": "^3.2.11",
128
128
  "@arcblock/did-connect-react": "^3.2.11",
129
129
  "@arcblock/icons": "^3.2.11",
@@ -133,7 +133,7 @@
133
133
  "@blocklet/did-space-react": "^1.2.6",
134
134
  "@blocklet/launcher-layout": "^3.2.11",
135
135
  "@blocklet/payment-react": "^1.22.24",
136
- "@blocklet/tracker": "1.17.4-beta-20251201-225048-b1682a09",
136
+ "@blocklet/tracker": "1.17.4-beta-20251202-034514-637cd8e2",
137
137
  "@blocklet/ui-react": "^3.2.11",
138
138
  "@blocklet/uploader": "^0.3.12",
139
139
  "@emotion/react": "^11.14.0",
@@ -213,5 +213,5 @@
213
213
  "url": "https://github.com/ArcBlock/blocklet-server/issues",
214
214
  "email": "shijun@arcblock.io"
215
215
  },
216
- "gitHead": "150c52d552b8b2d8c3a5bae57818aabf90dfd115"
216
+ "gitHead": "d0e748c5cae1a08c63cfc5bb59a6d471c3120e7a"
217
217
  }