@abtnode/core 1.17.5-beta-20251209-090953-3a59e7ac → 1.17.5-beta-20251214-122206-29056e8c

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
@@ -10,6 +10,7 @@ const { joinURL, withoutTrailingSlash } = require('ufo');
10
10
  const { Op } = require('sequelize');
11
11
  const dayjs = require('@abtnode/util/lib/dayjs');
12
12
  const logger = require('@abtnode/logger')('@abtnode/core:api:team');
13
+ const pAll = require('p-all');
13
14
  const {
14
15
  ROLES,
15
16
  genPermissionName,
@@ -287,6 +288,69 @@ class TeamAPI extends EventEmitter {
287
288
  const backup = Array.isArray(backups) && backups.length > 0 ? backups[0] : null;
288
289
  const appRuntimeInfo = await this.runtimeMonitor.getBlockletRuntimeInfo(teamDid);
289
290
 
291
+ const [traffic, integrations, studio] = await pAll([
292
+ // 查询最近 30 天的 traffic insights 聚合数据
293
+ async () => {
294
+ const endDate = dayjs().format('YYYY-MM-DD');
295
+ const startDate = dayjs().subtract(30, 'day').format('YYYY-MM-DD');
296
+ const [totalRequests, failedRequests] = await Promise.all([
297
+ this.states.trafficInsight.sum('totalRequests', {
298
+ did: teamDid,
299
+ date: { [Op.gte]: startDate, [Op.lte]: endDate },
300
+ }),
301
+ this.states.trafficInsight.sum('failedRequests', {
302
+ did: teamDid,
303
+ date: { [Op.gte]: startDate, [Op.lte]: endDate },
304
+ }),
305
+ ]);
306
+ return {
307
+ totalRequests: Number(totalRequests || 0),
308
+ failedRequests: Number(failedRequests || 0),
309
+ };
310
+ },
311
+ // 查询集成资源的数量
312
+ async () => {
313
+ const [webhookCount, accessKeyCount, oauthAppCount] = await pAll([
314
+ async () => {
315
+ const { webhookEndpointState } = await this.teamManager.getWebhookState(teamDid);
316
+ return webhookEndpointState.count({});
317
+ },
318
+ async () => {
319
+ const accessKeyState = await this.getAccessKeyState(teamDid);
320
+ return accessKeyState.count({});
321
+ },
322
+ async () => {
323
+ const { oauthClientState } = await this.teamManager.getOAuthState(teamDid);
324
+ return oauthClientState.count({});
325
+ },
326
+ ]);
327
+
328
+ return {
329
+ webhooks: webhookCount,
330
+ accessKeys: accessKeyCount,
331
+ oauthApps: oauthAppCount,
332
+ };
333
+ },
334
+ // 查询 Studio 创建的 Blocklet 数量
335
+ async () => {
336
+ try {
337
+ const { projectState, releaseState } = await this.teamManager.getProjectState(teamDid);
338
+ const projects = await projectState.getProjects({});
339
+ const projectIds = projects.map((p) => p.id);
340
+ const releasesCount =
341
+ projectIds.length > 0 ? await releaseState.count({ projectId: { [Op.in]: projectIds } }) : 0;
342
+ return {
343
+ blocklets: projects.length,
344
+ releases: releasesCount,
345
+ };
346
+ } catch (error) {
347
+ // 如果项目状态不存在或出错,忽略错误,使用默认值
348
+ logger.debug('Failed to get studio stats', { error, teamDid });
349
+ return { blocklets: 0, releases: 0 };
350
+ }
351
+ },
352
+ ]);
353
+
290
354
  return {
291
355
  user: {
292
356
  users,
@@ -298,6 +362,9 @@ class TeamAPI extends EventEmitter {
298
362
  },
299
363
  backup,
300
364
  appRuntimeInfo,
365
+ traffic,
366
+ integrations,
367
+ studio,
301
368
  };
302
369
  }
303
370
 
@@ -3526,10 +3593,11 @@ class TeamAPI extends EventEmitter {
3526
3593
  if (since && typeof since === 'string') {
3527
3594
  const sinceMatch = since.match(/^(\d+)h$/);
3528
3595
  if (sinceMatch) {
3529
- const hours = parseInt(sinceMatch[1], 10);
3530
- if (hours >= 1 && hours <= 24) {
3531
- startTime = dayjs().subtract(hours, 'hours').toDate();
3596
+ let hours = parseInt(sinceMatch[1], 10);
3597
+ if (hours < 1 || hours > 24) {
3598
+ hours = Math.min(Math.max(hours, 1), 24);
3532
3599
  }
3600
+ startTime = dayjs().subtract(hours, 'hours').toDate();
3533
3601
  }
3534
3602
  }
3535
3603
 
@@ -186,7 +186,6 @@ const {
186
186
  const states = require('../../states');
187
187
  const BaseBlockletManager = require('./base');
188
188
  const { get: getEngine } = require('./engine');
189
- const blockletPm2Events = require('./pm2-events');
190
189
  const runBlockletMigrationScripts = require('../migration');
191
190
  const hooks = require('../hooks');
192
191
  const { BlockletRuntimeMonitor } = require('../../monitor/blocklet-runtime-monitor');
@@ -269,11 +268,6 @@ const startLock = new DBCache(() => ({
269
268
  ...getAbtNodeRedisAndSQLiteUrl(),
270
269
  }));
271
270
 
272
- const pm2StatusMap = {
273
- online: BlockletStatus.running,
274
- stop: BlockletStatus.stopped,
275
- };
276
-
277
271
  const getWalletAppNotification = async (blocklet, tempBlockletInfo) => {
278
272
  let blockletInfo = tempBlockletInfo;
279
273
  if (!blockletInfo) {
@@ -334,7 +328,6 @@ class DiskBlockletManager extends BaseBlockletManager {
334
328
  checkUpdateQueue,
335
329
  reportComponentUsageQueue,
336
330
  resendNotificationQueue,
337
- daemon = false,
338
331
  teamManager,
339
332
  nodeAPI,
340
333
  teamAPI,
@@ -399,11 +392,6 @@ class DiskBlockletManager extends BaseBlockletManager {
399
392
 
400
393
  this.configSynchronizer = new ConfigSynchronizer({ manager: this, states });
401
394
 
402
- if (daemon) {
403
- blockletPm2Events.on('online', (data) => this._syncPm2Status('online', data.blockletDid, data.componentDid));
404
- blockletPm2Events.on('stop', (data) => this._syncPm2Status('stop', data.blockletDid, data.componentDid));
405
- }
406
-
407
395
  if (isFunction(this.checkUpdateQueue?.on)) {
408
396
  /**
409
397
  *
@@ -905,6 +893,45 @@ class DiskBlockletManager extends BaseBlockletManager {
905
893
  this.emit(BlockletEvents.statusChange, doc1);
906
894
  const startedBlockletDids = [];
907
895
  const errorBlockletDids = [];
896
+ const parentBlockletId = blocklet.id;
897
+
898
+ // Helper function to update child status immediately and emit events
899
+ const updateChildStatusImmediately = async (componentDid, status) => {
900
+ if (!states.blockletChild || !parentBlockletId) {
901
+ return;
902
+ }
903
+
904
+ try {
905
+ await states.blockletChild.updateChildStatus(parentBlockletId, componentDid, {
906
+ status,
907
+ operator,
908
+ });
909
+
910
+ // Get updated blocklet to emit events
911
+ const updatedBlocklet = await this.getBlocklet(did);
912
+ const componentsInfo = getComponentsInternalInfo(updatedBlocklet);
913
+
914
+ this.emit(BlockletInternalEvents.componentUpdated, {
915
+ appDid: blocklet.appDid,
916
+ components: componentsInfo.filter((c) => c.did === componentDid),
917
+ });
918
+
919
+ if (status === BlockletStatus.running) {
920
+ this.emit(BlockletInternalEvents.componentStarted, {
921
+ appDid: blocklet.appDid,
922
+ components: [{ did: componentDid }],
923
+ });
924
+ }
925
+
926
+ this.emit(BlockletEvents.statusChange, updatedBlocklet);
927
+ } catch (err) {
928
+ logger.error('Failed to update child status immediately', {
929
+ componentDid,
930
+ status,
931
+ error: err.message,
932
+ });
933
+ }
934
+ };
908
935
 
909
936
  const notStartedComponentDids = await this.startRequiredComponents({
910
937
  componentDids,
@@ -915,11 +942,13 @@ class DiskBlockletManager extends BaseBlockletManager {
915
942
  e2eMode,
916
943
  context,
917
944
  atomic,
918
- onStarted: (subDid) => {
945
+ onStarted: async (subDid) => {
919
946
  startedBlockletDids.push({ did: subDid });
947
+ await updateChildStatusImmediately(subDid, BlockletStatus.running);
920
948
  },
921
- onError: (subDid, error) => {
949
+ onError: async (subDid, error) => {
922
950
  errorBlockletDids.push({ did: subDid, error });
951
+ await updateChildStatusImmediately(subDid, BlockletStatus.error);
923
952
  },
924
953
  });
925
954
 
@@ -933,11 +962,13 @@ class DiskBlockletManager extends BaseBlockletManager {
933
962
  e2eMode,
934
963
  componentDids: [componentDid],
935
964
  operator,
936
- onStarted: (subDid) => {
965
+ onStarted: async (subDid) => {
937
966
  startedBlockletDids.push({ did: subDid });
967
+ await updateChildStatusImmediately(subDid, BlockletStatus.running);
938
968
  },
939
- onError: (subDid, error) => {
969
+ onError: async (subDid, error) => {
940
970
  errorBlockletDids.push({ did: subDid, error });
971
+ await updateChildStatusImmediately(subDid, BlockletStatus.error);
941
972
  },
942
973
  },
943
974
  context
@@ -950,15 +981,12 @@ class DiskBlockletManager extends BaseBlockletManager {
950
981
  let errorDescription = '';
951
982
  let resultBlocklet = nextBlocklet;
952
983
 
984
+ // Status updates are now done immediately in callbacks, so we only need to handle final events and cleanup
953
985
  if (startedBlockletDids.length) {
954
- await states.blocklet.setBlockletStatus(did, BlockletStatus.running, {
955
- componentDids: startedBlockletDids.map((x) => x.did),
956
- operator,
957
- });
958
-
959
986
  const finalBlocklet = await this.getBlocklet(did);
960
987
  resultBlocklet = finalBlocklet;
961
988
 
989
+ // Sync app config after all components started
962
990
  await this.configSynchronizer.throttledSyncAppConfig(finalBlocklet, { wait: 200 });
963
991
  const componentsInfo = getComponentsInternalInfo(finalBlocklet);
964
992
  this.emit(BlockletInternalEvents.componentUpdated, {
@@ -1009,10 +1037,7 @@ class DiskBlockletManager extends BaseBlockletManager {
1009
1037
  componentDids: errorBlockletDids.map((x) => x.did),
1010
1038
  shouldUpdateBlockletStatus: false,
1011
1039
  });
1012
- await states.blocklet.setBlockletStatus(did, BlockletStatus.error, {
1013
- componentDids: errorBlockletDids.map((x) => x.did),
1014
- operator,
1015
- });
1040
+
1016
1041
  const finalBlocklet = await this.getBlocklet(did);
1017
1042
  resultBlocklet = finalBlocklet;
1018
1043
  this.emit(BlockletEvents.startFailed, {
@@ -3413,7 +3438,7 @@ class DiskBlockletManager extends BaseBlockletManager {
3413
3438
  return x;
3414
3439
  });
3415
3440
 
3416
- const blueGreenComponentIds = await blueGreenGetComponentIds(oldBlocklet || blocklet, componentDids);
3441
+ const blueGreenComponentIds = blueGreenGetComponentIds(oldBlocklet || blocklet, componentDids);
3417
3442
 
3418
3443
  try {
3419
3444
  if (process.env.ABT_NODE_DISABLE_BLUE_GREEN === 'true' || process.env.ABT_NODE_DISABLE_BLUE_GREEN === '1') {
@@ -4926,20 +4951,6 @@ class DiskBlockletManager extends BaseBlockletManager {
4926
4951
  });
4927
4952
  }
4928
4953
 
4929
- async _syncPm2Status(pm2Status, did, componentDid) {
4930
- try {
4931
- const blocklet = await states.blocklet.getBlocklet(did);
4932
- const component = findComponentByIdV2(blocklet, componentDid);
4933
- if (component && util.shouldUpdateBlockletStatus(component.status)) {
4934
- const newStatus = pm2StatusMap[pm2Status];
4935
- await states.blocklet.setBlockletStatus(did, newStatus, { componentDids: [componentDid] });
4936
- logger.info('sync pm2 status to blocklet', { did, componentDid, pm2Status, newStatus });
4937
- }
4938
- } catch (error) {
4939
- logger.error('sync pm2 status to blocklet failed', { did, componentDid, pm2Status, error });
4940
- }
4941
- }
4942
-
4943
4954
  /**
4944
4955
  * @param {string} action install, upgrade, installComponent, upgradeComponent
4945
4956
  * @param {string} did
@@ -1,7 +1,6 @@
1
1
  const { BlockletStatus } = require('../../../states/blocklet');
2
- const { forEachBlocklet } = require('../../../util/blocklet');
3
2
 
4
- const blueGreenGetComponentIds = async (blocklet, componentDids) => {
3
+ const blueGreenGetComponentIds = (blocklet, componentDids) => {
5
4
  if (!blocklet) {
6
5
  return {
7
6
  componentDids: componentDids || [],
@@ -29,20 +28,16 @@ const blueGreenGetComponentIds = async (blocklet, componentDids) => {
29
28
  ];
30
29
  }
31
30
 
32
- await forEachBlocklet(
33
- blocklet,
34
- (b) => {
35
- if (!componentDidsSet.has(b.meta.did)) {
36
- return;
37
- }
38
- if (runningStatuses.has(b.greenStatus)) {
39
- greenComponentIds.push(b.meta.did);
40
- } else {
41
- blueComponentIds.push(b.meta.did);
42
- }
43
- },
44
- { parallel: true, concurrencyLimit: 10 }
45
- );
31
+ for (const b of children) {
32
+ if (!componentDidsSet.has(b.meta.did)) {
33
+ continue;
34
+ }
35
+ if (runningStatuses.has(b.greenStatus)) {
36
+ greenComponentIds.push(b.meta.did);
37
+ } else {
38
+ blueComponentIds.push(b.meta.did);
39
+ }
40
+ }
46
41
 
47
42
  return [
48
43
  {
@@ -73,6 +73,8 @@ const blueGreenStartBlocklet = async (
73
73
  throw new Error('No runnable component found');
74
74
  }
75
75
 
76
+ blocklet1.children = await states.blocklet.loadChildren(blocklet1.id);
77
+
76
78
  const customerDockerUseVolumeComponentIds = [];
77
79
  const otherComponentIds = [];
78
80
  await forEachBlockletSync(blocklet1, (b) => {
@@ -86,14 +88,6 @@ const blueGreenStartBlocklet = async (
86
88
  }
87
89
  });
88
90
 
89
- if (customerDockerUseVolumeComponentIds.length) {
90
- await manager.stop(
91
- { did, componentDids: customerDockerUseVolumeComponentIds, updateStatus: false, operator },
92
- context
93
- );
94
- await manager.start({ did, componentDids: customerDockerUseVolumeComponentIds, operator }, context);
95
- }
96
-
97
91
  // 分类组件 ID
98
92
  const entryComponentIds = [];
99
93
  const nonEntryComponentIds = [];
@@ -169,10 +163,94 @@ const blueGreenStartBlocklet = async (
169
163
  return;
170
164
  }
171
165
 
172
- const blueGreenComponentIds = await blueGreenGetComponentIds(blocklet1, entryComponentIds);
166
+ const blueGreenComponentIds = blueGreenGetComponentIds(blocklet1, entryComponentIds);
173
167
 
174
168
  const startedBlockletDids = [];
175
169
  const errorBlockletDids = [];
170
+ const appId = blocklet1.id;
171
+
172
+ const notificationChange = async () => {
173
+ // Get latest children from blocklet_children table instead of reloading entire blocklet
174
+ const latestChildren = await states.blocklet.loadChildren(appId);
175
+
176
+ // Merge latest children into blocklet1 for event emission
177
+ const finalBlocklet = {
178
+ ...blocklet1,
179
+ children: latestChildren,
180
+ };
181
+
182
+ await manager.configSynchronizer.throttledSyncAppConfig(finalBlocklet);
183
+ const componentsInfo = getComponentsInternalInfo(finalBlocklet);
184
+ manager.emit(BlockletInternalEvents.componentUpdated, {
185
+ appDid: blocklet1.appDid,
186
+ components: componentsInfo,
187
+ });
188
+
189
+ manager.emit(BlockletInternalEvents.componentStarted, {
190
+ appDid: blocklet1.appDid,
191
+ components: startedBlockletDids.map((x) => ({ did: x.did })),
192
+ });
193
+
194
+ // Emit statusChange event so UI can see the status change
195
+ manager.emit(BlockletEvents.statusChange, finalBlocklet);
196
+
197
+ manager.emit(BlockletEvents.started, { ...finalBlocklet, componentDids: startedBlockletDids.map((x) => x.did) });
198
+ };
199
+
200
+ // Helper function to update child status immediately and emit events
201
+ const updateChildStatusImmediately = async (componentDid, status, isGreen = false) => {
202
+ if (!states.blockletChild || !appId) {
203
+ return;
204
+ }
205
+
206
+ try {
207
+ const updates = {
208
+ operator,
209
+ inProgressStart: Date.now(),
210
+ };
211
+
212
+ if (status === BlockletStatus.running) {
213
+ await states.blockletChild.updateChildStatusRunning(appId, componentDid, isGreen, updates);
214
+ } else {
215
+ await states.blockletChild.updateChildStatusError(appId, componentDid, isGreen, updates);
216
+ }
217
+
218
+ // Get latest children from blocklet_children table and emit events immediately
219
+ const latestChildren = await states.blocklet.loadChildren(appId);
220
+ const updatedBlocklet = {
221
+ ...blocklet1,
222
+ children: latestChildren,
223
+ };
224
+ const componentsInfo = getComponentsInternalInfo(updatedBlocklet);
225
+
226
+ manager.emit(BlockletInternalEvents.componentUpdated, {
227
+ appDid: blocklet1.appDid,
228
+ components: componentsInfo.filter((c) => c.did === componentDid),
229
+ });
230
+
231
+ if (status === BlockletStatus.running) {
232
+ manager.emit(BlockletInternalEvents.componentStarted, {
233
+ appDid: blocklet1.appDid,
234
+ components: [{ did: componentDid }],
235
+ });
236
+ }
237
+
238
+ manager.emit(BlockletEvents.statusChange, updatedBlocklet);
239
+
240
+ // Emit statusChange event immediately so UI can see each component's status change
241
+ manager.emit(BlockletEvents.started, {
242
+ ...updatedBlocklet,
243
+ componentDids: startedBlockletDids.map((x) => x.did),
244
+ });
245
+ } catch (err) {
246
+ logger.error('Failed to update child status immediately', {
247
+ componentDid,
248
+ status,
249
+ isGreen,
250
+ error: err.message,
251
+ });
252
+ }
253
+ };
176
254
 
177
255
  for (const item of blueGreenComponentIds) {
178
256
  if (!item.componentDids.length) {
@@ -194,6 +272,14 @@ const blueGreenStartBlocklet = async (
194
272
  });
195
273
  }
196
274
 
275
+ if (customerDockerUseVolumeComponentIds.length) {
276
+ await manager.stop(
277
+ { did, componentDids: customerDockerUseVolumeComponentIds, updateStatus: false, operator },
278
+ context
279
+ );
280
+ await manager.start({ did, componentDids: customerDockerUseVolumeComponentIds, operator }, context);
281
+ }
282
+
197
283
  const nextBlocklet = await manager.ensureBlocklet(did, { e2eMode });
198
284
 
199
285
  manager.emit(BlockletEvents.statusChange, nextBlocklet);
@@ -274,6 +360,16 @@ const blueGreenStartBlocklet = async (
274
360
  // 收集成功的组件(排除已经在 errorBlockletDids 中的组件)
275
361
  startedBlockletDids.push({ did: subDid, isGreen: item.changeToGreen });
276
362
 
363
+ // Update status immediately
364
+ await updateChildStatusImmediately(subDid, BlockletStatus.running, item.changeToGreen);
365
+
366
+ await manager.deleteProcess({
367
+ did,
368
+ componentDids: [subDid],
369
+ isGreen: !item.changeToGreen,
370
+ shouldUpdateBlockletStatus: false,
371
+ });
372
+
277
373
  logger.info('Green environment started successfully', {
278
374
  did,
279
375
  componentDids: [subDid],
@@ -285,8 +381,16 @@ const blueGreenStartBlocklet = async (
285
381
  // 收集失败的组件
286
382
  errorBlockletDids.push({ did: subDid, error, isGreen: item.changeToGreen });
287
383
 
384
+ // Update status immediately
385
+ await updateChildStatusImmediately(subDid, BlockletStatus.error, item.changeToGreen);
386
+
288
387
  try {
289
- await manager.deleteProcess({ did, componentDids: [subDid], isGreen: item.changeToGreen });
388
+ await manager.deleteProcess({
389
+ did,
390
+ componentDids: [subDid],
391
+ isGreen: item.changeToGreen,
392
+ shouldUpdateBlockletStatus: false,
393
+ });
290
394
  } catch (cleanupError) {
291
395
  logger.error('Failed to cleanup green environment', { cleanupError });
292
396
  }
@@ -297,7 +401,7 @@ const blueGreenStartBlocklet = async (
297
401
 
298
402
  await pAll(tasks, { concurrency: 6 });
299
403
 
300
- const lastBlocklet = await manager.ensureBlocklet(did, { e2eMode });
404
+ const lastBlocklet = await manager.getBlocklet(did, { e2eMode });
301
405
  let errorDescription = '';
302
406
 
303
407
  // 处理启动失败的组件
@@ -326,37 +430,6 @@ const blueGreenStartBlocklet = async (
326
430
  });
327
431
  }
328
432
 
329
- const greenBlockletDids = errorBlockletDids.filter((x) => x.isGreen).map((x) => x.did);
330
- const blueBlockletDids = errorBlockletDids.filter((x) => !x.isGreen).map((x) => x.did);
331
-
332
- if (greenBlockletDids.length) {
333
- await manager.deleteProcess({
334
- did,
335
- componentDids: greenBlockletDids,
336
- shouldUpdateBlockletStatus: false,
337
- isGreen: true,
338
- });
339
- await states.blocklet.setBlockletStatus(did, BlockletStatus.error, {
340
- componentDids: greenBlockletDids,
341
- operator,
342
- isGreen: true,
343
- });
344
- }
345
-
346
- if (blueBlockletDids.length) {
347
- await manager.deleteProcess({
348
- did,
349
- componentDids: blueBlockletDids,
350
- shouldUpdateBlockletStatus: false,
351
- isGreen: false,
352
- });
353
- await states.blocklet.setBlockletStatus(did, BlockletStatus.error, {
354
- componentDids: blueBlockletDids,
355
- operator,
356
- isGreen: false,
357
- });
358
- }
359
-
360
433
  const finalBlocklet = await manager.getBlocklet(did);
361
434
  manager.emit(BlockletEvents.startFailed, {
362
435
  ...finalBlocklet,
@@ -368,51 +441,14 @@ const blueGreenStartBlocklet = async (
368
441
 
369
442
  // 处理成功启动的组件
370
443
  if (startedBlockletDids.length) {
371
- const startedGreenBlockletDids = startedBlockletDids.filter((x) => x.isGreen).map((x) => x.did);
372
- const startedBlueBlockletDids = startedBlockletDids.filter((x) => !x.isGreen).map((x) => x.did);
373
-
374
- if (startedGreenBlockletDids.length) {
375
- await states.blocklet.setBlockletStatus(did, BlockletStatus.running, {
376
- componentDids: startedGreenBlockletDids,
377
- operator,
378
- isGreen: true,
379
- });
380
- await manager.deleteProcess({ did, componentDids: startedGreenBlockletDids, isGreen: false });
381
- }
382
-
383
- if (startedBlueBlockletDids.length) {
384
- await states.blocklet.setBlockletStatus(did, BlockletStatus.running, {
385
- componentDids: startedBlueBlockletDids,
386
- operator,
387
- isGreen: false,
388
- });
389
- await manager.deleteProcess({ did, componentDids: startedBlueBlockletDids, isGreen: true });
390
- }
391
-
392
- const finalBlocklet = await manager.getBlocklet(did);
393
-
394
- await manager.configSynchronizer.throttledSyncAppConfig(finalBlocklet);
395
- const componentsInfo = getComponentsInternalInfo(finalBlocklet);
396
- manager.emit(BlockletInternalEvents.componentUpdated, {
397
- appDid: blocklet1.appDid,
398
- components: componentsInfo,
399
- });
400
- manager.emit(BlockletInternalEvents.componentStarted, {
401
- appDid: blocklet1.appDid,
402
- components: startedBlockletDids.map((x) => ({ did: x.did })),
403
- });
404
-
405
- manager.emit(BlockletEvents.statusChange, finalBlocklet);
406
- manager.emit(BlockletEvents.started, {
407
- ...finalBlocklet,
408
- componentDids: startedBlockletDids.map((x) => x.did),
409
- });
444
+ await notificationChange();
410
445
  }
411
446
 
412
447
  // 根据情况更新 route table, 会判断只有包含多 interfaces 的 DID 才会更新 route table
413
448
  if (!['true', '1'].includes(process.env.ABT_NODE_DISABLE_BLUE_GREEN)) {
449
+ const latestBlocklet = await manager.getBlocklet(did, { e2eMode });
414
450
  manager.emit(BlockletEvents.blurOrGreenStarted, {
415
- blocklet: lastBlocklet,
451
+ blocklet: latestBlocklet,
416
452
  componentDids,
417
453
  context,
418
454
  });
@@ -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.4","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.4","@abtnode/auth":"1.17.4","@abtnode/certificate-manager":"1.17.4","@abtnode/constant":"1.17.4","@abtnode/cron":"1.17.4","@abtnode/db-cache":"1.17.4","@abtnode/docker-utils":"1.17.4","@abtnode/logger":"1.17.4","@abtnode/models":"1.17.4","@abtnode/queue":"1.17.4","@abtnode/rbac":"1.17.4","@abtnode/router-provider":"1.17.4","@abtnode/static-server":"1.17.4","@abtnode/timemachine":"1.17.4","@abtnode/util":"1.17.4","@aigne/aigne-hub":"^0.10.10","@arcblock/did":"^1.27.12","@arcblock/did-connect-js":"^1.27.12","@arcblock/did-ext":"^1.27.12","@arcblock/did-motif":"^1.1.14","@arcblock/did-util":"^1.27.12","@arcblock/event-hub":"^1.27.12","@arcblock/jwt":"^1.27.12","@arcblock/pm2-events":"^0.0.5","@arcblock/validator":"^1.27.12","@arcblock/vc":"^1.27.12","@blocklet/constant":"1.17.4","@blocklet/did-space-js":"^1.2.6","@blocklet/env":"1.17.4","@blocklet/error":"^0.3.3","@blocklet/meta":"1.17.4","@blocklet/resolver":"1.17.4","@blocklet/sdk":"1.17.4","@blocklet/server-js":"1.17.4","@blocklet/store":"1.17.4","@blocklet/theme":"^3.2.11","@fidm/x509":"^1.2.1","@ocap/mcrypto":"^1.27.12","@ocap/util":"^1.27.12","@ocap/wallet":"^1.27.12","@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.4","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.4","@abtnode/auth":"1.17.4","@abtnode/certificate-manager":"1.17.4","@abtnode/constant":"1.17.4","@abtnode/cron":"1.17.4","@abtnode/db-cache":"1.17.4","@abtnode/docker-utils":"1.17.4","@abtnode/logger":"1.17.4","@abtnode/models":"1.17.4","@abtnode/queue":"1.17.4","@abtnode/rbac":"1.17.4","@abtnode/router-provider":"1.17.4","@abtnode/static-server":"1.17.4","@abtnode/timemachine":"1.17.4","@abtnode/util":"1.17.4","@aigne/aigne-hub":"^0.10.14","@arcblock/did":"^1.27.14","@arcblock/did-connect-js":"^1.27.14","@arcblock/did-ext":"^1.27.14","@arcblock/did-motif":"^1.1.14","@arcblock/did-util":"^1.27.14","@arcblock/event-hub":"^1.27.14","@arcblock/jwt":"^1.27.14","@arcblock/pm2-events":"^0.0.5","@arcblock/validator":"^1.27.14","@arcblock/vc":"^1.27.14","@blocklet/constant":"1.17.4","@blocklet/did-space-js":"^1.2.9","@blocklet/env":"1.17.4","@blocklet/error":"^0.3.4","@blocklet/meta":"1.17.4","@blocklet/resolver":"1.17.4","@blocklet/sdk":"1.17.4","@blocklet/server-js":"1.17.4","@blocklet/store":"1.17.4","@blocklet/theme":"^3.2.13","@fidm/x509":"^1.2.1","@ocap/mcrypto":"^1.27.14","@ocap/util":"^1.27.14","@ocap/wallet":"^1.27.14","@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
 
@@ -169,11 +169,11 @@ const runSchemaMigrations = async ({
169
169
 
170
170
  // migrate server schema
171
171
  dbPaths.server = getDbFilePath(path.join(dataDir, 'core/server.db'));
172
- await doSchemaMigration(dbPaths.server, 'server');
172
+ await doSchemaMigration(dbPaths.server, 'server', true);
173
173
  printSuccess(`Server schema successfully migrated: ${dbPaths.server}`);
174
174
  // migrate service schema
175
175
  dbPaths.service = getDbFilePath(path.join(dataDir, 'services/service.db'));
176
- await doSchemaMigration(dbPaths.service, 'service');
176
+ await doSchemaMigration(dbPaths.service, 'service', true);
177
177
  printSuccess(`Service schema successfully migrated: ${dbPaths.service}`);
178
178
 
179
179
  // migrate blocklet schema
@@ -183,7 +183,7 @@ const runSchemaMigrations = async ({
183
183
  if (env) {
184
184
  const filePath = getDbFilePath(path.join(env.value, 'blocklet.db'));
185
185
  dbPaths.blocklets.push(filePath);
186
- await doSchemaMigration(filePath, 'blocklet');
186
+ await doSchemaMigration(filePath, 'blocklet', true);
187
187
  printSuccess(`Blocklet schema successfully migrated: ${blocklet.appPid}: ${filePath}`);
188
188
  } else {
189
189
  printInfo(`Skip migrate schema for blocklet: ${blocklet.appPid}`);
@@ -193,7 +193,7 @@ const runSchemaMigrations = async ({
193
193
  // migrate certificate manager schema
194
194
  for (let i = 0; i < MODULES.length; i++) {
195
195
  const filePath = getDbFilePath(path.join(dataDir, `modules/${MODULES[i]}/module.db`));
196
- await doSchemaMigration(filePath, MODULES[i]);
196
+ await doSchemaMigration(filePath, MODULES[i], true);
197
197
  dbPaths.certificateManagers.push(filePath);
198
198
  printSuccess(`${MODULES[i]} schema successfully migrated: ${filePath}`);
199
199
  }
@@ -230,16 +230,14 @@ class BlockletRuntimeMonitor extends EventEmitter {
230
230
 
231
231
  insertThrottleMap.set(blockletDid, now);
232
232
 
233
- return this.states.runtimeInsight.insert({ did: blockletDid, ...value }).catch((err) => {
233
+ return this.states.runtimeInsight.insert({ did: blockletDid, ...value }).catch((error) => {
234
+ const err = typeof error === 'string' ? error : error.message;
234
235
  if (err.includes('duplicate key value violates unique constraint ')) {
235
236
  console.error('RuntimeInsight insert error duplicate key value violates unique constraint');
236
237
  return;
237
238
  }
238
239
  if (err.name === 'SequelizeValidationError') {
239
- console.error(
240
- 'RuntimeInsight validation error',
241
- err.errors.map((e) => e.message)
242
- );
240
+ console.error('RuntimeInsight validation error:', err);
243
241
  } else {
244
242
  console.error('RuntimeInsight insert error', err);
245
243
  }