@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.
- package/lib/api/node.js +14 -1
- package/lib/api/team.js +54 -22
- package/lib/blocklet/manager/disk.js +378 -99
- package/lib/cert.js +10 -5
- package/lib/event.js +8 -0
- package/lib/index.js +8 -1
- package/lib/router/helper.js +2 -1
- package/lib/router/index.js +2 -5
- package/lib/router/manager.js +0 -34
- package/lib/states/audit-log.js +6 -1
- package/lib/states/session.js +7 -1
- package/lib/states/user.js +41 -5
- package/lib/team/manager.js +34 -4
- package/lib/util/blocklet.js +2 -22
- package/lib/util/index.js +4 -1
- package/package.json +17 -16
|
@@ -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
|
-
*
|
|
213
|
-
*
|
|
214
|
-
* @param {
|
|
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(
|
|
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({
|
|
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 },
|
|
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
|
-
|
|
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
|
-
|
|
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, {
|
|
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
|
-
|
|
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
|
-
|
|
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({
|
|
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
|
|
1728
|
-
{
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
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,
|
|
1870
|
+
checkDuplicateComponents([...blocklet.children, newChild]);
|
|
1738
1871
|
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
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,
|
|
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
|
-
|
|
2066
|
-
if (latestFromSameRegistry) {
|
|
2067
|
-
return latestFromSameRegistry;
|
|
2068
|
-
}
|
|
2187
|
+
return this._getLatestBlockletVersionFromStore({ blocklet, version });
|
|
2069
2188
|
}
|
|
2070
2189
|
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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, {
|
|
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
|
-
*
|
|
2661
|
-
*
|
|
2662
|
-
*
|
|
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
|
-
*
|
|
2777
|
-
*
|
|
2778
|
-
* @param {
|
|
2779
|
-
* @
|
|
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
|
-
|
|
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;
|