@abtnode/core 1.17.6-beta-20251221-021146-1a145a92 → 1.17.7-beta-20251223-090654-55d57623

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
@@ -58,6 +58,7 @@ const { getEmailServiceProvider } = require('@abtnode/auth/lib/email');
58
58
  const md5 = require('@abtnode/util/lib/md5');
59
59
  const { sanitizeTag } = require('@abtnode/util/lib/sanitize');
60
60
  const { Joi } = require('@arcblock/validator');
61
+ const { getBlockletAppIdList } = require('@blocklet/meta/lib/util');
61
62
 
62
63
  const { validateTrustedPassportIssuers } = require('../validators/trusted-passport');
63
64
  const { validateTrustedFactories } = require('../validators/trusted-factory');
@@ -3119,6 +3120,7 @@ class TeamAPI extends EventEmitter {
3119
3120
  {
3120
3121
  action: 'issueOrgOwnerPassport',
3121
3122
  entity: 'blocklet',
3123
+ entityId: teamDid,
3122
3124
  params: {
3123
3125
  teamDid,
3124
3126
  org: result,
@@ -3623,10 +3625,43 @@ class TeamAPI extends EventEmitter {
3623
3625
 
3624
3626
  const pushState = getNotificationPushState(results, channelsAvailable, isServer);
3625
3627
 
3628
+ let teamDids = [teamDid];
3629
+ if (isServer) {
3630
+ const nodeInfo = await this.node.read();
3631
+ teamDids = [nodeInfo.did];
3632
+ } else {
3633
+ teamDids = getBlockletAppIdList(blocklet);
3634
+ }
3635
+
3636
+ const pendingResult = await this.states.job.getPendingNotifications({
3637
+ teamDids,
3638
+ isServer,
3639
+ channels: Object.keys(channelsAvailable),
3640
+ createdAt: startTime,
3641
+ });
3642
+
3643
+ // pushState 的 key 与 pendingResult 的 key 映射关系
3644
+ const channelKeyMap = {
3645
+ pushKit: NOTIFICATION_SEND_CHANNEL.PUSH,
3646
+ wallet: NOTIFICATION_SEND_CHANNEL.WALLET,
3647
+ email: NOTIFICATION_SEND_CHANNEL.EMAIL,
3648
+ webhook: NOTIFICATION_SEND_CHANNEL.WEBHOOK,
3649
+ };
3650
+
3651
+ // 合并 pending 数量到对应的 channel
3652
+ const channels = Object.entries(pushState).reduce((acc, [key, value]) => {
3653
+ const pendingKey = channelKeyMap[key] || key;
3654
+ acc[key] = {
3655
+ ...value,
3656
+ pending: pendingResult[pendingKey] || 0,
3657
+ };
3658
+ return acc;
3659
+ }, {});
3660
+
3626
3661
  return {
3627
3662
  healthy: true,
3628
3663
  since: startTime,
3629
- channels: pushState,
3664
+ channels,
3630
3665
  };
3631
3666
  } catch (err) {
3632
3667
  logger.error('Get notification service health failed', err, { teamDid });
@@ -367,6 +367,7 @@ class DiskBlockletManager extends BaseBlockletManager {
367
367
  */
368
368
  const handleBackupComplete = async ({ id: jobId, job }) => {
369
369
  await this.backupQueue.delete(jobId);
370
+ SpacesBackup.abort(job.did);
370
371
 
371
372
  const autoBackup = await this.getAutoBackup({ did: job.did });
372
373
  if (autoBackup?.enabled) {
@@ -3009,17 +3010,19 @@ class DiskBlockletManager extends BaseBlockletManager {
3009
3010
  await spacesBackup.initialize();
3010
3011
  await spacesBackup.verifySpace();
3011
3012
 
3012
- this.backupQueue.push(
3013
- {
3014
- entity: 'blocklet',
3015
- action: 'backupToSpaces',
3016
- did,
3017
- context,
3018
- },
3019
- jobId,
3020
- true,
3021
- BACKUPS.JOB.INTERVAL
3022
- );
3013
+ if (!SpacesBackup.isRunning(did)) {
3014
+ this.backupQueue.push(
3015
+ {
3016
+ entity: 'blocklet',
3017
+ action: 'backupToSpaces',
3018
+ did,
3019
+ context,
3020
+ },
3021
+ jobId,
3022
+ true,
3023
+ BACKUPS.JOB.INTERVAL
3024
+ );
3025
+ }
3023
3026
  }
3024
3027
 
3025
3028
  await states.blockletExtras.setSettings(did, { autoBackup: value });
@@ -3748,17 +3751,37 @@ class DiskBlockletManager extends BaseBlockletManager {
3748
3751
  strategy: BACKUPS.STRATEGY.AUTO,
3749
3752
  },
3750
3753
  }) {
3751
- if (shouldJobBackoff()) {
3752
- logger.warn('Backup to spaces is not available when blocklet server is starting.');
3753
- return;
3754
- }
3755
-
3756
3754
  const blocklet = await states.blocklet.getBlocklet(did);
3757
3755
  const {
3758
3756
  appDid,
3759
3757
  meta: { did: appPid },
3760
3758
  } = blocklet;
3761
3759
 
3760
+ if (shouldJobBackoff()) {
3761
+ const backup = await states.backup.findOne({ appPid }, {}, { createdAt: -1 });
3762
+ const message = 'Backup to spaces is not available when blocklet server is starting.';
3763
+
3764
+ if (backup.status === BACKUPS.STATUS.PROGRESS) {
3765
+ await states.backup.fail(backup.id, {
3766
+ message,
3767
+ });
3768
+ }
3769
+
3770
+ this.emit(BlockletEvents.backupProgress, {
3771
+ appDid,
3772
+ meta: { did: appPid },
3773
+ completed: true,
3774
+ progress: -1,
3775
+ message,
3776
+ backup,
3777
+ context,
3778
+ blocklet,
3779
+ });
3780
+
3781
+ logger.warn(message);
3782
+ return;
3783
+ }
3784
+
3762
3785
  const spaceGateways = await this.getBlockletSpaceGateways({ did });
3763
3786
  const backupEndpoint = getBackupEndpoint(blocklet.environments);
3764
3787
  if (isEmpty(spaceGateways) || isEmpty(backupEndpoint)) {
@@ -5488,28 +5511,33 @@ class DiskBlockletManager extends BaseBlockletManager {
5488
5511
  const { did } = blocklet.meta;
5489
5512
  const jobId = getBackupJobId(did);
5490
5513
  const { job, willRunAt } = (await this.backupQueue.get(jobId, { full: true })) ?? {};
5491
- if (job) {
5492
- if (job.backupState?.strategy === BACKUPS.STRATEGY.MANUAL || willRunAt <= Date.now()) {
5493
- logger.warn(`This app(${did})'s manual backup is already running, skip it`, { job });
5494
- await this.backupQueue.restoreCancelled(jobId);
5495
- return blocklet;
5496
- }
5497
- await this.backupQueue.delete(jobId);
5498
- logger.warn(`This app(${did})'s auto backup is already removed from backup queue`, { job });
5499
- }
5500
5514
 
5501
- this.backupQueue.push(
5502
- {
5503
- entity: 'blocklet',
5504
- action: 'backupToSpaces',
5505
- did,
5506
- context,
5507
- backupState: {
5508
- strategy: BACKUPS.STRATEGY.MANUAL,
5515
+ // 任务正在运行或者将要在 1s 内运行,或者任务可能已过期,都是表示任务可用
5516
+ const waitBackupDone = (job && willRunAt - Date.now() <= 1_000) || SpacesBackup.isRunning(did);
5517
+
5518
+ if (waitBackupDone) {
5519
+ logger.warn(`This app(${did})'s manual or auto backup is already running, skip manual backup`, {
5520
+ job,
5521
+ willRunAt,
5522
+ now: Date.now(),
5523
+ isRunning: SpacesBackup.isRunning(did),
5524
+ });
5525
+ await this.backupQueue.restoreCancelled(jobId);
5526
+ } else {
5527
+ await this.backupQueue.delete(jobId);
5528
+ this.backupQueue.push(
5529
+ {
5530
+ entity: 'blocklet',
5531
+ action: 'backupToSpaces',
5532
+ did,
5533
+ context,
5534
+ backupState: {
5535
+ strategy: BACKUPS.STRATEGY.MANUAL,
5536
+ },
5509
5537
  },
5510
- },
5511
- jobId
5512
- );
5538
+ jobId
5539
+ );
5540
+ }
5513
5541
 
5514
5542
  return blocklet;
5515
5543
  } catch (error) {
@@ -366,6 +366,7 @@ class EnsureBlockletRunning {
366
366
 
367
367
  this.restartQueue.push({
368
368
  rootDid,
369
+ entityId: rootDid,
369
370
  componentDids: dids,
370
371
  firstCycle: !this.whenCycleCheck,
371
372
  });
@@ -269,6 +269,7 @@ const installApplicationFromBackup = async ({
269
269
  entity: 'blocklet',
270
270
  action: 'download',
271
271
  id: did,
272
+ entityId: did,
272
273
  ...downloadParams,
273
274
  },
274
275
  did
@@ -201,6 +201,7 @@ const installApplicationFromGeneral = async ({
201
201
  entity: 'blocklet',
202
202
  action: 'download',
203
203
  id: did,
204
+ entityId: did,
204
205
  ...downloadParams,
205
206
  },
206
207
  did
@@ -187,6 +187,7 @@ const installComponentFromUrl = async ({
187
187
  entity: 'blocklet',
188
188
  action: 'download',
189
189
  id: rootDid,
190
+ entityId: rootDid,
190
191
  ...downloadParams,
191
192
  },
192
193
  rootDid
@@ -145,6 +145,7 @@ const upgrade = async ({ updateId, componentDids, context, states, manager }) =>
145
145
  componentDids: componentDids || [],
146
146
  context,
147
147
  postAction: action,
148
+ entityId: did,
148
149
  },
149
150
  did
150
151
  );
@@ -39044,7 +39044,7 @@ module.exports = require("zlib");
39044
39044
  /***/ ((module) => {
39045
39045
 
39046
39046
  "use strict";
39047
- module.exports = /*#__PURE__*/JSON.parse('{"name":"@abtnode/core","publishConfig":{"access":"public"},"version":"1.17.5","description":"","main":"lib/index.js","files":["lib"],"scripts":{"lint":"eslint tests lib --ignore-pattern \'tests/assets/*\'","lint:fix":"eslint --fix tests lib"},"keywords":[],"author":"wangshijun <wangshijun2010@gmail.com> (http://github.com/wangshijun)","license":"Apache-2.0","dependencies":{"@abtnode/analytics":"1.17.5","@abtnode/auth":"1.17.5","@abtnode/certificate-manager":"1.17.5","@abtnode/constant":"1.17.5","@abtnode/cron":"1.17.5","@abtnode/db-cache":"1.17.5","@abtnode/docker-utils":"1.17.5","@abtnode/logger":"1.17.5","@abtnode/models":"1.17.5","@abtnode/queue":"1.17.5","@abtnode/rbac":"1.17.5","@abtnode/router-provider":"1.17.5","@abtnode/static-server":"1.17.5","@abtnode/timemachine":"1.17.5","@abtnode/util":"1.17.5","@aigne/aigne-hub":"^0.10.15","@arcblock/did":"^1.27.15","@arcblock/did-connect-js":"^1.27.15","@arcblock/did-ext":"^1.27.15","@arcblock/did-motif":"^1.1.14","@arcblock/did-util":"^1.27.15","@arcblock/event-hub":"^1.27.15","@arcblock/jwt":"^1.27.15","@arcblock/pm2-events":"^0.0.5","@arcblock/validator":"^1.27.15","@arcblock/vc":"^1.27.15","@blocklet/constant":"1.17.5","@blocklet/did-space-js":"^1.2.11","@blocklet/env":"1.17.5","@blocklet/error":"^0.3.5","@blocklet/meta":"1.17.5","@blocklet/resolver":"1.17.5","@blocklet/sdk":"1.17.5","@blocklet/server-js":"1.17.5","@blocklet/store":"1.17.5","@blocklet/theme":"^3.2.19","@fidm/x509":"^1.2.1","@ocap/mcrypto":"^1.27.15","@ocap/util":"^1.27.15","@ocap/wallet":"^1.27.15","@slack/webhook":"^7.0.6","archiver":"^7.0.1","axios":"^1.7.9","axon":"^2.0.3","chalk":"^4.1.2","cross-spawn":"^7.0.3","dayjs":"^1.11.13","deep-diff":"^1.0.2","detect-port":"^1.5.1","envfile":"^7.1.0","escape-string-regexp":"^4.0.0","fast-glob":"^3.3.2","filesize":"^10.1.1","flat":"^5.0.2","fs-extra":"^11.2.0","get-port":"^5.1.1","hasha":"^5.2.2","is-base64":"^1.1.0","is-cidr":"4","is-ip":"3","is-url":"^1.2.4","joi":"17.12.2","joi-extension-semver":"^5.0.0","js-yaml":"^4.1.0","kill-port":"^2.0.1","lodash":"^4.17.21","node-stream-zip":"^1.15.0","p-all":"^3.0.0","p-limit":"^3.1.0","p-map":"^4.0.0","p-retry":"^4.6.2","p-wait-for":"^3.2.0","private-ip":"^2.3.4","rate-limiter-flexible":"^5.0.5","read-last-lines":"^1.8.0","semver":"^7.6.3","sequelize":"^6.35.0","shelljs":"^0.8.5","slugify":"^1.6.6","ssri":"^8.0.1","stream-throttle":"^0.1.3","stream-to-promise":"^3.0.0","systeminformation":"^5.23.3","tail":"^2.2.4","tar":"^6.1.11","transliteration":"2.3.5","ua-parser-js":"^1.0.2","ufo":"^1.5.3","uuid":"^11.1.0","valid-url":"^1.0.9","which":"^2.0.2","xbytes":"^1.8.0"},"devDependencies":{"axios-mock-adapter":"^2.1.0","expand-tilde":"^2.0.2","express":"^4.18.2","unzipper":"^0.10.11"},"gitHead":"e5764f753181ed6a7c615cd4fc6682aacf0cb7cd"}');
39047
+ module.exports = /*#__PURE__*/JSON.parse('{"name":"@abtnode/core","publishConfig":{"access":"public"},"version":"1.17.6","description":"","main":"lib/index.js","files":["lib"],"scripts":{"lint":"eslint tests lib --ignore-pattern \'tests/assets/*\'","lint:fix":"eslint --fix tests lib"},"keywords":[],"author":"wangshijun <wangshijun2010@gmail.com> (http://github.com/wangshijun)","license":"Apache-2.0","dependencies":{"@abtnode/analytics":"1.17.6","@abtnode/auth":"1.17.6","@abtnode/certificate-manager":"1.17.6","@abtnode/constant":"1.17.6","@abtnode/cron":"1.17.6","@abtnode/db-cache":"1.17.6","@abtnode/docker-utils":"1.17.6","@abtnode/logger":"1.17.6","@abtnode/models":"1.17.6","@abtnode/queue":"1.17.6","@abtnode/rbac":"1.17.6","@abtnode/router-provider":"1.17.6","@abtnode/static-server":"1.17.6","@abtnode/timemachine":"1.17.6","@abtnode/util":"1.17.6","@aigne/aigne-hub":"^0.10.15","@arcblock/did":"^1.27.15","@arcblock/did-connect-js":"^1.27.15","@arcblock/did-ext":"^1.27.15","@arcblock/did-motif":"^1.1.14","@arcblock/did-util":"^1.27.15","@arcblock/event-hub":"^1.27.15","@arcblock/jwt":"^1.27.15","@arcblock/pm2-events":"^0.0.5","@arcblock/validator":"^1.27.15","@arcblock/vc":"^1.27.15","@blocklet/constant":"1.17.6","@blocklet/did-space-js":"^1.2.11","@blocklet/env":"1.17.6","@blocklet/error":"^0.3.5","@blocklet/meta":"1.17.6","@blocklet/resolver":"1.17.6","@blocklet/sdk":"1.17.6","@blocklet/server-js":"1.17.6","@blocklet/store":"1.17.6","@blocklet/theme":"^3.2.19","@fidm/x509":"^1.2.1","@ocap/mcrypto":"^1.27.15","@ocap/util":"^1.27.15","@ocap/wallet":"^1.27.15","@slack/webhook":"^7.0.6","archiver":"^7.0.1","axios":"^1.7.9","axon":"^2.0.3","chalk":"^4.1.2","cross-spawn":"^7.0.3","dayjs":"^1.11.13","deep-diff":"^1.0.2","detect-port":"^1.5.1","envfile":"^7.1.0","escape-string-regexp":"^4.0.0","fast-glob":"^3.3.2","filesize":"^10.1.1","flat":"^5.0.2","fs-extra":"^11.2.0","get-port":"^5.1.1","hasha":"^5.2.2","is-base64":"^1.1.0","is-cidr":"4","is-ip":"3","is-url":"^1.2.4","joi":"17.12.2","joi-extension-semver":"^5.0.0","js-yaml":"^4.1.0","kill-port":"^2.0.1","lodash":"^4.17.21","node-stream-zip":"^1.15.0","p-all":"^3.0.0","p-limit":"^3.1.0","p-map":"^4.0.0","p-retry":"^4.6.2","p-wait-for":"^3.2.0","private-ip":"^2.3.4","rate-limiter-flexible":"^5.0.5","read-last-lines":"^1.8.0","semver":"^7.6.3","sequelize":"^6.35.0","shelljs":"^0.8.5","slugify":"^1.6.6","ssri":"^8.0.1","stream-throttle":"^0.1.3","stream-to-promise":"^3.0.0","systeminformation":"^5.23.3","tail":"^2.2.4","tar":"^6.1.11","transliteration":"2.3.5","ua-parser-js":"^1.0.2","ufo":"^1.5.3","uuid":"^11.1.0","valid-url":"^1.0.9","which":"^2.0.2","xbytes":"^1.8.0"},"devDependencies":{"axios-mock-adapter":"^2.1.0","expand-tilde":"^2.0.2","express":"^4.18.2","unzipper":"^0.10.11"},"gitHead":"e5764f753181ed6a7c615cd4fc6682aacf0cb7cd"}');
39048
39048
 
39049
39049
  /***/ }),
39050
39050
 
@@ -406,6 +406,17 @@ class SpacesBackup extends BaseBackup {
406
406
  }
407
407
  }
408
408
 
409
+ /**
410
+ *
411
+ *
412
+ * @static
413
+ * @param {string} appPid
414
+ * @memberof SpacesBackup
415
+ */
416
+ static isRunning(appPid) {
417
+ return SpacesBackup.instanceMap.has(appPid);
418
+ }
419
+
409
420
  /**
410
421
  *
411
422
  *
@@ -104,6 +104,8 @@ function getFolderSize(folderPath) {
104
104
  */
105
105
  // eslint-disable-next-line require-await
106
106
  async function getFolderObjects(path, prefix = '') {
107
+ // @note: 我尝试动手解析 .gitignore 文件,但是发现 fast-glob 不支持解析它的规则,并找到了对应的 issue: https://github.com/mrmlnc/fast-glob/issues/265#issue-579211456
108
+ const ignore = ['**/node_modules/**', '**/.next/**', '**/.DS_Store'];
107
109
  const stream = FastGlob.stream('**', {
108
110
  cwd: path,
109
111
  objectMode: true,
@@ -112,6 +114,7 @@ async function getFolderObjects(path, prefix = '') {
112
114
  absolute: true,
113
115
  dot: true,
114
116
  concurrency: 2,
117
+ ignore,
115
118
  });
116
119
 
117
120
  const objects = [];
@@ -2,6 +2,7 @@ const debounce = require('lodash/debounce');
2
2
  const logger = require('@abtnode/logger')('@abtnode/core:event:auto-backup-handler');
3
3
  const { BACKUPS } = require('@abtnode/constant');
4
4
  const { getBackupJobId } = require('../util/spaces');
5
+ const { SpacesBackup } = require('../blocklet/storage/backup/spaces');
5
6
 
6
7
  /**
7
8
  * @description
@@ -16,19 +17,22 @@ async function autoBackupHandler(_eventName, payload, blockletManager) {
16
17
  if (autoBackup.enabled && payload.context) {
17
18
  const jobId = getBackupJobId(did);
18
19
  const { job, willRunAt } = (await blockletManager.backupQueue.get(jobId, { full: true })) ?? {};
19
- if (job) {
20
- // 如果手动备份正在运行,或者自动备份正在运行,则跳过
21
- if (job.backupState?.strategy === BACKUPS.STRATEGY.MANUAL || willRunAt <= Date.now()) {
22
- logger.warn(`This app(${did})'s manual or auto backup is already running, skip it`, {
23
- job,
24
- });
25
- await blockletManager.backupQueue.restoreCancelled(jobId);
26
- return;
27
- }
28
- await blockletManager.backupQueue.delete(jobId);
29
- logger.warn(`This app(${did})'s auto backup is already removed from backup queue`, { job });
20
+
21
+ // 任务正在运行或者将要在 3s 内运行,或者任务可能已过期,都是表示任务可用
22
+ const waitBackupDone = (job && willRunAt - Date.now() <= 3_000) || SpacesBackup.isRunning(did);
23
+
24
+ if (waitBackupDone) {
25
+ logger.warn(`This app(${did})'s manual or auto backup is already running, skip auto backup`, {
26
+ job,
27
+ willRunAt,
28
+ now: Date.now(),
29
+ isRunning: SpacesBackup.isRunning(did),
30
+ });
31
+ await blockletManager.backupQueue.restoreCancelled(jobId);
32
+ return;
30
33
  }
31
34
 
35
+ await blockletManager.backupQueue.delete(jobId);
32
36
  blockletManager.backupQueue.push(
33
37
  {
34
38
  entity: 'blocklet',
@@ -695,8 +695,10 @@ module.exports = ({
695
695
  * @returns
696
696
  */
697
697
  (_eventName, payload, ...args) => {
698
- const id = payload.meta.did;
699
- return autoBackupHandlerFactory(id, autoBackupHandler)(eventName, payload, blockletManager, ...args);
698
+ if (payload.context) {
699
+ const id = payload.meta.did;
700
+ autoBackupHandlerFactory(id, autoBackupHandler)(eventName, payload, blockletManager, ...args);
701
+ }
700
702
  }
701
703
  );
702
704
  });
package/lib/states/job.js CHANGED
@@ -1,8 +1,62 @@
1
+ const { NOTIFICATION_SEND_CHANNEL } = require('@abtnode/constant');
2
+ const dayjs = require('@abtnode/util/lib/dayjs');
3
+ const { Op, Sequelize } = require('sequelize');
4
+ const { CustomError } = require('@blocklet/error');
5
+
1
6
  const BaseState = require('./base');
2
7
 
8
+ // 根据 channel 映射对于的查询类别
9
+ const CHANNEL_MAP = {
10
+ [NOTIFICATION_SEND_CHANNEL.WALLET]: 'send-notification-wallet',
11
+ [NOTIFICATION_SEND_CHANNEL.PUSH]: 'send-notification-push',
12
+ [NOTIFICATION_SEND_CHANNEL.EMAIL]: 'send-notification-email',
13
+ [NOTIFICATION_SEND_CHANNEL.WEBHOOK]: 'send-notification-webhook',
14
+ };
15
+
3
16
  /**
4
17
  * @extends BaseState<import('@abtnode/models').JobState>
5
18
  */
6
- class Job extends BaseState {}
19
+ class Job extends BaseState {
20
+ async getPendingNotifications({ teamDids = [], channels = [], createdAt = '', isServer = false }) {
21
+ if (!teamDids.length || !channels.length) {
22
+ throw new CustomError(400, 'teamDids and channels are required');
23
+ }
24
+ let startTime = createdAt;
25
+ if (!startTime) {
26
+ startTime = dayjs().subtract(1, 'hours').toDate();
27
+ }
28
+
29
+ // 过滤有效 channel,获取对应的 queue 名称
30
+ const validChannels = channels.filter((channel) => CHANNEL_MAP[channel]);
31
+ const queueNames = validChannels.map((channel) => CHANNEL_MAP[channel]);
32
+
33
+ // 构建 entityId 查询条件
34
+ // isServer 为 true 时,使用 COALESCE 查询 entityId 在 teamDids 中或为空(null/'')的记录
35
+ // isServer 为 false 时,直接查询 entityId 在 teamDids 中的记录,索引利用率最高
36
+ const entityIdCondition = isServer
37
+ ? Sequelize.where(Sequelize.fn('COALESCE', Sequelize.col('entityId'), ''), { [Op.in]: [...teamDids, ''] })
38
+ : { entityId: { [Op.in]: teamDids } };
39
+
40
+ // 单次查询,使用 GROUP BY 获取所有 channel 的统计
41
+ const results = await this.model.findAll({
42
+ attributes: ['queue', [Sequelize.fn('COUNT', Sequelize.col('id')), 'count']],
43
+ where: {
44
+ queue: { [Op.in]: queueNames },
45
+ ...(isServer ? { [Op.and]: entityIdCondition } : entityIdCondition),
46
+ createdAt: { [Op.gte]: startTime },
47
+ },
48
+ group: ['queue'],
49
+ raw: true,
50
+ });
51
+
52
+ // 将结果映射回 channel 名称,确保所有请求的 channel 都有返回值
53
+ return validChannels.reduce((acc, channel) => {
54
+ const queueName = CHANNEL_MAP[channel];
55
+ const row = results.find((r) => r.queue === queueName);
56
+ acc[channel] = row ? Number(row.count) : 0;
57
+ return acc;
58
+ }, {});
59
+ }
60
+ }
7
61
 
8
62
  module.exports = Job;
@@ -1035,32 +1035,32 @@ class NotificationState extends BaseState {
1035
1035
  // 计算时间范围
1036
1036
  const startTime = dayjs().subtract(hours, 'hours').toDate();
1037
1037
 
1038
- // 第一步:在 notifications 表中查询符合时间范围的通知 ID
1039
- const notifications = await this.model.findAll({
1040
- where: {
1041
- createdAt: {
1042
- [Op.gte]: startTime,
1043
- },
1044
- },
1045
- attributes: ['id'],
1046
- });
1047
-
1048
- // 如果没有找到符合条件的通知,直接返回空数组
1049
- if (notifications.length === 0) {
1050
- return [];
1051
- }
1052
-
1053
- // 提取通知 ID 列表
1054
- const notificationIds = notifications.map((n) => n.id);
1038
+ // 只查询统计所需字段,排除大的 JSON 字段 (walletSendRecord, pushKitSendRecord, emailSendRecord)
1039
+ // 这样可以大幅减少数据传输量
1040
+ const results = await this.model.sequelize.query(
1041
+ `SELECT
1042
+ "walletSendAt", "walletSendStatus", "walletSendFailedReason",
1043
+ "pushKitSendAt", "pushKitSendStatus", "pushKitSendFailedReason",
1044
+ "emailSendAt", "emailSendStatus", "emailSendFailedReason",
1045
+ "webhook", "createdAt"
1046
+ FROM notification_receivers
1047
+ WHERE "createdAt" >= :startTime`,
1048
+ {
1049
+ replacements: { startTime },
1050
+ type: Sequelize.QueryTypes.SELECT,
1051
+ }
1052
+ );
1055
1053
 
1056
- // 第二步:根据通知 ID notification_receivers 表中查询数据
1057
- return this.notificationReceivers.model.findAll({
1058
- where: {
1059
- notificationId: {
1060
- [Op.in]: notificationIds,
1061
- },
1062
- },
1063
- order: [['createdAt', 'DESC']],
1054
+ // 只需要解析 webhook JSON 字段
1055
+ return results.map((item) => {
1056
+ if (typeof item.webhook === 'string') {
1057
+ try {
1058
+ item.webhook = JSON.parse(item.webhook);
1059
+ } catch {
1060
+ item.webhook = {};
1061
+ }
1062
+ }
1063
+ return item;
1064
1064
  });
1065
1065
  }
1066
1066
  }
@@ -251,6 +251,7 @@ module.exports = ({ events, dataDirs, instance }) => {
251
251
  status: severity,
252
252
  action,
253
253
  entityType,
254
+ entityId,
254
255
  blockletUrl,
255
256
  extra,
256
257
  receiver,
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "1.17.6-beta-20251221-021146-1a145a92",
6
+ "version": "1.17.7-beta-20251223-090654-55d57623",
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.6-beta-20251221-021146-1a145a92",
21
- "@abtnode/auth": "1.17.6-beta-20251221-021146-1a145a92",
22
- "@abtnode/certificate-manager": "1.17.6-beta-20251221-021146-1a145a92",
23
- "@abtnode/constant": "1.17.6-beta-20251221-021146-1a145a92",
24
- "@abtnode/cron": "1.17.6-beta-20251221-021146-1a145a92",
25
- "@abtnode/db-cache": "1.17.6-beta-20251221-021146-1a145a92",
26
- "@abtnode/docker-utils": "1.17.6-beta-20251221-021146-1a145a92",
27
- "@abtnode/logger": "1.17.6-beta-20251221-021146-1a145a92",
28
- "@abtnode/models": "1.17.6-beta-20251221-021146-1a145a92",
29
- "@abtnode/queue": "1.17.6-beta-20251221-021146-1a145a92",
30
- "@abtnode/rbac": "1.17.6-beta-20251221-021146-1a145a92",
31
- "@abtnode/router-provider": "1.17.6-beta-20251221-021146-1a145a92",
32
- "@abtnode/static-server": "1.17.6-beta-20251221-021146-1a145a92",
33
- "@abtnode/timemachine": "1.17.6-beta-20251221-021146-1a145a92",
34
- "@abtnode/util": "1.17.6-beta-20251221-021146-1a145a92",
20
+ "@abtnode/analytics": "1.17.7-beta-20251223-090654-55d57623",
21
+ "@abtnode/auth": "1.17.7-beta-20251223-090654-55d57623",
22
+ "@abtnode/certificate-manager": "1.17.7-beta-20251223-090654-55d57623",
23
+ "@abtnode/constant": "1.17.7-beta-20251223-090654-55d57623",
24
+ "@abtnode/cron": "1.17.7-beta-20251223-090654-55d57623",
25
+ "@abtnode/db-cache": "1.17.7-beta-20251223-090654-55d57623",
26
+ "@abtnode/docker-utils": "1.17.7-beta-20251223-090654-55d57623",
27
+ "@abtnode/logger": "1.17.7-beta-20251223-090654-55d57623",
28
+ "@abtnode/models": "1.17.7-beta-20251223-090654-55d57623",
29
+ "@abtnode/queue": "1.17.7-beta-20251223-090654-55d57623",
30
+ "@abtnode/rbac": "1.17.7-beta-20251223-090654-55d57623",
31
+ "@abtnode/router-provider": "1.17.7-beta-20251223-090654-55d57623",
32
+ "@abtnode/static-server": "1.17.7-beta-20251223-090654-55d57623",
33
+ "@abtnode/timemachine": "1.17.7-beta-20251223-090654-55d57623",
34
+ "@abtnode/util": "1.17.7-beta-20251223-090654-55d57623",
35
35
  "@aigne/aigne-hub": "^0.10.15",
36
36
  "@arcblock/did": "^1.27.15",
37
37
  "@arcblock/did-connect-js": "^1.27.15",
@@ -43,15 +43,15 @@
43
43
  "@arcblock/pm2-events": "^0.0.5",
44
44
  "@arcblock/validator": "^1.27.15",
45
45
  "@arcblock/vc": "^1.27.15",
46
- "@blocklet/constant": "1.17.6-beta-20251221-021146-1a145a92",
46
+ "@blocklet/constant": "1.17.7-beta-20251223-090654-55d57623",
47
47
  "@blocklet/did-space-js": "^1.2.11",
48
- "@blocklet/env": "1.17.6-beta-20251221-021146-1a145a92",
48
+ "@blocklet/env": "1.17.7-beta-20251223-090654-55d57623",
49
49
  "@blocklet/error": "^0.3.5",
50
- "@blocklet/meta": "1.17.6-beta-20251221-021146-1a145a92",
51
- "@blocklet/resolver": "1.17.6-beta-20251221-021146-1a145a92",
52
- "@blocklet/sdk": "1.17.6-beta-20251221-021146-1a145a92",
53
- "@blocklet/server-js": "1.17.6-beta-20251221-021146-1a145a92",
54
- "@blocklet/store": "1.17.6-beta-20251221-021146-1a145a92",
50
+ "@blocklet/meta": "1.17.7-beta-20251223-090654-55d57623",
51
+ "@blocklet/resolver": "1.17.7-beta-20251223-090654-55d57623",
52
+ "@blocklet/sdk": "1.17.7-beta-20251223-090654-55d57623",
53
+ "@blocklet/server-js": "1.17.7-beta-20251223-090654-55d57623",
54
+ "@blocklet/store": "1.17.7-beta-20251223-090654-55d57623",
55
55
  "@blocklet/theme": "^3.2.19",
56
56
  "@fidm/x509": "^1.2.1",
57
57
  "@ocap/mcrypto": "^1.27.15",
@@ -116,5 +116,5 @@
116
116
  "express": "^4.18.2",
117
117
  "unzipper": "^0.10.11"
118
118
  },
119
- "gitHead": "6922b56b5f282ba14c864f6a7969d218e4065104"
119
+ "gitHead": "00a82b6f4ef5818d6ec37d74be404031c693247c"
120
120
  }