@abtnode/core 1.8.65-beta-5405baf2 → 1.8.65-beta-bfcc12ce

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.
@@ -129,6 +129,7 @@ const { SpacesRestore } = require('../storage/restore/spaces');
129
129
  const installFromBackup = require('./helper/install-from-backup');
130
130
  const { resolveDownload, resolveDiffDownload } = require('../downloader/resolve-download');
131
131
  const BlockletDownloader = require('../downloader/blocklet-downloader');
132
+ const RollbackCache = require('./helper/rollback-cache');
132
133
 
133
134
  const {
134
135
  isInProgress,
@@ -218,6 +219,8 @@ class BlockletManager extends BaseBlockletManager {
218
219
  cache: states.cache,
219
220
  });
220
221
 
222
+ this._rollbackCache = new RollbackCache({ dir: this.dataDirs.tmp });
223
+
221
224
  if (daemon) {
222
225
  blockletPm2Events.on('online', (data) => this._syncPm2Status('online', data.blockletDid));
223
226
  blockletPm2Events.on('stop', (data) => this._syncPm2Status('stop', data.blockletDid));
@@ -255,6 +258,10 @@ class BlockletManager extends BaseBlockletManager {
255
258
  async install(params, context = {}) {
256
259
  logger.debug('install blocklet', { params, context });
257
260
 
261
+ if (!params.controller && context?.user?.controller) {
262
+ params.controller = context.user.controller;
263
+ }
264
+
258
265
  const info = await states.node.read();
259
266
 
260
267
  context.headers = Object.assign(context?.headers || {}, {
@@ -444,6 +451,7 @@ class BlockletManager extends BaseBlockletManager {
444
451
 
445
452
  async start({ did, throwOnError, checkHealthImmediately = false, e2eMode = false }, context) {
446
453
  logger.info('start blocklet', { did });
454
+ // should check blocklet integrity
447
455
  const blocklet = await this.ensureBlocklet(did, { e2eMode });
448
456
 
449
457
  try {
@@ -557,7 +565,7 @@ class BlockletManager extends BaseBlockletManager {
557
565
  async stop({ did, updateStatus = true, silent = false }, context) {
558
566
  logger.info('stop blocklet', { did });
559
567
 
560
- const blocklet = await this.ensureBlocklet(did);
568
+ const blocklet = await this.getBlocklet(did);
561
569
  const { processId } = blocklet.env;
562
570
 
563
571
  if (updateStatus) {
@@ -655,7 +663,7 @@ class BlockletManager extends BaseBlockletManager {
655
663
 
656
664
  // eslint-disable-next-line no-unused-vars
657
665
  async reload({ did }, context) {
658
- const blocklet = await this.ensureBlocklet(did);
666
+ const blocklet = await this.getBlocklet(did);
659
667
 
660
668
  await states.blocklet.setBlockletStatus(did, BlockletStatus.stopping);
661
669
  await reloadBlockletProcess(blocklet);
@@ -671,13 +679,12 @@ class BlockletManager extends BaseBlockletManager {
671
679
  logger.info('delete blocklet', { did, keepData });
672
680
 
673
681
  try {
674
- const blocklet = await this.ensureBlocklet(did, { validateEnv: false });
682
+ const blocklet = await this.getBlocklet(did);
675
683
  if (isDeletableBlocklet(blocklet) === false) {
676
684
  throw new Error('Blocklet is protected from accidental deletion');
677
685
  }
678
686
 
679
687
  const nodeEnvironments = await states.node.getEnvironments();
680
-
681
688
  await deleteBlockletProcess(blocklet, {
682
689
  preDelete: (b, { ancestors }) =>
683
690
  hooks.preUninstall(b.env.processId, {
@@ -700,31 +707,27 @@ class BlockletManager extends BaseBlockletManager {
700
707
  severity: 'success',
701
708
  });
702
709
  return doc;
703
- } catch (err) {
710
+ } catch (error) {
704
711
  // If we installed a corrupted blocklet accidentally, just cleanup the disk and state db
705
- if (err.code === 'BLOCKLET_CORRUPTED') {
706
- logger.info('blocklet is corrupted, will delete again', { did });
707
- const doc = await this._deleteBlocklet({ did, keepData, keepLogsDir, keepConfigs }, context);
708
-
709
- this._createNotification(doc.meta.did, {
710
- title: 'Blocklet Deleted',
711
- description: `Blocklet ${doc.meta.name}@${doc.meta.version} is deleted.`,
712
- entityType: 'blocklet',
713
- entityId: doc.meta.did,
714
- severity: 'success',
715
- });
712
+ logger.error('blocklet delete failed, will delete again', { did, error });
713
+ const doc = await this._deleteBlocklet({ did, keepData, keepLogsDir, keepConfigs }, context);
716
714
 
717
- return doc;
718
- }
715
+ this._createNotification(doc.meta.did, {
716
+ title: 'Blocklet Deleted',
717
+ description: `Blocklet ${doc.meta.name}@${doc.meta.version} is deleted.`,
718
+ entityType: 'blocklet',
719
+ entityId: doc.meta.did,
720
+ severity: 'success',
721
+ });
719
722
 
720
- throw err;
723
+ return doc;
721
724
  }
722
725
  }
723
726
 
724
727
  async reset({ did, childDid }, context = {}) {
725
728
  logger.info('reset blocklet', { did, childDid });
726
729
 
727
- const blocklet = await this.ensureBlocklet(did);
730
+ const blocklet = await this.getBlocklet(did);
728
731
 
729
732
  if (isInProgress(blocklet.status || blocklet.status === BlockletStatus.running)) {
730
733
  throw new Error('Cannot reset when blocklet is in progress');
@@ -774,7 +777,7 @@ class BlockletManager extends BaseBlockletManager {
774
777
  async deleteComponent({ did, rootDid, keepData, keepState }, context) {
775
778
  logger.info('delete blocklet component', { did, rootDid, keepData });
776
779
 
777
- const blocklet = await this.ensureBlocklet(rootDid, { validateEnv: false });
780
+ const blocklet = await this.getBlocklet(rootDid);
778
781
  const child = blocklet.children.find((x) => x.meta.did === did);
779
782
  if (!child) {
780
783
  throw new Error('Component does not exist');
@@ -828,7 +831,7 @@ class BlockletManager extends BaseBlockletManager {
828
831
  await states.blockletExtras.delConfigs([blocklet.meta.did, child.meta.did]);
829
832
  }
830
833
 
831
- const newBlocklet = await this.ensureBlocklet(rootDid);
834
+ const newBlocklet = await this.getBlocklet(rootDid);
832
835
  this.emit(BlockletEvents.upgraded, { blocklet: newBlocklet, context: { ...context, createAuditLog: false } }); // trigger router refresh
833
836
 
834
837
  this._createNotification(newBlocklet.meta.did, {
@@ -843,12 +846,13 @@ class BlockletManager extends BaseBlockletManager {
843
846
  return newBlocklet;
844
847
  }
845
848
 
849
+ // eslint-disable-next-line no-unused-vars
846
850
  async cancelDownload({ did: inputDid }, context) {
847
851
  try {
848
852
  await statusLock.acquire();
849
853
  const blocklet = await states.blocklet.getBlocklet(inputDid);
850
854
  if (!blocklet) {
851
- throw new Error('Can not cancel download for non-exist blocklet in database.', { inputDid });
855
+ throw new Error(`Can not cancel download for non-exist blocklet in database. did: ${inputDid}`);
852
856
  }
853
857
 
854
858
  const { name, did, version } = blocklet.meta;
@@ -858,15 +862,35 @@ class BlockletManager extends BaseBlockletManager {
858
862
  }
859
863
 
860
864
  const job = await this.installQueue.get(did);
861
- if (job) {
862
- const { postAction, oldBlocklet } = job;
863
- await this._rollback(postAction, did, oldBlocklet);
864
- }
865
865
 
866
+ // cancel job
866
867
  if (blocklet.status === BlockletStatus.downloading) {
867
- await this.blockletDownloader.cancelDownload(blocklet.meta.did);
868
+ try {
869
+ await this.blockletDownloader.cancelDownload(blocklet.meta.did);
870
+ } catch (error) {
871
+ logger.error('failed to exec blockletDownloader.download', { did: blocklet.meta.did, error });
872
+ }
868
873
  } else if (blocklet.status === BlockletStatus.waiting) {
869
- await this._cancelWaiting(blocklet.meta, context);
874
+ try {
875
+ await this.installQueue.cancel(blocklet.meta.did);
876
+ } catch (error) {
877
+ logger.error('failed to cancel waiting', { did: blocklet.meta.did, error });
878
+ }
879
+ }
880
+
881
+ // rollback
882
+ if (job) {
883
+ const { postAction, oldBlocklet } = job;
884
+ await this._rollback(postAction, did, oldBlocklet);
885
+ } else {
886
+ const data = await this._rollbackCache.restore({ did });
887
+ if (data) {
888
+ const { action, oldBlocklet } = data;
889
+ await this._rollback(action, did, oldBlocklet);
890
+ await this._rollbackCache.remove({ did });
891
+ } else {
892
+ throw new Error(`Cannot find rollback data in queue or backup file of blocklet ${inputDid}`);
893
+ }
870
894
  }
871
895
 
872
896
  logger.info('cancel download blocklet', { did, name, version, status: fromBlockletStatus(blocklet.status) });
@@ -874,14 +898,25 @@ class BlockletManager extends BaseBlockletManager {
874
898
  statusLock.release();
875
899
  return blocklet;
876
900
  } catch (error) {
877
- statusLock.release();
901
+ try {
902
+ // fallback blocklet status to error
903
+ const blocklet = await states.blocklet.getBlocklet(inputDid);
904
+ if (blocklet) {
905
+ await states.blocklet.setBlockletStatus(blocklet.meta.did, BlockletStatus.error);
906
+ }
907
+ statusLock.release();
908
+ } catch (err) {
909
+ statusLock.release();
910
+ logger.error('Failed to fallback blocklet status to error on cancelDownload', { error });
911
+ }
912
+
878
913
  throw error;
879
914
  }
880
915
  }
881
916
 
882
917
  // eslint-disable-next-line no-unused-vars
883
918
  async deleteProcess({ did }, context) {
884
- const blocklet = await this.ensureBlocklet(did);
919
+ const blocklet = await this.getBlocklet(did);
885
920
 
886
921
  logger.info('delete blocklet process', { did });
887
922
 
@@ -904,17 +939,17 @@ class BlockletManager extends BaseBlockletManager {
904
939
 
905
940
  if (!attachRuntimeInfo) {
906
941
  try {
907
- const blocklet = await this.ensureBlocklet(did, { throwOnNotExist: false });
942
+ const blocklet = await this.getBlocklet(did, { throwOnNotExist: false });
908
943
  return blocklet;
909
944
  } catch (e) {
910
945
  logger.error('get blocklet detail error', { error: e });
911
- return null; // TODO: 直接返回没有 runtime info 的数据
946
+ return states.blocklet.getBlocklet(did);
912
947
  }
913
948
  }
914
949
 
915
950
  const nodeInfo = await states.node.read();
916
951
 
917
- return this.attachRuntimeInfo({ did, nodeInfo, diskInfo: true, context });
952
+ return this._attachRuntimeInfo({ did, nodeInfo, diskInfo: true, context });
918
953
  }
919
954
 
920
955
  async attachBlockletListRuntimeInfo({ blocklets, useCache }, context) {
@@ -929,7 +964,7 @@ class BlockletManager extends BaseBlockletManager {
929
964
  const cachedBlocklet =
930
965
  useCache && this.cachedBlocklets ? this.cachedBlocklets.find((y) => y.meta.did === x.meta.did) : null;
931
966
 
932
- return this.attachRuntimeInfo({
967
+ return this._attachRuntimeInfo({
933
968
  did: x.meta.did,
934
969
  nodeInfo,
935
970
  diskInfo: false,
@@ -977,7 +1012,7 @@ class BlockletManager extends BaseBlockletManager {
977
1012
  const [rootDid, ...childDids] = dids;
978
1013
  logger.info('config blocklet', { dids });
979
1014
 
980
- let blocklet = await this.ensureBlocklet(rootDid);
1015
+ let blocklet = await this.getBlocklet(rootDid);
981
1016
  for (const childDid of childDids) {
982
1017
  blocklet = blocklet.children.find((x) => x.meta.did === childDid);
983
1018
  if (!blocklet) {
@@ -1035,20 +1070,20 @@ class BlockletManager extends BaseBlockletManager {
1035
1070
  await this.updateBlockletEnvironment(rootDid);
1036
1071
 
1037
1072
  // response
1038
- const newState = await this.ensureBlocklet(rootDid);
1073
+ const newState = await this.getBlocklet(rootDid);
1039
1074
  this.emit(BlockletEvents.updated, newState);
1040
1075
  return newState;
1041
1076
  }
1042
1077
 
1043
1078
  async configPublicToStore({ did, publicToStore = false }) {
1044
- const blocklet = await this.ensureBlocklet(did);
1079
+ const blocklet = await this.getBlocklet(did);
1045
1080
  // publicToStore 由用户传入
1046
1081
  // handleInstanceInStore 方法写在前面,保证向 store 操作成功后才会更改 blocklet 中的 publicToStore值
1047
1082
  // handleInstanceInStore 中会校验修改 publicToStore字段 的条件,不符合则会抛错,就不会执行下面更新 publicToStore 的逻辑
1048
1083
  await handleInstanceInStore(blocklet, { publicToStore });
1049
1084
  await states.blockletExtras.setSettings(did, { publicToStore });
1050
1085
 
1051
- const newState = await this.ensureBlocklet(did);
1086
+ const newState = await this.getBlocklet(did);
1052
1087
  return newState;
1053
1088
  }
1054
1089
 
@@ -1058,7 +1093,7 @@ class BlockletManager extends BaseBlockletManager {
1058
1093
  }
1059
1094
  await states.blockletExtras.setSettings(did, { navigations });
1060
1095
 
1061
- const newState = await this.ensureBlocklet(did);
1096
+ const newState = await this.getBlocklet(did);
1062
1097
  this.emit(BlockletEvents.updated, newState);
1063
1098
  return newState;
1064
1099
  }
@@ -1110,7 +1145,7 @@ class BlockletManager extends BaseBlockletManager {
1110
1145
  await states.blockletExtras.setConfigs(dids, configs);
1111
1146
  }
1112
1147
 
1113
- const blocklet = await this.ensureBlocklet(rootDid);
1148
+ const blocklet = await this.getBlocklet(rootDid);
1114
1149
 
1115
1150
  this.emit(BlockletEvents.updated, { meta: { did: blocklet.meta.did } });
1116
1151
 
@@ -1158,7 +1193,7 @@ class BlockletManager extends BaseBlockletManager {
1158
1193
  // trigger dashboard frontend refresh
1159
1194
  this.emit(BlockletEvents.updated, blocklet);
1160
1195
 
1161
- return this.ensureBlocklet(rootDid);
1196
+ return this.getBlocklet(rootDid);
1162
1197
  }
1163
1198
 
1164
1199
  async updateComponentMountPoint({ did, rootDid: inputRootDid, mountPoint }, context) {
@@ -1194,7 +1229,7 @@ class BlockletManager extends BaseBlockletManager {
1194
1229
 
1195
1230
  this.emit(BlockletEvents.upgraded, { blocklet, context: { ...context, createAuditLog: false } }); // trigger router refresh
1196
1231
 
1197
- return this.ensureBlocklet(rootDid);
1232
+ return this.getBlocklet(rootDid);
1198
1233
  }
1199
1234
 
1200
1235
  /**
@@ -1429,6 +1464,9 @@ class BlockletManager extends BaseBlockletManager {
1429
1464
 
1430
1465
  this.emit(BlockletEvents.statusChange, newBlocklet);
1431
1466
 
1467
+ // backup rollback data
1468
+ await this._rollbackCache.backup({ did, action, oldBlocklet });
1469
+
1432
1470
  // add to queue
1433
1471
  const ticket = this.installQueue.push(
1434
1472
  {
@@ -1540,6 +1578,8 @@ class BlockletManager extends BaseBlockletManager {
1540
1578
 
1541
1579
  // Add Config
1542
1580
  await this._setConfigsFromMeta(did);
1581
+
1582
+ // should ensure blocklet integrity
1543
1583
  let blocklet = await this.ensureBlocklet(did);
1544
1584
 
1545
1585
  // pre install
@@ -1547,14 +1587,14 @@ class BlockletManager extends BaseBlockletManager {
1547
1587
 
1548
1588
  // Add environments
1549
1589
  await this.updateBlockletEnvironment(did);
1550
- blocklet = await this.ensureBlocklet(did);
1590
+ blocklet = await this.getBlocklet(did);
1551
1591
 
1552
1592
  // post install
1553
1593
  await this._runPostInstallHook(blocklet);
1554
1594
 
1555
1595
  await states.blocklet.setBlockletStatus(did, BlockletStatus.installed);
1556
1596
 
1557
- blocklet = await this.ensureBlocklet(did);
1597
+ blocklet = await this.getBlocklet(did);
1558
1598
 
1559
1599
  await fs.writeFile(path.join(blocklet.env.dataDir, 'logo.svg'), createDidLogo(blocklet.meta.did));
1560
1600
 
@@ -1607,6 +1647,8 @@ class BlockletManager extends BaseBlockletManager {
1607
1647
 
1608
1648
  // Add Config
1609
1649
  await this._setConfigsFromMeta(rootDid);
1650
+
1651
+ // should ensure blocklet integrity
1610
1652
  let blocklet = await this.ensureBlocklet(rootDid);
1611
1653
 
1612
1654
  // pre install
@@ -1614,14 +1656,14 @@ class BlockletManager extends BaseBlockletManager {
1614
1656
 
1615
1657
  // Add environments
1616
1658
  await this.updateBlockletEnvironment(rootDid);
1617
- blocklet = await this.ensureBlocklet(rootDid);
1659
+ blocklet = await this.getBlocklet(rootDid);
1618
1660
 
1619
1661
  // post install
1620
1662
  await this._runPostInstallHook(blocklet);
1621
1663
 
1622
1664
  logger.info('add blocklet component for dev', { did, version, meta });
1623
1665
 
1624
- blocklet = await this.ensureBlocklet(rootDid);
1666
+ blocklet = await this.getBlocklet(rootDid);
1625
1667
 
1626
1668
  return blocklet;
1627
1669
  }
@@ -1645,7 +1687,11 @@ class BlockletManager extends BaseBlockletManager {
1645
1687
  }
1646
1688
 
1647
1689
  async ensureBlocklet(did, opts = {}) {
1648
- return getBlocklet({ ...opts, states, dataDirs: this.dataDirs, did });
1690
+ return getBlocklet({ ...opts, states, dataDirs: this.dataDirs, did, ensureIntegrity: true });
1691
+ }
1692
+
1693
+ async getBlocklet(did, opts = {}) {
1694
+ return getBlocklet({ ...opts, states, dataDirs: this.dataDirs, did, ensureIntegrity: false });
1649
1695
  }
1650
1696
 
1651
1697
  async hasBlocklet({ did }) {
@@ -1663,7 +1709,7 @@ class BlockletManager extends BaseBlockletManager {
1663
1709
 
1664
1710
  this.emit(BlockletEvents.updated, { meta: { did: blocklet.meta.did } });
1665
1711
 
1666
- return this.ensureBlocklet(did);
1712
+ return this.getBlocklet(did);
1667
1713
  }
1668
1714
 
1669
1715
  async status(did, { forceSync = false } = {}) {
@@ -1680,7 +1726,7 @@ class BlockletManager extends BaseBlockletManager {
1680
1726
  return res;
1681
1727
  };
1682
1728
 
1683
- const blocklet = await this.ensureBlocklet(did);
1729
+ const blocklet = await this.getBlocklet(did);
1684
1730
 
1685
1731
  let shouldUpdateStatus = forceSync || shouldUpdateBlockletStatus(blocklet.status);
1686
1732
  if (isInProgress(blocklet.status)) {
@@ -1709,80 +1755,6 @@ class BlockletManager extends BaseBlockletManager {
1709
1755
  }
1710
1756
  }
1711
1757
 
1712
- async attachRuntimeInfo({ did, nodeInfo, diskInfo = true, context, cachedBlocklet }) {
1713
- if (!did) {
1714
- throw new Error('did should not be empty');
1715
- }
1716
-
1717
- try {
1718
- const blocklet = await this.ensureBlocklet(did, { throwOnNotExist: false });
1719
-
1720
- if (!blocklet) {
1721
- return null;
1722
- }
1723
-
1724
- const fromCache = !!cachedBlocklet;
1725
-
1726
- // if from cached data, only use cache data of runtime info (engine, diskInfo, runtimeInfo...)
1727
- if (fromCache) {
1728
- const cached = {};
1729
- forEachBlockletSync(cachedBlocklet, (component, { id }) => {
1730
- cached[id] = component;
1731
- });
1732
-
1733
- Object.assign(blocklet, pick(cachedBlocklet, ['appRuntimeInfo', 'diskInfo']));
1734
-
1735
- forEachBlockletSync(blocklet, (component, { id }) => {
1736
- if (cached[id]) {
1737
- Object.assign(component, pick(cached[id], ['runtimeInfo']));
1738
- }
1739
- });
1740
- }
1741
-
1742
- // 处理 domainAliases#value SLOT_FOR_IP_DNS_SITE
1743
- if (blocklet?.site?.domainAliases?.length) {
1744
- const nodeIp = await getAccessibleExternalNodeIp(nodeInfo);
1745
- blocklet.site.domainAliases = blocklet.site.domainAliases.map((x) => ({
1746
- ...x,
1747
- value: util.replaceDomainSlot({ domain: x.value, context, nodeIp }),
1748
- }));
1749
- }
1750
-
1751
- // app runtime info, app status
1752
- blocklet.appRuntimeInfo = this.runtimeMonitor.getRuntimeInfo(blocklet.meta.did);
1753
-
1754
- if (!fromCache) {
1755
- // app disk info, component runtime info, component status, component engine
1756
- await forEachBlocklet(blocklet, async (component, { level }) => {
1757
- component.engine = getEngine(getBlockletEngineNameByPlatform(component.meta)).describe();
1758
-
1759
- if (level === 0) {
1760
- component.diskInfo = await getDiskInfo(component, {
1761
- useFakeDiskInfo: !diskInfo,
1762
- });
1763
- }
1764
-
1765
- component.runtimeInfo = this.runtimeMonitor.getRuntimeInfo(blocklet.meta.did, component.env.id);
1766
-
1767
- if (component.runtimeInfo?.status && shouldUpdateBlockletStatus(component.status)) {
1768
- component.status = statusMap[component.runtimeInfo.status];
1769
- }
1770
- });
1771
- }
1772
-
1773
- return blocklet;
1774
- } catch (err) {
1775
- const simpleState = await states.blocklet.getBlocklet(did);
1776
- logger.error('failed to get blocklet info', {
1777
- did,
1778
- name: get(simpleState, 'meta.name'),
1779
- status: get(simpleState, 'status'),
1780
- error: err,
1781
- });
1782
- return simpleState;
1783
- }
1784
- }
1785
-
1786
1758
  async refreshListCache() {
1787
1759
  this.list({ useCache: false }).catch((err) => {
1788
1760
  logger.error('refresh blocklet list failed', { error: err });
@@ -1792,7 +1764,7 @@ class BlockletManager extends BaseBlockletManager {
1792
1764
  async onJob(job) {
1793
1765
  if (job.entity === 'blocklet') {
1794
1766
  if (job.action === 'download') {
1795
- await this.onDownload(job);
1767
+ await this.downloadAndInstall(job);
1796
1768
  }
1797
1769
  if (job.action === 'restart') {
1798
1770
  await this.onRestart(job);
@@ -1817,33 +1789,35 @@ class BlockletManager extends BaseBlockletManager {
1817
1789
  * @return {*}
1818
1790
  * @memberof BlockletManager
1819
1791
  */
1820
- async onDownload(params) {
1821
- const { blocklet, context, postAction, oldBlocklet, throwOnError } = params;
1792
+ async downloadAndInstall(params) {
1793
+ const { blocklet, context, postAction, oldBlocklet, throwOnError, skipCheckStatusBeforeDownload } = params;
1822
1794
  const { meta } = blocklet;
1823
1795
  const { name, did, version } = meta;
1824
1796
 
1825
1797
  // check status
1826
- try {
1827
- await statusLock.acquire();
1828
-
1829
- const b0 = await states.blocklet.getBlocklet(did);
1830
- if (!b0 || ![BlockletStatus.waiting].includes(b0.status)) {
1831
- if (!b0) {
1832
- throw new Error('blocklet does not exist before downloading');
1833
- } else {
1834
- throw new Error(`blocklet status is invalid before downloading: ${fromBlockletStatus(b0.status)}`);
1798
+ if (!skipCheckStatusBeforeDownload) {
1799
+ try {
1800
+ await statusLock.acquire();
1801
+
1802
+ const b0 = await states.blocklet.getBlocklet(did);
1803
+ if (!b0 || ![BlockletStatus.waiting].includes(b0.status)) {
1804
+ if (!b0) {
1805
+ throw new Error('blocklet does not exist before downloading');
1806
+ } else {
1807
+ throw new Error(`blocklet status is invalid before downloading: ${fromBlockletStatus(b0.status)}`);
1808
+ }
1835
1809
  }
1810
+ statusLock.release();
1811
+ } catch (error) {
1812
+ statusLock.release();
1813
+ logger.error('Check blocklet status failed before downloading', {
1814
+ name,
1815
+ did,
1816
+ error,
1817
+ });
1818
+ await this._rollback(postAction, did, oldBlocklet);
1819
+ return;
1836
1820
  }
1837
- statusLock.release();
1838
- } catch (error) {
1839
- statusLock.release();
1840
- logger.error('Check blocklet status failed before downloading', {
1841
- name,
1842
- did,
1843
- error,
1844
- });
1845
- await this._rollback(postAction, did, oldBlocklet);
1846
- return;
1847
1821
  }
1848
1822
 
1849
1823
  // download bundle
@@ -1853,8 +1827,16 @@ class BlockletManager extends BaseBlockletManager {
1853
1827
  if (isCancelled) {
1854
1828
  logger.info('Download was canceled', { name, did, version });
1855
1829
 
1856
- if ((await states.blocklet.getBlockletStatus(did)) === BlockletStatus.downloading) {
1857
- await this._rollback(postAction, did, oldBlocklet);
1830
+ // rollback on download cancelled
1831
+ await statusLock.acquire();
1832
+ try {
1833
+ if ((await states.blocklet.getBlockletStatus(did)) === BlockletStatus.downloading) {
1834
+ await this._rollback(postAction, did, oldBlocklet);
1835
+ }
1836
+ statusLock.release();
1837
+ } catch (error) {
1838
+ statusLock.release();
1839
+ logger.error('Rollback blocklet failed on download canceled', { postAction, name, did, version, error });
1858
1840
  }
1859
1841
  return;
1860
1842
  }
@@ -1875,10 +1857,16 @@ class BlockletManager extends BaseBlockletManager {
1875
1857
  severity: 'error',
1876
1858
  });
1877
1859
 
1860
+ // rollback on download failed
1861
+ await statusLock.acquire();
1878
1862
  try {
1879
- await this._rollback(postAction, did, oldBlocklet);
1880
- } catch (e) {
1881
- logger.error('Rollback blocklet failed', { postAction, name, did, version });
1863
+ if ((await states.blocklet.getBlockletStatus(did)) === BlockletStatus.downloading) {
1864
+ await this._rollback(postAction, did, oldBlocklet);
1865
+ }
1866
+ statusLock.release();
1867
+ } catch (error) {
1868
+ statusLock.release();
1869
+ logger.error('Rollback blocklet failed on download failed', { postAction, name, did, version, error });
1882
1870
  }
1883
1871
 
1884
1872
  if (throwOnError) {
@@ -1981,7 +1969,7 @@ class BlockletManager extends BaseBlockletManager {
1981
1969
 
1982
1970
  async onCheckIfStarted(jobInfo, { throwOnError } = {}) {
1983
1971
  const { did, context, minConsecutiveTime = 5000, timeout } = jobInfo;
1984
- const blocklet = await this.ensureBlocklet(did);
1972
+ const blocklet = await this.getBlocklet(did);
1985
1973
 
1986
1974
  const { meta } = blocklet;
1987
1975
  const { name } = meta;
@@ -2022,7 +2010,7 @@ class BlockletManager extends BaseBlockletManager {
2022
2010
  }
2023
2011
 
2024
2012
  async updateBlockletEnvironment(did) {
2025
- const blockletWithEnv = await this.ensureBlocklet(did);
2013
+ const blockletWithEnv = await this.getBlocklet(did);
2026
2014
  const blocklet = await states.blocklet.getBlocklet(did);
2027
2015
  const nodeInfo = await states.node.read();
2028
2016
 
@@ -2406,13 +2394,20 @@ class BlockletManager extends BaseBlockletManager {
2406
2394
  newBlocklet.deployedFrom = `Upload by ${context.user.fullName}`;
2407
2395
  newBlocklet.children = await this._getChildrenForInstallation(meta);
2408
2396
  await validateBlocklet(newBlocklet);
2409
- await this._downloadBlocklet(newBlocklet);
2410
2397
 
2411
- return this._upgradeBlocklet({
2398
+ // backup rollback data
2399
+ const action = 'upgrade';
2400
+ await this._rollbackCache.backup({ did: newBlocklet.meta.did, action, oldBlocklet });
2401
+
2402
+ await this.downloadAndInstall({
2403
+ blocklet: newBlocklet,
2412
2404
  oldBlocklet,
2413
- newBlocklet,
2414
2405
  context: { ...context, forceStartProcessIds: [getComponentProcessId(newBlocklet)] },
2406
+ throwOnError: true,
2407
+ postAction: action,
2408
+ skipCheckStatusBeforeDownload: true,
2415
2409
  });
2410
+ return this.getBlocklet(newBlocklet.meta.did);
2416
2411
  }
2417
2412
 
2418
2413
  // full deploy
@@ -2432,14 +2427,20 @@ class BlockletManager extends BaseBlockletManager {
2432
2427
  newBlocklet.deployedFrom = `Upload by ${context.user.fullName}`;
2433
2428
  newBlocklet.children = await this._getChildrenForInstallation(meta);
2434
2429
 
2435
- await validateBlocklet(newBlocklet);
2436
- await this._downloadBlocklet(newBlocklet);
2430
+ // backup rollback data
2431
+ const action = 'upgrade';
2432
+ await this._rollbackCache.backup({ did: newBlocklet.meta.did, action, oldBlocklet });
2437
2433
 
2438
- return this._upgradeBlocklet({
2434
+ await validateBlocklet(newBlocklet);
2435
+ await this.downloadAndInstall({
2436
+ blocklet: newBlocklet,
2439
2437
  oldBlocklet,
2440
- newBlocklet,
2441
2438
  context: { ...context, forceStartProcessIds: [getComponentProcessId(newBlocklet)] },
2439
+ throwOnError: true,
2440
+ postAction: action,
2441
+ skipCheckStatusBeforeDownload: true,
2442
2442
  });
2443
+ return this.getBlocklet(newBlocklet.meta.did);
2443
2444
  }
2444
2445
 
2445
2446
  // full deploy - install
@@ -2453,25 +2454,31 @@ class BlockletManager extends BaseBlockletManager {
2453
2454
  children,
2454
2455
  });
2455
2456
 
2457
+ const action = 'install';
2458
+ const oldState = { extraState: oldExtraState };
2456
2459
  try {
2457
2460
  await this._setConfigsFromMeta(meta.did);
2458
2461
  await validateBlocklet(blocklet);
2459
2462
 
2460
2463
  // check duplicate appSk
2461
2464
  await checkDuplicateAppSk({ did: meta.did, states });
2462
-
2463
- // download
2464
- await this._downloadBlocklet(blocklet);
2465
-
2466
- return this._installBlocklet({
2467
- did: meta.did,
2468
- context,
2469
- oldBlocklet: { extraState: oldExtraState },
2470
- });
2471
2465
  } catch (error) {
2472
- await this._rollback('install', meta.did, { extraState: oldExtraState });
2466
+ await this._rollback(action, meta.did, oldState);
2473
2467
  throw error;
2474
2468
  }
2469
+
2470
+ // backup rollback data
2471
+ await this._rollbackCache.backup({ did: meta.did, action, oldBlocklet: oldState });
2472
+
2473
+ await this.downloadAndInstall({
2474
+ blocklet,
2475
+ oldBlocklet: oldState,
2476
+ context,
2477
+ throwOnError: true,
2478
+ postAction: action,
2479
+ skipCheckStatusBeforeDownload: true,
2480
+ });
2481
+ return this.getBlocklet(meta.did);
2475
2482
  }
2476
2483
 
2477
2484
  async _installComponentFromUpload({
@@ -2552,34 +2559,19 @@ class BlockletManager extends BaseBlockletManager {
2552
2559
 
2553
2560
  await this._upsertDynamicNavigation(newBlocklet.meta.did, newChild, { skipNavigation });
2554
2561
 
2555
- await this._downloadBlocklet(newBlocklet);
2556
-
2557
- await validateBlocklet(newBlocklet);
2562
+ // backup rollback data
2563
+ const action = 'upgrade';
2564
+ await this._rollbackCache.backup({ did: newBlocklet.meta.did, action, oldBlocklet });
2558
2565
 
2559
- return this._upgradeBlocklet({
2566
+ await this.downloadAndInstall({
2567
+ blocklet: newBlocklet,
2560
2568
  oldBlocklet,
2561
- newBlocklet,
2562
2569
  context: { ...context, forceStartProcessIds: [getComponentProcessId(newChild, [newBlocklet])] },
2570
+ throwOnError: true,
2571
+ postAction: action,
2572
+ skipCheckStatusBeforeDownload: true,
2563
2573
  });
2564
- }
2565
-
2566
- /**
2567
- * add to download job queue
2568
- * @param {string} did blocklet did
2569
- * @param {object} blocklet object
2570
- */
2571
- async download(did, blocklet) {
2572
- await states.blocklet.setBlockletStatus(did, BlockletStatus.waiting);
2573
- this.installQueue.push(
2574
- {
2575
- entity: 'blocklet',
2576
- action: 'download',
2577
- id: did,
2578
- blocklet: { ...blocklet },
2579
- postAction: 'install',
2580
- },
2581
- did
2582
- );
2574
+ return this.getBlocklet(newBlocklet.meta.did);
2583
2575
  }
2584
2576
 
2585
2577
  async prune() {
@@ -2650,6 +2642,80 @@ class BlockletManager extends BaseBlockletManager {
2650
2642
  ];
2651
2643
  }
2652
2644
 
2645
+ async _attachRuntimeInfo({ did, nodeInfo, diskInfo = true, context, cachedBlocklet }) {
2646
+ if (!did) {
2647
+ throw new Error('did should not be empty');
2648
+ }
2649
+
2650
+ try {
2651
+ const blocklet = await this.getBlocklet(did, { throwOnNotExist: false });
2652
+
2653
+ if (!blocklet) {
2654
+ return null;
2655
+ }
2656
+
2657
+ const fromCache = !!cachedBlocklet;
2658
+
2659
+ // if from cached data, only use cache data of runtime info (engine, diskInfo, runtimeInfo...)
2660
+ if (fromCache) {
2661
+ const cached = {};
2662
+ forEachBlockletSync(cachedBlocklet, (component, { id }) => {
2663
+ cached[id] = component;
2664
+ });
2665
+
2666
+ Object.assign(blocklet, pick(cachedBlocklet, ['appRuntimeInfo', 'diskInfo']));
2667
+
2668
+ forEachBlockletSync(blocklet, (component, { id }) => {
2669
+ if (cached[id]) {
2670
+ Object.assign(component, pick(cached[id], ['runtimeInfo']));
2671
+ }
2672
+ });
2673
+ }
2674
+
2675
+ // 处理 domainAliases#value SLOT_FOR_IP_DNS_SITE
2676
+ if (blocklet?.site?.domainAliases?.length) {
2677
+ const nodeIp = await getAccessibleExternalNodeIp(nodeInfo);
2678
+ blocklet.site.domainAliases = blocklet.site.domainAliases.map((x) => ({
2679
+ ...x,
2680
+ value: util.replaceDomainSlot({ domain: x.value, context, nodeIp }),
2681
+ }));
2682
+ }
2683
+
2684
+ // app runtime info, app status
2685
+ blocklet.appRuntimeInfo = this.runtimeMonitor.getRuntimeInfo(blocklet.meta.did);
2686
+
2687
+ if (!fromCache) {
2688
+ // app disk info, component runtime info, component status, component engine
2689
+ await forEachBlocklet(blocklet, async (component, { level }) => {
2690
+ component.engine = getEngine(getBlockletEngineNameByPlatform(component.meta)).describe();
2691
+
2692
+ if (level === 0) {
2693
+ component.diskInfo = await getDiskInfo(component, {
2694
+ useFakeDiskInfo: !diskInfo,
2695
+ });
2696
+ }
2697
+
2698
+ component.runtimeInfo = this.runtimeMonitor.getRuntimeInfo(blocklet.meta.did, component.env.id);
2699
+
2700
+ if (component.runtimeInfo?.status && shouldUpdateBlockletStatus(component.status)) {
2701
+ component.status = statusMap[component.runtimeInfo.status];
2702
+ }
2703
+ });
2704
+ }
2705
+
2706
+ return blocklet;
2707
+ } catch (err) {
2708
+ const simpleState = await states.blocklet.getBlocklet(did);
2709
+ logger.error('failed to get blocklet info', {
2710
+ did,
2711
+ name: get(simpleState, 'meta.name'),
2712
+ status: get(simpleState, 'status'),
2713
+ error: err,
2714
+ });
2715
+ return simpleState;
2716
+ }
2717
+ }
2718
+
2653
2719
  async _syncBlockletStatus() {
2654
2720
  const run = async (blocklet) => {
2655
2721
  try {
@@ -2728,6 +2794,11 @@ class BlockletManager extends BaseBlockletManager {
2728
2794
  const blocklet1 = await states.blocklet.setBlockletStatus(did, BlockletStatus.waiting);
2729
2795
  this.emit(BlockletEvents.added, blocklet1);
2730
2796
 
2797
+ const action = 'install';
2798
+ const oldBlocklet = {
2799
+ extraState: oldExtraState,
2800
+ };
2801
+
2731
2802
  /** @type {{
2732
2803
  * blocklet: any;
2733
2804
  * oldBlocklet: {
@@ -2739,16 +2810,16 @@ class BlockletManager extends BaseBlockletManager {
2739
2810
  * }} */
2740
2811
  const downloadParams = {
2741
2812
  blocklet: { ...blocklet1 },
2742
- oldBlocklet: {
2743
- children: children.filter((x) => x.dynamic), // let downloader skip re-downloading dynamic blocklet
2744
- extraState: oldExtraState,
2745
- },
2746
2813
  context,
2747
- postAction: 'install',
2814
+ postAction: action,
2815
+ oldBlocklet,
2748
2816
  };
2749
2817
 
2818
+ // backup rollback data
2819
+ await this._rollbackCache.backup({ did, action, oldBlocklet });
2820
+
2750
2821
  if (sync) {
2751
- await this.onDownload({ ...downloadParams, throwOnError: true });
2822
+ await this.downloadAndInstall({ ...downloadParams, throwOnError: true });
2752
2823
  return states.blocklet.getBlocklet(did);
2753
2824
  }
2754
2825
 
@@ -2859,8 +2930,11 @@ class BlockletManager extends BaseBlockletManager {
2859
2930
  postAction: action,
2860
2931
  };
2861
2932
 
2933
+ // backup rollback data
2934
+ await this._rollbackCache.backup({ did, action, oldBlocklet });
2935
+
2862
2936
  if (sync) {
2863
- await this.onDownload({ ...downloadParams, throwOnError: true });
2937
+ await this.downloadAndInstall({ ...downloadParams, throwOnError: true });
2864
2938
  return states.blocklet.getBlocklet(did);
2865
2939
  }
2866
2940
  const ticket = this.installQueue.push(
@@ -2898,6 +2972,7 @@ class BlockletManager extends BaseBlockletManager {
2898
2972
  */
2899
2973
  async _installBlocklet({ did, oldBlocklet, context }) {
2900
2974
  try {
2975
+ // should ensure blocklet integrity
2901
2976
  let blocklet = await this.ensureBlocklet(did);
2902
2977
  const { meta, source, deployedFrom } = blocklet;
2903
2978
 
@@ -2914,13 +2989,13 @@ class BlockletManager extends BaseBlockletManager {
2914
2989
 
2915
2990
  // Add environments
2916
2991
  await this.updateBlockletEnvironment(meta.did);
2917
- blocklet = await this.ensureBlocklet(did);
2992
+ blocklet = await this.getBlocklet(did);
2918
2993
 
2919
2994
  // post install
2920
2995
  await this._runPostInstallHook(blocklet, context);
2921
2996
 
2922
2997
  await states.blocklet.setBlockletStatus(did, BlockletStatus.installed);
2923
- blocklet = await this.ensureBlocklet(did);
2998
+ blocklet = await this.getBlocklet(did);
2924
2999
  logger.info('blocklet installed', { source, did: meta.did });
2925
3000
 
2926
3001
  await fs.writeFile(path.join(blocklet.env.dataDir, 'logo.svg'), createDidLogo(blocklet.meta.did));
@@ -2956,6 +3031,8 @@ class BlockletManager extends BaseBlockletManager {
2956
3031
  entityId: did,
2957
3032
  severity: 'success',
2958
3033
  });
3034
+
3035
+ await this._rollbackCache.remove({ did: blocklet.meta.did });
2959
3036
  return blocklet;
2960
3037
  } catch (err) {
2961
3038
  const { meta } = await states.blocklet.getBlocklet(did);
@@ -3005,6 +3082,7 @@ class BlockletManager extends BaseBlockletManager {
3005
3082
  await states.blocklet.upgradeBlocklet({ meta, source, deployedFrom, children });
3006
3083
  await this._setConfigsFromMeta(did);
3007
3084
 
3085
+ // should ensure blocklet integrity
3008
3086
  let blocklet = await this.ensureBlocklet(did);
3009
3087
 
3010
3088
  // pre install
@@ -3012,7 +3090,7 @@ class BlockletManager extends BaseBlockletManager {
3012
3090
 
3013
3091
  // Add environments
3014
3092
  await this.updateBlockletEnvironment(did);
3015
- blocklet = await this.ensureBlocklet(did);
3093
+ blocklet = await this.getBlocklet(did);
3016
3094
 
3017
3095
  // post install
3018
3096
  await this._runPostInstallHook(blocklet, context);
@@ -3052,7 +3130,7 @@ class BlockletManager extends BaseBlockletManager {
3052
3130
  logger.info('started blocklet for upgrading', { did, version });
3053
3131
  }
3054
3132
 
3055
- blocklet = await this.ensureBlocklet(did, context);
3133
+ blocklet = await this.getBlocklet(did, context);
3056
3134
 
3057
3135
  await fs.writeFile(path.join(blocklet.env.dataDir, 'logo.svg'), createDidLogo(blocklet.meta.did));
3058
3136
 
@@ -3081,6 +3159,8 @@ class BlockletManager extends BaseBlockletManager {
3081
3159
  // Update dynamic component meta in blocklet settings
3082
3160
  await this._ensureDynamicChildrenInSettings(blocklet);
3083
3161
 
3162
+ await this._rollbackCache.remove({ did: blocklet.meta.did });
3163
+
3084
3164
  return blocklet;
3085
3165
  } catch (err) {
3086
3166
  const b = await this._rollback(action, did, oldBlocklet);
@@ -3144,9 +3224,11 @@ class BlockletManager extends BaseBlockletManager {
3144
3224
  });
3145
3225
  this.emit(BlockletEvents.statusChange, blocklet1);
3146
3226
  },
3147
- postDownload: async () => {
3148
- // since preferences only exist in blocklet bundle, we need to populate then after downloaded
3149
- await this._setConfigsFromMeta(did);
3227
+ postDownload: async ({ isCancelled }) => {
3228
+ if (!isCancelled) {
3229
+ // since preferences only exist in blocklet bundle, we need to populate then after downloaded
3230
+ await this._setConfigsFromMeta(did);
3231
+ }
3150
3232
  },
3151
3233
  });
3152
3234
  }
@@ -3164,12 +3246,6 @@ class BlockletManager extends BaseBlockletManager {
3164
3246
  }
3165
3247
  }
3166
3248
 
3167
- // eslint-disable-next-line no-unused-vars
3168
- async _cancelWaiting(blockletMeta, context) {
3169
- const { did } = blockletMeta;
3170
- return this.installQueue.cancel(did);
3171
- }
3172
-
3173
3249
  /**
3174
3250
  * @param {string} action install, upgrade, downgrade
3175
3251
  * @param {string} did
@@ -3283,7 +3359,7 @@ class BlockletManager extends BaseBlockletManager {
3283
3359
  }
3284
3360
 
3285
3361
  async _setConfigsFromMeta(did, childDid) {
3286
- const blocklet = await getBlocklet({ states, dataDirs: this.dataDirs, did, validateEnv: false, ensureDirs: false });
3362
+ const blocklet = await getBlocklet({ states, dataDirs: this.dataDirs, did });
3287
3363
 
3288
3364
  if (!childDid) {
3289
3365
  await forEachBlocklet(blocklet, async (b, { ancestors }) => {
@@ -3337,7 +3413,7 @@ class BlockletManager extends BaseBlockletManager {
3337
3413
  }
3338
3414
 
3339
3415
  async _getBlockletForInstallation(did) {
3340
- const blocklet = await states.blocklet.getBlocklet(did);
3416
+ const blocklet = await states.blocklet.getBlocklet(did, { decryptSk: false });
3341
3417
  if (!blocklet) {
3342
3418
  return null;
3343
3419
  }
@@ -3563,7 +3639,7 @@ class BlockletManager extends BaseBlockletManager {
3563
3639
 
3564
3640
  this.emit(BlockletEvents.appDidChanged, blocklet);
3565
3641
 
3566
- const blockletWithEnv = await this.ensureBlocklet(blocklet.meta.did);
3642
+ const blockletWithEnv = await this.getBlocklet(blocklet.meta.did);
3567
3643
  const appSystemEnvironments = getAppOverwrittenEnvironments(blockletWithEnv, nodeInfo);
3568
3644
 
3569
3645
  await didDocument.updateBlockletDocument({