@abtnode/core 1.7.14 → 1.7.15

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.
@@ -491,12 +491,12 @@ class BlockletManager extends BaseBlockletManager {
491
491
  }
492
492
  }
493
493
 
494
- async reset({ did }, context = {}) {
495
- logger.info('reset blocklet', { did });
494
+ async reset({ did, childDid }, context = {}) {
495
+ logger.info('reset blocklet', { did, childDid });
496
496
 
497
497
  const blocklet = await this.ensureBlocklet(did);
498
498
 
499
- if (isInProgress(blocklet.status)) {
499
+ if (isInProgress(blocklet.status || blocklet.status === BlockletStatus.running)) {
500
500
  throw new Error('Cannot reset when blocklet is in progress');
501
501
  }
502
502
 
@@ -506,27 +506,43 @@ class BlockletManager extends BaseBlockletManager {
506
506
  // do nothing
507
507
  }
508
508
 
509
- // Cleanup disk storage
510
- const { name } = blocklet.meta;
511
- const dataDir = path.join(this.dataDirs.data, name);
512
- const logsDir = path.join(this.dataDirs.logs, name);
513
- const cacheDir = path.join(this.dataDirs.cache, name);
514
- fs.removeSync(cacheDir);
515
- fs.removeSync(dataDir);
516
- fs.removeSync(logsDir);
509
+ if (!childDid) {
510
+ // Cleanup disk storage
511
+ const { cacheDir, logsDir, dataDir } = blocklet.env;
512
+ fs.removeSync(cacheDir);
513
+ fs.removeSync(dataDir);
514
+ fs.removeSync(logsDir);
517
515
 
518
- // Reset config in db
519
- await this._delExtras(did);
520
- await this._setConfigs(did);
521
- await this.updateBlockletEnvironment(did);
522
- await this.resetSiteByDid(did, context);
516
+ // Reset config in db
517
+ await states.blockletExtras.remove({ did: blocklet.meta.did });
518
+ await this._setConfigs(did);
519
+ await this.updateBlockletEnvironment(did);
520
+ await this.resetSiteByDid(did, context);
521
+ } else {
522
+ const child = blocklet.children.find((x) => x.meta.did === childDid);
523
523
 
524
- logger.info('blocklet reset', { did });
524
+ if (!child) {
525
+ throw new Error('Child does not exist');
526
+ }
527
+
528
+ // Cleanup disk storage
529
+ const { cacheDir, logsDir, dataDir } = child.env;
530
+ fs.removeSync(cacheDir);
531
+ fs.removeSync(dataDir);
532
+ fs.removeSync(logsDir);
533
+
534
+ // Reset config in db
535
+ await states.blockletExtras.delChildConfigs(blocklet.meta.did, child.meta.did);
536
+ await this._setConfigs(blocklet.meta.did, child.meta.did);
537
+ await this.updateBlockletEnvironment(did);
538
+ }
539
+
540
+ logger.info('blocklet reset', { did, childDid });
525
541
  return blocklet;
526
542
  }
527
543
 
528
- async deleteComponent({ did, rootDid }, context) {
529
- logger.info('delete blocklet component', { did, rootDid });
544
+ async deleteComponent({ did, rootDid, keepData, keepState }, context) {
545
+ logger.info('delete blocklet component', { did, rootDid, keepData });
530
546
 
531
547
  const blocklet = await this.ensureBlocklet(rootDid);
532
548
  const doc = await states.blocklet.getBlocklet(rootDid);
@@ -538,22 +554,36 @@ class BlockletManager extends BaseBlockletManager {
538
554
  }
539
555
 
540
556
  doc.children = doc.children.filter((x) => x.meta.did !== did);
541
- const children = (await this._getDynamicChildrenFromSettings(rootDid)).filter((x) => x.meta.did !== did);
542
- children.push({
543
- meta: pick(child.meta, ['did', 'name', 'bundleDid', 'bundleName', 'version', 'title', 'description']),
544
- mountPoint: child.mountPoint,
545
- status: BlockletStatus.deleted,
546
- deletedAt: new Date(),
547
- });
557
+ const children = (await this._getDynamicChildrenFromSettings(blocklet.meta.did)).filter((x) => x.meta.did !== did);
558
+ if (keepState !== false) {
559
+ children.push({
560
+ meta: pick(child.meta, ['did', 'name', 'bundleDid', 'bundleName', 'version', 'title', 'description']),
561
+ mountPoint: child.mountPoint,
562
+ status: BlockletStatus.deleted,
563
+ deletedAt: new Date(),
564
+ });
565
+ }
548
566
 
549
567
  await states.blocklet.updateBlocklet(rootDid, doc);
550
- states.blockletExtras.setSettings(rootDid, { children });
568
+ states.blockletExtras.setSettings(blocklet.meta.did, { children });
569
+
570
+ // delete navigation
571
+ const navigation = await states.blockletExtras.getSettings(blocklet.meta.did, 'navigation', []);
572
+ await states.blockletExtras.setSettings(
573
+ blocklet.meta.did,
574
+ 'navigation',
575
+ navigation.filter((x) => x.child !== blocklet.meta.name)
576
+ );
551
577
 
552
578
  // delete storage
553
579
  const childBlocklet = blocklet.children.find((x) => x.meta.did === did);
554
- const { cacheDir, logsDir } = childBlocklet.env;
580
+ const { cacheDir, logsDir, dataDir } = childBlocklet.env;
555
581
  fs.removeSync(cacheDir);
556
582
  fs.removeSync(logsDir);
583
+ if (keepData === false) {
584
+ fs.removeSync(dataDir);
585
+ await states.blockletExtras.delChildConfigs(blocklet.meta.did, child.meta.did);
586
+ }
557
587
 
558
588
  const newBlocklet = await this.ensureBlocklet(rootDid);
559
589
  this.emit(BlockletEvents.upgraded, { blocklet: newBlocklet, context }); // trigger router refresh
@@ -887,13 +917,13 @@ class BlockletManager extends BaseBlockletManager {
887
917
  const blocklet = await states.blocklet.getBlocklet(did);
888
918
  const newStaticChildren = await parseChildrenFromMeta(blocklet.meta);
889
919
 
890
- const oldDynamicChildren = await this._getDynamicChildrenFromSettings(did, { skipDeleted: true });
891
- const noneSourceUrlChildren = oldDynamicChildren.filter((x) => !x.sourceUrl);
920
+ const oldDynamicChildren = await this._getDynamicChildrenFromSettings(blocklet.meta.did, { skipDeleted: true });
921
+ const noneBundleSourceChildren = oldDynamicChildren.filter((x) => !x.bundleSource);
892
922
  const dynamicConfig = oldDynamicChildren
893
- .filter((x) => x.sourceUrl)
894
- .map((x) => ({ resolved: x.sourceUrl, name: x.meta.name, mountPoint: x.mountPoint }));
923
+ .filter((x) => x.bundleSource)
924
+ .map((x) => ({ source: x.bundleSource, name: x.meta.name, title: x.meta.title, mountPoint: x.mountPoint }));
895
925
  const newDynamicChildren = [
896
- ...noneSourceUrlChildren,
926
+ ...noneBundleSourceChildren,
897
927
  ...(await parseChildrenFromMeta(dynamicConfig, { dynamic: true })),
898
928
  ];
899
929
 
@@ -934,7 +964,7 @@ class BlockletManager extends BaseBlockletManager {
934
964
  did = sessionData.did;
935
965
  const { staticChildren = [], dynamicChildren = [] } = sessionData;
936
966
  children = [...staticChildren, ...dynamicChildren.map((x) => ({ ...x, dynamic: true }))];
937
- oldBlocklet = await states.blocklet.getBlocklet(did);
967
+ oldBlocklet = await this._getBlockletForInstallation(did);
938
968
  }
939
969
 
940
970
  // get old blocklet
@@ -1041,6 +1071,7 @@ class BlockletManager extends BaseBlockletManager {
1041
1071
  }
1042
1072
 
1043
1073
  const children = await this._getChildren(meta);
1074
+ await validateBlocklet({ meta, children });
1044
1075
  const added = await states.blocklet.addBlocklet({
1045
1076
  meta,
1046
1077
  source: BlockletSource.local,
@@ -1101,7 +1132,9 @@ class BlockletManager extends BaseBlockletManager {
1101
1132
  dynamic: true,
1102
1133
  },
1103
1134
  ]);
1135
+ await validateBlocklet(children[0]);
1104
1136
  await states.blocklet.addChildren(rootDid, children);
1137
+ await this._upsertDynamicNavigation(existRoot.meta.did, children[0]);
1105
1138
 
1106
1139
  logger.info('add blocklet component for dev', { did, version, meta });
1107
1140
 
@@ -1395,7 +1428,7 @@ class BlockletManager extends BaseBlockletManager {
1395
1428
  try {
1396
1429
  // install blocklet
1397
1430
  if (postAction === 'install') {
1398
- await this.onInstall({ blocklet, context });
1431
+ await this.onInstall({ blocklet, context, oldBlocklet });
1399
1432
  return;
1400
1433
  }
1401
1434
 
@@ -1410,7 +1443,7 @@ class BlockletManager extends BaseBlockletManager {
1410
1443
  }
1411
1444
  }
1412
1445
 
1413
- async onInstall({ blocklet, context }) {
1446
+ async onInstall({ blocklet, context, oldBlocklet }) {
1414
1447
  const { meta } = blocklet;
1415
1448
  const { did, version } = meta;
1416
1449
  logger.info('do install blocklet', { did, version });
@@ -1422,6 +1455,7 @@ class BlockletManager extends BaseBlockletManager {
1422
1455
  const installedBlocklet = await this._installBlocklet({
1423
1456
  did,
1424
1457
  context,
1458
+ oldBlocklet,
1425
1459
  });
1426
1460
 
1427
1461
  if (context.startImmediately) {
@@ -1612,7 +1646,7 @@ class BlockletManager extends BaseBlockletManager {
1612
1646
  }
1613
1647
 
1614
1648
  async _installComponentFromUrl({ rootDid, mountPoint, url, context, title, name: inputName, did: inputDid }) {
1615
- const blocklet = await states.blocklet.getBlocklet(rootDid);
1649
+ const blocklet = await this._getBlockletForInstallation(rootDid);
1616
1650
  if (!blocklet) {
1617
1651
  throw new Error('Root blocklet does not exist');
1618
1652
  }
@@ -1638,7 +1672,7 @@ class BlockletManager extends BaseBlockletManager {
1638
1672
  if (!inputName && !inputDid) {
1639
1673
  const found = findAvailableDid(meta, [
1640
1674
  ...blocklet.children.map((x) => x.meta),
1641
- ...(await states.blockletExtras.getSettings(rootDid, 'children', [])).map((x) => x.meta),
1675
+ ...(await states.blockletExtras.getSettings(blocklet.meta.did, 'children', [])).map((x) => x.meta),
1642
1676
  ]);
1643
1677
  name = found.name;
1644
1678
  did = found.did;
@@ -1648,7 +1682,7 @@ class BlockletManager extends BaseBlockletManager {
1648
1682
  {
1649
1683
  meta: ensureMeta(meta, { did, name }),
1650
1684
  mountPoint,
1651
- sourceUrl: url,
1685
+ bundleSource: { url },
1652
1686
  dynamic: true,
1653
1687
  },
1654
1688
  ]);
@@ -1657,6 +1691,7 @@ class BlockletManager extends BaseBlockletManager {
1657
1691
 
1658
1692
  // add component to db
1659
1693
  await states.blocklet.addChildren(rootDid, newChildren);
1694
+ await this._upsertDynamicNavigation(blocklet.meta.did, newChildren[0]);
1660
1695
 
1661
1696
  return this.updateChildren(
1662
1697
  {
@@ -1743,7 +1778,7 @@ class BlockletManager extends BaseBlockletManager {
1743
1778
 
1744
1779
  // diff deploy
1745
1780
  if (did && diffVersion) {
1746
- const oldBlocklet = await states.blocklet.getBlocklet(did);
1781
+ const oldBlocklet = await this._getBlockletForInstallation(did);
1747
1782
  if (!oldBlocklet) {
1748
1783
  throw new Error(`Blocklet ${did} not found when diff deploying`);
1749
1784
  }
@@ -1779,7 +1814,7 @@ class BlockletManager extends BaseBlockletManager {
1779
1814
 
1780
1815
  // full deploy
1781
1816
  const { meta } = await this._resolveDownload(cwd, tarFile);
1782
- const oldBlocklet = await states.blocklet.getBlocklet(meta.did);
1817
+ const oldBlocklet = await this._getBlockletForInstallation(meta.did);
1783
1818
 
1784
1819
  // full deploy - upgrade
1785
1820
  if (oldBlocklet) {
@@ -1805,6 +1840,7 @@ class BlockletManager extends BaseBlockletManager {
1805
1840
  }
1806
1841
 
1807
1842
  // full deploy - install
1843
+ const oldExtraState = await states.blockletExtras.findOne({ did: meta.did });
1808
1844
  const children = await this._getChildren(meta);
1809
1845
  const blocklet = await states.blocklet.addBlocklet({
1810
1846
  meta,
@@ -1826,6 +1862,7 @@ class BlockletManager extends BaseBlockletManager {
1826
1862
  return this._installBlocklet({
1827
1863
  did: meta.did,
1828
1864
  context,
1865
+ oldBlocklet: { extraState: oldExtraState },
1829
1866
  });
1830
1867
  }
1831
1868
 
@@ -1834,7 +1871,7 @@ class BlockletManager extends BaseBlockletManager {
1834
1871
  // download
1835
1872
  const { cwd, tarFile } = await this._downloadFromUpload(file);
1836
1873
 
1837
- const oldBlocklet = await states.blocklet.getBlocklet(rootDid);
1874
+ const oldBlocklet = await this._getBlockletForInstallation(rootDid);
1838
1875
  if (!oldBlocklet) {
1839
1876
  throw new Error('Root blocklet does not exist');
1840
1877
  }
@@ -1883,7 +1920,7 @@ class BlockletManager extends BaseBlockletManager {
1883
1920
  mountPoint,
1884
1921
  source: BlockletSource.upload,
1885
1922
  deployedFrom: `Upload by ${context.user.fullName}`,
1886
- sourceUrl: '',
1923
+ bundleSource: null,
1887
1924
  dynamic: true,
1888
1925
  };
1889
1926
  const index = newBlocklet.children.findIndex((child) => child.meta.did === meta.did);
@@ -1892,6 +1929,7 @@ class BlockletManager extends BaseBlockletManager {
1892
1929
  } else {
1893
1930
  newBlocklet.children.push(newChild);
1894
1931
  }
1932
+ await this._upsertDynamicNavigation(newBlocklet.meta.did, newChild);
1895
1933
 
1896
1934
  await validateBlocklet(newBlocklet);
1897
1935
 
@@ -2046,6 +2084,8 @@ class BlockletManager extends BaseBlockletManager {
2046
2084
 
2047
2085
  const { name, did, version } = meta;
2048
2086
 
2087
+ const oldExtraState = await states.blockletExtras.findOne({ did: meta.did });
2088
+
2049
2089
  const children = await this._getChildren(meta);
2050
2090
  try {
2051
2091
  const blocklet = await states.blocklet.addBlocklet({
@@ -2067,7 +2107,10 @@ class BlockletManager extends BaseBlockletManager {
2067
2107
  // download
2068
2108
  const downloadParams = {
2069
2109
  blocklet: { ...blocklet1 },
2070
- oldBlocklet: { children: children.filter((x) => x.dynamic) }, // let downloader skip re-downloading dynamic blocklet
2110
+ oldBlocklet: {
2111
+ children: children.filter((x) => x.dynamic), // let downloader skip re-downloading dynamic blocklet
2112
+ extraState: oldExtraState,
2113
+ },
2071
2114
  context,
2072
2115
  postAction: 'install',
2073
2116
  };
@@ -2089,8 +2132,7 @@ class BlockletManager extends BaseBlockletManager {
2089
2132
  ticket.on('failed', async (err) => {
2090
2133
  logger.error('failed to install blocklet', { name, did, version, error: err });
2091
2134
  try {
2092
- await this._delExtras(did);
2093
- await states.blocklet.deleteBlocklet(did);
2135
+ await this._rollback('install', did, { extraState: oldExtraState });
2094
2136
  } catch (e) {
2095
2137
  logger.error('failed to remove blocklet on install error', { did: meta.did, error: e });
2096
2138
  }
@@ -2116,7 +2158,7 @@ class BlockletManager extends BaseBlockletManager {
2116
2158
  });
2117
2159
 
2118
2160
  try {
2119
- await this._rollback('install', did);
2161
+ await this._rollback('install', did, { extraState: oldExtraState });
2120
2162
  } catch (e) {
2121
2163
  logger.error('failed to remove blocklet on install error', { did: meta.did, error: e });
2122
2164
  }
@@ -2130,7 +2172,7 @@ class BlockletManager extends BaseBlockletManager {
2130
2172
 
2131
2173
  const { name, version, did } = meta;
2132
2174
 
2133
- const oldBlocklet = await states.blocklet.getBlocklet(did);
2175
+ const oldBlocklet = await this._getBlockletForInstallation(did);
2134
2176
 
2135
2177
  // NOTE: 目前的版本移除了降级通道,所以不需要考虑降级通道的情况
2136
2178
  const action = semver.gt(oldBlocklet.meta.version, version) ? 'downgrade' : 'upgrade';
@@ -2248,9 +2290,10 @@ class BlockletManager extends BaseBlockletManager {
2248
2290
  if (fs.existsSync(installDir)) {
2249
2291
  fs.removeSync(installDir);
2250
2292
  logger.info('cleanup blocklet upgrade dir', { name, version, installDir });
2251
- } else {
2252
- fs.mkdirSync(installDir, { recursive: true });
2253
2293
  }
2294
+
2295
+ fs.mkdirSync(installDir, { recursive: true });
2296
+
2254
2297
  await fs.move(downloadDir, installDir, { overwrite: true });
2255
2298
  } catch (error) {
2256
2299
  fs.removeSync(downloadDir);
@@ -2303,6 +2346,7 @@ class BlockletManager extends BaseBlockletManager {
2303
2346
 
2304
2347
  await ensureBlockletExpanded(meta, downloadDir);
2305
2348
 
2349
+ // move to installDir
2306
2350
  const bundleDir = getBundleDir(this.installDir, meta);
2307
2351
  logger.info('Move downloadDir to installDir', { downloadDir, bundleDir });
2308
2352
  await fs.move(downloadDir, bundleDir, { overwrite: true });
@@ -2319,7 +2363,7 @@ class BlockletManager extends BaseBlockletManager {
2319
2363
  * @param {string} opt.did
2320
2364
  * @param {object} opt.context
2321
2365
  */
2322
- async _installBlocklet({ did, context }) {
2366
+ async _installBlocklet({ did, oldBlocklet, context }) {
2323
2367
  try {
2324
2368
  let blocklet = await this.ensureBlocklet(did);
2325
2369
  const { meta, source, deployedFrom } = blocklet;
@@ -2388,7 +2432,7 @@ class BlockletManager extends BaseBlockletManager {
2388
2432
  const { name, version } = meta;
2389
2433
  logger.error('failed to install blocklet', { name, did, version, error: err });
2390
2434
  try {
2391
- await this._rollback('install', did);
2435
+ await this._rollback('install', did, oldBlocklet);
2392
2436
  this.emit(BlockletEvents.installFailed, { meta: { did } });
2393
2437
  states.notification.create({
2394
2438
  title: 'Blocklet Install Failed',
@@ -2544,7 +2588,7 @@ class BlockletManager extends BaseBlockletManager {
2544
2588
  const { did } = blocklet.meta;
2545
2589
  const dynamicChildren = blocklet.children
2546
2590
  .filter((x) => x.dynamic)
2547
- .map((x) => pick(x, ['meta', 'mountPoint', 'sourceUrl', 'source']));
2591
+ .map((x) => pick(x, ['meta', 'mountPoint', 'bundleSource', 'source']));
2548
2592
 
2549
2593
  // deleted blocklet
2550
2594
  let deletes = await states.blockletExtras.getSettings(did, 'children', []);
@@ -2822,14 +2866,27 @@ class BlockletManager extends BaseBlockletManager {
2822
2866
  */
2823
2867
  async _rollback(action, did, oldBlocklet) {
2824
2868
  if (action === 'install') {
2825
- // remove blocklet
2869
+ const extraState = oldBlocklet?.extraState;
2870
+
2871
+ // rollback blocklet extra state
2872
+ if (extraState) {
2873
+ await states.blockletExtras.update({ did }, extraState);
2874
+ } else {
2875
+ await states.blockletExtras.remove({ did });
2876
+ }
2877
+
2878
+ // remove blocklet state
2826
2879
  return this._deleteBlocklet({ did, keepData: true });
2827
2880
  }
2828
2881
 
2829
2882
  if (['upgrade', 'downgrade'].includes(action)) {
2830
- // rollback blocklet
2831
- const result = await states.blocklet.updateBlocklet(did, oldBlocklet);
2832
- await this._setConfigs(did);
2883
+ const { extraState, ...blocklet } = oldBlocklet;
2884
+ // rollback blocklet state
2885
+ const result = await states.blocklet.updateBlocklet(did, blocklet);
2886
+
2887
+ // rollback blocklet extra state
2888
+ await states.blockletExtras.update({ did: blocklet.meta.did }, extraState);
2889
+
2833
2890
  logger.info('blocklet rollback successfully', { did });
2834
2891
  this.emit(BlockletEvents.updated, result);
2835
2892
  return result;
@@ -2851,14 +2908,14 @@ class BlockletManager extends BaseBlockletManager {
2851
2908
  if (keepData === false) {
2852
2909
  fs.removeSync(dataDir);
2853
2910
  fs.removeSync(logsDir);
2854
- await this._delExtras(did);
2911
+ await states.blockletExtras.remove({ did: blocklet.meta.did });
2855
2912
  } else {
2856
2913
  if (keepLogsDir === false) {
2857
2914
  fs.removeSync(logsDir);
2858
2915
  }
2859
2916
 
2860
2917
  if (keepConfigs === false) {
2861
- await this._delExtras(did);
2918
+ await states.blockletExtras.remove({ did: blocklet.meta.did });
2862
2919
  }
2863
2920
  }
2864
2921
 
@@ -2880,21 +2937,18 @@ class BlockletManager extends BaseBlockletManager {
2880
2937
  return blocklet;
2881
2938
  }
2882
2939
 
2883
- async _setConfigs(did) {
2940
+ async _setConfigs(did, childDid) {
2884
2941
  const blocklet = await states.blocklet.getBlocklet(did);
2885
2942
  const { meta } = blocklet;
2886
- await states.blockletExtras.setConfigs(did, get(meta, 'environments', []));
2887
- for (const child of blocklet.children) {
2888
- await states.blockletExtras.setChildConfigs(did, child.meta.did, get(child.meta, 'environments'));
2889
- }
2890
- }
2891
2943
 
2892
- async _delExtras(did) {
2893
- const blocklet = await states.blocklet.getBlocklet(did);
2894
- await states.blockletExtras.delConfigs(did);
2895
- await states.blockletExtras.delSettings(did);
2896
- for (const child of blocklet.children) {
2897
- await states.blockletExtras.delChildConfigs(did, child.meta.did);
2944
+ if (!childDid) {
2945
+ await states.blockletExtras.setConfigs(blocklet.meta.did, get(meta, 'environments', []));
2946
+ for (const child of blocklet.children) {
2947
+ await states.blockletExtras.setChildConfigs(blocklet.meta.did, child.meta.did, get(child.meta, 'environments'));
2948
+ }
2949
+ } else {
2950
+ const child = blocklet.children.find((x) => x.meta.did === childDid);
2951
+ await states.blockletExtras.setChildConfigs(blocklet.meta.did, child.meta.did, get(child.meta, 'environments'));
2898
2952
  }
2899
2953
  }
2900
2954
 
@@ -2911,6 +2965,30 @@ class BlockletManager extends BaseBlockletManager {
2911
2965
  }
2912
2966
  return { did, name };
2913
2967
  }
2968
+
2969
+ async _upsertDynamicNavigation(did, child) {
2970
+ const navigation = await states.blockletExtras.getSettings(did, 'navigation', []);
2971
+ const item = { title: child.meta.title, child: child.meta.name };
2972
+ const index = navigation.findIndex((x) => x.child === item.child);
2973
+ if (index > -1) {
2974
+ navigation.splice(index, 1, item);
2975
+ } else {
2976
+ navigation.push(item);
2977
+ }
2978
+ await states.blockletExtras.setSettings(did, { navigation });
2979
+ }
2980
+
2981
+ async _getBlockletForInstallation(did) {
2982
+ const blocklet = await states.blocklet.getBlocklet(did);
2983
+ if (!blocklet) {
2984
+ return null;
2985
+ }
2986
+
2987
+ const extraState = await states.blockletExtras.findOne({ did: blocklet.meta.did });
2988
+ blocklet.extraState = extraState;
2989
+
2990
+ return blocklet;
2991
+ }
2914
2992
  }
2915
2993
 
2916
2994
  module.exports = BlockletManager;
@@ -123,7 +123,7 @@ class BlockletRegistry {
123
123
 
124
124
  if (res.status === 304 && this.blocklets.length > 0) {
125
125
  logger.debug('use cached data', { etag: res.headers.etag, registryUrl });
126
- return this.blocklets;
126
+ return this.blocklets.filter((x) => isRequirementsSatisfied(x.requirements, false));
127
127
  }
128
128
 
129
129
  if (res.status === 304) {
@@ -144,7 +144,7 @@ class BlockletRegistry {
144
144
  const blocklets = res.data.filter((x) => BlockletGroup[x.group]);
145
145
  logger.debug('blocklets loaded', { url, blocklets: blocklets.map((x) => ({ did: x.did, name: x.name })) });
146
146
  this.cacheId = res.headers.etag;
147
- this.blocklets = blocklets;
147
+ this.blocklets = blocklets.filter((x) => isRequirementsSatisfied(x.requirements, false));
148
148
  } catch (error) {
149
149
  logger.error('refresh blocklet registry failed', { error, registryUrl });
150
150
  this.cacheId = '';
package/lib/index.js CHANGED
@@ -249,6 +249,7 @@ function ABTNode(options) {
249
249
  selectBlockletStore: nodeAPI.selectRegistry.bind(nodeAPI),
250
250
  cleanupDirtyUpgradeState: states.node.cleanupDirtyUpgradeState.bind(states.node),
251
251
  addNodeOwner: states.node.addNodeOwner.bind(states.node),
252
+ updateNodeOwner: states.node.updateNodeOwner.bind(states.node),
252
253
  updateNodeRouting,
253
254
  isInitialized,
254
255
  resetNode: (params, context) =>
@@ -0,0 +1,42 @@
1
+ /* eslint-disable no-continue */
2
+ /* eslint-disable no-await-in-loop */
3
+ /* eslint-disable no-underscore-dangle */
4
+
5
+ module.exports = async ({ states, printInfo }) => {
6
+ printInfo('Try to update blocklet server to 1.7.15...');
7
+
8
+ const blocklets = await states.blocklet.getBlocklets();
9
+
10
+ for (const blocklet of blocklets) {
11
+ (blocklet.children || []).forEach((child) => {
12
+ const { sourceUrl } = child;
13
+ delete child.sourceUrl;
14
+ if (sourceUrl && !child.bundleSource) {
15
+ child.bundleSource = {
16
+ url: sourceUrl,
17
+ };
18
+ }
19
+ });
20
+
21
+ await states.blocklet.updateBlocklet(blocklet.meta.did, { children: blocklet.children });
22
+ printInfo(`Blocklet children in blocklet.db updated: ${blocklet.meta.did}`);
23
+ }
24
+
25
+ const blockletExtras = await states.blockletExtras.find({});
26
+
27
+ for (const extra of blockletExtras) {
28
+ const children = await states.blockletExtras.getSettings(extra.did, 'children', []);
29
+ (children || []).forEach((child) => {
30
+ const { sourceUrl } = child;
31
+ delete child.sourceUrl;
32
+ if (sourceUrl && !child.bundleSource) {
33
+ child.bundleSource = {
34
+ url: sourceUrl,
35
+ };
36
+ }
37
+ });
38
+
39
+ await states.blockletExtras.setSettings(extra.did, { children });
40
+ printInfo(`Blocklet dynamic component in blocklet_extra.db updated: ${extra.did}`);
41
+ }
42
+ };
@@ -311,8 +311,8 @@ const ensureWellknownRule = async (sites) => {
311
311
  for (const site of tempSites) {
312
312
  // 不向 default site & ip site & wellknown site 添加 wellknown rule
313
313
  if (![DOMAIN_FOR_INTERNAL_SITE, DOMAIN_FOR_IP_SITE, DOMAIN_FOR_DEFAULT_SITE].includes(site.domain)) {
314
+ // add /.well-known for blocklet
314
315
  const isExists = site.rules.find((x) => x.from.pathPrefix === WELLKNOWN_PATH_PREFIX);
315
-
316
316
  if (!isExists) {
317
317
  site.rules.push({
318
318
  from: { pathPrefix: WELLKNOWN_PATH_PREFIX },
@@ -326,31 +326,21 @@ const ensureWellknownRule = async (sites) => {
326
326
  }
327
327
 
328
328
  // add /.well-known/service for blocklet
329
- const rules = cloneDeep(site.rules);
330
- rules.forEach((rule) => {
331
- if (
332
- rule.to.type !== ROUTING_RULE_TYPES.BLOCKLET || // is not blocklet
333
- (rule.to.did !== rule.to.realDid && rule.from.pathPrefix !== '/') // is a component endpoint in sub path
334
- ) {
335
- return;
329
+ const blockletRules = site.rules.filter((x) => x.to.type === ROUTING_RULE_TYPES.BLOCKLET);
330
+ if (blockletRules.length) {
331
+ // get pathPrefix for blocklet-service
332
+ const rootBlockletRule = blockletRules.find((x) => x.to.did === x.to.realDid);
333
+ const pathPrefix = joinUrl(rootBlockletRule?.from?.pathPrefix || '/', WELLKNOWN_SERVICE_PATH_PREFIX);
334
+
335
+ // requests for /.well-known/service will stay in blocklet-service and never proxy back to blocklet
336
+ // so any rule is ok to be cloned
337
+ if (!site.rules.some((x) => x.from.pathPrefix === pathPrefix)) {
338
+ const rule = cloneDeep(rootBlockletRule || blockletRules[0]);
339
+ rule.from.pathPrefix = pathPrefix;
340
+ rule.isProtected = true;
341
+ site.rules.push(rule);
336
342
  }
337
-
338
- // already exists
339
- if (rule.from.pathPrefix.endsWith(WELLKNOWN_SERVICE_PATH_PREFIX)) {
340
- return;
341
- }
342
-
343
- const pathPrefix = joinUrl(rule.from.pathPrefix, WELLKNOWN_SERVICE_PATH_PREFIX);
344
-
345
- // already exists
346
- if (site.rules.some((x) => x.from.pathPrefix === pathPrefix)) {
347
- return;
348
- }
349
-
350
- rule.from.pathPrefix = pathPrefix;
351
- rule.isProtected = true;
352
- site.rules.push(rule);
353
- });
343
+ }
354
344
  }
355
345
  }
356
346
 
@@ -17,9 +17,11 @@ All Blocklet Server states are managed by files in this folder.
17
17
  - `scripts`: for hook of component lifecycle
18
18
  - `engine`, `timeout`, `group`, `main`: related to process startup
19
19
  - `dist`: bundle source
20
+ - `theme`: app theme
21
+ - `navigation`: app navigation`
20
22
  - `source`: type of bundle source
21
- - `deployedFrom`: information of bundle source
22
- - `sourceUrl`: url of bundle source
23
+ - `bundleSource`: download infomation of bundle srouce
24
+ - `deployedFrom`: extra information of bundle source
23
25
  - `appDid`: app instance id _app only_
24
26
  - `status`, `startedAt`, `installedAt`, `stoppedAt` app status
25
27
  - `deletedAt` _component only_
@@ -94,7 +94,7 @@ const getLogContent = async (action, args, context, result, info, node) => {
94
94
  case 'installBlocklet':
95
95
  return `installed blocklet ${getBlockletInfo(result, info)} from ${result.deployedFrom}`;
96
96
  case 'startBlocklet':
97
- return `started blocklet ${getBlockletInfo(result, info)}`;
97
+ return `started blocklet ${getBlockletInfo(result, info)} ${args.reason || ''}`;
98
98
  case 'restartBlocklet':
99
99
  return `restarted blocklet ${getBlockletInfo(result, info)}`;
100
100
  case 'reloadBlocklet':
@@ -132,6 +132,12 @@ const getLogContent = async (action, args, context, result, info, node) => {
132
132
  return `joined team ${team} by ${args.reason}`;
133
133
  case 'updateUser':
134
134
  return `${args.reason} and received **${args.passport.name}** passport from ${team}`;
135
+ case 'switchProfile':
136
+ return `switched profile to ${args.profile.fullName} for ${team}`;
137
+ case 'switchPassport':
138
+ return `switched passport to ${args.passport.role} for ${team}`;
139
+ case 'login':
140
+ return `${user} logged in to ${team} with passport ${args.passport.name}`;
135
141
  case 'configWhoCanAccess':
136
142
  return `updated access control policy to **${args.value}** for ${team} when ${args.reason}`;
137
143
  case 'configPassportIssuance':
@@ -241,6 +247,9 @@ const getLogCategory = (action) => {
241
247
  // teams: members/passports
242
248
  case 'addUser':
243
249
  case 'updateUser':
250
+ case 'switchProfile':
251
+ case 'switchPassport':
252
+ case 'login':
244
253
  case 'configWhoCanAccess':
245
254
  case 'processPassportIssuance':
246
255
  case 'configPassportIssuance':
@@ -522,7 +522,7 @@ class BlockletState extends BaseState {
522
522
 
523
523
  const newChildren = [...oldChildren];
524
524
  for (const child of children) {
525
- const { meta, mountPoint, sourceUrl = '', source = '', deployedFrom = '', mode } = child;
525
+ const { meta, mountPoint, bundleSource = null, source = '', deployedFrom = '', mode } = child;
526
526
 
527
527
  if (!mountPoint) {
528
528
  throw new Error(`mountPoint is required when adding component ${getDisplayName(child, true)}`);
@@ -537,7 +537,7 @@ class BlockletState extends BaseState {
537
537
  newChildren.push({
538
538
  mountPoint,
539
539
  meta,
540
- sourceUrl,
540
+ bundleSource,
541
541
  source,
542
542
  deployedFrom,
543
543
  mode,
@@ -97,6 +97,7 @@ class NodeState extends BaseState {
97
97
  didDomain,
98
98
  enablePassportIssuance = true,
99
99
  trustedPassports = [],
100
+ webWalletUrl,
100
101
  } = this.options;
101
102
 
102
103
  if (nodeOwner && !validateOwner(nodeOwner)) {
@@ -134,6 +135,7 @@ class NodeState extends BaseState {
134
135
  enablePassportIssuance,
135
136
  trustedPassports,
136
137
  customBlockletNumber: 0,
138
+ webWalletUrl,
137
139
  },
138
140
  async (e, data) => {
139
141
  if (e) {
@@ -197,34 +199,48 @@ class NodeState extends BaseState {
197
199
  });
198
200
  }
199
201
 
200
- addNodeOwner(owner) {
201
- if (owner && !validateOwner(owner)) {
202
+ async addOwner(owner, doc) {
203
+ const initialized = this.isInitialized({ nodeOwner: owner });
204
+
205
+ if (this.notification && typeof this.notification.setDefaultReceiver === 'function') {
206
+ this.notification.setDefaultReceiver(owner.did);
207
+ }
208
+
209
+ const updateResult = await this.update(doc._id, {
210
+ $set: { nodeOwner: owner, initialized, initializedAt: initialized ? new Date() : null },
211
+ });
212
+
213
+ this.emit(EVENTS.NODE_ADDED_OWNER, updateResult);
214
+
215
+ return updateResult;
216
+ }
217
+
218
+ async addNodeOwner(owner) {
219
+ if (!validateOwner(owner)) {
202
220
  throw new Error('Node owner is invalid');
203
221
  }
204
222
 
205
- return this.read().then(async (doc) => {
206
- if (doc.nodeOwner) {
207
- if (isEqual(owner, doc.nodeOwner)) {
208
- return doc;
209
- }
223
+ const doc = await this.read();
210
224
 
211
- throw new Error('Cannot set owner because owner already exists');
225
+ if (doc.nodeOwner) {
226
+ if (isEqual(owner, doc.nodeOwner)) {
227
+ return doc;
212
228
  }
213
229
 
214
- const initialized = this.isInitialized({ nodeOwner: owner });
230
+ throw new Error('Cannot set owner because owner already exists');
231
+ }
215
232
 
216
- if (this.notification && typeof this.notification.setDefaultReceiver === 'function') {
217
- this.notification.setDefaultReceiver(owner.did);
218
- }
233
+ return this.addOwner(owner, doc);
234
+ }
219
235
 
220
- const updateResult = await this.update(doc._id, {
221
- $set: { nodeOwner: owner, initialized, initializedAt: initialized ? new Date() : null },
222
- });
236
+ async updateNodeOwner(owner) {
237
+ if (!validateOwner(owner)) {
238
+ throw new Error('Node owner is invalid');
239
+ }
223
240
 
224
- this.emit(EVENTS.NODE_ADDED_OWNER, updateResult);
241
+ const doc = await this.read();
225
242
 
226
- return updateResult;
227
- });
243
+ return this.addOwner(owner, doc);
228
244
  }
229
245
 
230
246
  async enterMode(mode) {
@@ -10,6 +10,8 @@ const streamToPromise = require('stream-to-promise');
10
10
  const { Throttle } = require('stream-throttle');
11
11
  const ssri = require('ssri');
12
12
  const diff = require('deep-diff');
13
+ const any = require('promise.any');
14
+ const joinUrl = require('url-join');
13
15
 
14
16
  const { toHex } = require('@ocap/util');
15
17
  const logger = require('@abtnode/logger')('@abtnode/core:util:blocklet');
@@ -20,7 +22,7 @@ const CustomError = require('@abtnode/util/lib/custom-error');
20
22
  const getFolderSize = require('@abtnode/util/lib/get-folder-size');
21
23
  const normalizePathPrefix = require('@abtnode/util/lib/normalize-path-prefix');
22
24
  const hashFiles = require('@abtnode/util/lib/hash-files');
23
- const { BLOCKLET_MAX_MEM_LIMIT_IN_MB } = require('@abtnode/constant');
25
+ const { BLOCKLET_MAX_MEM_LIMIT_IN_MB, BLOCKLET_STORE_API_BLOCKLET_PREFIX } = require('@abtnode/constant');
24
26
 
25
27
  const {
26
28
  BlockletStatus,
@@ -411,12 +413,24 @@ const getBlockletMetaFromUrl = async (url) => {
411
413
 
412
414
  meta.dist.tarball = tarball;
413
415
  } catch (err) {
414
- throw new Error(`Invalid blocklet meta: dist.tarball is not a valid url ${err.message}`);
416
+ const msg = `Invalid blocklet meta: dist.tarball is not a valid url ${err.message}`;
417
+ logger.error(msg);
418
+ throw new Error(msg);
415
419
  }
416
420
 
417
421
  return meta;
418
422
  };
419
423
 
424
+ const getBlockletMetaFromUrls = async (urls) => {
425
+ try {
426
+ const meta = await any(urls.map(getBlockletMetaFromUrl));
427
+ return meta;
428
+ } catch (err) {
429
+ logger.error('failed get blocklet meta', { urls });
430
+ throw new Error('Failed get blocklet meta');
431
+ }
432
+ };
433
+
420
434
  /**
421
435
  * Start all precesses of a blocklet
422
436
  * @param {*} blocklet should contain env props
@@ -649,6 +663,36 @@ const parseChildren = (children, parentMeta = {}, { dynamic } = {}) => {
649
663
  return children;
650
664
  };
651
665
 
666
+ /**
667
+ * @param {*} config defined in childrenSchema in blocklet meta schema
668
+ */
669
+ const getSourceUrlsFromConfig = (config) => {
670
+ if (config.source) {
671
+ if (config.source.url) {
672
+ return [config.source.url].flat();
673
+ }
674
+
675
+ const { store, version, name } = config.source;
676
+ return [store]
677
+ .flat()
678
+ .map((x) =>
679
+ joinUrl(
680
+ x,
681
+ BLOCKLET_STORE_API_BLOCKLET_PREFIX,
682
+ toBlockletDid(name),
683
+ !version || version === 'latest' ? '' : version,
684
+ 'blocklet.json'
685
+ )
686
+ );
687
+ }
688
+
689
+ if (config.resolved) {
690
+ return [config.resolved];
691
+ }
692
+
693
+ throw new Error('Invalid child config');
694
+ };
695
+
652
696
  /**
653
697
  * this function has side effect on children
654
698
  */
@@ -667,7 +711,15 @@ const parseChildrenFromMeta = async (src, { dynamic } = {}) => {
667
711
  throw new Error(`MountPoint does not found in child ${config.name}`);
668
712
  }
669
713
 
670
- const m = await getBlockletMetaFromUrl(config.resolved);
714
+ const urls = getSourceUrlsFromConfig(config);
715
+
716
+ let m;
717
+ try {
718
+ m = await getBlockletMetaFromUrls(urls);
719
+ } catch {
720
+ throw new Error('Failed get component meta');
721
+ }
722
+
671
723
  validateBlockletMeta(m, { ensureDist: true });
672
724
 
673
725
  const webInterface = findWebInterface(m);
@@ -691,7 +743,7 @@ const parseChildrenFromMeta = async (src, { dynamic } = {}) => {
691
743
  const child = {
692
744
  mountPoint,
693
745
  meta,
694
- sourceUrl: config.resolved,
746
+ bundleSource: config.source || { url: config.resolved },
695
747
  };
696
748
 
697
749
  children.push(child);
@@ -1234,4 +1286,5 @@ module.exports = {
1234
1286
  verifyPurchase,
1235
1287
  findAvailableDid,
1236
1288
  ensureMeta,
1289
+ getSourceUrlsFromConfig,
1237
1290
  };
@@ -1,4 +1,4 @@
1
- const { NODE_REGISTER_URL, BLOCKLET_STORE_URL, BLOCKLET_STORE_URL_DEV, WEB_WALLET_URL } = require('@abtnode/constant');
1
+ const { NODE_REGISTER_URL, BLOCKLET_STORE_URL, BLOCKLET_STORE_URL_DEV } = require('@abtnode/constant');
2
2
  const canPackageReadWrite = require('@abtnode/util/lib/can-pkg-rw');
3
3
 
4
4
  const getDefaultAutoUpgrade = () => {
@@ -36,7 +36,6 @@ const defaultNodeConfigs = {
36
36
  ],
37
37
  },
38
38
  registerUrl: { getDefaultValue: () => NODE_REGISTER_URL },
39
- webWalletUrl: { getDefaultValue: () => WEB_WALLET_URL },
40
39
  };
41
40
 
42
41
  const lib = {
package/lib/util/index.js CHANGED
@@ -358,15 +358,11 @@ const getBlockletMetaByUrl = async (url) => {
358
358
  }
359
359
 
360
360
  if (protocol.startsWith('http')) {
361
- try {
362
- const { data: meta } = await request({ url, method: 'GET', timeout: 1000 * 20 });
363
- if (Object.prototype.toString.call(meta) !== '[object Object]') {
364
- throw new Error('Url is not valid');
365
- }
366
- return meta;
367
- } catch (err) {
368
- throw new Error(`Failed to get blocklet meta from ${url}: ${err.message}`);
361
+ const { data: meta } = await request({ url, method: 'GET', timeout: 1000 * 20 });
362
+ if (Object.prototype.toString.call(meta) !== '[object Object]') {
363
+ throw new Error('Url is not valid');
369
364
  }
365
+ return meta;
370
366
  }
371
367
 
372
368
  throw new Error(`Invalid url protocol: ${protocol.replace(/:$/, '')}`);
@@ -28,7 +28,9 @@ const isSatisfied = (requirements, throwOnUnsatisfied = true) => {
28
28
  throw new Error(`Your blocklet is not supported on architecture ${actualCpu}`);
29
29
  }
30
30
  if (isVersionSatisfied === false) {
31
- throw new Error(`Your blocklet is not supported on server version v${actualVersion}`);
31
+ throw new Error(
32
+ `Expected server version for the blocklet is ${expectedVersion}, but your server version is ${actualVersion}, please upgrade or downgrade your server to continue.`
33
+ );
32
34
  }
33
35
  }
34
36
 
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "1.7.14",
6
+ "version": "1.7.15",
7
7
  "description": "",
8
8
  "main": "lib/index.js",
9
9
  "files": [
@@ -19,29 +19,29 @@
19
19
  "author": "wangshijun <wangshijun2010@gmail.com> (http://github.com/wangshijun)",
20
20
  "license": "MIT",
21
21
  "dependencies": {
22
- "@abtnode/certificate-manager": "1.7.14",
23
- "@abtnode/constant": "1.7.14",
24
- "@abtnode/cron": "1.7.14",
25
- "@abtnode/db": "1.7.14",
26
- "@abtnode/logger": "1.7.14",
27
- "@abtnode/queue": "1.7.14",
28
- "@abtnode/rbac": "1.7.14",
29
- "@abtnode/router-provider": "1.7.14",
30
- "@abtnode/static-server": "1.7.14",
31
- "@abtnode/timemachine": "1.7.14",
32
- "@abtnode/util": "1.7.14",
33
- "@arcblock/did": "^1.16.6",
22
+ "@abtnode/certificate-manager": "1.7.15",
23
+ "@abtnode/constant": "1.7.15",
24
+ "@abtnode/cron": "1.7.15",
25
+ "@abtnode/db": "1.7.15",
26
+ "@abtnode/logger": "1.7.15",
27
+ "@abtnode/queue": "1.7.15",
28
+ "@abtnode/rbac": "1.7.15",
29
+ "@abtnode/router-provider": "1.7.15",
30
+ "@abtnode/static-server": "1.7.15",
31
+ "@abtnode/timemachine": "1.7.15",
32
+ "@abtnode/util": "1.7.15",
33
+ "@arcblock/did": "^1.16.8",
34
34
  "@arcblock/did-motif": "^1.1.5",
35
- "@arcblock/event-hub": "1.16.6",
35
+ "@arcblock/event-hub": "1.16.8",
36
36
  "@arcblock/pm2-events": "^0.0.5",
37
- "@arcblock/vc": "^1.16.6",
38
- "@blocklet/meta": "1.7.14",
37
+ "@arcblock/vc": "^1.16.8",
38
+ "@blocklet/meta": "1.7.15",
39
39
  "@fidm/x509": "^1.2.1",
40
40
  "@nedb/core": "^1.2.2",
41
41
  "@nedb/multi": "^1.2.2",
42
- "@ocap/mcrypto": "^1.16.6",
43
- "@ocap/util": "^1.16.6",
44
- "@ocap/wallet": "^1.16.6",
42
+ "@ocap/mcrypto": "^1.16.8",
43
+ "@ocap/util": "^1.16.8",
44
+ "@ocap/wallet": "^1.16.8",
45
45
  "@slack/webhook": "^5.0.3",
46
46
  "axios": "^0.26.1",
47
47
  "axon": "^2.0.3",
@@ -59,6 +59,7 @@
59
59
  "lodash": "^4.17.21",
60
60
  "lru-cache": "^6.0.0",
61
61
  "pm2": "^5.1.2",
62
+ "promise.any": "^2.0.4",
62
63
  "semver": "^7.3.2",
63
64
  "shelljs": "^0.8.4",
64
65
  "slugify": "^1.4.6",
@@ -78,5 +79,5 @@
78
79
  "express": "^4.17.1",
79
80
  "jest": "^27.4.5"
80
81
  },
81
- "gitHead": "1f3855e6dc8b6d6c91526264e2f63215df6b962c"
82
+ "gitHead": "d95421b38c281ed41fa7490a707f149aa737f3d3"
82
83
  }