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

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.
@@ -1,7 +1,6 @@
1
1
  /* eslint-disable no-underscore-dangle */
2
2
  /* eslint-disable no-await-in-loop */
3
3
  const fs = require('fs-extra');
4
- const { fileURLToPath } = require('url');
5
4
  const path = require('path');
6
5
  const flat = require('flat');
7
6
  const get = require('lodash/get');
@@ -17,14 +16,12 @@ const didDocument = require('@abtnode/util/lib/did-document');
17
16
  const { sign } = require('@arcblock/jwt');
18
17
  const { isValid: isValidDid } = require('@arcblock/did');
19
18
  const { verifyPresentation } = require('@arcblock/vc');
20
- const { toBase58 } = require('@ocap/util');
21
19
  const { toSvg: createDidLogo } =
22
20
  process.env.NODE_ENV !== 'test' ? require('@arcblock/did-motif') : require('@arcblock/did-motif/dist/did-motif.cjs');
23
21
  const getBlockletInfo = require('@blocklet/meta/lib/info');
22
+ const sleep = require('@abtnode/util/lib/sleep');
24
23
 
25
24
  const logger = require('@abtnode/logger')('@abtnode/core:blocklet:manager');
26
- const downloadFile = require('@abtnode/util/lib/download-file');
27
- const Lock = require('@abtnode/util/lib/lock');
28
25
  const { getVcFromPresentation } = require('@abtnode/util/lib/vc');
29
26
  const {
30
27
  VC_TYPE_BLOCKLET_PURCHASE,
@@ -45,17 +42,16 @@ const {
45
42
  forEachChildSync,
46
43
  forEachBlocklet,
47
44
  getComponentId,
48
- getComponentBundleId,
49
45
  isPreferenceKey,
50
46
  getRolesFromAuthConfig,
51
47
  } = require('@blocklet/meta/lib/util');
52
48
  const getComponentProcessId = require('@blocklet/meta/lib/get-component-process-id');
53
- const validateBlockletEntry = require('@blocklet/meta/lib/entry');
54
49
  const toBlockletDid = require('@blocklet/meta/lib/did');
55
50
  const { validateMeta } = require('@blocklet/meta/lib/validate');
56
51
  const { update: updateMetaFile } = require('@blocklet/meta/lib/file');
57
52
  const { titleSchema, mountPointSchema, environmentNameSchema } = require('@blocklet/meta/lib/schema');
58
53
  const hasReservedKey = require('@blocklet/meta/lib/has-reserved-key');
54
+ const Lock = require('@abtnode/util/lib/lock');
59
55
 
60
56
  const { toExternalBlocklet } = toBlockletDid;
61
57
 
@@ -63,7 +59,6 @@ const {
63
59
  BlockletStatus,
64
60
  BlockletSource,
65
61
  BlockletEvents,
66
- BLOCKLET_BUNDLE_FOLDER,
67
62
  BLOCKLET_MODES,
68
63
  BlockletGroup,
69
64
  fromBlockletStatus,
@@ -86,7 +81,6 @@ const {
86
81
  } = require('../../util/get-accessible-external-node-ip');
87
82
  const {
88
83
  getBlockletMetaFromUrl,
89
- ensureBlockletExpanded,
90
84
  getAppSystemEnvironments,
91
85
  getComponentSystemEnvironments,
92
86
  getAppOverwrittenEnvironments,
@@ -99,8 +93,6 @@ const {
99
93
  checkBlockletProcessHealthy,
100
94
  validateBlocklet,
101
95
  statusMap,
102
- expandTarball,
103
- verifyIntegrity,
104
96
  pruneBlockletBundle,
105
97
  getDiskInfo,
106
98
  getUpdateMetaList,
@@ -110,7 +102,6 @@ const {
110
102
  checkDuplicateComponents,
111
103
  getDiffFiles,
112
104
  getBundleDir,
113
- needBlockletDownload,
114
105
  findAvailableDid,
115
106
  ensureMeta,
116
107
  getBlocklet,
@@ -131,12 +122,13 @@ const runMigrationScripts = require('../migration');
131
122
  const hooks = require('../hooks');
132
123
  const { formatName, getDidDomainForBlocklet } = require('../../util/get-domain-for-blocklet');
133
124
  const handleInstanceInStore = require('../../util/public-to-store');
134
- const { getNFTState, getServerDidDomain } = require('../../util');
135
125
  const { BlockletRuntimeMonitor } = require('../../monitor/blocklet-runtime-monitor');
136
126
  const getHistoryList = require('../../monitor/get-history-list');
137
127
  const { SpacesBackup } = require('../storage/backup/spaces');
138
128
  const { SpacesRestore } = require('../storage/restore/spaces');
139
129
  const installFromBackup = require('./helper/install-from-backup');
130
+ const { resolveDownload, resolveDiffDownload } = require('../downloader/resolve-download');
131
+ const BlockletDownloader = require('../downloader/blocklet-downloader');
140
132
 
141
133
  const {
142
134
  isInProgress,
@@ -150,8 +142,6 @@ const {
150
142
 
151
143
  const statusLock = new Lock('blocklet-status-lock');
152
144
 
153
- const asyncFs = fs.promises;
154
-
155
145
  const pm2StatusMap = {
156
146
  online: BlockletStatus.running,
157
147
  stop: BlockletStatus.stopped,
@@ -211,15 +201,6 @@ class BlockletManager extends BaseBlockletManager {
211
201
  this.installQueue = installQueue;
212
202
  this.teamManager = teamManager;
213
203
 
214
- /**
215
- * { did: Map({ <childDid>: <downloadFile.cancelCtrl> }) }
216
- */
217
- this.downloadCtrls = {};
218
- /**
219
- * { [download-did-version]: Lock }
220
- */
221
- this.downloadLocks = {};
222
-
223
204
  // cached installed blocklets for performance
224
205
  this.cachedBlocklets = null;
225
206
 
@@ -231,6 +212,12 @@ class BlockletManager extends BaseBlockletManager {
231
212
 
232
213
  this.runtimeMonitor = new BlockletRuntimeMonitor({ historyLength: MONITOR_HISTORY_LENGTH, states });
233
214
 
215
+ this.blockletDownloader = new BlockletDownloader({
216
+ installDir: this.installDir,
217
+ downloadDir: this.dataDirs.tmp,
218
+ cache: states.cache,
219
+ });
220
+
234
221
  if (daemon) {
235
222
  blockletPm2Events.on('online', (data) => this._syncPm2Status('online', data.blockletDid));
236
223
  blockletPm2Events.on('stop', (data) => this._syncPm2Status('stop', data.blockletDid));
@@ -877,7 +864,7 @@ class BlockletManager extends BaseBlockletManager {
877
864
  }
878
865
 
879
866
  if (blocklet.status === BlockletStatus.downloading) {
880
- await this._cancelDownload(blocklet.meta, context);
867
+ await this.blockletDownloader.cancelDownload(blocklet.meta.did);
881
868
  } else if (blocklet.status === BlockletStatus.waiting) {
882
869
  await this._cancelWaiting(blocklet.meta, context);
883
870
  }
@@ -920,8 +907,8 @@ class BlockletManager extends BaseBlockletManager {
920
907
  const blocklet = await this.ensureBlocklet(did, { throwOnNotExist: false });
921
908
  return blocklet;
922
909
  } catch (e) {
923
- logger.error('get blocklet detail error', { error: e.message });
924
- return null;
910
+ logger.error('get blocklet detail error', { error: e });
911
+ return null; // TODO: 直接返回没有 runtime info 的数据
925
912
  }
926
913
  }
927
914
 
@@ -1549,8 +1536,7 @@ class BlockletManager extends BaseBlockletManager {
1549
1536
  });
1550
1537
  logger.info('add blocklet for dev', { did, version, meta });
1551
1538
 
1552
- const oldBlocklet = { children: children.filter((x) => x.dynamic) }; // let downloader skip re-downloading dynamic blocklet
1553
- await this._downloadBlocklet(added, oldBlocklet);
1539
+ await this._downloadBlocklet(added);
1554
1540
 
1555
1541
  // Add Config
1556
1542
  await this._setConfigsFromMeta(did);
@@ -1614,7 +1600,7 @@ class BlockletManager extends BaseBlockletManager {
1614
1600
  await states.blocklet.addChildren(rootDid, [component]);
1615
1601
 
1616
1602
  const newBlocklet = await states.blocklet.getBlocklet(rootDid);
1617
- await this._downloadBlocklet(newBlocklet, existRoot);
1603
+ await this._downloadBlocklet(newBlocklet);
1618
1604
  await states.blocklet.setBlockletStatus(rootDid, BlockletStatus.stopped);
1619
1605
 
1620
1606
  await this._upsertDynamicNavigation(existRoot.meta.did, component, { skipNavigation });
@@ -1862,7 +1848,7 @@ class BlockletManager extends BaseBlockletManager {
1862
1848
 
1863
1849
  // download bundle
1864
1850
  try {
1865
- const { isCancelled } = await this._downloadBlocklet(blocklet, oldBlocklet, context);
1851
+ const { isCancelled } = await this._downloadBlocklet(blocklet, context);
1866
1852
 
1867
1853
  if (isCancelled) {
1868
1854
  logger.info('Download was canceled', { name, did, version });
@@ -2388,7 +2374,7 @@ class BlockletManager extends BaseBlockletManager {
2388
2374
 
2389
2375
  async _installFromUpload({ file, did, diffVersion, deleteSet, context }) {
2390
2376
  logger.info('install blocklet', { from: 'upload file' });
2391
- const { cwd, tarFile } = await this._downloadFromUpload(file);
2377
+ const { tarFile } = await this._downloadFromUpload(file);
2392
2378
 
2393
2379
  // diff deploy
2394
2380
  if (did && diffVersion) {
@@ -2410,14 +2396,17 @@ class BlockletManager extends BaseBlockletManager {
2410
2396
  throw new Error(`Can not deploy blocklet when it is ${fromBlockletStatus(oldBlocklet.status)}`);
2411
2397
  }
2412
2398
 
2413
- const { meta } = await this._resolveDiffDownload(cwd, tarFile, deleteSet, oldBlocklet.meta);
2399
+ const { meta } = await resolveDiffDownload(tarFile, this.installDir, {
2400
+ deleteSet,
2401
+ meta: oldBlocklet.meta,
2402
+ });
2414
2403
  const newBlocklet = await states.blocklet.getBlocklet(did);
2415
2404
  newBlocklet.meta = ensureMeta(meta);
2416
2405
  newBlocklet.source = BlockletSource.upload;
2417
2406
  newBlocklet.deployedFrom = `Upload by ${context.user.fullName}`;
2418
2407
  newBlocklet.children = await this._getChildrenForInstallation(meta);
2419
2408
  await validateBlocklet(newBlocklet);
2420
- await this._downloadBlocklet(newBlocklet, oldBlocklet);
2409
+ await this._downloadBlocklet(newBlocklet);
2421
2410
 
2422
2411
  return this._upgradeBlocklet({
2423
2412
  oldBlocklet,
@@ -2427,7 +2416,7 @@ class BlockletManager extends BaseBlockletManager {
2427
2416
  }
2428
2417
 
2429
2418
  // full deploy
2430
- const { meta } = await this._resolveDownload(cwd, tarFile);
2419
+ const { meta } = await resolveDownload(tarFile, this.installDir);
2431
2420
  const oldBlocklet = await this._getBlockletForInstallation(meta.did);
2432
2421
 
2433
2422
  // full deploy - upgrade
@@ -2444,7 +2433,7 @@ class BlockletManager extends BaseBlockletManager {
2444
2433
  newBlocklet.children = await this._getChildrenForInstallation(meta);
2445
2434
 
2446
2435
  await validateBlocklet(newBlocklet);
2447
- await this._downloadBlocklet(newBlocklet, oldBlocklet);
2436
+ await this._downloadBlocklet(newBlocklet);
2448
2437
 
2449
2438
  return this._upgradeBlocklet({
2450
2439
  oldBlocklet,
@@ -2472,11 +2461,7 @@ class BlockletManager extends BaseBlockletManager {
2472
2461
  await checkDuplicateAppSk({ did: meta.did, states });
2473
2462
 
2474
2463
  // download
2475
- await this._downloadBlocklet(
2476
- blocklet,
2477
- // let downloader skip re-downloading dynamic blocklet
2478
- { children: children.filter((x) => x.dynamic) }
2479
- );
2464
+ await this._downloadBlocklet(blocklet);
2480
2465
 
2481
2466
  return this._installBlocklet({
2482
2467
  did: meta.did,
@@ -2501,7 +2486,7 @@ class BlockletManager extends BaseBlockletManager {
2501
2486
  }) {
2502
2487
  logger.info('install blocklet', { from: 'upload file' });
2503
2488
  // download
2504
- const { cwd, tarFile } = await this._downloadFromUpload(file);
2489
+ const { tarFile } = await this._downloadFromUpload(file);
2505
2490
 
2506
2491
  const oldBlocklet = await this._getBlockletForInstallation(rootDid);
2507
2492
  if (!oldBlocklet) {
@@ -2530,10 +2515,10 @@ class BlockletManager extends BaseBlockletManager {
2530
2515
  throw new Error('Blocklet version changed when diff deploying');
2531
2516
  }
2532
2517
 
2533
- meta = (await this._resolveDiffDownload(cwd, tarFile, deleteSet, oldChild.meta)).meta;
2518
+ meta = (await resolveDiffDownload(tarFile, this.installDir, { deleteSet, meta: oldChild.meta })).meta;
2534
2519
  } else {
2535
2520
  // full deploy
2536
- meta = (await this._resolveDownload(cwd, tarFile)).meta;
2521
+ meta = (await resolveDownload(tarFile, this.installDir)).meta;
2537
2522
  }
2538
2523
 
2539
2524
  if (meta.did === rootDid) {
@@ -2567,7 +2552,7 @@ class BlockletManager extends BaseBlockletManager {
2567
2552
 
2568
2553
  await this._upsertDynamicNavigation(newBlocklet.meta.did, newChild, { skipNavigation });
2569
2554
 
2570
- await this._downloadBlocklet(newBlocklet, oldBlocklet);
2555
+ await this._downloadBlocklet(newBlocklet);
2571
2556
 
2572
2557
  await validateBlocklet(newBlocklet);
2573
2558
 
@@ -2649,6 +2634,7 @@ class BlockletManager extends BaseBlockletManager {
2649
2634
  {
2650
2635
  name: 'delete-expired-external-blocklet',
2651
2636
  time: '0 */30 * * * *', // 30min
2637
+ options: { runOnInit: false },
2652
2638
  fn: () => this._deleteExpiredExternalBlocklet(),
2653
2639
  },
2654
2640
  {
@@ -2906,137 +2892,6 @@ class BlockletManager extends BaseBlockletManager {
2906
2892
  return newBlocklet;
2907
2893
  }
2908
2894
 
2909
- /**
2910
- * decompress file, format dir and move to installDir
2911
- * @param {string} cwd
2912
- * @param {string} tarFile
2913
- * @param {object} originalMeta for verification
2914
- * @param {object} option
2915
- */
2916
- async _resolveDownload(cwd, tarFile, originalMeta, { removeTarFile = true } = {}) {
2917
- const downloadDir = path.join(cwd, `${path.basename(tarFile, path.extname(tarFile))}`);
2918
- const tmp = `${downloadDir}-tmp`;
2919
- try {
2920
- await expandTarball({ source: tarFile, dest: tmp, strip: 0 });
2921
- } catch (error) {
2922
- logger.error('expand blocklet tar file error');
2923
- throw error;
2924
- } finally {
2925
- if (removeTarFile) {
2926
- fs.removeSync(tarFile);
2927
- }
2928
- }
2929
- let installDir;
2930
- let meta;
2931
- try {
2932
- // resolve dir
2933
- let dir = tmp;
2934
- const files = await asyncFs.readdir(dir);
2935
- if (files.includes('package')) {
2936
- dir = path.join(tmp, 'package');
2937
- } else if (files.length === 1) {
2938
- const d = path.join(dir, files[0]);
2939
- if ((await asyncFs.stat(d)).isDirectory()) {
2940
- dir = d;
2941
- }
2942
- }
2943
-
2944
- if (fs.existsSync(path.join(dir, BLOCKLET_BUNDLE_FOLDER))) {
2945
- dir = path.join(dir, BLOCKLET_BUNDLE_FOLDER);
2946
- }
2947
-
2948
- logger.info('Move downloadDir to installDir', { downloadDir });
2949
- await fs.move(dir, downloadDir, { overwrite: true });
2950
- fs.removeSync(tmp);
2951
-
2952
- meta = getBlockletMeta(downloadDir, { ensureMain: true });
2953
- const { did, name, version } = meta;
2954
-
2955
- // validate
2956
- if (
2957
- originalMeta &&
2958
- (originalMeta.did !== did || originalMeta.name !== name || originalMeta.version !== version)
2959
- ) {
2960
- logger.error('Meta has differences', { originalMeta, meta });
2961
- throw new Error('There are differences between the meta from tarball file and the original meta');
2962
- }
2963
- await validateBlockletEntry(downloadDir, meta);
2964
-
2965
- await ensureBlockletExpanded(meta, downloadDir);
2966
-
2967
- installDir = getBundleDir(this.installDir, meta);
2968
- if (fs.existsSync(installDir)) {
2969
- fs.removeSync(installDir);
2970
- logger.info('cleanup blocklet upgrade dir', { name, version, installDir });
2971
- }
2972
-
2973
- fs.mkdirSync(installDir, { recursive: true });
2974
-
2975
- await fs.move(downloadDir, installDir, { overwrite: true });
2976
- } catch (error) {
2977
- fs.removeSync(downloadDir);
2978
- fs.removeSync(tmp);
2979
- throw error;
2980
- }
2981
-
2982
- return { meta, installDir };
2983
- }
2984
-
2985
- async _resolveDiffDownload(cwd, tarFile, deleteSet, bundle) {
2986
- logger.info('Resolve diff download', { tarFile, cwd });
2987
- const downloadDir = path.join(cwd, `${path.basename(tarFile, path.extname(tarFile))}`);
2988
- const diffDir = `${downloadDir}-diff`;
2989
- try {
2990
- await expandTarball({ source: tarFile, dest: diffDir, strip: 0 });
2991
- fs.removeSync(tarFile);
2992
- } catch (error) {
2993
- fs.removeSync(tarFile);
2994
- logger.error('expand blocklet tar file error');
2995
- throw error;
2996
- }
2997
- logger.info('Copy installDir to downloadDir', { installDir: this.installDir, downloadDir });
2998
- await fs.copy(getBundleDir(this.installDir, bundle), downloadDir);
2999
- try {
3000
- // delete
3001
- logger.info('Delete files from downloadDir', { fileNum: deleteSet.length });
3002
- // eslint-disable-next-line no-restricted-syntax
3003
- for (const file of deleteSet) {
3004
- await fs.remove(path.join(downloadDir, file));
3005
- }
3006
- // walk & cover
3007
- logger.info('Move files from diffDir to downloadDir', { diffDir, downloadDir });
3008
- const walkDiff = async (dir) => {
3009
- const files = await asyncFs.readdir(dir);
3010
- // eslint-disable-next-line no-restricted-syntax
3011
- for (const file of files) {
3012
- const p = path.join(dir, file);
3013
- const stat = await asyncFs.stat(p);
3014
- if (stat.isDirectory()) {
3015
- await walkDiff(p);
3016
- } else if (stat.isFile()) {
3017
- await fs.move(p, path.join(downloadDir, path.relative(diffDir, p)), { overwrite: true });
3018
- }
3019
- }
3020
- };
3021
- await walkDiff(diffDir);
3022
- fs.removeSync(diffDir);
3023
- const meta = getBlockletMeta(downloadDir, { ensureMain: true });
3024
-
3025
- await ensureBlockletExpanded(meta, downloadDir);
3026
-
3027
- // move to installDir
3028
- const bundleDir = getBundleDir(this.installDir, meta);
3029
- logger.info('Move downloadDir to installDir', { downloadDir, bundleDir });
3030
- await fs.move(downloadDir, bundleDir, { overwrite: true });
3031
-
3032
- return { meta, installDir: bundleDir };
3033
- } catch (error) {
3034
- fs.removeSync(downloadDir);
3035
- fs.removeSync(diffDir);
3036
- throw error;
3037
- }
3038
- }
3039
-
3040
2895
  /**
3041
2896
  * @param {string} opt.did
3042
2897
  * @param {object} opt.context
@@ -3267,275 +3122,33 @@ class BlockletManager extends BaseBlockletManager {
3267
3122
  await states.blockletExtras.setSettings(did, { children: dynamicChildren });
3268
3123
  }
3269
3124
 
3270
- /**
3271
- *
3272
- *
3273
- * @param {{
3274
- * url: string,
3275
- * cwd: string,
3276
- * tarball: string,
3277
- * did: string,
3278
- * integrity: string,
3279
- * verify: boolean = true,
3280
- * ctrlStore: {},
3281
- * rootDid: string,
3282
- * context: {} = {},
3283
- * }} { url, cwd, tarball, did, integrity, verify = true, ctrlStore = {}, rootDid, context = {} }
3284
- * @return {*}
3285
- * @memberof BlockletManager
3286
- */
3287
- async _downloadTarball({ url, cwd, tarball, did, integrity, verify = true, ctrlStore = {}, rootDid, context = {} }) {
3288
- fs.mkdirSync(cwd, { recursive: true });
3289
-
3290
- const tarballName = url.split('/').slice(-1)[0];
3291
-
3292
- const tarballPath = path.join(cwd, tarballName);
3293
-
3294
- const { protocol } = new URL(url);
3295
-
3296
- const cachedTarFile = await this._getCachedTarFile(integrity);
3297
- if (cachedTarFile) {
3298
- logger.info('found cache tarFile', { did, tarballName, integrity });
3299
- await fs.move(cachedTarFile, tarballPath, { overwrite: true });
3300
- } else if (protocol.startsWith('file')) {
3301
- await fs.copy(decodeURIComponent(fileURLToPath(url)), tarballPath);
3302
- } else {
3303
- const cancelCtrl = new downloadFile.CancelCtrl();
3304
-
3305
- if (!ctrlStore[rootDid]) {
3306
- ctrlStore[rootDid] = new Map();
3307
- }
3308
- ctrlStore[rootDid].set(did, cancelCtrl);
3309
-
3310
- const headers = context.headers ? cloneDeep(context.headers) : {};
3311
- const exist = (context.downloadTokenList || []).find((x) => x.did === did);
3312
- if (exist) {
3313
- headers['x-download-token'] = exist.token;
3314
- }
3315
-
3316
- await downloadFile(url, path.join(cwd, tarballName), { cancelCtrl }, { ...context, headers });
3317
-
3318
- if (ctrlStore[rootDid]) {
3319
- ctrlStore[rootDid].delete(did);
3320
- if (!ctrlStore[rootDid].size) {
3321
- delete ctrlStore[rootDid];
3322
- }
3323
- }
3324
-
3325
- if (cancelCtrl.isCancelled) {
3326
- return downloadFile.CANCEL;
3327
- }
3328
- }
3329
-
3330
- if (verify) {
3331
- try {
3332
- await verifyIntegrity({ file: tarballPath, integrity });
3333
- } catch (error) {
3334
- logger.error('verify integrity error', { error, tarball, url });
3335
- throw new Error(`${tarball} integrity check failed.`);
3336
- }
3337
- }
3338
-
3339
- return tarballPath;
3340
- }
3341
-
3342
- /**
3343
- * use LRU algorithm
3344
- */
3345
- async _addCacheTarFile(tarballPath, integrity) {
3346
- // eslint-disable-next-line no-param-reassign
3347
- integrity = toBase58(integrity);
3348
-
3349
- // move tarball to cache dir
3350
- const cwd = path.join(this.dataDirs.tmp, 'download-cache');
3351
- const cachePath = path.join(cwd, `${integrity}.tar.gz`);
3352
- await fs.ensureDir(cwd);
3353
- await fs.move(tarballPath, cachePath, { overwrite: true });
3354
-
3355
- const key = 'blocklet:manager:downloadCache';
3356
- const cacheList = (await states.cache.get(key)) || [];
3357
- const exist = cacheList.find((x) => x.integrity === integrity);
3358
-
3359
- // update
3360
- if (exist) {
3361
- logger.info('update cache tarFile', { base58: integrity });
3362
- exist.accessAt = Date.now();
3363
- await states.cache.set(key, cacheList);
3364
- return;
3365
- }
3366
-
3367
- // add
3368
- cacheList.push({ integrity, accessAt: Date.now() });
3369
- if (cacheList.length > 10) {
3370
- // find and remove
3371
- let minIndex = 0;
3372
- let min = cacheList[0];
3373
- cacheList.forEach((x, i) => {
3374
- if (x.accessAt < min.accessAt) {
3375
- minIndex = i;
3376
- min = x;
3377
- }
3378
- });
3379
-
3380
- cacheList.splice(minIndex, 1);
3381
- await fs.remove(path.join(cwd, `${min.integrity}.tar.gz`));
3382
- logger.info('remove cache tarFile', { base58: min.integrity });
3383
- }
3384
- logger.info('add cache tarFile', { base58: integrity });
3385
-
3386
- // update
3387
- await states.cache.set(key, cacheList);
3388
- }
3389
-
3390
- async _getCachedTarFile(integrity) {
3391
- // eslint-disable-next-line no-param-reassign
3392
- integrity = toBase58(integrity);
3393
-
3394
- const cwd = path.join(this.dataDirs.tmp, 'download-cache');
3395
- const cachePath = path.join(cwd, `${integrity}.tar.gz`);
3396
-
3397
- if (fs.existsSync(cachePath)) {
3398
- return cachePath;
3399
- }
3400
-
3401
- return null;
3402
- }
3403
-
3404
- /**
3405
- *
3406
- *
3407
- * @param {*} meta
3408
- * @param {*} rootDid
3409
- * @param {*} url
3410
- * @param {{}} [context={}]
3411
- * @return {*}
3412
- * @memberof BlockletManager
3413
- */
3414
- async _downloadBundle(meta, rootDid, url, context = {}) {
3415
- const { bundleName: name, bundleDid: did, version, dist = {} } = meta;
3416
- const { tarball, integrity } = dist;
3417
-
3418
- const lockName = `download-${did}-${version}`;
3419
- let lock = this.downloadLocks[lockName];
3420
- if (!lock) {
3421
- lock = new Lock(lockName);
3422
- this.downloadLocks[lockName] = lock;
3423
- }
3424
-
3425
- try {
3426
- await lock.acquire();
3427
- logger.info('downloaded blocklet for installing', { name, version, tarball, integrity });
3428
- const cwd = path.join(this.dataDirs.tmp, 'download', name);
3429
- await fs.ensureDir(cwd);
3430
- logger.info('start download blocklet', { name, version, cwd, tarball, integrity });
3431
- const tarballPath = await this._downloadTarball({
3432
- name,
3433
- did,
3434
- version,
3435
- cwd,
3436
- tarball,
3437
- integrity,
3438
- verify: true,
3439
- ctrlStore: this.downloadCtrls,
3440
- rootDid,
3441
- url,
3442
- context,
3443
- });
3444
- logger.info('downloaded blocklet tar file', { name, version, tarballPath });
3445
- if (tarballPath === downloadFile.CANCEL) {
3446
- lock.release();
3447
- return { isCancelled: true };
3448
- }
3449
-
3450
- // resolve tarball and mv tarball to cache after resolved
3451
- await this._resolveDownload(cwd, tarballPath, null, { removeTarFile: false });
3452
- await this._addCacheTarFile(tarballPath, integrity);
3453
-
3454
- logger.info('resolved blocklet tar file to install dir', { name, version });
3455
- lock.release();
3456
- return { isCancelled: false };
3457
- } catch (error) {
3458
- lock.release();
3459
- throw error;
3460
- }
3461
- }
3462
-
3463
3125
  /**
3464
3126
  *
3465
3127
  *
3466
3128
  * @param {{}} blocklet
3467
- * @param {{}} [oldBlocklet={}]
3468
3129
  * @param {{}} [context={}]
3469
3130
  * @return {*}
3470
3131
  * @memberof BlockletManager
3471
3132
  */
3472
- async _downloadBlocklet(blocklet, oldBlocklet = {}, context = {}) {
3133
+ async _downloadBlocklet(blocklet, context = {}) {
3473
3134
  const {
3474
- meta: { name, did },
3135
+ meta: { did },
3475
3136
  } = blocklet;
3476
3137
 
3477
- const oldComponentObj = {};
3478
- const downloadComponentIds = [];
3479
- const metaObj = {};
3480
- const metas = [];
3481
-
3482
- forEachBlockletSync(oldBlocklet, (oldComponent, { id }) => {
3483
- if (oldComponent.meta) {
3484
- oldComponentObj[id] = oldComponent;
3485
- metaObj[getComponentBundleId(oldComponent)] = oldComponent.meta;
3486
- }
3487
- });
3488
-
3489
- forEachBlockletSync(blocklet, (component, { id: componentId }) => {
3490
- if (needBlockletDownload(component, oldComponentObj[componentId])) {
3491
- const bundleId = getComponentBundleId(component);
3492
-
3493
- if (metaObj[bundleId]) {
3494
- if (get(component, 'meta.dist.integrity') !== get(metaObj[bundleId], 'dist.integrity')) {
3495
- // FIXME: what should we do if component bundle integrity not same in different apps
3496
- logger.error(`find duplicate bundle with different integrity when downloading ${blocklet.meta.title}`, {
3497
- bundleId,
3498
- });
3499
- }
3500
-
3501
- logger.info(`skip download duplicate bundle ${bundleId}`);
3502
- return;
3503
- }
3504
-
3505
- metaObj[bundleId] = component.meta;
3506
- metas.push(component.meta);
3507
- downloadComponentIds.push(componentId);
3508
- }
3509
- });
3510
-
3511
- // update children status
3512
- const blocklet1 = await states.blocklet.setBlockletStatus(did, BlockletStatus.downloading, {
3513
- children: downloadComponentIds,
3138
+ return this.blockletDownloader.download(blocklet, {
3139
+ ...context,
3140
+ preDownload: async ({ downloadComponentIds }) => {
3141
+ // update children status
3142
+ const blocklet1 = await states.blocklet.setBlockletStatus(did, BlockletStatus.downloading, {
3143
+ children: downloadComponentIds,
3144
+ });
3145
+ this.emit(BlockletEvents.statusChange, blocklet1);
3146
+ },
3147
+ postDownload: async () => {
3148
+ // since preferences only exist in blocklet bundle, we need to populate then after downloaded
3149
+ await this._setConfigsFromMeta(did);
3150
+ },
3514
3151
  });
3515
- this.emit(BlockletEvents.statusChange, blocklet1);
3516
-
3517
- try {
3518
- logger.info('Download Blocklet', { name, did, bundles: metas.map((x) => get(x, 'dist.tarball')) });
3519
- const tasks = [];
3520
- for (const meta of metas) {
3521
- const url = meta.dist.tarball;
3522
- tasks.push(this._downloadBundle(meta, did, url, context));
3523
- }
3524
- const results = await Promise.all(tasks);
3525
-
3526
- // since preferences only exist in blocklet bundle, we need to populate then after downloaded
3527
- await this._setConfigsFromMeta(did);
3528
-
3529
- if (results.find((x) => x.isCancelled)) {
3530
- return { isCancelled: true };
3531
- }
3532
- } catch (error) {
3533
- logger.error('Download blocklet failed', { did, name, error });
3534
- await this._cancelDownload(blocklet.meta);
3535
- throw error;
3536
- }
3537
-
3538
- return { isCancelled: false };
3539
3152
  }
3540
3153
 
3541
3154
  async _syncPm2Status(pm2Status, did) {
@@ -3551,17 +3164,6 @@ class BlockletManager extends BaseBlockletManager {
3551
3164
  }
3552
3165
  }
3553
3166
 
3554
- // eslint-disable-next-line no-unused-vars
3555
- async _cancelDownload(blockletMeta, context) {
3556
- const { did } = blockletMeta;
3557
-
3558
- if (this.downloadCtrls[did]) {
3559
- for (const cancelCtrl of this.downloadCtrls[did].values()) {
3560
- cancelCtrl.cancel();
3561
- }
3562
- }
3563
- }
3564
-
3565
3167
  // eslint-disable-next-line no-unused-vars
3566
3168
  async _cancelWaiting(blockletMeta, context) {
3567
3169
  const { did } = blockletMeta;
@@ -3848,48 +3450,55 @@ class BlockletManager extends BaseBlockletManager {
3848
3450
  async _deleteExpiredExternalBlocklet() {
3849
3451
  try {
3850
3452
  logger.info('start check expired external blocklet');
3851
- const blocklets = await states.blocklet.getBlocklets({
3852
- controller: {
3853
- $exists: true,
3453
+ const blockletExtras = await states.blockletExtras.find(
3454
+ {
3455
+ controller: {
3456
+ $exists: true,
3457
+ },
3458
+ 'controller.expiredAt': {
3459
+ $exists: false,
3460
+ },
3854
3461
  },
3855
- });
3856
-
3857
- const nodeInfo = await states.node.read();
3462
+ { did: 1, meta: 1, controller: 1 }
3463
+ );
3858
3464
 
3859
- const tasks = blocklets.map(async (blocklet) => {
3465
+ for (const data of blockletExtras) {
3860
3466
  try {
3861
- const assetState = await getNFTState(nodeInfo.launcher.chainHost, blocklet.controller.nftId);
3467
+ const assetState = await util.getNFTState(data.controller.chainHost, data.controller.nftId);
3862
3468
  const isExpired = isNFTExpired(assetState);
3469
+
3863
3470
  if (isExpired) {
3864
3471
  logger.info('the blocklet already expired', {
3865
- blockletId: blocklet._id,
3866
- nftId: blocklet.controller.nftId,
3472
+ blockletDid: data.meta.did,
3473
+ nftId: data.controller.nftId,
3867
3474
  });
3868
3475
 
3869
- await this.delete({ did: blocklet.meta.did, keepData: true, keepConfigs: true, keepLogsDir: true });
3476
+ await this.delete({ did: data.meta.did, keepData: true, keepConfigs: true, keepLogsDir: true });
3870
3477
  logger.info('the expired blocklet already deleted', {
3871
- blockletId: blocklet._id,
3872
- nftId: blocklet.controller.nftId,
3873
- did: blocklet.meta.did,
3478
+ blockletDid: data.meta.did,
3479
+ nftId: data.controller.nftId,
3874
3480
  });
3875
3481
 
3876
3482
  const expiredAt = getNftExpirationDate(assetState);
3877
- await states.blockletExtras.updateExpireInfo({ did: blocklet.meta.did, expiredAt });
3483
+ await states.blockletExtras.updateExpireInfo({ did: data.meta.did, expiredAt });
3878
3484
  logger.info('updated expired blocklet extra info', {
3879
- blockletId: blocklet._id,
3880
- nftId: blocklet.controller.nftId,
3881
- did: blocklet.meta.did,
3485
+ nftId: data.controller.nftId,
3486
+ blockletDid: data.meta.did,
3882
3487
  });
3488
+
3489
+ // 删除 blocklet 后会 reload nginx, 所以这里每次删除一个
3490
+ if (process.env.NODE_ENV !== 'test') {
3491
+ await sleep(10 * 1000);
3492
+ }
3883
3493
  }
3884
3494
  } catch (error) {
3885
- logger.error('get asset state failed when check expired external blocklet', {
3886
- blockletId: blocklet._id,
3887
- nftId: blocklet.controller.nftId,
3495
+ logger.error('delete expired blocklet failed', {
3496
+ blockletDid: data.meta.did,
3497
+ nftId: data.controller.nftId,
3498
+ error,
3888
3499
  });
3889
3500
  }
3890
- });
3891
-
3892
- await Promise.all(tasks);
3501
+ }
3893
3502
 
3894
3503
  logger.info('check expired external blocklet end');
3895
3504
  } catch (error) {
@@ -3960,7 +3569,7 @@ class BlockletManager extends BaseBlockletManager {
3960
3569
  await didDocument.updateBlockletDocument({
3961
3570
  wallet,
3962
3571
  blockletAppDid: appSystemEnvironments.BLOCKLET_APP_ID,
3963
- daemonDidDomain: getServerDidDomain(nodeInfo),
3572
+ daemonDidDomain: util.getServerDidDomain(nodeInfo),
3964
3573
  didRegistryUrl: nodeInfo.didRegistry,
3965
3574
  domain: nodeInfo.didDomain,
3966
3575
  });