@abtnode/core 1.7.26 → 1.8.1

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.
@@ -10,7 +10,7 @@ const capitalize = require('lodash/capitalize');
10
10
  const { Throttle } = require('stream-throttle');
11
11
  const LRU = require('lru-cache');
12
12
  const joi = require('joi');
13
-
13
+ const { sign } = require('@arcblock/jwt');
14
14
  const { isValid: isValidDid } = require('@arcblock/did');
15
15
  const { verifyPresentation } = require('@arcblock/vc');
16
16
  const { toBase58, isHex } = require('@ocap/util');
@@ -22,6 +22,7 @@ const logger = require('@abtnode/logger')('@abtnode/core:blocklet:manager');
22
22
  const downloadFile = require('@abtnode/util/lib/download-file');
23
23
  const Lock = require('@abtnode/util/lib/lock');
24
24
  const { getVcFromPresentation } = require('@abtnode/util/lib/vc');
25
+ const handleInstanceInStore = require('@abtnode/util/lib/public-to-store');
25
26
  const { VC_TYPE_BLOCKLET_PURCHASE } = require('@abtnode/constant');
26
27
 
27
28
  const getBlockletEngine = require('@blocklet/meta/lib/engine');
@@ -45,6 +46,7 @@ const toBlockletDid = require('@blocklet/meta/lib/did');
45
46
  const { validateMeta } = require('@blocklet/meta/lib/validate');
46
47
  const { update: updateMetaFile } = require('@blocklet/meta/lib/file');
47
48
  const { titleSchema, descriptionSchema } = require('@blocklet/meta/lib/schema');
49
+ const hasReservedKey = require('@blocklet/meta/lib/has-reserved-key');
48
50
 
49
51
  const {
50
52
  BlockletStatus,
@@ -101,7 +103,6 @@ const {
101
103
  getDiffFiles,
102
104
  getBundleDir,
103
105
  needBlockletDownload,
104
- verifyPurchase,
105
106
  findAvailableDid,
106
107
  ensureMeta,
107
108
  } = require('../../util/blocklet');
@@ -172,13 +173,15 @@ class BlockletManager extends BaseBlockletManager {
172
173
  /**
173
174
  * @param {*} dataDirs generate by ../../util:getDataDirs
174
175
  */
175
- constructor({ dataDirs, registry, startQueue, installQueue, daemon = false }) {
176
+ constructor({ dataDirs, registry, startQueue, installQueue, daemon = false, teamManager }) {
176
177
  super();
177
178
 
178
179
  this.dataDirs = dataDirs;
179
180
  this.installDir = dataDirs.blocklets;
180
181
  this.startQueue = startQueue;
181
182
  this.installQueue = installQueue;
183
+ this.teamManager = teamManager;
184
+
182
185
  /**
183
186
  * { did: Map({ <childDid>: <downloadFile.cancelCtrl> }) }
184
187
  */
@@ -209,13 +212,35 @@ class BlockletManager extends BaseBlockletManager {
209
212
  // ============================================================================================
210
213
 
211
214
  /**
212
- * @param {Object} params
213
- * @param {string} params.installId
214
- * @param {string} params.sync default: false
215
+ *
216
+ *
217
+ * @param {{
218
+ * url: string;
219
+ * sync: boolean = false;
220
+ * downloadToken: string;
221
+ * }} params
222
+ * @param {{
223
+ * [key: string]: any
224
+ * }} context
225
+ * @return {*}
226
+ * @memberof BlockletManager
215
227
  */
216
- async install(params, context) {
228
+ async install(params, context = {}) {
217
229
  logger.debug('install blocklet', { params, context });
218
230
 
231
+ if (params.downloadToken) {
232
+ const info = await states.node.read();
233
+
234
+ context.headers = Object.assign(context?.headers || {}, {
235
+ 'x-server-did': info.did,
236
+ 'x-download-token': params.downloadToken,
237
+ 'x-server-publick-key': info.pk,
238
+ 'x-server-signature': sign(info.did, info.sk, {
239
+ exp: (Date.now() + 5 * 60 * 1000) / 1000,
240
+ }),
241
+ });
242
+ }
243
+
219
244
  const source = getSourceFromInstallParams(params);
220
245
  if (typeof context.startImmediately === 'undefined') {
221
246
  context.startImmediately = !!params.startImmediately;
@@ -245,7 +270,6 @@ class BlockletManager extends BaseBlockletManager {
245
270
  /**
246
271
  * @param {String} rootDid
247
272
  * @param {String} mountPoint
248
- * @param {Boolean} context.blockletPurchaseVerified
249
273
  *
250
274
  * installFromUrl
251
275
  * @param {String} url
@@ -259,8 +283,13 @@ class BlockletManager extends BaseBlockletManager {
259
283
  * Custom info
260
284
  * @param {String} title custom component title
261
285
  * @param {String} name custom component name
286
+ *
287
+ * @param {ConfigEntry} configs pre configs
262
288
  */
263
- async installComponent({ rootDid, mountPoint, url, file, did, diffVersion, deleteSet, title, name }, context = {}) {
289
+ async installComponent(
290
+ { rootDid, mountPoint, url, file, did, diffVersion, deleteSet, title, name, configs, downloadToken },
291
+ context = {}
292
+ ) {
264
293
  logger.debug('start install component', { rootDid, mountPoint, url });
265
294
 
266
295
  if (file) {
@@ -268,7 +297,17 @@ class BlockletManager extends BaseBlockletManager {
268
297
  }
269
298
 
270
299
  if (url) {
271
- return this._installComponentFromUrl({ rootDid, mountPoint, url, context, title, did, name });
300
+ return this._installComponentFromUrl({
301
+ rootDid,
302
+ mountPoint,
303
+ url,
304
+ context,
305
+ title,
306
+ did,
307
+ name,
308
+ configs,
309
+ downloadToken,
310
+ });
272
311
  }
273
312
 
274
313
  // should not be here
@@ -295,10 +334,12 @@ class BlockletManager extends BaseBlockletManager {
295
334
  }
296
335
  }
297
336
 
337
+ const { inStore, registryUrl } = await parseSourceUrl(url);
338
+
298
339
  const blocklet = await states.blocklet.getBlocklet(meta.did);
299
340
  const isRunning = blocklet ? blocklet.status === BlockletStatus.running : false;
300
341
 
301
- return { meta, isFree, isInstalled: !!blocklet, isRunning };
342
+ return { meta, isFree, isInstalled: !!blocklet, isRunning, inStore, registryUrl };
302
343
  }
303
344
 
304
345
  async installBlockletFromVc({ vcPresentation, challenge }, context) {
@@ -317,7 +358,7 @@ class BlockletManager extends BaseBlockletManager {
317
358
  const did = get(vc, 'credentialSubject.purchased.blocklet.id');
318
359
  const registry = urlObject.origin;
319
360
 
320
- return this._installFromStore({ did, registry }, { ...context, blockletPurchaseVerified: true });
361
+ return this._installFromStore({ did, registry }, context);
321
362
  }
322
363
 
323
364
  async start({ did, throwOnError, checkHealthImmediately = false, e2eMode = false }, context) {
@@ -399,7 +440,7 @@ class BlockletManager extends BaseBlockletManager {
399
440
 
400
441
  await this.deleteProcess({ did });
401
442
  const res = await states.blocklet.setBlockletStatus(did, BlockletStatus.error);
402
- this.emit(BlockletEvents.startFailed, res);
443
+ this.emit(BlockletEvents.startFailed, { ...res, error: { message: error.message } });
403
444
 
404
445
  if (throwOnError) {
405
446
  throw new Error(description);
@@ -577,7 +618,7 @@ class BlockletManager extends BaseBlockletManager {
577
618
  async deleteComponent({ did, rootDid, keepData, keepState }, context) {
578
619
  logger.info('delete blocklet component', { did, rootDid, keepData });
579
620
 
580
- const blocklet = await this.ensureBlocklet(rootDid);
621
+ const blocklet = await this.ensureBlocklet(rootDid, { validateEnv: false });
581
622
  const child = blocklet.children.find((x) => x.meta.did === did);
582
623
  if (!child) {
583
624
  throw new Error('Component does not exist');
@@ -684,7 +725,7 @@ class BlockletManager extends BaseBlockletManager {
684
725
 
685
726
  if (!attachRuntimeInfo) {
686
727
  try {
687
- const blocklet = await this.ensureBlocklet(did);
728
+ const blocklet = await this.ensureBlocklet(did, { throwOnNotExist: false });
688
729
  return blocklet;
689
730
  } catch (e) {
690
731
  logger.error('get blocklet detail error', { error: e.message });
@@ -827,12 +868,26 @@ class BlockletManager extends BaseBlockletManager {
827
868
  return newState;
828
869
  }
829
870
 
871
+ async configPublicToStore({ did, publicToStore = false }) {
872
+ const blocklet = await this.ensureBlocklet(did);
873
+
874
+ await handleInstanceInStore(blocklet, { publicToStore });
875
+ await states.blockletExtras.setSettings(did, { publicToStore });
876
+
877
+ const newState = await this.ensureBlocklet(did);
878
+ return newState;
879
+ }
880
+
830
881
  /**
831
882
  * upgrade blocklet from registry
832
883
  */
833
884
  async upgrade({ did, registryUrl, sync }, context) {
834
885
  const blocklet = await states.blocklet.getBlocklet(did);
835
886
 
887
+ if (!registryUrl && blocklet.source === BlockletSource.url) {
888
+ return this._installFromUrl({ url: blocklet.deployedFrom }, context);
889
+ }
890
+
836
891
  // TODO: 查看了下目前页面中的升级按钮,都是会传 registryUrl 过来的,这个函数里的逻辑感觉需要在以后做一个简化
837
892
  if (!registryUrl && blocklet.source !== BlockletSource.registry) {
838
893
  throw new Error('Wrong upgrade source, empty registryUrl or not installed from blocklet registry');
@@ -1202,14 +1257,17 @@ class BlockletManager extends BaseBlockletManager {
1202
1257
  return rootBlocklet;
1203
1258
  }
1204
1259
 
1205
- async ensureBlocklet(did, { e2eMode = false, validateEnv = true } = {}) {
1260
+ async ensureBlocklet(did, { e2eMode = false, validateEnv = true, throwOnNotExist = true } = {}) {
1206
1261
  if (!isValidDid(did)) {
1207
1262
  throw new Error(`Blocklet did is invalid: ${did}`);
1208
1263
  }
1209
1264
 
1210
1265
  const blocklet = await states.blocklet.getBlocklet(did);
1211
1266
  if (!blocklet) {
1212
- throw new Error(`Can not find blocklet in database by did ${did}`);
1267
+ if (throwOnNotExist) {
1268
+ throw new Error(`Can not find blocklet in database by did ${did}`);
1269
+ }
1270
+ return null;
1213
1271
  }
1214
1272
 
1215
1273
  // app settings
@@ -1311,7 +1369,12 @@ class BlockletManager extends BaseBlockletManager {
1311
1369
  }
1312
1370
 
1313
1371
  try {
1314
- let blocklet = await this.ensureBlocklet(did);
1372
+ let blocklet = await this.ensureBlocklet(did, { throwOnNotExist: false });
1373
+
1374
+ if (!blocklet) {
1375
+ return null;
1376
+ }
1377
+
1315
1378
  const fromCache = !!cachedBlocklet;
1316
1379
 
1317
1380
  // if from cached data, only use cache data of runtime info (engine, diskInfo, runtimeInfo...)
@@ -1408,7 +1471,21 @@ class BlockletManager extends BaseBlockletManager {
1408
1471
  }
1409
1472
  }
1410
1473
 
1411
- async onDownload({ blocklet, context, postAction, oldBlocklet, throwOnError }) {
1474
+ /**
1475
+ *
1476
+ *
1477
+ * @param {{
1478
+ * blocklet: {},
1479
+ * context: {},
1480
+ * postAction: 'install' | 'upgrade' | 'downgrade',
1481
+ * oldBlocklet: {},
1482
+ * throwOnError: Error
1483
+ * }} params
1484
+ * @return {*}
1485
+ * @memberof BlockletManager
1486
+ */
1487
+ async onDownload(params) {
1488
+ const { blocklet, context, postAction, oldBlocklet, throwOnError } = params;
1412
1489
  const { meta } = blocklet;
1413
1490
  const { name, did, version } = meta;
1414
1491
 
@@ -1433,7 +1510,7 @@ class BlockletManager extends BaseBlockletManager {
1433
1510
 
1434
1511
  preDownloadLock.release();
1435
1512
 
1436
- const { isCancelled } = await this._downloadBlocklet(blocklet, oldBlocklet);
1513
+ const { isCancelled } = await this._downloadBlocklet(blocklet, oldBlocklet, context);
1437
1514
 
1438
1515
  if (isCancelled) {
1439
1516
  logger.info('Download was canceled manually', { name, did, version });
@@ -1445,7 +1522,12 @@ class BlockletManager extends BaseBlockletManager {
1445
1522
 
1446
1523
  preDownloadLock.release();
1447
1524
 
1448
- this.emit(BlockletEvents.downloadFailed, { meta: did });
1525
+ this.emit(BlockletEvents.downloadFailed, {
1526
+ meta: { did },
1527
+ error: {
1528
+ message: err.message,
1529
+ },
1530
+ });
1449
1531
  states.notification.create({
1450
1532
  title: 'Blocklet Download Failed',
1451
1533
  description: `Blocklet ${name}@${version} download failed with error: ${err.message}`,
@@ -1624,7 +1706,21 @@ class BlockletManager extends BaseBlockletManager {
1624
1706
  }
1625
1707
  }
1626
1708
 
1627
- async _installFromStore({ did, registry, sync }, context) {
1709
+ /**
1710
+ ***
1711
+ *
1712
+ * @param {{
1713
+ * did: string;
1714
+ * registry: string;
1715
+ * sync: boolean;
1716
+ * }} params
1717
+ * @param {*} context
1718
+ * @return {*}
1719
+ * @memberof BlockletManager
1720
+ */
1721
+ async _installFromStore(params, context) {
1722
+ const { did, registry, sync } = params;
1723
+
1628
1724
  logger.debug('start install blocklet', { did });
1629
1725
  if (!isValidDid(did)) {
1630
1726
  throw new Error('Blocklet did is invalid');
@@ -1642,8 +1738,6 @@ class BlockletManager extends BaseBlockletManager {
1642
1738
  throw new Error('Can not install an already installed blocklet');
1643
1739
  }
1644
1740
 
1645
- verifyPurchase(meta, context);
1646
-
1647
1741
  // install
1648
1742
  return this._install({
1649
1743
  meta,
@@ -1654,7 +1748,20 @@ class BlockletManager extends BaseBlockletManager {
1654
1748
  });
1655
1749
  }
1656
1750
 
1657
- async _installFromUrl({ url, sync }, context) {
1751
+ /**
1752
+ *
1753
+ *
1754
+ * @param {{
1755
+ * url: string;
1756
+ * sync: boolean;
1757
+ * }} params
1758
+ * @param {{}} context
1759
+ * @return {*}
1760
+ * @memberof BlockletManager
1761
+ */
1762
+ async _installFromUrl(params, context) {
1763
+ const { url, sync } = params;
1764
+
1658
1765
  logger.debug('start install blocklet', { url });
1659
1766
 
1660
1767
  const { inStore, registryUrl, blockletDid } = await parseSourceUrl(url);
@@ -1695,20 +1802,48 @@ class BlockletManager extends BaseBlockletManager {
1695
1802
  });
1696
1803
  }
1697
1804
 
1698
- async _installComponentFromUrl({ rootDid, mountPoint, url, context, title, name: inputName, did: inputDid }) {
1805
+ async _installComponentFromUrl({
1806
+ rootDid,
1807
+ mountPoint,
1808
+ url,
1809
+ context,
1810
+ title,
1811
+ name: inputName,
1812
+ did: inputDid,
1813
+ configs,
1814
+ downloadToken,
1815
+ }) {
1699
1816
  const blocklet = await this._getBlockletForInstallation(rootDid);
1700
1817
  if (!blocklet) {
1701
1818
  throw new Error('Root blocklet does not exist');
1702
1819
  }
1703
1820
 
1821
+ const { inStore } = await parseSourceUrl(url);
1822
+
1704
1823
  const meta = await getBlockletMetaFromUrl(url);
1705
1824
 
1825
+ // 如果是一个付费的blocklet,并且url来源为Store, 需要携带token才能下载成功
1826
+ if (!isFreeBlocklet(meta) && inStore) {
1827
+ const info = await states.node.read();
1828
+
1829
+ // eslint-disable-next-line no-param-reassign
1830
+ context = {
1831
+ ...context,
1832
+ headers: {
1833
+ 'x-server-did': info.did,
1834
+ 'x-download-token': downloadToken,
1835
+ 'x-server-publick-key': info.pk,
1836
+ 'x-server-signature': sign(info.did, info.sk, {
1837
+ exp: (Date.now() + 5 * 60 * 1000) / 1000,
1838
+ }),
1839
+ },
1840
+ };
1841
+ }
1842
+
1706
1843
  if (!isComponentBlocklet(meta)) {
1707
1844
  throw new Error('The blocklet cannot be a component');
1708
1845
  }
1709
1846
 
1710
- verifyPurchase(meta, context);
1711
-
1712
1847
  if (title) {
1713
1848
  meta.title = await titleSchema.validateAsync(title);
1714
1849
  }
@@ -1724,26 +1859,41 @@ class BlockletManager extends BaseBlockletManager {
1724
1859
  did = found.did;
1725
1860
  }
1726
1861
 
1727
- const newChildren = [
1728
- {
1729
- meta: ensureMeta(meta, { did, name }),
1730
- mountPoint,
1731
- bundleSource: { url },
1732
- dynamic: true,
1733
- children: await parseChildrenFromMeta(meta, { ...context, ancestors: [{ mountPoint }] }),
1734
- },
1735
- ];
1862
+ const newChild = {
1863
+ meta: ensureMeta(meta, { did, name }),
1864
+ mountPoint,
1865
+ bundleSource: { url },
1866
+ dynamic: true,
1867
+ children: await parseChildrenFromMeta(meta, { ...context, ancestors: [{ mountPoint }] }),
1868
+ };
1736
1869
 
1737
- checkDuplicateComponents([...blocklet.children, ...newChildren]);
1870
+ checkDuplicateComponents([...blocklet.children, newChild]);
1738
1871
 
1739
- // add component to db
1740
- await states.blocklet.addChildren(rootDid, newChildren);
1741
- await this._upsertDynamicNavigation(blocklet.meta.did, newChildren[0]);
1872
+ try {
1873
+ // add component to db
1874
+ await states.blocklet.addChildren(rootDid, [newChild]);
1875
+
1876
+ // update navigation
1877
+ await this._upsertDynamicNavigation(blocklet.meta.did, newChild);
1878
+
1879
+ // update configs
1880
+ if (Array.isArray(configs)) {
1881
+ if (hasReservedKey(configs)) {
1882
+ throw new Error('Component key of environments can not start with `ABT_NODE_` or `BLOCKLET_`');
1883
+ }
1884
+
1885
+ await states.blockletExtras.setConfigs([blocklet.meta.did, newChild.meta.did], configs);
1886
+ }
1887
+ } catch (err) {
1888
+ logger.error('Add component failed', err);
1889
+ await this._rollback('upgrade', rootDid, blocklet);
1890
+ throw err;
1891
+ }
1742
1892
 
1743
1893
  return this.updateChildren(
1744
1894
  {
1745
1895
  did: rootDid,
1746
- children: [...blocklet.children, ...newChildren],
1896
+ children: [...blocklet.children, newChild],
1747
1897
  oldBlocklet: blocklet,
1748
1898
  },
1749
1899
  context
@@ -2033,50 +2183,15 @@ class BlockletManager extends BaseBlockletManager {
2033
2183
  throw new Error('the blocklet is not installed');
2034
2184
  }
2035
2185
 
2036
- const { bundleDid } = blocklet.meta;
2037
-
2038
- let versions = this.cachedBlockletVersions.get(did);
2039
-
2040
- if (!versions) {
2041
- const { blockletRegistryList } = await states.node.read();
2042
- const tasks = blockletRegistryList.map((registry) =>
2043
- this.registry
2044
- .getBlockletMeta({ did: bundleDid, registryUrl: registry.url })
2045
- .then((item) => ({ did, version: item.version, registryUrl: registry.url }))
2046
- .catch((error) => {
2047
- if (error.response && error.response.status === 404) {
2048
- return;
2049
- }
2050
- logger.error('get blocklet meta from registry failed', { did, error });
2051
- })); // prettier-ignore
2052
-
2053
- versions = await Promise.all(tasks);
2054
- this.cachedBlockletVersions.set(did, versions);
2055
- }
2056
-
2057
- versions = versions.filter((item) => item && semver.gt(item.version, version));
2058
-
2059
- if (versions.length === 0) {
2060
- return null;
2061
- }
2062
-
2063
- // When new version found from the store where the blocklet was installed from, we should use that store first
2064
2186
  if (blocklet.source === BlockletSource.registry && blocklet.deployedFrom) {
2065
- const latestFromSameRegistry = versions.find((x) => x.registryUrl === blocklet.deployedFrom);
2066
- if (latestFromSameRegistry) {
2067
- return latestFromSameRegistry;
2068
- }
2187
+ return this._getLatestBlockletVersionFromStore({ blocklet, version });
2069
2188
  }
2070
2189
 
2071
- // Otherwise try upgrading from other store
2072
- let latestBlockletVersion = versions[0];
2073
- versions.forEach((item) => {
2074
- if (semver.lt(latestBlockletVersion.version, item.version)) {
2075
- latestBlockletVersion = item;
2076
- }
2077
- });
2190
+ if (blocklet.source === BlockletSource.url && blocklet.deployedFrom) {
2191
+ return this._getLatestBlockletVersionFromUrl({ blocklet, version });
2192
+ }
2078
2193
 
2079
- return latestBlockletVersion;
2194
+ return null;
2080
2195
  }
2081
2196
 
2082
2197
  getCrons() {
@@ -2132,7 +2247,22 @@ class BlockletManager extends BaseBlockletManager {
2132
2247
  .filter((x) => (skipDeleted ? x.status !== BlockletStatus.deleted : true));
2133
2248
  }
2134
2249
 
2135
- async _install({ meta, source, deployedFrom, context, sync }) {
2250
+ /**
2251
+ *
2252
+ *
2253
+ * @param {{
2254
+ * meta: {}; // blocklet meta
2255
+ * source: number; // example: BlockletSource.registry,
2256
+ * deployedFrom: string;
2257
+ * context: {}
2258
+ * sync: boolean = false;
2259
+ * }} params
2260
+ * @return {*}
2261
+ * @memberof BlockletManager
2262
+ */
2263
+ async _install(params) {
2264
+ const { meta, source, deployedFrom, context, sync } = params;
2265
+
2136
2266
  validateBlockletMeta(meta, { ensureDist: true });
2137
2267
 
2138
2268
  const { name, did, version } = meta;
@@ -2141,6 +2271,7 @@ class BlockletManager extends BaseBlockletManager {
2141
2271
 
2142
2272
  const children = await this._getChildrenForInstallation(meta);
2143
2273
  try {
2274
+ // 创建一个blocklet到database
2144
2275
  const blocklet = await states.blocklet.addBlocklet({
2145
2276
  meta,
2146
2277
  source,
@@ -2157,7 +2288,15 @@ class BlockletManager extends BaseBlockletManager {
2157
2288
  const blocklet1 = await states.blocklet.setBlockletStatus(did, BlockletStatus.waiting);
2158
2289
  this.emit(BlockletEvents.added, blocklet1);
2159
2290
 
2160
- // download
2291
+ /** @type {{
2292
+ * blocklet: any;
2293
+ * oldBlocklet: {
2294
+ * children: any[];
2295
+ * extraState: any;
2296
+ * };
2297
+ * context: {};
2298
+ * postAction: string
2299
+ * }} */
2161
2300
  const downloadParams = {
2162
2301
  blocklet: { ...blocklet1 },
2163
2302
  oldBlocklet: {
@@ -2220,6 +2359,13 @@ class BlockletManager extends BaseBlockletManager {
2220
2359
  }
2221
2360
  }
2222
2361
 
2362
+ /**
2363
+ *
2364
+ *
2365
+ * @param {{}} { meta, source, deployedFrom, context, sync }
2366
+ * @return {*}
2367
+ * @memberof BlockletManager
2368
+ */
2223
2369
  async _upgrade({ meta, source, deployedFrom, context, sync }) {
2224
2370
  validateBlockletMeta(meta, { ensureDist: meta.group !== BlockletGroup.gateway });
2225
2371
 
@@ -2242,6 +2388,26 @@ class BlockletManager extends BaseBlockletManager {
2242
2388
 
2243
2389
  this.emit(BlockletEvents.statusChange, newBlocklet);
2244
2390
 
2391
+ // 如果是一个付费的blocklet,需要携带token才能下载成功
2392
+ if (!isFreeBlocklet(meta)) {
2393
+ const blocklet = await states.blocklet.getBlocklet(did);
2394
+
2395
+ const info = await states.node.read();
2396
+
2397
+ // eslint-disable-next-line no-param-reassign
2398
+ context = {
2399
+ ...context,
2400
+ headers: {
2401
+ 'x-server-did': info.did,
2402
+ 'x-download-token': blocklet?.tokens?.paidBlockletDownloadToken,
2403
+ 'x-server-publick-key': info.pk,
2404
+ 'x-server-signature': sign(info.did, info.sk, {
2405
+ exp: (Date.now() + 5 * 60 * 1000) / 1000,
2406
+ }),
2407
+ },
2408
+ };
2409
+ }
2410
+
2245
2411
  // download
2246
2412
  const downloadParams = {
2247
2413
  oldBlocklet: { ...oldBlocklet },
@@ -2255,7 +2421,6 @@ class BlockletManager extends BaseBlockletManager {
2255
2421
  await this.onDownload({ ...downloadParams, throwOnError: true });
2256
2422
  return states.blocklet.getBlocklet(did);
2257
2423
  }
2258
-
2259
2424
  const ticket = this.installQueue.push(
2260
2425
  {
2261
2426
  entity: 'blocklet',
@@ -2469,6 +2634,14 @@ class BlockletManager extends BaseBlockletManager {
2469
2634
  // Update dynamic component meta in blocklet settings
2470
2635
  await this._ensureDynamicChildrenInSettings(blocklet);
2471
2636
 
2637
+ if (context?.headers?.['x-download-token']) {
2638
+ await states.blocklet.updateBlocklet(did, {
2639
+ tokens: {
2640
+ paidBlockletDownloadToken: context.headers['x-download-token'],
2641
+ },
2642
+ });
2643
+ }
2644
+
2472
2645
  states.notification.create({
2473
2646
  title: 'Blocklet Installed',
2474
2647
  description: `Blocklet ${meta.name}@${meta.version} is installed successfully. (Source: ${
@@ -2486,7 +2659,12 @@ class BlockletManager extends BaseBlockletManager {
2486
2659
  logger.error('failed to install blocklet', { name, did, version, error: err });
2487
2660
  try {
2488
2661
  await this._rollback('install', did, oldBlocklet);
2489
- this.emit(BlockletEvents.installFailed, { meta: { did } });
2662
+ this.emit(BlockletEvents.installFailed, {
2663
+ meta: { did },
2664
+ error: {
2665
+ message: err.message,
2666
+ },
2667
+ });
2490
2668
  states.notification.create({
2491
2669
  title: 'Blocklet Install Failed',
2492
2670
  description: `Blocklet ${meta.name}@${meta.version} install failed with error: ${err.message}`,
@@ -2657,11 +2835,23 @@ class BlockletManager extends BaseBlockletManager {
2657
2835
  }
2658
2836
 
2659
2837
  /**
2660
- * for download: cwd, tarball, did
2661
- * for verify: verify, integrity
2662
- * for cancel control: ctrlStore, rootDid
2838
+ *
2839
+ *
2840
+ * @param {{
2841
+ * url: string,
2842
+ * cwd: string,
2843
+ * tarball: string,
2844
+ * did: string,
2845
+ * integrity: string,
2846
+ * verify: boolean = true,
2847
+ * ctrlStore: {},
2848
+ * rootDid: string,
2849
+ * context: {} = {},
2850
+ * }} { url, cwd, tarball, did, integrity, verify = true, ctrlStore = {}, rootDid, context = {} }
2851
+ * @return {*}
2852
+ * @memberof BlockletManager
2663
2853
  */
2664
- async _downloadTarball({ url, cwd, tarball, did, integrity, verify = true, ctrlStore = {}, rootDid }) {
2854
+ async _downloadTarball({ url, cwd, tarball, did, integrity, verify = true, ctrlStore = {}, rootDid, context = {} }) {
2665
2855
  fs.mkdirSync(cwd, { recursive: true });
2666
2856
 
2667
2857
  const tarballName = url.split('/').slice(-1)[0];
@@ -2673,7 +2863,7 @@ class BlockletManager extends BaseBlockletManager {
2673
2863
  const cachedTarFile = await this._getCachedTarFile(integrity);
2674
2864
  if (cachedTarFile) {
2675
2865
  logger.info('found cache tarFile', { did, tarballName, integrity });
2676
- await fs.move(cachedTarFile, tarballPath);
2866
+ await fs.move(cachedTarFile, tarballPath, { overwrite: true });
2677
2867
  } else if (protocol.startsWith('file')) {
2678
2868
  await fs.copy(decodeURIComponent(pathname), tarballPath);
2679
2869
  } else {
@@ -2684,7 +2874,7 @@ class BlockletManager extends BaseBlockletManager {
2684
2874
  }
2685
2875
  ctrlStore[rootDid].set(did, cancelCtrl);
2686
2876
 
2687
- await downloadFile(url, path.join(cwd, tarballName), { cancelCtrl });
2877
+ await downloadFile(url, path.join(cwd, tarballName), { cancelCtrl }, context);
2688
2878
 
2689
2879
  if (ctrlStore[rootDid]) {
2690
2880
  ctrlStore[rootDid].delete(did);
@@ -2773,12 +2963,16 @@ class BlockletManager extends BaseBlockletManager {
2773
2963
  }
2774
2964
 
2775
2965
  /**
2776
- * download bundle, verify bundle, resolve bundle to installDir
2777
- * @param {object} meta
2778
- * @param {string} rootDid root blocklet did of the blocklet to be downloaded
2779
- * @return {object} { isCancelled: Boolean }
2966
+ *
2967
+ *
2968
+ * @param {*} meta
2969
+ * @param {*} rootDid
2970
+ * @param {*} url
2971
+ * @param {{}} [context={}]
2972
+ * @return {*}
2973
+ * @memberof BlockletManager
2780
2974
  */
2781
- async _downloadBundle(meta, rootDid, url) {
2975
+ async _downloadBundle(meta, rootDid, url, context = {}) {
2782
2976
  const { bundleName: name, bundleDid: did, version, dist = {} } = meta;
2783
2977
  const { tarball, integrity } = dist;
2784
2978
 
@@ -2806,6 +3000,7 @@ class BlockletManager extends BaseBlockletManager {
2806
3000
  ctrlStore: this.downloadCtrls,
2807
3001
  rootDid,
2808
3002
  url,
3003
+ context,
2809
3004
  });
2810
3005
  logger.info('downloaded blocklet tar file', { name, version, tarballPath });
2811
3006
  if (tarballPath === downloadFile.CANCEL) {
@@ -2826,7 +3021,16 @@ class BlockletManager extends BaseBlockletManager {
2826
3021
  }
2827
3022
  }
2828
3023
 
2829
- async _downloadBlocklet(blocklet, oldBlocklet = {}) {
3024
+ /**
3025
+ *
3026
+ *
3027
+ * @param {{}} blocklet
3028
+ * @param {{}} [oldBlocklet={}]
3029
+ * @param {{}} [context={}]
3030
+ * @return {*}
3031
+ * @memberof BlockletManager
3032
+ */
3033
+ async _downloadBlocklet(blocklet, oldBlocklet = {}, context = {}) {
2830
3034
  const {
2831
3035
  meta: { name, did },
2832
3036
  } = blocklet;
@@ -2880,7 +3084,7 @@ class BlockletManager extends BaseBlockletManager {
2880
3084
  tarball: get(meta, 'dist.tarball'),
2881
3085
  registryUrl: blocklet.source === BlockletSource.registry ? blocklet.deployedFrom : undefined,
2882
3086
  });
2883
- tasks.push(this._downloadBundle(meta, did, url));
3087
+ tasks.push(this._downloadBundle(meta, did, url, context));
2884
3088
  }
2885
3089
  const results = await Promise.all(tasks);
2886
3090
  if (results.find((x) => x.isCancelled)) {
@@ -2976,6 +3180,9 @@ class BlockletManager extends BaseBlockletManager {
2976
3180
  const logsDir = path.join(this.dataDirs.logs, name);
2977
3181
  const cacheDir = path.join(this.dataDirs.cache, name);
2978
3182
 
3183
+ // Cleanup db
3184
+ await this.teamManager.deleteTeam(blocklet.meta.did);
3185
+
2979
3186
  // Cleanup disk storage
2980
3187
  fs.removeSync(cacheDir);
2981
3188
  if (keepData === false) {
@@ -3068,6 +3275,78 @@ class BlockletManager extends BaseBlockletManager {
3068
3275
 
3069
3276
  return blocklet;
3070
3277
  }
3278
+
3279
+ async _getLatestBlockletVersionFromStore({ blocklet, version }) {
3280
+ const { did, bundleDid } = blocklet.meta;
3281
+
3282
+ let versions = this.cachedBlockletVersions.get(did);
3283
+
3284
+ if (!versions) {
3285
+ const { blockletRegistryList } = await states.node.read();
3286
+ const tasks = blockletRegistryList.map((registry) =>
3287
+ this.registry
3288
+ .getBlockletMeta({ did: bundleDid, registryUrl: registry.url })
3289
+ .then((item) => ({ did, version: item.version, registryUrl: registry.url }))
3290
+ .catch((error) => {
3291
+ if (error.response && error.response.status === 404) {
3292
+ return;
3293
+ }
3294
+ logger.error('get blocklet meta from registry failed', { did, error });
3295
+ })); // prettier-ignore
3296
+
3297
+ versions = await Promise.all(tasks);
3298
+ this.cachedBlockletVersions.set(did, versions);
3299
+ }
3300
+
3301
+ versions = versions.filter((item) => item && semver.gt(item.version, version));
3302
+
3303
+ if (versions.length === 0) {
3304
+ return null;
3305
+ }
3306
+
3307
+ // When new version found from the store where the blocklet was installed from, we should use that store first
3308
+ if (blocklet.source === BlockletSource.registry && blocklet.deployedFrom) {
3309
+ const latestFromSameRegistry = versions.find((x) => x.registryUrl === blocklet.deployedFrom);
3310
+ if (latestFromSameRegistry) {
3311
+ return latestFromSameRegistry;
3312
+ }
3313
+ }
3314
+
3315
+ // Otherwise try upgrading from other store
3316
+ let latestBlockletVersion = versions[0];
3317
+ versions.forEach((item) => {
3318
+ if (semver.lt(latestBlockletVersion.version, item.version)) {
3319
+ latestBlockletVersion = item;
3320
+ }
3321
+ });
3322
+
3323
+ return latestBlockletVersion;
3324
+ }
3325
+
3326
+ async _getLatestBlockletVersionFromUrl({ blocklet, version }) {
3327
+ const { did } = blocklet.meta;
3328
+
3329
+ let versions = this.cachedBlockletVersions.get(did);
3330
+
3331
+ if (!versions) {
3332
+ try {
3333
+ const item = await getBlockletMetaFromUrl(blocklet.deployedFrom);
3334
+ versions = [{ did, version: item.version }];
3335
+ } catch (error) {
3336
+ logger.error('get blocklet meta from url failed when checking latest version', { did, error });
3337
+ versions = [];
3338
+ }
3339
+ }
3340
+
3341
+ this.cachedBlockletVersions.set(did, versions);
3342
+ versions = versions.filter((item) => item && semver.gt(item.version, version));
3343
+
3344
+ if (versions.length === 0) {
3345
+ return null;
3346
+ }
3347
+
3348
+ return versions[0];
3349
+ }
3071
3350
  }
3072
3351
 
3073
3352
  module.exports = BlockletManager;