@abtnode/core 1.8.69-beta-54faead3 → 1.8.69-beta-76f8a46f
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/team.js +2 -2
- package/lib/blocklet/downloader/resolve-download.js +31 -0
- package/lib/blocklet/manager/base.js +8 -16
- package/lib/blocklet/manager/disk.js +367 -1659
- package/lib/blocklet/manager/helper/{install-from-backup.js → install-application-from-backup.js} +26 -19
- package/lib/blocklet/manager/helper/install-application-from-dev.js +94 -0
- package/lib/blocklet/manager/helper/install-application-from-general.js +188 -0
- package/lib/blocklet/manager/helper/install-component-from-dev.js +80 -0
- package/lib/blocklet/manager/helper/install-component-from-upload.js +181 -0
- package/lib/blocklet/manager/helper/install-component-from-url.js +173 -0
- package/lib/blocklet/manager/helper/migrate-application-to-struct-v2.js +377 -0
- package/lib/blocklet/manager/helper/upgrade-components.js +152 -0
- package/lib/blocklet/storage/backup/spaces.js +20 -12
- package/lib/index.js +7 -7
- package/lib/router/helper.js +5 -7
- package/lib/states/blocklet-extras.js +44 -0
- package/lib/states/blocklet.js +56 -4
- package/lib/states/node.js +1 -0
- package/lib/states/site.js +15 -6
- package/lib/team/manager.js +5 -0
- package/lib/util/blocklet.js +177 -132
- package/lib/util/get-domain-for-blocklet.js +5 -14
- package/lib/util/get-meta-from-url.js +33 -0
- package/lib/util/index.js +0 -5
- package/lib/util/store.js +44 -6
- package/lib/webhook/sender/wallet/index.js +3 -0
- package/package.json +26 -26
|
@@ -7,33 +7,19 @@ const get = require('lodash/get');
|
|
|
7
7
|
const merge = require('lodash/merge');
|
|
8
8
|
const pick = require('lodash/pick');
|
|
9
9
|
const cloneDeep = require('lodash/cloneDeep');
|
|
10
|
-
const semver = require('semver');
|
|
11
|
-
const capitalize = require('lodash/capitalize');
|
|
12
|
-
const { Throttle } = require('stream-throttle');
|
|
13
|
-
const LRU = require('lru-cache');
|
|
14
|
-
const joi = require('joi');
|
|
15
10
|
const { isNFTExpired, getNftExpirationDate } = require('@abtnode/util/lib/nft');
|
|
16
11
|
const didDocument = require('@abtnode/util/lib/did-document');
|
|
17
12
|
const { sign } = require('@arcblock/jwt');
|
|
18
|
-
const { isValid: isValidDid } = require('@arcblock/did');
|
|
19
13
|
const { toSvg: createDidLogo } =
|
|
20
14
|
process.env.NODE_ENV !== 'test' ? require('@arcblock/did-motif') : require('@arcblock/did-motif/dist/did-motif.cjs');
|
|
21
15
|
const getBlockletInfo = require('@blocklet/meta/lib/info');
|
|
22
16
|
const sleep = require('@abtnode/util/lib/sleep');
|
|
23
17
|
|
|
24
18
|
const logger = require('@abtnode/logger')('@abtnode/core:blocklet:manager');
|
|
25
|
-
const {
|
|
26
|
-
WHO_CAN_ACCESS,
|
|
27
|
-
SERVER_ROLES,
|
|
28
|
-
WHO_CAN_ACCESS_PREFIX_ROLES,
|
|
29
|
-
BLOCKLET_INSTALL_TYPE,
|
|
30
|
-
NODE_MODES,
|
|
31
|
-
} = require('@abtnode/constant');
|
|
19
|
+
const { WHO_CAN_ACCESS, WHO_CAN_ACCESS_PREFIX_ROLES, BLOCKLET_INSTALL_TYPE, NODE_MODES } = require('@abtnode/constant');
|
|
32
20
|
|
|
33
21
|
const getBlockletEngine = require('@blocklet/meta/lib/engine');
|
|
34
22
|
const {
|
|
35
|
-
isFreeBlocklet,
|
|
36
|
-
isComponentBlocklet,
|
|
37
23
|
isDeletableBlocklet,
|
|
38
24
|
getAppMissingConfigs,
|
|
39
25
|
hasRunnableComponent,
|
|
@@ -45,15 +31,10 @@ const {
|
|
|
45
31
|
getRolesFromAuthConfig,
|
|
46
32
|
} = require('@blocklet/meta/lib/util');
|
|
47
33
|
const getComponentProcessId = require('@blocklet/meta/lib/get-component-process-id');
|
|
48
|
-
const toBlockletDid = require('@blocklet/meta/lib/did');
|
|
49
|
-
const { validateMeta } = require('@blocklet/meta/lib/validate');
|
|
50
34
|
const { update: updateMetaFile } = require('@blocklet/meta/lib/file');
|
|
51
35
|
const { titleSchema, updateMountPointSchema, environmentNameSchema } = require('@blocklet/meta/lib/schema');
|
|
52
|
-
const hasReservedKey = require('@blocklet/meta/lib/has-reserved-key');
|
|
53
36
|
const Lock = require('@abtnode/util/lib/lock');
|
|
54
37
|
|
|
55
|
-
const { toExternalBlocklet } = toBlockletDid;
|
|
56
|
-
|
|
57
38
|
const {
|
|
58
39
|
BlockletStatus,
|
|
59
40
|
BlockletSource,
|
|
@@ -79,7 +60,6 @@ const {
|
|
|
79
60
|
getFromCache: getAccessibleExternalNodeIp,
|
|
80
61
|
} = require('../../util/get-accessible-external-node-ip');
|
|
81
62
|
const {
|
|
82
|
-
getBlockletMetaFromUrl,
|
|
83
63
|
getAppSystemEnvironments,
|
|
84
64
|
getComponentSystemEnvironments,
|
|
85
65
|
getAppOverwrittenEnvironments,
|
|
@@ -94,45 +74,45 @@ const {
|
|
|
94
74
|
statusMap,
|
|
95
75
|
pruneBlockletBundle,
|
|
96
76
|
getDiskInfo,
|
|
97
|
-
getUpdateMetaList,
|
|
98
77
|
getRuntimeEnvironments,
|
|
99
78
|
getTypeFromInstallParams,
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
getDiffFiles,
|
|
79
|
+
parseComponents,
|
|
80
|
+
filterDuplicateComponents,
|
|
103
81
|
getBundleDir,
|
|
104
|
-
findAvailableDid,
|
|
105
|
-
ensureMeta,
|
|
106
82
|
getBlocklet,
|
|
107
83
|
ensureEnvDefault,
|
|
108
84
|
getConfigFromPreferences,
|
|
109
85
|
consumeServerlessNFT,
|
|
110
86
|
validateAppConfig,
|
|
111
|
-
checkDuplicateAppSk,
|
|
112
87
|
checkDuplicateMountPoint,
|
|
113
88
|
validateStore,
|
|
114
|
-
validateInServerless,
|
|
115
89
|
isRotatingAppSk,
|
|
116
90
|
isRotatingAppDid,
|
|
91
|
+
checkVersionCompatibility,
|
|
92
|
+
getBlockletKnownAs,
|
|
117
93
|
} = require('../../util/blocklet');
|
|
118
|
-
const StoreUtil = require('../../util/store');
|
|
119
94
|
const states = require('../../states');
|
|
120
95
|
const BaseBlockletManager = require('./base');
|
|
121
96
|
const { get: getEngine } = require('./engine');
|
|
122
97
|
const blockletPm2Events = require('./pm2-events');
|
|
123
|
-
const { getFactoryState } = require('../../util/chain');
|
|
124
98
|
const runMigrationScripts = require('../migration');
|
|
125
99
|
const hooks = require('../hooks');
|
|
126
|
-
const {
|
|
100
|
+
const { getDidDomainForBlocklet } = require('../../util/get-domain-for-blocklet');
|
|
127
101
|
const handleInstanceInStore = require('../../util/public-to-store');
|
|
128
102
|
const { BlockletRuntimeMonitor } = require('../../monitor/blocklet-runtime-monitor');
|
|
129
103
|
const getHistoryList = require('../../monitor/get-history-list');
|
|
130
104
|
const { SpacesBackup } = require('../storage/backup/spaces');
|
|
131
105
|
const { SpacesRestore } = require('../storage/restore/spaces');
|
|
132
|
-
const
|
|
133
|
-
const {
|
|
106
|
+
const { installApplicationFromGeneral } = require('./helper/install-application-from-general');
|
|
107
|
+
const { installApplicationFromDev } = require('./helper/install-application-from-dev');
|
|
108
|
+
const { installApplicationFromBackup } = require('./helper/install-application-from-backup');
|
|
109
|
+
const { installComponentFromDev } = require('./helper/install-component-from-dev');
|
|
110
|
+
const { installComponentFromUrl } = require('./helper/install-component-from-url');
|
|
111
|
+
const { installComponentFromUpload, diff } = require('./helper/install-component-from-upload');
|
|
112
|
+
const UpgradeComponents = require('./helper/upgrade-components');
|
|
134
113
|
const BlockletDownloader = require('../downloader/blocklet-downloader');
|
|
135
114
|
const RollbackCache = require('./helper/rollback-cache');
|
|
115
|
+
const { migrateApplicationToStructV2 } = require('./helper/migrate-application-to-struct-v2');
|
|
136
116
|
|
|
137
117
|
const {
|
|
138
118
|
isInProgress,
|
|
@@ -140,7 +120,6 @@ const {
|
|
|
140
120
|
formatEnvironments,
|
|
141
121
|
shouldUpdateBlockletStatus,
|
|
142
122
|
getBlockletMeta,
|
|
143
|
-
validateBlockletMeta,
|
|
144
123
|
validateOwner,
|
|
145
124
|
} = util;
|
|
146
125
|
|
|
@@ -183,9 +162,6 @@ const getSkippedProcessIds = ({ newBlocklet, oldBlocklet, context = {} }) => {
|
|
|
183
162
|
return res;
|
|
184
163
|
};
|
|
185
164
|
|
|
186
|
-
const getBlockletIndex = (meta, controller) =>
|
|
187
|
-
controller ? toExternalBlocklet(meta.name, controller.nftId) : { did: meta.did, name: meta.name };
|
|
188
|
-
|
|
189
165
|
// 10s 上报统计一次
|
|
190
166
|
const MONITOR_RECORD_INTERVAL_SEC = 10;
|
|
191
167
|
|
|
@@ -209,12 +185,6 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
209
185
|
// cached installed blocklets for performance
|
|
210
186
|
this.cachedBlocklets = null;
|
|
211
187
|
|
|
212
|
-
// cached blocklet latest versions from each registries
|
|
213
|
-
this.cachedBlockletVersions = new LRU({
|
|
214
|
-
max: 40, // cache at most 40 blocklets
|
|
215
|
-
maxAge: process.env.NODE_ENV === 'test' ? 500 : 0.5 * 60 * 1000, // cache for 0.5 minute
|
|
216
|
-
});
|
|
217
|
-
|
|
218
188
|
this.runtimeMonitor = new BlockletRuntimeMonitor({ historyLength: MONITOR_HISTORY_LENGTH, states });
|
|
219
189
|
|
|
220
190
|
this.blockletDownloader = new BlockletDownloader({
|
|
@@ -232,7 +202,7 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
232
202
|
}
|
|
233
203
|
|
|
234
204
|
// ============================================================================================
|
|
235
|
-
// Public API
|
|
205
|
+
// Public API for Installing/Upgrading Application or Components
|
|
236
206
|
// ============================================================================================
|
|
237
207
|
|
|
238
208
|
/**
|
|
@@ -240,9 +210,6 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
240
210
|
*
|
|
241
211
|
* @param {{
|
|
242
212
|
* url: string;
|
|
243
|
-
* file: string;
|
|
244
|
-
* diffVersion: string;
|
|
245
|
-
* deleteSet: Array<string>;
|
|
246
213
|
* did: string;
|
|
247
214
|
* title: string;
|
|
248
215
|
* description: string;
|
|
@@ -264,6 +231,13 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
264
231
|
async install(params, context = {}) {
|
|
265
232
|
logger.debug('install blocklet', { params, context });
|
|
266
233
|
|
|
234
|
+
const type = getTypeFromInstallParams(params);
|
|
235
|
+
|
|
236
|
+
const { appSk } = params;
|
|
237
|
+
if (!appSk) {
|
|
238
|
+
throw new Error('appSk is required');
|
|
239
|
+
}
|
|
240
|
+
|
|
267
241
|
if (!params.controller && context?.user?.controller) {
|
|
268
242
|
params.controller = context.user.controller;
|
|
269
243
|
}
|
|
@@ -280,40 +254,21 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
280
254
|
});
|
|
281
255
|
context.downloadTokenList = params.downloadTokenList || [];
|
|
282
256
|
|
|
283
|
-
const type = getTypeFromInstallParams(params);
|
|
284
257
|
if (typeof context.startImmediately === 'undefined') {
|
|
285
258
|
context.startImmediately = !!params.startImmediately;
|
|
286
259
|
}
|
|
287
260
|
|
|
288
|
-
const { appSk } = params;
|
|
289
|
-
|
|
290
|
-
if (type === BLOCKLET_INSTALL_TYPE.URL) {
|
|
291
|
-
const { url, controller, sync, delay } = params;
|
|
292
|
-
return this._installFromUrl({ url, controller, sync, delay, appSk }, context);
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
if (type === BLOCKLET_INSTALL_TYPE.UPLOAD) {
|
|
296
|
-
const { file, did, diffVersion, deleteSet } = params;
|
|
297
|
-
return this._installFromUpload({ file, did, diffVersion, deleteSet, appSk }, context);
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
if (type === BLOCKLET_INSTALL_TYPE.STORE) {
|
|
301
|
-
const { did, controller, sync, delay, storeUrl } = params;
|
|
302
|
-
return this._installFromStore({ did, controller, sync, delay, storeUrl, appSk }, context);
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
if (type === BLOCKLET_INSTALL_TYPE.CREATE) {
|
|
306
|
-
const { title, description } = params;
|
|
307
|
-
return this._installFromCreate({ title, description, appSk }, context);
|
|
308
|
-
}
|
|
309
|
-
|
|
310
261
|
if (type === BLOCKLET_INSTALL_TYPE.RESTORE) {
|
|
311
262
|
const { url } = params;
|
|
312
|
-
return
|
|
263
|
+
return installApplicationFromBackup({ url, appSk, context, manager: this, states });
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
if ([BLOCKLET_INSTALL_TYPE.URL, BLOCKLET_INSTALL_TYPE.STORE, BLOCKLET_INSTALL_TYPE.CREATE].includes(type)) {
|
|
267
|
+
return installApplicationFromGeneral({ ...params, type, context, manager: this, states });
|
|
313
268
|
}
|
|
314
269
|
|
|
315
270
|
// should not be here
|
|
316
|
-
throw new Error(
|
|
271
|
+
throw new Error(`install from ${type} is not supported`);
|
|
317
272
|
}
|
|
318
273
|
|
|
319
274
|
/**
|
|
@@ -334,7 +289,6 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
334
289
|
* @param {String} name custom component name
|
|
335
290
|
*
|
|
336
291
|
* @param {ConfigEntry} configs pre configs
|
|
337
|
-
* @param {Boolean} skipNavigation
|
|
338
292
|
*/
|
|
339
293
|
async installComponent(
|
|
340
294
|
{
|
|
@@ -348,8 +302,8 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
348
302
|
title,
|
|
349
303
|
name,
|
|
350
304
|
configs,
|
|
305
|
+
sync,
|
|
351
306
|
downloadTokenList,
|
|
352
|
-
skipNavigation,
|
|
353
307
|
},
|
|
354
308
|
context = {}
|
|
355
309
|
) {
|
|
@@ -363,15 +317,16 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
363
317
|
throw new Error("Can't install component in serverless-mode server via upload");
|
|
364
318
|
}
|
|
365
319
|
|
|
366
|
-
return
|
|
320
|
+
return installComponentFromUpload({
|
|
367
321
|
rootDid,
|
|
368
322
|
mountPoint,
|
|
369
323
|
file,
|
|
370
324
|
did,
|
|
371
325
|
diffVersion,
|
|
372
326
|
deleteSet,
|
|
373
|
-
skipNavigation,
|
|
374
327
|
context,
|
|
328
|
+
states,
|
|
329
|
+
manager: this,
|
|
375
330
|
});
|
|
376
331
|
}
|
|
377
332
|
|
|
@@ -381,7 +336,7 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
381
336
|
validateStore(info, url);
|
|
382
337
|
}
|
|
383
338
|
|
|
384
|
-
return
|
|
339
|
+
return installComponentFromUrl({
|
|
385
340
|
rootDid,
|
|
386
341
|
mountPoint,
|
|
387
342
|
url,
|
|
@@ -390,8 +345,10 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
390
345
|
did,
|
|
391
346
|
name,
|
|
392
347
|
configs,
|
|
348
|
+
sync,
|
|
393
349
|
downloadTokenList,
|
|
394
|
-
|
|
350
|
+
states,
|
|
351
|
+
manager: this,
|
|
395
352
|
});
|
|
396
353
|
}
|
|
397
354
|
|
|
@@ -399,57 +356,49 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
399
356
|
throw new Error('Unknown source');
|
|
400
357
|
}
|
|
401
358
|
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
let isFree = isFreeBlocklet(meta);
|
|
359
|
+
async diff({ did, hashFiles, rootDid }) {
|
|
360
|
+
return diff({ did, hashFiles, rootDid, states });
|
|
361
|
+
}
|
|
406
362
|
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
363
|
+
/**
|
|
364
|
+
* After the dev function finished, the caller should send a BlockletEvents.installed event to the daemon
|
|
365
|
+
* @returns {Object} blocklet
|
|
366
|
+
*/
|
|
367
|
+
async dev(folder, { rootDid, mountPoint, defaultStoreUrl } = {}) {
|
|
368
|
+
logger.info('dev component', { folder, rootDid, mountPoint });
|
|
410
369
|
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
isFree = false;
|
|
415
|
-
}
|
|
416
|
-
}
|
|
417
|
-
} catch (error) {
|
|
418
|
-
logger.warn('failed when checking if the blocklet is free', { did: meta.did, error });
|
|
419
|
-
}
|
|
370
|
+
const meta = getBlockletMeta(folder, { defaultStoreUrl });
|
|
371
|
+
if (meta.group !== 'static' && (!meta.scripts || !meta.scripts.dev)) {
|
|
372
|
+
throw new Error('Incorrect blocklet.yml: missing `scripts.dev` field');
|
|
420
373
|
}
|
|
421
374
|
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
return { meta, isFree, inStore, registryUrl };
|
|
425
|
-
}
|
|
426
|
-
|
|
427
|
-
async getBlockletByBundle({ did, name, serverlessNftId }, context) {
|
|
428
|
-
if (toBlockletDid(name) !== did) {
|
|
429
|
-
throw new Error('did and name does not match');
|
|
375
|
+
if (!rootDid) {
|
|
376
|
+
return installApplicationFromDev({ folder, meta, manager: this, states });
|
|
430
377
|
}
|
|
431
378
|
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
}
|
|
379
|
+
return installComponentFromDev({ folder, meta, rootDid, mountPoint, manager: this, states });
|
|
380
|
+
}
|
|
435
381
|
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
382
|
+
async checkComponentsForUpdates({ did }) {
|
|
383
|
+
return UpgradeComponents.check({ did, states });
|
|
384
|
+
}
|
|
439
385
|
|
|
440
|
-
|
|
441
|
-
|
|
386
|
+
async upgradeComponents({ updateId, selectedComponents: selectedComponentDids }, context = {}) {
|
|
387
|
+
return UpgradeComponents.upgrade({ updateId, selectedComponentDids, context, states, manager: this });
|
|
388
|
+
}
|
|
442
389
|
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
}
|
|
390
|
+
async migrateApplicationToStructV2({ did, appSk, context = {} }) {
|
|
391
|
+
return migrateApplicationToStructV2({ did, appSk, context, manager: this, states });
|
|
392
|
+
}
|
|
447
393
|
|
|
448
|
-
|
|
394
|
+
// ============================================================================================
|
|
395
|
+
// Public API for GQL or internal
|
|
396
|
+
// ============================================================================================
|
|
449
397
|
|
|
398
|
+
async getBlockletForLauncher({ did }) {
|
|
399
|
+
const blocklet = await states.blocklet.getBlocklet(did);
|
|
450
400
|
const isRunning = blocklet ? blocklet.status === BlockletStatus.running : false;
|
|
451
|
-
|
|
452
|
-
return { isInstalled: !!blocklet, isRunning, blockletDid, isExternal };
|
|
401
|
+
return { did, isInstalled: !!blocklet, isRunning };
|
|
453
402
|
}
|
|
454
403
|
|
|
455
404
|
async start({ did, throwOnError, checkHealthImmediately = false, e2eMode = false }, context) {
|
|
@@ -524,7 +473,7 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
524
473
|
};
|
|
525
474
|
|
|
526
475
|
if (checkHealthImmediately) {
|
|
527
|
-
await this.
|
|
476
|
+
await this._onCheckIfStarted(params, { throwOnError });
|
|
528
477
|
} else {
|
|
529
478
|
this.startQueue.push({
|
|
530
479
|
entity: 'blocklet',
|
|
@@ -662,15 +611,23 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
662
611
|
const params = await spacesRestore.restore();
|
|
663
612
|
|
|
664
613
|
this.emit(BlockletEvents.restoreProgress, { appDid: input.appDid, message: 'Installing blocklet...' });
|
|
665
|
-
await
|
|
614
|
+
await installApplicationFromBackup({
|
|
666
615
|
url: `file://${spacesRestore.restoreDir}`,
|
|
667
616
|
moveDir: true,
|
|
668
617
|
...merge(...params),
|
|
618
|
+
manager: this,
|
|
619
|
+
states,
|
|
669
620
|
});
|
|
670
621
|
|
|
671
622
|
this.emit(BlockletEvents.restoreProgress, { appDid: input.appDid, completed: true });
|
|
672
623
|
}
|
|
673
624
|
|
|
625
|
+
/**
|
|
626
|
+
*
|
|
627
|
+
* @param {import('@abtnode/client').RequestBlockletInput} param0
|
|
628
|
+
* @param {Record<string, any>} context
|
|
629
|
+
* @returns {import('@abtnode/client').BlockletState}
|
|
630
|
+
*/
|
|
674
631
|
async restart({ did }, context) {
|
|
675
632
|
logger.info('restart blocklet', { did });
|
|
676
633
|
|
|
@@ -714,8 +671,9 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
714
671
|
async delete({ did, keepData, keepLogsDir, keepConfigs }, context) {
|
|
715
672
|
logger.info('delete blocklet', { did, keepData });
|
|
716
673
|
|
|
674
|
+
const blocklet = await this.getBlocklet(did);
|
|
675
|
+
|
|
717
676
|
try {
|
|
718
|
-
const blocklet = await this.getBlocklet(did);
|
|
719
677
|
if (isDeletableBlocklet(blocklet) === false) {
|
|
720
678
|
throw new Error('Blocklet is protected from accidental deletion');
|
|
721
679
|
}
|
|
@@ -785,7 +743,7 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
785
743
|
// Reset config in db
|
|
786
744
|
await states.blockletExtras.remove({ did: blocklet.meta.did });
|
|
787
745
|
await this._setConfigsFromMeta(did);
|
|
788
|
-
await this.
|
|
746
|
+
await this._updateBlockletEnvironment(did);
|
|
789
747
|
await this.resetSiteByDid(did, context);
|
|
790
748
|
} else {
|
|
791
749
|
const child = blocklet.children.find((x) => x.meta.did === childDid);
|
|
@@ -803,7 +761,7 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
803
761
|
// Reset config in db
|
|
804
762
|
await states.blockletExtras.delConfigs([blocklet.meta.did, child.meta.did]);
|
|
805
763
|
await this._setConfigsFromMeta(blocklet.meta.did, child.meta.did);
|
|
806
|
-
await this.
|
|
764
|
+
await this._updateBlockletEnvironment(did);
|
|
807
765
|
}
|
|
808
766
|
|
|
809
767
|
logger.info('blocklet reset', { did, childDid });
|
|
@@ -814,6 +772,7 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
814
772
|
logger.info('delete blocklet component', { did, rootDid, keepData });
|
|
815
773
|
|
|
816
774
|
const blocklet = await this.getBlocklet(rootDid);
|
|
775
|
+
|
|
817
776
|
const child = blocklet.children.find((x) => x.meta.did === did);
|
|
818
777
|
if (!child) {
|
|
819
778
|
throw new Error('Component does not exist');
|
|
@@ -822,9 +781,9 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
822
781
|
// delete state
|
|
823
782
|
const doc = await states.blocklet.getBlocklet(rootDid);
|
|
824
783
|
doc.children = doc.children.filter((x) => x.meta.did !== did);
|
|
825
|
-
const
|
|
784
|
+
const deletedChildren = await states.blockletExtras.getSettings(did, 'children', []);
|
|
826
785
|
if (keepData !== false && keepState !== false) {
|
|
827
|
-
|
|
786
|
+
deletedChildren.push({
|
|
828
787
|
meta: pick(child.meta, ['did', 'name', 'bundleDid', 'bundleName', 'version', 'title', 'description']),
|
|
829
788
|
mountPoint: child.mountPoint,
|
|
830
789
|
status: BlockletStatus.deleted,
|
|
@@ -833,7 +792,7 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
833
792
|
}
|
|
834
793
|
|
|
835
794
|
await states.blocklet.updateBlocklet(rootDid, doc);
|
|
836
|
-
states.blockletExtras.setSettings(doc.meta.did, { children });
|
|
795
|
+
states.blockletExtras.setSettings(doc.meta.did, { children: deletedChildren });
|
|
837
796
|
|
|
838
797
|
// delete process
|
|
839
798
|
try {
|
|
@@ -849,14 +808,6 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
849
808
|
logger.error('delete blocklet process for deleting component', { did, rootDid, error: err });
|
|
850
809
|
}
|
|
851
810
|
|
|
852
|
-
// delete navigation
|
|
853
|
-
const navigation = await states.blockletExtras.getSettings(blocklet.meta.did, 'navigation', []);
|
|
854
|
-
await states.blockletExtras.setSettings(
|
|
855
|
-
blocklet.meta.did,
|
|
856
|
-
'navigation',
|
|
857
|
-
navigation.filter((x) => x.child !== blocklet.meta.name)
|
|
858
|
-
);
|
|
859
|
-
|
|
860
811
|
// delete storage
|
|
861
812
|
const childBlocklet = blocklet.children.find((x) => x.meta.did === did);
|
|
862
813
|
const { cacheDir, logsDir, dataDir } = childBlocklet.env;
|
|
@@ -868,6 +819,9 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
868
819
|
}
|
|
869
820
|
|
|
870
821
|
const newBlocklet = await this.getBlocklet(rootDid);
|
|
822
|
+
|
|
823
|
+
await this._updateDependents(rootDid);
|
|
824
|
+
|
|
871
825
|
this.emit(BlockletEvents.upgraded, { blocklet: newBlocklet, context: { ...context, createAuditLog: false } }); // trigger router refresh
|
|
872
826
|
|
|
873
827
|
this._createNotification(newBlocklet.meta.did, {
|
|
@@ -882,8 +836,7 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
882
836
|
return newBlocklet;
|
|
883
837
|
}
|
|
884
838
|
|
|
885
|
-
|
|
886
|
-
async cancelDownload({ did: inputDid }, context) {
|
|
839
|
+
async cancelDownload({ did: inputDid }) {
|
|
887
840
|
try {
|
|
888
841
|
await statusLock.acquire();
|
|
889
842
|
const blocklet = await states.blocklet.getBlocklet(inputDid);
|
|
@@ -1041,6 +994,7 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
1041
994
|
// CAUTION: this method currently only support config by blocklet.meta.did
|
|
1042
995
|
// eslint-disable-next-line no-unused-vars
|
|
1043
996
|
async config({ did, configs: newConfigs, skipHook, skipDidDocument }, context) {
|
|
997
|
+
// todo: skipDidDocument will be deleted
|
|
1044
998
|
if (!Array.isArray(newConfigs)) {
|
|
1045
999
|
throw new Error('configs list is not an array');
|
|
1046
1000
|
}
|
|
@@ -1116,12 +1070,11 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
1116
1070
|
this.emit(BlockletEvents.spaceConnected, blocklet);
|
|
1117
1071
|
}
|
|
1118
1072
|
|
|
1119
|
-
// FIXME: @zhenqiang best way to handle the did document: allow all appDids to resolve
|
|
1120
1073
|
if (willAppDidChange && !skipDidDocument) {
|
|
1121
1074
|
await this._updateDidDocument(blocklet);
|
|
1122
1075
|
}
|
|
1123
1076
|
|
|
1124
|
-
await this.
|
|
1077
|
+
await this._updateBlockletEnvironment(rootDid);
|
|
1125
1078
|
|
|
1126
1079
|
// response
|
|
1127
1080
|
const newState = await this.getBlocklet(rootDid);
|
|
@@ -1224,23 +1177,7 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
1224
1177
|
throw new Error('component does not exist');
|
|
1225
1178
|
}
|
|
1226
1179
|
|
|
1227
|
-
if (!component.dynamic) {
|
|
1228
|
-
throw new Error('cannot update title of non-dynamic component');
|
|
1229
|
-
}
|
|
1230
|
-
|
|
1231
1180
|
component.meta.title = title;
|
|
1232
|
-
|
|
1233
|
-
const navigation = await states.blockletExtras.getSettings(rootDid, 'navigation', []);
|
|
1234
|
-
const nav = navigation.find((x) => x.child === component.meta.name);
|
|
1235
|
-
|
|
1236
|
-
if (nav) {
|
|
1237
|
-
nav.title = title;
|
|
1238
|
-
}
|
|
1239
|
-
|
|
1240
|
-
// update db
|
|
1241
|
-
if (nav) {
|
|
1242
|
-
await states.blockletExtras.setSettings(rootDid, { navigation });
|
|
1243
|
-
}
|
|
1244
1181
|
await states.blocklet.updateBlocklet(rootDid, { children });
|
|
1245
1182
|
|
|
1246
1183
|
// trigger meta.js refresh
|
|
@@ -1260,6 +1197,7 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
1260
1197
|
}
|
|
1261
1198
|
|
|
1262
1199
|
const rootDid = blocklet.meta.did;
|
|
1200
|
+
|
|
1263
1201
|
const isRootComponent = !did;
|
|
1264
1202
|
|
|
1265
1203
|
const component = isRootComponent ? blocklet : blocklet.children.find((x) => x.meta.did === did);
|
|
@@ -1267,10 +1205,6 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
1267
1205
|
throw new Error('component does not exist');
|
|
1268
1206
|
}
|
|
1269
1207
|
|
|
1270
|
-
if (!isRootComponent && !component.dynamic) {
|
|
1271
|
-
throw new Error('cannot update mountPoint of non-dynamic component');
|
|
1272
|
-
}
|
|
1273
|
-
|
|
1274
1208
|
if (isRootComponent && component.group === BlockletGroup.gateway) {
|
|
1275
1209
|
throw new Error('cannot update mountPoint of gateway blocklet');
|
|
1276
1210
|
}
|
|
@@ -1286,550 +1220,172 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
1286
1220
|
return this.getBlocklet(rootDid);
|
|
1287
1221
|
}
|
|
1288
1222
|
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
async upgrade({ did, storeUrl, sync }, context) {
|
|
1293
|
-
const blocklet = await states.blocklet.getBlocklet(did);
|
|
1294
|
-
|
|
1295
|
-
if (!storeUrl && blocklet.source === BlockletSource.url) {
|
|
1296
|
-
return this._installFromUrl({ url: blocklet.deployedFrom }, context);
|
|
1297
|
-
}
|
|
1298
|
-
|
|
1299
|
-
// TODO: 查看了下目前页面中的升级按钮,都是会传 storeUrl 过来的,这个函数里的逻辑感觉需要在以后做一个简化
|
|
1300
|
-
if (!storeUrl && blocklet.source !== BlockletSource.registry) {
|
|
1301
|
-
throw new Error('Wrong upgrade source, empty storeUrl or not installed from blocklet registry');
|
|
1302
|
-
}
|
|
1303
|
-
|
|
1304
|
-
const upgradeFromStore = storeUrl || blocklet.deployedFrom;
|
|
1305
|
-
|
|
1306
|
-
let newVersionMeta = await StoreUtil.getBlockletMeta({
|
|
1307
|
-
did: blocklet.meta.bundleDid,
|
|
1308
|
-
storeUrl: upgradeFromStore,
|
|
1309
|
-
});
|
|
1310
|
-
newVersionMeta = ensureMeta(newVersionMeta, { name: blocklet.meta.name, did: blocklet.meta.did });
|
|
1311
|
-
|
|
1312
|
-
function getSignature(signatures = [], oldSignatures = []) {
|
|
1313
|
-
// if blocklet installed from local, upload, url, the signature is undefined, should return null
|
|
1314
|
-
if (!Array.isArray(signatures) || signatures.length === 0) {
|
|
1315
|
-
return null;
|
|
1316
|
-
}
|
|
1317
|
-
if (signatures.length > 3) {
|
|
1318
|
-
throw new Error('Invalid blocklet signature length');
|
|
1319
|
-
}
|
|
1320
|
-
// if old signature is old registry version, return new signatures last one signature
|
|
1321
|
-
if (oldSignatures.length > 0 && oldSignatures.length < 3) {
|
|
1322
|
-
return signatures[signatures.length - 1];
|
|
1323
|
-
}
|
|
1324
|
-
// old registry signatures: [ registry 签名, developer-sk 签名]
|
|
1325
|
-
// new registry signatures [ registry 签名, user wallet 签名, access-token 签名 ]
|
|
1326
|
-
// old -> old: 需要对比 developer-sk 签名
|
|
1327
|
-
// old -> new: 需要对比 developer-sk 和 access-token 签名
|
|
1328
|
-
// new -> new: 需要对比 user-wallet 签名
|
|
1329
|
-
return signatures.length === 1 ? signatures[0] : signatures[1];
|
|
1330
|
-
}
|
|
1331
|
-
|
|
1332
|
-
const currentDeveloperSignature = getSignature(blocklet.meta.signatures);
|
|
1333
|
-
const newVersionDeveloperSignature = getSignature(newVersionMeta.signatures, blocklet.meta.signatures);
|
|
1334
|
-
|
|
1335
|
-
if (!newVersionDeveloperSignature) {
|
|
1336
|
-
throw new Error('Invalid upgrade blocklet signature');
|
|
1337
|
-
}
|
|
1338
|
-
|
|
1339
|
-
if (
|
|
1340
|
-
currentDeveloperSignature &&
|
|
1341
|
-
blocklet.source === BlockletSource.registry &&
|
|
1342
|
-
(currentDeveloperSignature.signer !== newVersionDeveloperSignature.signer ||
|
|
1343
|
-
currentDeveloperSignature.pk !== newVersionDeveloperSignature.pk)
|
|
1344
|
-
) {
|
|
1345
|
-
logger.error('invalid developer signature', { did, currentDeveloperSignature, newVersionDeveloperSignature });
|
|
1346
|
-
throw new Error('Invalid developer signature');
|
|
1347
|
-
}
|
|
1348
|
-
|
|
1349
|
-
if (blocklet.meta.version === newVersionMeta.version) {
|
|
1350
|
-
throw new Error('Upgrade/downgrade blocklet to same version is noop');
|
|
1351
|
-
}
|
|
1223
|
+
// eslint-disable-next-line no-unused-vars
|
|
1224
|
+
async getRuntimeHistory({ did, hours }, context) {
|
|
1225
|
+
const metaDid = await states.blocklet.getBlockletMetaDid(did);
|
|
1352
1226
|
|
|
1353
|
-
const
|
|
1354
|
-
logger.info(`${action} blocklet`, { did });
|
|
1227
|
+
const history = this.runtimeMonitor.getHistory(metaDid);
|
|
1355
1228
|
|
|
1356
|
-
return
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
sync,
|
|
1229
|
+
return getHistoryList({
|
|
1230
|
+
history,
|
|
1231
|
+
hours,
|
|
1232
|
+
recordIntervalSec: MONITOR_RECORD_INTERVAL_SEC,
|
|
1233
|
+
props: ['date', 'cpu', 'mem'],
|
|
1362
1234
|
});
|
|
1363
1235
|
}
|
|
1364
1236
|
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
throw new Error('did is empty');
|
|
1369
|
-
}
|
|
1370
|
-
|
|
1371
|
-
if (!clientFiles || !clientFiles.length) {
|
|
1372
|
-
throw new Error('hashFiles is empty');
|
|
1373
|
-
}
|
|
1374
|
-
|
|
1375
|
-
const rootDid = inputRootDid || did;
|
|
1376
|
-
const childDid = inputRootDid ? did : '';
|
|
1237
|
+
async ensureBlocklet(did, opts = {}) {
|
|
1238
|
+
return getBlocklet({ ...opts, states, dataDirs: this.dataDirs, did, ensureIntegrity: true });
|
|
1239
|
+
}
|
|
1377
1240
|
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1241
|
+
async getBlocklet(did, opts = {}) {
|
|
1242
|
+
return getBlocklet({ ...opts, states, dataDirs: this.dataDirs, did, ensureIntegrity: false });
|
|
1243
|
+
}
|
|
1381
1244
|
|
|
1382
|
-
|
|
1245
|
+
async hasBlocklet({ did }) {
|
|
1246
|
+
return states.blocklet.hasBlocklet(did);
|
|
1247
|
+
}
|
|
1383
1248
|
|
|
1384
|
-
|
|
1385
|
-
if (
|
|
1386
|
-
throw new Error('
|
|
1249
|
+
async setInitialized({ did, owner }) {
|
|
1250
|
+
if (!validateOwner(owner)) {
|
|
1251
|
+
throw new Error('Blocklet owner is invalid');
|
|
1387
1252
|
}
|
|
1388
1253
|
|
|
1389
|
-
const
|
|
1254
|
+
const blocklet = await states.blocklet.getBlocklet(did);
|
|
1255
|
+
await states.blockletExtras.setSettings(blocklet.meta.did, { initialized: true, owner });
|
|
1256
|
+
logger.info('Blocklet initialized', { did, owner });
|
|
1390
1257
|
|
|
1391
|
-
|
|
1392
|
-
return {
|
|
1393
|
-
hasBlocklet: false,
|
|
1394
|
-
};
|
|
1395
|
-
}
|
|
1258
|
+
this.emit(BlockletEvents.updated, { meta: { did: blocklet.meta.did } });
|
|
1396
1259
|
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
}
|
|
1260
|
+
return this.getBlocklet(did);
|
|
1261
|
+
}
|
|
1400
1262
|
|
|
1401
|
-
|
|
1402
|
-
const
|
|
1403
|
-
|
|
1263
|
+
async status(did, { forceSync = false } = {}) {
|
|
1264
|
+
const fastReturnOnForceSync = async (blocklet = {}) => {
|
|
1265
|
+
const { status } = blocklet;
|
|
1266
|
+
const { added, waiting, downloading, installing, installed, upgrading } = BlockletStatus;
|
|
1404
1267
|
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
version: state.meta.version,
|
|
1409
|
-
addNum: addSet.length,
|
|
1410
|
-
changeNum: changeSet.length,
|
|
1411
|
-
deleteNum: deleteSet.length,
|
|
1412
|
-
});
|
|
1268
|
+
if ([added, waiting, downloading, installing, installed, upgrading].includes(status)) {
|
|
1269
|
+
return blocklet;
|
|
1270
|
+
}
|
|
1413
1271
|
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
addSet,
|
|
1418
|
-
changeSet,
|
|
1419
|
-
deleteSet,
|
|
1272
|
+
const res = await states.blocklet.setBlockletStatus(did, BlockletStatus.stopped);
|
|
1273
|
+
this.emit(BlockletEvents.statusChange, res);
|
|
1274
|
+
return res;
|
|
1420
1275
|
};
|
|
1421
|
-
}
|
|
1422
1276
|
|
|
1423
|
-
|
|
1424
|
-
const blocklet = await states.blocklet.getBlocklet(did);
|
|
1425
|
-
|
|
1426
|
-
const newChildren = [];
|
|
1277
|
+
const blocklet = await this.getBlocklet(did);
|
|
1427
1278
|
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
mountPoint: child.mountPoint,
|
|
1435
|
-
};
|
|
1436
|
-
newChildren.push(...(await parseChildrenFromMeta([config], { dynamic: child.dynamic })));
|
|
1437
|
-
} else {
|
|
1438
|
-
newChildren.push({
|
|
1439
|
-
...child,
|
|
1440
|
-
children: await parseChildrenFromMeta(child.meta),
|
|
1441
|
-
});
|
|
1279
|
+
let shouldUpdateStatus = forceSync || shouldUpdateBlockletStatus(blocklet.status);
|
|
1280
|
+
if (isInProgress(blocklet.status)) {
|
|
1281
|
+
const uptime = Date.now() - new Date(blocklet.updatedAt).getTime();
|
|
1282
|
+
// if uptime great than 30s, sync the status from pm2
|
|
1283
|
+
if (uptime > 30 * 1000) {
|
|
1284
|
+
shouldUpdateStatus = true;
|
|
1442
1285
|
}
|
|
1443
1286
|
}
|
|
1444
1287
|
|
|
1445
|
-
|
|
1288
|
+
if (!shouldUpdateStatus) {
|
|
1289
|
+
return blocklet;
|
|
1290
|
+
}
|
|
1446
1291
|
|
|
1447
|
-
|
|
1292
|
+
try {
|
|
1293
|
+
const status = await getBlockletStatusFromProcess(blocklet);
|
|
1294
|
+
if (blocklet.status !== status) {
|
|
1295
|
+
const res = await states.blocklet.setBlockletStatus(did, status);
|
|
1296
|
+
this.emit(BlockletEvents.statusChange, res);
|
|
1297
|
+
return res;
|
|
1298
|
+
}
|
|
1448
1299
|
|
|
1449
|
-
|
|
1450
|
-
|
|
1300
|
+
return blocklet;
|
|
1301
|
+
} catch (err) {
|
|
1302
|
+
return fastReturnOnForceSync(blocklet);
|
|
1451
1303
|
}
|
|
1304
|
+
}
|
|
1452
1305
|
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
children: newChildren,
|
|
1306
|
+
async refreshListCache() {
|
|
1307
|
+
this.list({ useCache: false }).catch((err) => {
|
|
1308
|
+
logger.error('refresh blocklet list failed', { error: err });
|
|
1457
1309
|
});
|
|
1458
|
-
|
|
1459
|
-
return {
|
|
1460
|
-
updateId,
|
|
1461
|
-
updateList,
|
|
1462
|
-
};
|
|
1463
1310
|
}
|
|
1464
1311
|
|
|
1465
|
-
async
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
oldBlocklet: inputOldBlocklet,
|
|
1471
|
-
selectedComponents: selectedComponentDids,
|
|
1472
|
-
},
|
|
1473
|
-
context
|
|
1474
|
-
) {
|
|
1475
|
-
let did;
|
|
1476
|
-
let children;
|
|
1477
|
-
let oldBlocklet;
|
|
1478
|
-
if (!updateId && inputDid && inputChildren) {
|
|
1479
|
-
did = inputDid;
|
|
1480
|
-
children = inputChildren;
|
|
1481
|
-
oldBlocklet = inputOldBlocklet;
|
|
1482
|
-
} else {
|
|
1483
|
-
const sessionData = await states.session.end(updateId);
|
|
1484
|
-
did = sessionData.did;
|
|
1485
|
-
oldBlocklet = await this._getBlockletForInstallation(did);
|
|
1486
|
-
|
|
1487
|
-
if (selectedComponentDids && selectedComponentDids.length) {
|
|
1488
|
-
children = cloneDeep(oldBlocklet.children).map((oldComponent) => {
|
|
1489
|
-
const newComponent = sessionData.children.find((x) => x.meta.did === oldComponent.meta.did);
|
|
1490
|
-
if (newComponent && selectedComponentDids.includes(newComponent.meta.did)) {
|
|
1491
|
-
return newComponent;
|
|
1492
|
-
}
|
|
1493
|
-
return oldComponent;
|
|
1494
|
-
});
|
|
1495
|
-
} else {
|
|
1496
|
-
children = sessionData.children;
|
|
1497
|
-
}
|
|
1312
|
+
async updateAllBlockletEnvironment() {
|
|
1313
|
+
const blocklets = await states.blocklet.getBlocklets();
|
|
1314
|
+
for (let i = 0; i < blocklets.length; i++) {
|
|
1315
|
+
const blocklet = blocklets[i];
|
|
1316
|
+
await this._updateBlockletEnvironment(blocklet.meta.did);
|
|
1498
1317
|
}
|
|
1318
|
+
}
|
|
1499
1319
|
|
|
1500
|
-
|
|
1501
|
-
const
|
|
1502
|
-
const
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
version,
|
|
1510
|
-
children: children.map((x) => ({ name: x.meta.name, version: x.meta.version })),
|
|
1511
|
-
});
|
|
1512
|
-
|
|
1513
|
-
// new blocklet
|
|
1514
|
-
const newBlocklet = await states.blocklet.setBlockletStatus(did, BlockletStatus.waiting);
|
|
1515
|
-
|
|
1516
|
-
newBlocklet.children = children;
|
|
1517
|
-
await validateBlocklet(newBlocklet);
|
|
1518
|
-
|
|
1519
|
-
this.emit(BlockletEvents.statusChange, newBlocklet);
|
|
1520
|
-
|
|
1521
|
-
// backup rollback data
|
|
1522
|
-
await this._rollbackCache.backup({ did, action, oldBlocklet });
|
|
1523
|
-
|
|
1524
|
-
// add to queue
|
|
1525
|
-
const ticket = this.installQueue.push(
|
|
1526
|
-
{
|
|
1527
|
-
entity: 'blocklet',
|
|
1528
|
-
action: 'download',
|
|
1529
|
-
id: did,
|
|
1530
|
-
oldBlocklet: { ...oldBlocklet },
|
|
1531
|
-
blocklet: { ...newBlocklet },
|
|
1532
|
-
selectedComponentDids: selectedComponentDids || [],
|
|
1533
|
-
version,
|
|
1534
|
-
context,
|
|
1535
|
-
postAction: action,
|
|
1536
|
-
},
|
|
1537
|
-
did
|
|
1538
|
-
);
|
|
1539
|
-
|
|
1540
|
-
ticket.on('failed', async (err) => {
|
|
1541
|
-
logger.error('queue failed', { entity: 'blocklet', action, did, version, name, error: err });
|
|
1542
|
-
await this._rollback(action, did, oldBlocklet);
|
|
1543
|
-
this.emit(`blocklet.${action}.failed`, { did, version, err });
|
|
1544
|
-
this._createNotification(did, {
|
|
1545
|
-
title: `Blocklet ${capitalize(action)} Failed`,
|
|
1546
|
-
description: `Blocklet ${name}@${version} ${action} failed with error: ${err.message || 'queue exception'}`,
|
|
1547
|
-
entityType: 'blocklet',
|
|
1548
|
-
entityId: did,
|
|
1549
|
-
severity: 'error',
|
|
1550
|
-
});
|
|
1320
|
+
async prune() {
|
|
1321
|
+
const blocklets = await states.blocklet.getBlocklets();
|
|
1322
|
+
const settings = await states.blockletExtras.listSettings();
|
|
1323
|
+
await pruneBlockletBundle({
|
|
1324
|
+
installDir: this.dataDirs.blocklets,
|
|
1325
|
+
blocklets,
|
|
1326
|
+
blockletSettings: settings
|
|
1327
|
+
.filter((x) => x.settings.children && x.settings.children.length)
|
|
1328
|
+
.map((x) => x.settings),
|
|
1551
1329
|
});
|
|
1552
|
-
return newBlocklet;
|
|
1553
1330
|
}
|
|
1554
1331
|
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1332
|
+
async onJob(job) {
|
|
1333
|
+
if (job.entity === 'blocklet') {
|
|
1334
|
+
if (job.action === 'download') {
|
|
1335
|
+
await this._downloadAndInstall(job);
|
|
1336
|
+
}
|
|
1337
|
+
if (job.action === 'restart') {
|
|
1338
|
+
await this._onRestart(job);
|
|
1339
|
+
}
|
|
1560
1340
|
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
props: ['date', 'cpu', 'mem'],
|
|
1566
|
-
});
|
|
1341
|
+
if (job.action === 'check_if_started') {
|
|
1342
|
+
await this._onCheckIfStarted(job);
|
|
1343
|
+
}
|
|
1344
|
+
}
|
|
1567
1345
|
}
|
|
1568
1346
|
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
}
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
}
|
|
1605
|
-
|
|
1606
|
-
logger.info('remove blocklet for dev', { did, version });
|
|
1607
|
-
|
|
1608
|
-
await this.delete({ did, keepLogsDir: false });
|
|
1609
|
-
}
|
|
1610
|
-
|
|
1611
|
-
// delete process
|
|
1612
|
-
try {
|
|
1613
|
-
await this.deleteProcess({ did });
|
|
1614
|
-
logger.info('delete blocklet precess for dev', { did, version });
|
|
1615
|
-
} catch (err) {
|
|
1616
|
-
if (process.env.NODE_ENV !== 'development') {
|
|
1617
|
-
logger.error('failed to delete blocklet process for dev', { error: err });
|
|
1618
|
-
}
|
|
1619
|
-
}
|
|
1620
|
-
|
|
1621
|
-
const children = await this._getChildrenForInstallation(meta);
|
|
1622
|
-
await validateBlocklet({ meta, children });
|
|
1623
|
-
const added = await states.blocklet.addBlocklet({
|
|
1624
|
-
meta,
|
|
1625
|
-
source: BlockletSource.local,
|
|
1626
|
-
deployedFrom: folder,
|
|
1627
|
-
mode: BLOCKLET_MODES.DEVELOPMENT,
|
|
1628
|
-
children,
|
|
1629
|
-
});
|
|
1630
|
-
logger.info('add blocklet for dev', { did, version, meta });
|
|
1631
|
-
|
|
1632
|
-
await this._downloadBlocklet(added);
|
|
1633
|
-
|
|
1634
|
-
// Add Config
|
|
1635
|
-
await this._setConfigsFromMeta(did);
|
|
1636
|
-
|
|
1637
|
-
// should ensure blocklet integrity
|
|
1638
|
-
let blocklet = await this.ensureBlocklet(did);
|
|
1639
|
-
|
|
1640
|
-
// pre install
|
|
1641
|
-
await this._runPreInstallHook(blocklet);
|
|
1642
|
-
|
|
1643
|
-
// Add environments
|
|
1644
|
-
await this.updateBlockletEnvironment(did);
|
|
1645
|
-
blocklet = await this.getBlocklet(did);
|
|
1646
|
-
|
|
1647
|
-
// post install
|
|
1648
|
-
await this._runPostInstallHook(blocklet);
|
|
1649
|
-
|
|
1650
|
-
await states.blocklet.setBlockletStatus(did, BlockletStatus.installed);
|
|
1651
|
-
|
|
1652
|
-
blocklet = await this.getBlocklet(did);
|
|
1653
|
-
|
|
1654
|
-
await fs.writeFile(path.join(blocklet.env.dataDir, 'logo.svg'), createDidLogo(blocklet.meta.did));
|
|
1655
|
-
|
|
1656
|
-
return blocklet;
|
|
1657
|
-
}
|
|
1658
|
-
|
|
1659
|
-
async _devComponent({ folder, meta, rootDid, mountPoint, skipNavigation = true }) {
|
|
1660
|
-
const { did, version } = meta;
|
|
1661
|
-
|
|
1662
|
-
const existRoot = await states.blocklet.getBlocklet(rootDid);
|
|
1663
|
-
if (!existRoot) {
|
|
1664
|
-
throw new Error('Root blocklet does not exist');
|
|
1665
|
-
}
|
|
1666
|
-
|
|
1667
|
-
const exist = existRoot.children.find((x) => x.meta.did === meta.did);
|
|
1668
|
-
if (exist) {
|
|
1669
|
-
if (exist.mode === BLOCKLET_MODES.PRODUCTION) {
|
|
1670
|
-
throw new Error('The blocklet component of production mode already exists, please remove it before developing');
|
|
1671
|
-
}
|
|
1672
|
-
|
|
1673
|
-
const status = fromBlockletStatus(exist.status);
|
|
1674
|
-
if (['starting', 'running'].includes(status)) {
|
|
1675
|
-
throw new Error(`The blocklet component is already on ${status}, please stop it before developing`);
|
|
1676
|
-
}
|
|
1677
|
-
|
|
1678
|
-
logger.info('remove blocklet component for dev', { did, version });
|
|
1679
|
-
|
|
1680
|
-
await this.deleteComponent({ did, rootDid });
|
|
1681
|
-
}
|
|
1682
|
-
|
|
1683
|
-
const defaultPath = formatName(meta.name);
|
|
1684
|
-
const component = {
|
|
1685
|
-
meta: ensureMeta(meta),
|
|
1686
|
-
mountPoint: mountPoint || `/${defaultPath}`,
|
|
1687
|
-
source: BlockletSource.local,
|
|
1688
|
-
deployedFrom: folder,
|
|
1689
|
-
status: BlockletStatus.installed,
|
|
1690
|
-
mode: BLOCKLET_MODES.DEVELOPMENT,
|
|
1691
|
-
dynamic: true,
|
|
1692
|
-
children: await parseChildrenFromMeta(meta, { ancestors: [{ mountPoint: mountPoint || `/${defaultPath}` }] }),
|
|
1693
|
-
};
|
|
1694
|
-
await validateBlocklet(component);
|
|
1695
|
-
await states.blocklet.addChildren(rootDid, [component]);
|
|
1696
|
-
|
|
1697
|
-
const newBlocklet = await states.blocklet.getBlocklet(rootDid);
|
|
1698
|
-
await this._downloadBlocklet(newBlocklet);
|
|
1699
|
-
await states.blocklet.setBlockletStatus(rootDid, BlockletStatus.stopped);
|
|
1700
|
-
|
|
1701
|
-
await this._upsertDynamicNavigation(existRoot.meta.did, component, { skipNavigation });
|
|
1702
|
-
|
|
1703
|
-
// Add Config
|
|
1704
|
-
await this._setConfigsFromMeta(rootDid);
|
|
1705
|
-
|
|
1706
|
-
// should ensure blocklet integrity
|
|
1707
|
-
let blocklet = await this.ensureBlocklet(rootDid);
|
|
1708
|
-
|
|
1709
|
-
// pre install
|
|
1710
|
-
await this._runPreInstallHook(blocklet);
|
|
1711
|
-
|
|
1712
|
-
// Add environments
|
|
1713
|
-
await this.updateBlockletEnvironment(rootDid);
|
|
1714
|
-
blocklet = await this.getBlocklet(rootDid);
|
|
1715
|
-
|
|
1716
|
-
// post install
|
|
1717
|
-
await this._runPostInstallHook(blocklet);
|
|
1718
|
-
|
|
1719
|
-
logger.info('add blocklet component for dev', { did, version, meta });
|
|
1720
|
-
|
|
1721
|
-
blocklet = await this.getBlocklet(rootDid);
|
|
1722
|
-
|
|
1723
|
-
return blocklet;
|
|
1724
|
-
}
|
|
1725
|
-
|
|
1726
|
-
/**
|
|
1727
|
-
* backup 目录结构
|
|
1728
|
-
* /blocklets/<name1>version>
|
|
1729
|
-
* /blocklets/<name2>version>
|
|
1730
|
-
* /blocklets/<name3>version>
|
|
1731
|
-
* /data
|
|
1732
|
-
* /blocklet.json
|
|
1733
|
-
* /blocklet_extras.json
|
|
1734
|
-
* @see Blocklet 端到端备份方案的设计与实现(一期) https://team.arcblock.io/comment/discussions/e62084d5-fafa-489e-91d5-defcd6e93223
|
|
1735
|
-
* @param {{ url: string, appSk: string, moveDir: boolean}} [{ url }={}]
|
|
1736
|
-
* @param {Record<string, string>} [context={}]
|
|
1737
|
-
* @return {Promise<any>}
|
|
1738
|
-
* @memberof BlockletManager
|
|
1739
|
-
*/
|
|
1740
|
-
async _installFromBackup({ url, appSk, moveDir } = {}, context = {}) {
|
|
1741
|
-
return installFromBackup({ url, appSk, moveDir, context, manager: this, states });
|
|
1742
|
-
}
|
|
1743
|
-
|
|
1744
|
-
async ensureBlocklet(did, opts = {}) {
|
|
1745
|
-
return getBlocklet({ ...opts, states, dataDirs: this.dataDirs, did, ensureIntegrity: true });
|
|
1746
|
-
}
|
|
1747
|
-
|
|
1748
|
-
async getBlocklet(did, opts = {}) {
|
|
1749
|
-
return getBlocklet({ ...opts, states, dataDirs: this.dataDirs, did, ensureIntegrity: false });
|
|
1750
|
-
}
|
|
1751
|
-
|
|
1752
|
-
async hasBlocklet({ did }) {
|
|
1753
|
-
return states.blocklet.hasBlocklet(did);
|
|
1754
|
-
}
|
|
1755
|
-
|
|
1756
|
-
async setInitialized({ did, owner }) {
|
|
1757
|
-
if (!validateOwner(owner)) {
|
|
1758
|
-
throw new Error('Blocklet owner is invalid');
|
|
1759
|
-
}
|
|
1760
|
-
|
|
1761
|
-
const blocklet = await states.blocklet.getBlocklet(did);
|
|
1762
|
-
await states.blockletExtras.setSettings(blocklet.meta.did, { initialized: true, owner });
|
|
1763
|
-
logger.info('Blocklet initialized', { did, owner });
|
|
1764
|
-
|
|
1765
|
-
this.emit(BlockletEvents.updated, { meta: { did: blocklet.meta.did } });
|
|
1766
|
-
|
|
1767
|
-
return this.getBlocklet(did);
|
|
1768
|
-
}
|
|
1769
|
-
|
|
1770
|
-
async status(did, { forceSync = false } = {}) {
|
|
1771
|
-
const fastReturnOnForceSync = async (blocklet = {}) => {
|
|
1772
|
-
const { status } = blocklet;
|
|
1773
|
-
const { added, waiting, downloading, installing, installed, upgrading } = BlockletStatus;
|
|
1774
|
-
|
|
1775
|
-
if ([added, waiting, downloading, installing, installed, upgrading].includes(status)) {
|
|
1776
|
-
return blocklet;
|
|
1777
|
-
}
|
|
1778
|
-
|
|
1779
|
-
const res = await states.blocklet.setBlockletStatus(did, BlockletStatus.stopped);
|
|
1780
|
-
this.emit(BlockletEvents.statusChange, res);
|
|
1781
|
-
return res;
|
|
1782
|
-
};
|
|
1783
|
-
|
|
1784
|
-
const blocklet = await this.getBlocklet(did);
|
|
1785
|
-
|
|
1786
|
-
let shouldUpdateStatus = forceSync || shouldUpdateBlockletStatus(blocklet.status);
|
|
1787
|
-
if (isInProgress(blocklet.status)) {
|
|
1788
|
-
const uptime = Date.now() - new Date(blocklet.updatedAt).getTime();
|
|
1789
|
-
// if uptime great than 30s, sync the status from pm2
|
|
1790
|
-
if (uptime > 30 * 1000) {
|
|
1791
|
-
shouldUpdateStatus = true;
|
|
1792
|
-
}
|
|
1793
|
-
}
|
|
1794
|
-
|
|
1795
|
-
if (!shouldUpdateStatus) {
|
|
1796
|
-
return blocklet;
|
|
1797
|
-
}
|
|
1798
|
-
|
|
1799
|
-
try {
|
|
1800
|
-
const status = await getBlockletStatusFromProcess(blocklet);
|
|
1801
|
-
if (blocklet.status !== status) {
|
|
1802
|
-
const res = await states.blocklet.setBlockletStatus(did, status);
|
|
1803
|
-
this.emit(BlockletEvents.statusChange, res);
|
|
1804
|
-
return res;
|
|
1805
|
-
}
|
|
1806
|
-
|
|
1807
|
-
return blocklet;
|
|
1808
|
-
} catch (err) {
|
|
1809
|
-
return fastReturnOnForceSync(blocklet);
|
|
1810
|
-
}
|
|
1811
|
-
}
|
|
1812
|
-
|
|
1813
|
-
async refreshListCache() {
|
|
1814
|
-
this.list({ useCache: false }).catch((err) => {
|
|
1815
|
-
logger.error('refresh blocklet list failed', { error: err });
|
|
1816
|
-
});
|
|
1347
|
+
getCrons() {
|
|
1348
|
+
return [
|
|
1349
|
+
{
|
|
1350
|
+
name: 'sync-blocklet-status',
|
|
1351
|
+
time: '*/60 * * * * *', // 60s
|
|
1352
|
+
fn: this._syncBlockletStatus.bind(this),
|
|
1353
|
+
},
|
|
1354
|
+
{
|
|
1355
|
+
name: 'sync-blocklet-list',
|
|
1356
|
+
time: '*/60 * * * * *', // 60s
|
|
1357
|
+
fn: this.refreshListCache.bind(this),
|
|
1358
|
+
},
|
|
1359
|
+
{
|
|
1360
|
+
name: 'refresh-accessible-ip',
|
|
1361
|
+
time: '0 */10 * * * *', // 10min
|
|
1362
|
+
fn: async () => {
|
|
1363
|
+
const nodeInfo = await states.node.read();
|
|
1364
|
+
await refreshAccessibleExternalNodeIp(nodeInfo);
|
|
1365
|
+
},
|
|
1366
|
+
},
|
|
1367
|
+
{
|
|
1368
|
+
name: 'delete-expired-external-blocklet',
|
|
1369
|
+
time: '0 */30 * * * *', // 30min
|
|
1370
|
+
options: { runOnInit: false },
|
|
1371
|
+
fn: () => this._deleteExpiredExternalBlocklet(),
|
|
1372
|
+
},
|
|
1373
|
+
{
|
|
1374
|
+
name: 'clean-expired-blocklet-data',
|
|
1375
|
+
time: '0 */20 0 * * *', // 每天凌晨 0 点的每 20 分钟
|
|
1376
|
+
fn: () => this._cleanExpiredBlockletData(),
|
|
1377
|
+
},
|
|
1378
|
+
{
|
|
1379
|
+
name: 'record-blocklet-runtime-history',
|
|
1380
|
+
time: `*/${MONITOR_RECORD_INTERVAL_SEC} * * * * *`, // 10s
|
|
1381
|
+
fn: () => this.runtimeMonitor.monitAll(),
|
|
1382
|
+
},
|
|
1383
|
+
];
|
|
1817
1384
|
}
|
|
1818
1385
|
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
await this.downloadAndInstall(job);
|
|
1823
|
-
}
|
|
1824
|
-
if (job.action === 'restart') {
|
|
1825
|
-
await this.onRestart(job);
|
|
1826
|
-
}
|
|
1827
|
-
|
|
1828
|
-
if (job.action === 'check_if_started') {
|
|
1829
|
-
await this.onCheckIfStarted(job);
|
|
1830
|
-
}
|
|
1831
|
-
}
|
|
1832
|
-
}
|
|
1386
|
+
// ============================================================================================
|
|
1387
|
+
// Private API that are used by self of helper function
|
|
1388
|
+
// ============================================================================================
|
|
1833
1389
|
|
|
1834
1390
|
/**
|
|
1835
1391
|
*
|
|
@@ -1837,7 +1393,7 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
1837
1393
|
* @param {{
|
|
1838
1394
|
* blocklet: {},
|
|
1839
1395
|
* context: {},
|
|
1840
|
-
* postAction: 'install' | 'upgrade'
|
|
1396
|
+
* postAction: 'install' | 'upgrade',
|
|
1841
1397
|
* oldBlocklet: {},
|
|
1842
1398
|
* throwOnError: Error,
|
|
1843
1399
|
* skipCheckStatusBeforeDownload: boolean,
|
|
@@ -1846,7 +1402,7 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
1846
1402
|
* @return {*}
|
|
1847
1403
|
* @memberof BlockletManager
|
|
1848
1404
|
*/
|
|
1849
|
-
async
|
|
1405
|
+
async _downloadAndInstall(params) {
|
|
1850
1406
|
const {
|
|
1851
1407
|
blocklet,
|
|
1852
1408
|
context,
|
|
@@ -1963,7 +1519,7 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
1963
1519
|
this.emit(BlockletEvents.statusChange, state);
|
|
1964
1520
|
}
|
|
1965
1521
|
|
|
1966
|
-
if (
|
|
1522
|
+
if (postAction === 'upgrade') {
|
|
1967
1523
|
const state = await states.blocklet.setBlockletStatus(did, BlockletStatus.upgrading);
|
|
1968
1524
|
this.emit(BlockletEvents.statusChange, state);
|
|
1969
1525
|
}
|
|
@@ -1978,13 +1534,13 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
1978
1534
|
try {
|
|
1979
1535
|
// install blocklet
|
|
1980
1536
|
if (postAction === 'install') {
|
|
1981
|
-
await this.
|
|
1537
|
+
await this._onInstall({ blocklet, context, oldBlocklet });
|
|
1982
1538
|
return;
|
|
1983
1539
|
}
|
|
1984
1540
|
|
|
1985
1541
|
// upgrade blocklet
|
|
1986
|
-
if (
|
|
1987
|
-
await this.
|
|
1542
|
+
if (postAction === 'upgrade') {
|
|
1543
|
+
await this._onUpgrade({ oldBlocklet, newBlocklet: blocklet, context });
|
|
1988
1544
|
}
|
|
1989
1545
|
} catch (error) {
|
|
1990
1546
|
if (throwOnError) {
|
|
@@ -1993,7 +1549,7 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
1993
1549
|
}
|
|
1994
1550
|
}
|
|
1995
1551
|
|
|
1996
|
-
async
|
|
1552
|
+
async _onInstall({ blocklet, context, oldBlocklet }) {
|
|
1997
1553
|
const { meta } = blocklet;
|
|
1998
1554
|
const { did, version } = meta;
|
|
1999
1555
|
logger.info('do install blocklet', { did, version });
|
|
@@ -2021,9 +1577,9 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
2021
1577
|
}
|
|
2022
1578
|
}
|
|
2023
1579
|
|
|
2024
|
-
async
|
|
1580
|
+
async _onUpgrade({ oldBlocklet, newBlocklet, context }) {
|
|
2025
1581
|
const { version, did } = newBlocklet.meta;
|
|
2026
|
-
logger.info(
|
|
1582
|
+
logger.info('do upgrade blocklet', { did, version });
|
|
2027
1583
|
|
|
2028
1584
|
try {
|
|
2029
1585
|
await this._upgradeBlocklet({
|
|
@@ -2036,12 +1592,12 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
2036
1592
|
}
|
|
2037
1593
|
}
|
|
2038
1594
|
|
|
2039
|
-
async
|
|
1595
|
+
async _onRestart({ did, context }) {
|
|
2040
1596
|
await this.stop({ did, updateStatus: false }, context);
|
|
2041
1597
|
await this.start({ did }, context);
|
|
2042
1598
|
}
|
|
2043
1599
|
|
|
2044
|
-
async
|
|
1600
|
+
async _onCheckIfStarted(jobInfo, { throwOnError } = {}) {
|
|
2045
1601
|
const { did, context, minConsecutiveTime = 5000, timeout } = jobInfo;
|
|
2046
1602
|
const blocklet = await this.getBlocklet(did);
|
|
2047
1603
|
|
|
@@ -2083,7 +1639,7 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
2083
1639
|
}
|
|
2084
1640
|
}
|
|
2085
1641
|
|
|
2086
|
-
async
|
|
1642
|
+
async _updateBlockletEnvironment(did) {
|
|
2087
1643
|
const blockletWithEnv = await this.getBlocklet(did);
|
|
2088
1644
|
const blocklet = await states.blocklet.getBlocklet(did);
|
|
2089
1645
|
const nodeInfo = await states.node.read();
|
|
@@ -2111,628 +1667,25 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
2111
1667
|
const childWithEnv = envMap[id];
|
|
2112
1668
|
if (childWithEnv) {
|
|
2113
1669
|
child.environments = formatEnvironments({
|
|
2114
|
-
...getComponentSystemEnvironments(childWithEnv), // system env of child blocklet
|
|
2115
|
-
...appSystemEnvironments, // system env of root blocklet
|
|
2116
|
-
});
|
|
2117
|
-
}
|
|
2118
|
-
});
|
|
2119
|
-
|
|
2120
|
-
// put BLOCKLET_APP_ID at root level for indexing
|
|
2121
|
-
blocklet.appDid = appSystemEnvironments.BLOCKLET_APP_ID;
|
|
2122
|
-
|
|
2123
|
-
if (!Array.isArray(blocklet.migratedFrom)) {
|
|
2124
|
-
blocklet.migratedFrom = [];
|
|
2125
|
-
}
|
|
2126
|
-
// This can only be set once, can be used for indexing, will not change ever
|
|
2127
|
-
if (!blocklet.appPid) {
|
|
2128
|
-
blocklet.appPid = appSystemEnvironments.BLOCKLET_APP_PID;
|
|
2129
|
-
}
|
|
2130
|
-
|
|
2131
|
-
// update state to db
|
|
2132
|
-
return states.blocklet.updateBlocklet(did, blocklet);
|
|
2133
|
-
}
|
|
2134
|
-
|
|
2135
|
-
async updateAllBlockletEnvironment() {
|
|
2136
|
-
const blocklets = await states.blocklet.getBlocklets();
|
|
2137
|
-
for (let i = 0; i < blocklets.length; i++) {
|
|
2138
|
-
const blocklet = blocklets[i];
|
|
2139
|
-
await this.updateBlockletEnvironment(blocklet.meta.did);
|
|
2140
|
-
}
|
|
2141
|
-
}
|
|
2142
|
-
|
|
2143
|
-
/**
|
|
2144
|
-
***
|
|
2145
|
-
*
|
|
2146
|
-
* @param {{
|
|
2147
|
-
* did: string;
|
|
2148
|
-
* sync: boolean;
|
|
2149
|
-
* delay: number;
|
|
2150
|
-
* controller: Controller;
|
|
2151
|
-
* appSk: string;
|
|
2152
|
-
* storeUrl: string;
|
|
2153
|
-
* }} params
|
|
2154
|
-
* @param {*} context
|
|
2155
|
-
* @return {*}
|
|
2156
|
-
* @memberof BlockletManager
|
|
2157
|
-
*/
|
|
2158
|
-
async _installFromStore(params, context) {
|
|
2159
|
-
const { did, storeUrl, sync, delay, controller, appSk } = params;
|
|
2160
|
-
|
|
2161
|
-
logger.debug('start install blocklet', { did });
|
|
2162
|
-
if (!isValidDid(did)) {
|
|
2163
|
-
throw new Error('Blocklet did is invalid');
|
|
2164
|
-
}
|
|
2165
|
-
|
|
2166
|
-
if (!storeUrl) {
|
|
2167
|
-
throw new Error('registry url should not be empty');
|
|
2168
|
-
}
|
|
2169
|
-
|
|
2170
|
-
const info = await StoreUtil.getRegistryMeta(storeUrl);
|
|
2171
|
-
const meta = await StoreUtil.getBlockletMeta({ did, storeUrl });
|
|
2172
|
-
if (!meta) {
|
|
2173
|
-
throw new Error('Can not install blocklet that not found in registry');
|
|
2174
|
-
}
|
|
2175
|
-
|
|
2176
|
-
const { did: blockletDid, name: blockletName } = getBlockletIndex(meta, controller);
|
|
2177
|
-
|
|
2178
|
-
const state = await states.blocklet.getBlocklet(blockletDid);
|
|
2179
|
-
|
|
2180
|
-
if (state) {
|
|
2181
|
-
throw new Error('Can not install an already installed blocklet');
|
|
2182
|
-
}
|
|
2183
|
-
|
|
2184
|
-
if (controller?.nftId) {
|
|
2185
|
-
// sometimes nedb will throw error if use states.blocklet.count({ ['controller.nftId']: controller.nftId })
|
|
2186
|
-
const installedCount = await states.blockletExtras.count({ 'controller.nftId': controller.nftId });
|
|
2187
|
-
|
|
2188
|
-
if (installedCount >= (controller.appMaxCount || 1)) {
|
|
2189
|
-
throw new Error(
|
|
2190
|
-
`You can only install ${controller.appMaxCount} blocklet with this credential: ${controller.nftId}`
|
|
2191
|
-
);
|
|
2192
|
-
}
|
|
2193
|
-
}
|
|
2194
|
-
|
|
2195
|
-
// install
|
|
2196
|
-
return this._install({
|
|
2197
|
-
meta: ensureMeta(meta, { did: blockletDid, name: blockletName }),
|
|
2198
|
-
source: BlockletSource.registry,
|
|
2199
|
-
deployedFrom: info.cdnUrl || storeUrl,
|
|
2200
|
-
sync,
|
|
2201
|
-
delay,
|
|
2202
|
-
controller,
|
|
2203
|
-
appSk,
|
|
2204
|
-
context,
|
|
2205
|
-
});
|
|
2206
|
-
}
|
|
2207
|
-
|
|
2208
|
-
/**
|
|
2209
|
-
* @type {{
|
|
2210
|
-
* id: string;
|
|
2211
|
-
* nftId: string;
|
|
2212
|
-
* expireDate: Date;
|
|
2213
|
-
* }} Controller
|
|
2214
|
-
*
|
|
2215
|
-
* @param {{
|
|
2216
|
-
* url: string;
|
|
2217
|
-
* sync: boolean;
|
|
2218
|
-
* delay: number;
|
|
2219
|
-
* controller: Controller;
|
|
2220
|
-
* appSk: string;
|
|
2221
|
-
* }} params
|
|
2222
|
-
* @param {{}} context
|
|
2223
|
-
* @return {*}
|
|
2224
|
-
* @memberof BlockletManager
|
|
2225
|
-
*/
|
|
2226
|
-
async _installFromUrl(params, context) {
|
|
2227
|
-
const { url, sync, delay, controller, appSk } = params;
|
|
2228
|
-
|
|
2229
|
-
logger.debug('start install blocklet', { url });
|
|
2230
|
-
|
|
2231
|
-
const bundleMeta = await getBlockletMetaFromUrl(url);
|
|
2232
|
-
|
|
2233
|
-
if (!bundleMeta) {
|
|
2234
|
-
throw new Error(`Can not install blocklet that not found by url: ${url}`);
|
|
2235
|
-
}
|
|
2236
|
-
|
|
2237
|
-
const { did: blockletDid, name: blockletName } = getBlockletIndex(bundleMeta, controller);
|
|
2238
|
-
|
|
2239
|
-
// install from store if url is a store url
|
|
2240
|
-
const { inStore, registryUrl, blockletDid: bundleDid } = await StoreUtil.parseSourceUrl(url, controller);
|
|
2241
|
-
|
|
2242
|
-
const nodeInfo = await states.node.read();
|
|
2243
|
-
|
|
2244
|
-
await validateStore(nodeInfo, registryUrl);
|
|
2245
|
-
|
|
2246
|
-
if (inStore) {
|
|
2247
|
-
const exist = await states.blocklet.getBlocklet(blockletDid);
|
|
2248
|
-
if (exist) {
|
|
2249
|
-
return this.upgrade({ did: blockletDid, storeUrl: registryUrl, sync, delay }, context);
|
|
2250
|
-
}
|
|
2251
|
-
|
|
2252
|
-
return this._installFromStore({ did: bundleDid, storeUrl: registryUrl, controller, sync, delay, appSk }, context);
|
|
2253
|
-
}
|
|
2254
|
-
|
|
2255
|
-
const meta = ensureMeta(bundleMeta, { name: blockletName, did: blockletDid });
|
|
2256
|
-
|
|
2257
|
-
// upgrade
|
|
2258
|
-
const exist = await states.blocklet.getBlocklet(blockletDid);
|
|
2259
|
-
if (exist) {
|
|
2260
|
-
return this._upgrade({
|
|
2261
|
-
meta,
|
|
2262
|
-
source: BlockletSource.url,
|
|
2263
|
-
deployedFrom: url,
|
|
2264
|
-
sync,
|
|
2265
|
-
delay,
|
|
2266
|
-
context,
|
|
2267
|
-
});
|
|
2268
|
-
}
|
|
2269
|
-
|
|
2270
|
-
// install
|
|
2271
|
-
return this._install({
|
|
2272
|
-
meta,
|
|
2273
|
-
source: BlockletSource.url,
|
|
2274
|
-
deployedFrom: url,
|
|
2275
|
-
sync,
|
|
2276
|
-
delay,
|
|
2277
|
-
controller,
|
|
2278
|
-
appSk,
|
|
2279
|
-
context,
|
|
2280
|
-
});
|
|
2281
|
-
}
|
|
2282
|
-
|
|
2283
|
-
async _installComponentFromUrl({
|
|
2284
|
-
rootDid,
|
|
2285
|
-
mountPoint,
|
|
2286
|
-
url,
|
|
2287
|
-
context,
|
|
2288
|
-
title,
|
|
2289
|
-
name: inputName,
|
|
2290
|
-
did: inputDid,
|
|
2291
|
-
configs,
|
|
2292
|
-
downloadTokenList,
|
|
2293
|
-
skipNavigation,
|
|
2294
|
-
}) {
|
|
2295
|
-
const blocklet = await this._getBlockletForInstallation(rootDid);
|
|
2296
|
-
if (!blocklet) {
|
|
2297
|
-
throw new Error('Root blocklet does not exist');
|
|
2298
|
-
}
|
|
2299
|
-
|
|
2300
|
-
const { inStore } = await StoreUtil.parseSourceUrl(url);
|
|
2301
|
-
|
|
2302
|
-
const meta = await getBlockletMetaFromUrl(url);
|
|
2303
|
-
|
|
2304
|
-
// 如果是一个付费的blocklet,并且url来源为Store, 需要携带token才能下载成功
|
|
2305
|
-
if (!isFreeBlocklet(meta) && inStore) {
|
|
2306
|
-
const info = await states.node.read();
|
|
2307
|
-
|
|
2308
|
-
// eslint-disable-next-line no-param-reassign
|
|
2309
|
-
context = {
|
|
2310
|
-
...context,
|
|
2311
|
-
headers: {
|
|
2312
|
-
'x-server-did': info.did,
|
|
2313
|
-
'x-server-public-key': info.pk,
|
|
2314
|
-
'x-server-signature': sign(info.did, info.sk, {
|
|
2315
|
-
exp: (Date.now() + 5 * 60 * 1000) / 1000,
|
|
2316
|
-
}),
|
|
2317
|
-
},
|
|
2318
|
-
downloadTokenList: downloadTokenList || [],
|
|
2319
|
-
};
|
|
2320
|
-
}
|
|
2321
|
-
|
|
2322
|
-
if (!isComponentBlocklet(meta)) {
|
|
2323
|
-
throw new Error('The blocklet cannot be a component');
|
|
2324
|
-
}
|
|
2325
|
-
|
|
2326
|
-
if (title) {
|
|
2327
|
-
meta.title = await titleSchema.validateAsync(title);
|
|
2328
|
-
}
|
|
2329
|
-
|
|
2330
|
-
let name = inputName;
|
|
2331
|
-
let did = inputDid;
|
|
2332
|
-
if (!inputName && !inputDid) {
|
|
2333
|
-
const found = findAvailableDid(meta, [
|
|
2334
|
-
...blocklet.children.map((x) => x.meta),
|
|
2335
|
-
...(await states.blockletExtras.getSettings(blocklet.meta.did, 'children', [])).map((x) => x.meta),
|
|
2336
|
-
]);
|
|
2337
|
-
name = found.name;
|
|
2338
|
-
did = found.did;
|
|
2339
|
-
}
|
|
2340
|
-
|
|
2341
|
-
const newChild = {
|
|
2342
|
-
meta: ensureMeta(meta, { did, name }),
|
|
2343
|
-
mountPoint,
|
|
2344
|
-
bundleSource: { url },
|
|
2345
|
-
dynamic: true,
|
|
2346
|
-
children: await parseChildrenFromMeta(meta, { ...context, ancestors: [{ mountPoint }] }),
|
|
2347
|
-
};
|
|
2348
|
-
|
|
2349
|
-
checkDuplicateComponents([...blocklet.children, newChild]);
|
|
2350
|
-
|
|
2351
|
-
try {
|
|
2352
|
-
// add component to db
|
|
2353
|
-
await states.blocklet.addChildren(rootDid, [newChild]);
|
|
2354
|
-
|
|
2355
|
-
// update navigation
|
|
2356
|
-
await this._upsertDynamicNavigation(blocklet.meta.did, newChild, { skipNavigation });
|
|
2357
|
-
|
|
2358
|
-
// update configs
|
|
2359
|
-
if (Array.isArray(configs)) {
|
|
2360
|
-
if (hasReservedKey(configs)) {
|
|
2361
|
-
throw new Error('Component key of environments can not start with `ABT_NODE_` or `BLOCKLET_`');
|
|
2362
|
-
}
|
|
2363
|
-
|
|
2364
|
-
await states.blockletExtras.setConfigs([blocklet.meta.did, newChild.meta.did], configs);
|
|
2365
|
-
}
|
|
2366
|
-
} catch (err) {
|
|
2367
|
-
logger.error('Add component failed', err);
|
|
2368
|
-
await this._rollback('upgrade', rootDid, blocklet);
|
|
2369
|
-
throw err;
|
|
2370
|
-
}
|
|
2371
|
-
|
|
2372
|
-
return this.updateChildren(
|
|
2373
|
-
{
|
|
2374
|
-
did: rootDid,
|
|
2375
|
-
children: [...blocklet.children, newChild],
|
|
2376
|
-
oldBlocklet: blocklet,
|
|
2377
|
-
selectedComponents: [newChild.meta.did],
|
|
2378
|
-
},
|
|
2379
|
-
context
|
|
2380
|
-
);
|
|
2381
|
-
}
|
|
2382
|
-
|
|
2383
|
-
async _installFromCreate({ title, description, appSk }, context = {}) {
|
|
2384
|
-
logger.debug('create blocklet', { title, description });
|
|
2385
|
-
|
|
2386
|
-
await joi.string().label('title').max(20).required().validateAsync(title);
|
|
2387
|
-
await joi.string().label('description').max(80).required().validateAsync(description);
|
|
2388
|
-
|
|
2389
|
-
const { did, name } = await this._findNextCustomBlockletName();
|
|
2390
|
-
const rawMeta = {
|
|
2391
|
-
name,
|
|
2392
|
-
did,
|
|
2393
|
-
title,
|
|
2394
|
-
description,
|
|
2395
|
-
version: BLOCKLET_DEFAULT_VERSION,
|
|
2396
|
-
group: BlockletGroup.gateway,
|
|
2397
|
-
interfaces: [
|
|
2398
|
-
{
|
|
2399
|
-
type: BLOCKLET_INTERFACE_TYPE_WEB,
|
|
2400
|
-
name: BLOCKLET_INTERFACE_PUBLIC,
|
|
2401
|
-
path: BLOCKLET_DEFAULT_PATH_REWRITE,
|
|
2402
|
-
prefix: BLOCKLET_DYNAMIC_PATH_PREFIX,
|
|
2403
|
-
port: BLOCKLET_DEFAULT_PORT_NAME,
|
|
2404
|
-
protocol: BLOCKLET_INTERFACE_PROTOCOL_HTTP,
|
|
2405
|
-
},
|
|
2406
|
-
],
|
|
2407
|
-
specVersion: BLOCKLET_LATEST_SPEC_VERSION,
|
|
2408
|
-
};
|
|
2409
|
-
const meta = validateMeta(rawMeta);
|
|
2410
|
-
|
|
2411
|
-
await states.blocklet.addBlocklet({ meta, source: BlockletSource.custom, externalSk: !!appSk });
|
|
2412
|
-
await this._setConfigsFromMeta(did);
|
|
2413
|
-
await this._setAppSk(did, appSk);
|
|
2414
|
-
|
|
2415
|
-
// check duplicate appSk
|
|
2416
|
-
await checkDuplicateAppSk({ did, states });
|
|
2417
|
-
|
|
2418
|
-
// fake install bundle
|
|
2419
|
-
const bundleDir = getBundleDir(this.installDir, meta);
|
|
2420
|
-
fs.mkdirSync(bundleDir, { recursive: true });
|
|
2421
|
-
updateMetaFile(path.join(bundleDir, BLOCKLET_META_FILE), meta);
|
|
2422
|
-
|
|
2423
|
-
return this._installBlocklet({ did, context });
|
|
2424
|
-
}
|
|
2425
|
-
|
|
2426
|
-
async _downloadFromUpload(file) {
|
|
2427
|
-
// const { filename, mimetype, encoding, createReadStream } = await file;
|
|
2428
|
-
const { filename, createReadStream } = await file;
|
|
2429
|
-
const cwd = path.join(this.dataDirs.tmp, 'download');
|
|
2430
|
-
const tarFile = path.join(cwd, `${path.basename(filename, path.extname(filename))}.tgz`);
|
|
2431
|
-
await fs.ensureDir(cwd);
|
|
2432
|
-
await new Promise((resolve, reject) => {
|
|
2433
|
-
const readStream = createReadStream();
|
|
2434
|
-
const writeStream = fs.createWriteStream(tarFile);
|
|
2435
|
-
readStream
|
|
2436
|
-
.pipe(new Throttle({ rate: 1024 * 1024 * 20 })) // 20MB
|
|
2437
|
-
.pipe(writeStream);
|
|
2438
|
-
readStream.on('error', (error) => {
|
|
2439
|
-
logger.error('File upload read stream failed', { error });
|
|
2440
|
-
writeStream.destroy(new Error(`File upload read stream failed, ${error.message}`));
|
|
2441
|
-
});
|
|
2442
|
-
writeStream.on('error', (error) => {
|
|
2443
|
-
logger.error('File upload write stream failed', { error });
|
|
2444
|
-
fs.removeSync(tarFile);
|
|
2445
|
-
reject(error);
|
|
2446
|
-
});
|
|
2447
|
-
writeStream.on('finish', resolve);
|
|
2448
|
-
});
|
|
2449
|
-
|
|
2450
|
-
return { cwd, tarFile };
|
|
2451
|
-
}
|
|
2452
|
-
|
|
2453
|
-
async _installFromUpload({ file, did, diffVersion, deleteSet, appSk }, context) {
|
|
2454
|
-
logger.info('install blocklet', { from: 'upload file' });
|
|
2455
|
-
const { tarFile } = await this._downloadFromUpload(file);
|
|
2456
|
-
|
|
2457
|
-
// diff deploy
|
|
2458
|
-
if (did && diffVersion) {
|
|
2459
|
-
const oldBlocklet = await this._getBlockletForInstallation(did);
|
|
2460
|
-
if (!oldBlocklet) {
|
|
2461
|
-
throw new Error(`Blocklet ${did} not found when diff deploying`);
|
|
2462
|
-
}
|
|
2463
|
-
if (oldBlocklet.meta.version !== diffVersion) {
|
|
2464
|
-
logger.error('Diff deploy: Blocklet version changed', {
|
|
2465
|
-
preVersion: diffVersion,
|
|
2466
|
-
changedVersion: oldBlocklet.meta.version,
|
|
2467
|
-
name: oldBlocklet.meta.name,
|
|
2468
|
-
did: oldBlocklet.meta.did,
|
|
2469
|
-
});
|
|
2470
|
-
throw new Error('Blocklet version changed when diff deploying');
|
|
2471
|
-
}
|
|
2472
|
-
if (isInProgress(oldBlocklet.status)) {
|
|
2473
|
-
logger.error(`Can not deploy blocklet when it is ${fromBlockletStatus(oldBlocklet.status)}`);
|
|
2474
|
-
throw new Error(`Can not deploy blocklet when it is ${fromBlockletStatus(oldBlocklet.status)}`);
|
|
2475
|
-
}
|
|
2476
|
-
|
|
2477
|
-
const { meta } = await resolveDiffDownload(tarFile, this.installDir, {
|
|
2478
|
-
deleteSet,
|
|
2479
|
-
meta: oldBlocklet.meta,
|
|
2480
|
-
});
|
|
2481
|
-
const newBlocklet = await states.blocklet.getBlocklet(did);
|
|
2482
|
-
newBlocklet.meta = ensureMeta(meta);
|
|
2483
|
-
newBlocklet.source = BlockletSource.upload;
|
|
2484
|
-
newBlocklet.deployedFrom = `Upload by ${context.user.fullName}`;
|
|
2485
|
-
newBlocklet.children = await this._getChildrenForInstallation(meta);
|
|
2486
|
-
await validateBlocklet(newBlocklet);
|
|
2487
|
-
|
|
2488
|
-
// backup rollback data
|
|
2489
|
-
const action = 'upgrade';
|
|
2490
|
-
await this._rollbackCache.backup({ did: newBlocklet.meta.did, action, oldBlocklet });
|
|
2491
|
-
|
|
2492
|
-
await this.downloadAndInstall({
|
|
2493
|
-
blocklet: newBlocklet,
|
|
2494
|
-
oldBlocklet,
|
|
2495
|
-
context: { ...context, forceStartProcessIds: [getComponentProcessId(newBlocklet)] },
|
|
2496
|
-
throwOnError: true,
|
|
2497
|
-
postAction: action,
|
|
2498
|
-
skipCheckStatusBeforeDownload: true,
|
|
2499
|
-
});
|
|
2500
|
-
return this.getBlocklet(newBlocklet.meta.did);
|
|
2501
|
-
}
|
|
2502
|
-
|
|
2503
|
-
// full deploy
|
|
2504
|
-
const { meta } = await resolveDownload(tarFile, this.installDir);
|
|
2505
|
-
const oldBlocklet = await this._getBlockletForInstallation(meta.did);
|
|
2506
|
-
|
|
2507
|
-
// full deploy - upgrade
|
|
2508
|
-
if (oldBlocklet) {
|
|
2509
|
-
if (isInProgress(oldBlocklet.status)) {
|
|
2510
|
-
logger.error(`Can not deploy blocklet when it is ${fromBlockletStatus(oldBlocklet.status)}`);
|
|
2511
|
-
throw new Error(`Can not deploy blocklet when it is ${fromBlockletStatus(oldBlocklet.status)}`);
|
|
2512
|
-
}
|
|
2513
|
-
|
|
2514
|
-
const newBlocklet = await states.blocklet.getBlocklet(meta.did);
|
|
2515
|
-
newBlocklet.meta = ensureMeta(meta);
|
|
2516
|
-
newBlocklet.source = BlockletSource.upload;
|
|
2517
|
-
newBlocklet.deployedFrom = `Upload by ${context.user.fullName}`;
|
|
2518
|
-
newBlocklet.children = await this._getChildrenForInstallation(meta);
|
|
2519
|
-
|
|
2520
|
-
// backup rollback data
|
|
2521
|
-
const action = 'upgrade';
|
|
2522
|
-
await this._rollbackCache.backup({ did: newBlocklet.meta.did, action, oldBlocklet });
|
|
2523
|
-
|
|
2524
|
-
await validateBlocklet(newBlocklet);
|
|
2525
|
-
await this.downloadAndInstall({
|
|
2526
|
-
blocklet: newBlocklet,
|
|
2527
|
-
oldBlocklet,
|
|
2528
|
-
context: { ...context, forceStartProcessIds: [getComponentProcessId(newBlocklet)] },
|
|
2529
|
-
throwOnError: true,
|
|
2530
|
-
postAction: action,
|
|
2531
|
-
skipCheckStatusBeforeDownload: true,
|
|
2532
|
-
});
|
|
2533
|
-
return this.getBlocklet(newBlocklet.meta.did);
|
|
2534
|
-
}
|
|
2535
|
-
|
|
2536
|
-
// full deploy - install
|
|
2537
|
-
|
|
2538
|
-
const oldExtraState = await states.blockletExtras.findOne({ did: meta.did });
|
|
2539
|
-
const children = await this._getChildrenForInstallation(meta);
|
|
2540
|
-
const blocklet = await states.blocklet.addBlocklet({
|
|
2541
|
-
meta,
|
|
2542
|
-
source: BlockletSource.upload,
|
|
2543
|
-
deployedFrom: `Upload by ${context.user.fullName}`,
|
|
2544
|
-
children,
|
|
2545
|
-
externalSk: !!appSk,
|
|
2546
|
-
});
|
|
2547
|
-
|
|
2548
|
-
const action = 'install';
|
|
2549
|
-
const oldState = { extraState: oldExtraState };
|
|
2550
|
-
try {
|
|
2551
|
-
await this._setConfigsFromMeta(meta.did);
|
|
2552
|
-
await this._setAppSk(appSk);
|
|
2553
|
-
await validateBlocklet(blocklet);
|
|
2554
|
-
|
|
2555
|
-
// check duplicate appSk
|
|
2556
|
-
await checkDuplicateAppSk({ did: meta.did, states });
|
|
2557
|
-
} catch (error) {
|
|
2558
|
-
await this._rollback(action, meta.did, oldState);
|
|
2559
|
-
throw error;
|
|
2560
|
-
}
|
|
2561
|
-
|
|
2562
|
-
// backup rollback data
|
|
2563
|
-
await this._rollbackCache.backup({ did: meta.did, action, oldBlocklet: oldState });
|
|
2564
|
-
|
|
2565
|
-
await this.downloadAndInstall({
|
|
2566
|
-
blocklet,
|
|
2567
|
-
oldBlocklet: oldState,
|
|
2568
|
-
context,
|
|
2569
|
-
throwOnError: true,
|
|
2570
|
-
postAction: action,
|
|
2571
|
-
skipCheckStatusBeforeDownload: true,
|
|
2572
|
-
});
|
|
2573
|
-
return this.getBlocklet(meta.did);
|
|
2574
|
-
}
|
|
2575
|
-
|
|
2576
|
-
async _installComponentFromUpload({
|
|
2577
|
-
rootDid,
|
|
2578
|
-
mountPoint,
|
|
2579
|
-
file,
|
|
2580
|
-
did,
|
|
2581
|
-
diffVersion,
|
|
2582
|
-
deleteSet,
|
|
2583
|
-
skipNavigation,
|
|
2584
|
-
context,
|
|
2585
|
-
}) {
|
|
2586
|
-
logger.info('install blocklet', { from: 'upload file' });
|
|
2587
|
-
// download
|
|
2588
|
-
const { tarFile } = await this._downloadFromUpload(file);
|
|
2589
|
-
|
|
2590
|
-
const oldBlocklet = await this._getBlockletForInstallation(rootDid);
|
|
2591
|
-
if (!oldBlocklet) {
|
|
2592
|
-
throw new Error('Root blocklet does not exist');
|
|
2593
|
-
}
|
|
2594
|
-
|
|
2595
|
-
if (isInProgress(oldBlocklet.status)) {
|
|
2596
|
-
logger.error(`Can not deploy blocklet when it is ${fromBlockletStatus(oldBlocklet.status)}`);
|
|
2597
|
-
throw new Error(`Can not deploy blocklet when it is ${fromBlockletStatus(oldBlocklet.status)}`);
|
|
2598
|
-
}
|
|
2599
|
-
|
|
2600
|
-
let meta;
|
|
2601
|
-
// diff upload
|
|
2602
|
-
if (did && diffVersion) {
|
|
2603
|
-
const oldChild = oldBlocklet.children.find((x) => x.meta.did === did);
|
|
2604
|
-
if (!oldChild) {
|
|
2605
|
-
throw new Error(`Blocklet ${did} not found when diff deploying`);
|
|
2606
|
-
}
|
|
2607
|
-
if (oldChild.meta.version !== diffVersion) {
|
|
2608
|
-
logger.error('Diff deploy: Blocklet version changed', {
|
|
2609
|
-
preVersion: diffVersion,
|
|
2610
|
-
changedVersion: oldChild.meta.version,
|
|
2611
|
-
name: oldChild.meta.name,
|
|
2612
|
-
did: oldChild.meta.did,
|
|
2613
|
-
});
|
|
2614
|
-
throw new Error('Blocklet version changed when diff deploying');
|
|
2615
|
-
}
|
|
2616
|
-
|
|
2617
|
-
meta = (await resolveDiffDownload(tarFile, this.installDir, { deleteSet, meta: oldChild.meta })).meta;
|
|
2618
|
-
} else {
|
|
2619
|
-
// full deploy
|
|
2620
|
-
meta = (await resolveDownload(tarFile, this.installDir)).meta;
|
|
2621
|
-
}
|
|
2622
|
-
|
|
2623
|
-
if (meta.did === rootDid) {
|
|
2624
|
-
// should not be here
|
|
2625
|
-
throw new Error('Cannot add self as a component');
|
|
2626
|
-
}
|
|
2627
|
-
|
|
2628
|
-
const newBlocklet = await states.blocklet.getBlocklet(rootDid);
|
|
2629
|
-
|
|
2630
|
-
if (newBlocklet.children.some((x) => !x.dynamic && x.meta.did === meta.did)) {
|
|
2631
|
-
throw new Error('Cannot add duplicate component defined in app meta');
|
|
2632
|
-
}
|
|
2633
|
-
|
|
2634
|
-
const newChild = {
|
|
2635
|
-
meta: ensureMeta(meta),
|
|
2636
|
-
mountPoint,
|
|
2637
|
-
source: BlockletSource.upload,
|
|
2638
|
-
deployedFrom: `Upload by ${context.user.fullName}`,
|
|
2639
|
-
bundleSource: null,
|
|
2640
|
-
dynamic: true,
|
|
2641
|
-
children: await parseChildrenFromMeta(meta, { ancestors: [{ mountPoint }] }),
|
|
2642
|
-
};
|
|
2643
|
-
const index = newBlocklet.children.findIndex((child) => child.meta.did === meta.did);
|
|
2644
|
-
if (index >= 0) {
|
|
2645
|
-
newBlocklet.children.splice(index, 1, newChild);
|
|
2646
|
-
} else {
|
|
2647
|
-
newBlocklet.children.push(newChild);
|
|
2648
|
-
}
|
|
2649
|
-
|
|
2650
|
-
checkDuplicateComponents(newBlocklet.children);
|
|
2651
|
-
|
|
2652
|
-
await this._upsertDynamicNavigation(newBlocklet.meta.did, newChild, { skipNavigation });
|
|
2653
|
-
|
|
2654
|
-
// backup rollback data
|
|
2655
|
-
const action = 'upgrade';
|
|
2656
|
-
await this._rollbackCache.backup({ did: newBlocklet.meta.did, action, oldBlocklet });
|
|
2657
|
-
|
|
2658
|
-
await this.downloadAndInstall({
|
|
2659
|
-
blocklet: newBlocklet,
|
|
2660
|
-
oldBlocklet,
|
|
2661
|
-
context: { ...context, forceStartProcessIds: [getComponentProcessId(newChild, [newBlocklet])] },
|
|
2662
|
-
throwOnError: true,
|
|
2663
|
-
postAction: action,
|
|
2664
|
-
skipCheckStatusBeforeDownload: true,
|
|
2665
|
-
selectedComponentDids: [newChild.meta.did],
|
|
2666
|
-
});
|
|
2667
|
-
return this.getBlocklet(newBlocklet.meta.did);
|
|
2668
|
-
}
|
|
2669
|
-
|
|
2670
|
-
async prune() {
|
|
2671
|
-
const blocklets = await states.blocklet.getBlocklets();
|
|
2672
|
-
const settings = await states.blockletExtras.listSettings();
|
|
2673
|
-
await pruneBlockletBundle({
|
|
2674
|
-
installDir: this.dataDirs.blocklets,
|
|
2675
|
-
blocklets,
|
|
2676
|
-
blockletSettings: settings
|
|
2677
|
-
.filter((x) => x.settings.children && x.settings.children.length)
|
|
2678
|
-
.map((x) => x.settings),
|
|
1670
|
+
...getComponentSystemEnvironments(childWithEnv), // system env of child blocklet
|
|
1671
|
+
...appSystemEnvironments, // system env of root blocklet
|
|
1672
|
+
});
|
|
1673
|
+
}
|
|
2679
1674
|
});
|
|
2680
|
-
}
|
|
2681
1675
|
|
|
2682
|
-
|
|
2683
|
-
|
|
2684
|
-
if (!blocklet) {
|
|
2685
|
-
throw new Error('the blocklet is not installed');
|
|
2686
|
-
}
|
|
1676
|
+
// put BLOCKLET_APP_ID at root level for indexing
|
|
1677
|
+
blocklet.appDid = appSystemEnvironments.BLOCKLET_APP_ID;
|
|
2687
1678
|
|
|
2688
|
-
if (
|
|
2689
|
-
|
|
1679
|
+
if (!Array.isArray(blocklet.migratedFrom)) {
|
|
1680
|
+
blocklet.migratedFrom = [];
|
|
2690
1681
|
}
|
|
2691
|
-
|
|
2692
|
-
if (blocklet.
|
|
2693
|
-
|
|
1682
|
+
// This can only be set once, can be used for indexing, will not change ever
|
|
1683
|
+
if (!blocklet.appPid) {
|
|
1684
|
+
blocklet.appPid = appSystemEnvironments.BLOCKLET_APP_PID;
|
|
2694
1685
|
}
|
|
2695
1686
|
|
|
2696
|
-
|
|
2697
|
-
|
|
2698
|
-
|
|
2699
|
-
getCrons() {
|
|
2700
|
-
return [
|
|
2701
|
-
{
|
|
2702
|
-
name: 'sync-blocklet-status',
|
|
2703
|
-
time: '*/60 * * * * *', // 60s
|
|
2704
|
-
fn: this._syncBlockletStatus.bind(this),
|
|
2705
|
-
},
|
|
2706
|
-
{
|
|
2707
|
-
name: 'sync-blocklet-list',
|
|
2708
|
-
time: '*/60 * * * * *', // 60s
|
|
2709
|
-
fn: this.refreshListCache.bind(this),
|
|
2710
|
-
},
|
|
2711
|
-
{
|
|
2712
|
-
name: 'refresh-accessible-ip',
|
|
2713
|
-
time: '0 */10 * * * *', // 10min
|
|
2714
|
-
fn: async () => {
|
|
2715
|
-
const nodeInfo = await states.node.read();
|
|
2716
|
-
await refreshAccessibleExternalNodeIp(nodeInfo);
|
|
2717
|
-
},
|
|
2718
|
-
},
|
|
2719
|
-
{
|
|
2720
|
-
name: 'delete-expired-external-blocklet',
|
|
2721
|
-
time: '0 */30 * * * *', // 30min
|
|
2722
|
-
options: { runOnInit: false },
|
|
2723
|
-
fn: () => this._deleteExpiredExternalBlocklet(),
|
|
2724
|
-
},
|
|
2725
|
-
{
|
|
2726
|
-
name: 'clean-expired-blocklet-data',
|
|
2727
|
-
time: '0 */20 0 * * *', // 每天凌晨 0 点的每 20 分钟
|
|
2728
|
-
fn: () => this._cleanExpiredBlockletData(),
|
|
2729
|
-
},
|
|
2730
|
-
{
|
|
2731
|
-
name: 'record-blocklet-runtime-history',
|
|
2732
|
-
time: `*/${MONITOR_RECORD_INTERVAL_SEC} * * * * *`, // 10s
|
|
2733
|
-
fn: () => this.runtimeMonitor.monitAll(),
|
|
2734
|
-
},
|
|
2735
|
-
];
|
|
1687
|
+
// update state to db
|
|
1688
|
+
return states.blocklet.updateBlocklet(did, blocklet);
|
|
2736
1689
|
}
|
|
2737
1690
|
|
|
2738
1691
|
async _attachRuntimeInfo({ did, nodeInfo, diskInfo = true, context, cachedBlocklet }) {
|
|
@@ -2821,257 +1774,75 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
2821
1774
|
blocklets.forEach(run);
|
|
2822
1775
|
}
|
|
2823
1776
|
|
|
2824
|
-
async _getChildrenForInstallation(
|
|
2825
|
-
|
|
2826
|
-
|
|
2827
|
-
for (const dynamicChild of dynamicChildren) {
|
|
2828
|
-
dynamicChild.children = await parseChildrenFromMeta(dynamicChild.meta, { ancestors: [dynamicChild] });
|
|
1777
|
+
async _getChildrenForInstallation(component) {
|
|
1778
|
+
if (!component) {
|
|
1779
|
+
return [];
|
|
2829
1780
|
}
|
|
2830
|
-
checkDuplicateComponents([...dynamicChildren, ...staticChildren]);
|
|
2831
|
-
|
|
2832
|
-
return [...staticChildren, ...dynamicChildren];
|
|
2833
|
-
}
|
|
2834
|
-
|
|
2835
|
-
async _getDynamicChildrenFromSettings(did, { skipDeleted } = {}) {
|
|
2836
|
-
const children = await states.blockletExtras.getSettings(did, 'children', []);
|
|
2837
|
-
return children
|
|
2838
|
-
.map((x) => ({ ...x, dynamic: true }))
|
|
2839
|
-
.filter((x) => (skipDeleted ? x.status !== BlockletStatus.deleted : true));
|
|
2840
|
-
}
|
|
2841
|
-
|
|
2842
|
-
/**
|
|
2843
|
-
*
|
|
2844
|
-
*
|
|
2845
|
-
* @param {{
|
|
2846
|
-
* meta: {}; // blocklet meta
|
|
2847
|
-
* source: number; // example: BlockletSource.registry,
|
|
2848
|
-
* deployedFrom: string;
|
|
2849
|
-
* appSk: string;
|
|
2850
|
-
* context: {}
|
|
2851
|
-
* sync: boolean = false;
|
|
2852
|
-
* delay: number;
|
|
2853
|
-
* controller: Controller;
|
|
2854
|
-
* }} params
|
|
2855
|
-
* @return {*}
|
|
2856
|
-
* @memberof BlockletManager
|
|
2857
|
-
*/
|
|
2858
|
-
async _install(params) {
|
|
2859
|
-
const { meta, source, deployedFrom, context, sync, delay, controller, appSk } = params;
|
|
2860
1781
|
|
|
2861
|
-
|
|
2862
|
-
|
|
2863
|
-
|
|
2864
|
-
if (info.mode === NODE_MODES.SERVERLESS) {
|
|
2865
|
-
validateInServerless({ blockletMeta: meta });
|
|
1782
|
+
const { dynamicComponents } = await parseComponents(component);
|
|
1783
|
+
if (component.meta.group !== BlockletGroup.gateway) {
|
|
1784
|
+
dynamicComponents.unshift(component);
|
|
2866
1785
|
}
|
|
2867
1786
|
|
|
2868
|
-
const
|
|
2869
|
-
|
|
2870
|
-
|
|
2871
|
-
|
|
2872
|
-
const children = await this._getChildrenForInstallation(meta);
|
|
2873
|
-
try {
|
|
2874
|
-
// 创建一个blocklet到database
|
|
2875
|
-
const blocklet = await states.blocklet.addBlocklet({
|
|
2876
|
-
meta,
|
|
2877
|
-
source,
|
|
2878
|
-
deployedFrom,
|
|
2879
|
-
children,
|
|
2880
|
-
externalSk: !!appSk,
|
|
2881
|
-
});
|
|
2882
|
-
|
|
2883
|
-
await validateBlocklet(blocklet);
|
|
2884
|
-
|
|
2885
|
-
await states.blockletExtras.addMeta({ did, meta: { did, name }, controller });
|
|
2886
|
-
|
|
2887
|
-
await this._setConfigsFromMeta(did);
|
|
2888
|
-
await this._setAppSk(did, appSk);
|
|
2889
|
-
|
|
2890
|
-
// check duplicate appSk
|
|
2891
|
-
await checkDuplicateAppSk({ did, states });
|
|
2892
|
-
|
|
2893
|
-
logger.info('blocklet added to database', { did: meta.did });
|
|
2894
|
-
|
|
2895
|
-
const blocklet1 = await states.blocklet.setBlockletStatus(did, BlockletStatus.waiting);
|
|
2896
|
-
this.emit(BlockletEvents.added, blocklet1);
|
|
2897
|
-
|
|
2898
|
-
const action = 'install';
|
|
2899
|
-
const oldBlocklet = {
|
|
2900
|
-
extraState: oldExtraState,
|
|
2901
|
-
};
|
|
2902
|
-
|
|
2903
|
-
/** @type {{
|
|
2904
|
-
* blocklet: any;
|
|
2905
|
-
* oldBlocklet: {
|
|
2906
|
-
* children: any[];
|
|
2907
|
-
* extraState: any;
|
|
2908
|
-
* };
|
|
2909
|
-
* context: {};
|
|
2910
|
-
* postAction: string
|
|
2911
|
-
* }} */
|
|
2912
|
-
const downloadParams = {
|
|
2913
|
-
blocklet: { ...blocklet1 },
|
|
2914
|
-
context,
|
|
2915
|
-
postAction: action,
|
|
2916
|
-
oldBlocklet,
|
|
2917
|
-
};
|
|
2918
|
-
|
|
2919
|
-
// backup rollback data
|
|
2920
|
-
await this._rollbackCache.backup({ did, action, oldBlocklet });
|
|
2921
|
-
|
|
2922
|
-
if (sync) {
|
|
2923
|
-
await this.downloadAndInstall({ ...downloadParams, throwOnError: true });
|
|
2924
|
-
return states.blocklet.getBlocklet(did);
|
|
2925
|
-
}
|
|
2926
|
-
|
|
2927
|
-
setTimeout(() => {
|
|
2928
|
-
const ticket = this.installQueue.push(
|
|
2929
|
-
{
|
|
2930
|
-
entity: 'blocklet',
|
|
2931
|
-
action: 'download',
|
|
2932
|
-
id: did,
|
|
2933
|
-
...downloadParams,
|
|
2934
|
-
},
|
|
2935
|
-
did
|
|
2936
|
-
);
|
|
2937
|
-
ticket.on('failed', async (err) => {
|
|
2938
|
-
logger.error('failed to install blocklet', { name, did, version, error: err });
|
|
2939
|
-
try {
|
|
2940
|
-
await this._rollback('install', did, { extraState: oldExtraState });
|
|
2941
|
-
} catch (e) {
|
|
2942
|
-
logger.error('failed to remove blocklet on install error', { did: meta.did, error: e });
|
|
2943
|
-
}
|
|
2944
|
-
|
|
2945
|
-
this._createNotification(did, {
|
|
2946
|
-
title: 'Blocklet Install Failed',
|
|
2947
|
-
description: `Blocklet ${name}@${version} install failed with error: ${err.message || 'queue exception'}`,
|
|
2948
|
-
entityType: 'blocklet',
|
|
2949
|
-
entityId: did,
|
|
2950
|
-
severity: 'error',
|
|
2951
|
-
});
|
|
2952
|
-
});
|
|
2953
|
-
}, delay || 0);
|
|
2954
|
-
|
|
2955
|
-
return blocklet1;
|
|
2956
|
-
} catch (err) {
|
|
2957
|
-
logger.error('failed to install blocklet', { name, did, version, error: err });
|
|
2958
|
-
this._createNotification(did, {
|
|
2959
|
-
title: 'Blocklet Install Failed',
|
|
2960
|
-
description: `Blocklet ${name}@${version} install failed with error: ${err.message || 'queue exception'}`,
|
|
2961
|
-
entityType: 'blocklet',
|
|
2962
|
-
entityId: did,
|
|
2963
|
-
severity: 'error',
|
|
2964
|
-
});
|
|
2965
|
-
|
|
2966
|
-
try {
|
|
2967
|
-
await this._rollback('install', did, { extraState: oldExtraState });
|
|
2968
|
-
} catch (e) {
|
|
2969
|
-
logger.error('failed to remove blocklet on install error', { did: meta.did, error: e });
|
|
2970
|
-
}
|
|
2971
|
-
|
|
2972
|
-
throw err;
|
|
2973
|
-
}
|
|
1787
|
+
const children = filterDuplicateComponents(dynamicComponents);
|
|
1788
|
+
checkVersionCompatibility(children);
|
|
1789
|
+
return children;
|
|
2974
1790
|
}
|
|
2975
1791
|
|
|
2976
|
-
|
|
2977
|
-
|
|
2978
|
-
|
|
2979
|
-
|
|
2980
|
-
|
|
2981
|
-
|
|
2982
|
-
|
|
2983
|
-
|
|
2984
|
-
|
|
2985
|
-
|
|
2986
|
-
|
|
2987
|
-
|
|
2988
|
-
|
|
2989
|
-
|
|
2990
|
-
|
|
2991
|
-
|
|
2992
|
-
logger.info(`${action} blocklet`, { did, name, version });
|
|
2993
|
-
|
|
2994
|
-
const newBlocklet = await states.blocklet.setBlockletStatus(did, BlockletStatus.waiting);
|
|
2995
|
-
|
|
2996
|
-
newBlocklet.meta = ensureMeta(meta);
|
|
2997
|
-
newBlocklet.source = source;
|
|
2998
|
-
newBlocklet.deployedFrom = deployedFrom;
|
|
2999
|
-
newBlocklet.children = await this._getChildrenForInstallation(meta);
|
|
3000
|
-
|
|
3001
|
-
await validateBlocklet(newBlocklet);
|
|
3002
|
-
|
|
3003
|
-
this.emit(BlockletEvents.statusChange, newBlocklet);
|
|
3004
|
-
|
|
3005
|
-
// 如果是一个付费的blocklet,需要携带token才能下载成功
|
|
3006
|
-
if (!isFreeBlocklet(meta)) {
|
|
3007
|
-
const blocklet = await states.blocklet.getBlocklet(did);
|
|
3008
|
-
|
|
3009
|
-
const info = await states.node.read();
|
|
3010
|
-
|
|
3011
|
-
// eslint-disable-next-line no-param-reassign
|
|
3012
|
-
context = {
|
|
3013
|
-
...context,
|
|
3014
|
-
headers: {
|
|
3015
|
-
'x-server-did': info.did,
|
|
3016
|
-
'x-server-public-key': info.pk,
|
|
3017
|
-
'x-server-signature': sign(info.did, info.sk, {
|
|
3018
|
-
exp: (Date.now() + 5 * 60 * 1000) / 1000,
|
|
3019
|
-
}),
|
|
1792
|
+
async _addBlocklet({ component, mode = BLOCKLET_MODES.PRODUCTION, name, did, title, description }) {
|
|
1793
|
+
const meta = {
|
|
1794
|
+
name,
|
|
1795
|
+
did,
|
|
1796
|
+
title: title || component?.meta?.title || '',
|
|
1797
|
+
description: description || component?.meta?.description || '',
|
|
1798
|
+
version: BLOCKLET_DEFAULT_VERSION,
|
|
1799
|
+
group: BlockletGroup.gateway,
|
|
1800
|
+
interfaces: [
|
|
1801
|
+
{
|
|
1802
|
+
type: BLOCKLET_INTERFACE_TYPE_WEB,
|
|
1803
|
+
name: BLOCKLET_INTERFACE_PUBLIC,
|
|
1804
|
+
path: BLOCKLET_DEFAULT_PATH_REWRITE,
|
|
1805
|
+
prefix: BLOCKLET_DYNAMIC_PATH_PREFIX,
|
|
1806
|
+
port: BLOCKLET_DEFAULT_PORT_NAME,
|
|
1807
|
+
protocol: BLOCKLET_INTERFACE_PROTOCOL_HTTP,
|
|
3020
1808
|
},
|
|
3021
|
-
|
|
3022
|
-
|
|
3023
|
-
|
|
3024
|
-
|
|
3025
|
-
|
|
3026
|
-
|
|
3027
|
-
oldBlocklet: { ...oldBlocklet },
|
|
3028
|
-
blocklet: { ...newBlocklet },
|
|
3029
|
-
version,
|
|
3030
|
-
context,
|
|
3031
|
-
postAction: action,
|
|
1809
|
+
],
|
|
1810
|
+
specVersion: BLOCKLET_LATEST_SPEC_VERSION,
|
|
1811
|
+
environments: component?.meta?.environments || [],
|
|
1812
|
+
timeout: {
|
|
1813
|
+
start: process.env.NODE_ENV === 'test' ? 10 : 60,
|
|
1814
|
+
},
|
|
3032
1815
|
};
|
|
3033
1816
|
|
|
3034
|
-
|
|
3035
|
-
await this._rollbackCache.backup({ did, action, oldBlocklet });
|
|
1817
|
+
const children = component ? await this._getChildrenForInstallation(component) : [];
|
|
3036
1818
|
|
|
3037
|
-
if (
|
|
3038
|
-
|
|
3039
|
-
return states.blocklet.getBlocklet(did);
|
|
1819
|
+
if (children[0]?.meta?.logo) {
|
|
1820
|
+
meta.logo = children[0].meta.logo;
|
|
3040
1821
|
}
|
|
3041
|
-
const ticket = this.installQueue.push(
|
|
3042
|
-
{
|
|
3043
|
-
entity: 'blocklet',
|
|
3044
|
-
action: 'download',
|
|
3045
|
-
id: did,
|
|
3046
|
-
...downloadParams,
|
|
3047
|
-
},
|
|
3048
|
-
did
|
|
3049
|
-
);
|
|
3050
1822
|
|
|
3051
|
-
|
|
3052
|
-
|
|
3053
|
-
|
|
3054
|
-
|
|
3055
|
-
|
|
3056
|
-
|
|
3057
|
-
|
|
3058
|
-
|
|
3059
|
-
|
|
3060
|
-
|
|
3061
|
-
|
|
3062
|
-
|
|
3063
|
-
|
|
3064
|
-
severity: 'error',
|
|
3065
|
-
});
|
|
1823
|
+
await validateBlocklet({ meta, children });
|
|
1824
|
+
|
|
1825
|
+
// fake install bundle
|
|
1826
|
+
const bundleDir = getBundleDir(this.installDir, meta);
|
|
1827
|
+
fs.mkdirSync(bundleDir, { recursive: true });
|
|
1828
|
+
updateMetaFile(path.join(bundleDir, BLOCKLET_META_FILE), meta);
|
|
1829
|
+
|
|
1830
|
+
// add blocklet to db
|
|
1831
|
+
const blocklet = await states.blocklet.addBlocklet({
|
|
1832
|
+
meta,
|
|
1833
|
+
source: BlockletSource.custom,
|
|
1834
|
+
children,
|
|
1835
|
+
mode,
|
|
3066
1836
|
});
|
|
3067
|
-
|
|
1837
|
+
|
|
1838
|
+
return blocklet;
|
|
3068
1839
|
}
|
|
3069
1840
|
|
|
3070
1841
|
/**
|
|
3071
1842
|
* @param {string} opt.did
|
|
3072
1843
|
* @param {object} opt.context
|
|
3073
1844
|
*/
|
|
3074
|
-
async _installBlocklet({ did, oldBlocklet, context }) {
|
|
1845
|
+
async _installBlocklet({ did, oldBlocklet, context, createNotification = true }) {
|
|
3075
1846
|
try {
|
|
3076
1847
|
// should ensure blocklet integrity
|
|
3077
1848
|
let blocklet = await this.ensureBlocklet(did);
|
|
@@ -3089,7 +1860,7 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
3089
1860
|
await this._runPreInstallHook(blocklet, context);
|
|
3090
1861
|
|
|
3091
1862
|
// Add environments
|
|
3092
|
-
await this.
|
|
1863
|
+
await this._updateBlockletEnvironment(meta.did);
|
|
3093
1864
|
blocklet = await this.getBlocklet(did);
|
|
3094
1865
|
|
|
3095
1866
|
// post install
|
|
@@ -3099,15 +1870,27 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
3099
1870
|
blocklet = await this.getBlocklet(did);
|
|
3100
1871
|
logger.info('blocklet installed', { source, did: meta.did });
|
|
3101
1872
|
|
|
1873
|
+
// logo
|
|
1874
|
+
if (blocklet.children[0]?.meta?.logo) {
|
|
1875
|
+
const fileName = blocklet.children[0].meta.logo;
|
|
1876
|
+
const src = path.join(getBundleDir(this.installDir, blocklet.children[0].meta), fileName);
|
|
1877
|
+
const dist = path.join(getBundleDir(this.installDir, blocklet.meta), fileName);
|
|
1878
|
+
await fs.copy(src, dist);
|
|
1879
|
+
}
|
|
1880
|
+
|
|
3102
1881
|
await fs.writeFile(path.join(blocklet.env.dataDir, 'logo.svg'), createDidLogo(blocklet.meta.did));
|
|
3103
1882
|
|
|
3104
1883
|
// Init db
|
|
3105
1884
|
await this.teamManager.initTeam(blocklet.meta.did);
|
|
3106
1885
|
|
|
1886
|
+
// Update dependents
|
|
1887
|
+
await this._updateDependents(did);
|
|
1888
|
+
blocklet = await this.getBlocklet(did);
|
|
1889
|
+
|
|
3107
1890
|
this.emit(BlockletEvents.installed, { blocklet, context });
|
|
3108
1891
|
|
|
3109
1892
|
// Update dynamic component meta in blocklet settings
|
|
3110
|
-
await this.
|
|
1893
|
+
await this._ensureDeletedChildrenInSettings(blocklet);
|
|
3111
1894
|
|
|
3112
1895
|
if (context?.downloadTokenList?.length) {
|
|
3113
1896
|
await states.blocklet.updateBlocklet(did, {
|
|
@@ -3122,16 +1905,18 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
3122
1905
|
await consumeServerlessNFT({ nftId: blocklet.controller.nftId, nodeInfo, blocklet });
|
|
3123
1906
|
}
|
|
3124
1907
|
|
|
3125
|
-
|
|
3126
|
-
|
|
3127
|
-
|
|
3128
|
-
|
|
3129
|
-
|
|
3130
|
-
|
|
3131
|
-
|
|
3132
|
-
|
|
3133
|
-
|
|
3134
|
-
|
|
1908
|
+
if (createNotification) {
|
|
1909
|
+
this._createNotification(did, {
|
|
1910
|
+
title: 'Blocklet Installed',
|
|
1911
|
+
description: `Blocklet ${meta.name}@${meta.version} is installed successfully. (Source: ${
|
|
1912
|
+
deployedFrom || fromBlockletSource(source)
|
|
1913
|
+
})`,
|
|
1914
|
+
action: `/blocklets/${did}/overview`,
|
|
1915
|
+
entityType: 'blocklet',
|
|
1916
|
+
entityId: did,
|
|
1917
|
+
severity: 'success',
|
|
1918
|
+
});
|
|
1919
|
+
}
|
|
3135
1920
|
|
|
3136
1921
|
await this._rollbackCache.remove({ did: blocklet.meta.did });
|
|
3137
1922
|
return blocklet;
|
|
@@ -3169,7 +1954,6 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
3169
1954
|
// ids
|
|
3170
1955
|
context.skippedProcessIds = getSkippedProcessIds({ newBlocklet, oldBlocklet, context });
|
|
3171
1956
|
|
|
3172
|
-
const action = semver.gt(oldBlocklet.meta.version, version) ? 'downgrade' : 'upgrade';
|
|
3173
1957
|
try {
|
|
3174
1958
|
// delete old process
|
|
3175
1959
|
try {
|
|
@@ -3190,7 +1974,7 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
3190
1974
|
await this._runPreInstallHook(blocklet, context);
|
|
3191
1975
|
|
|
3192
1976
|
// Add environments
|
|
3193
|
-
await this.
|
|
1977
|
+
await this._updateBlockletEnvironment(did);
|
|
3194
1978
|
blocklet = await this.getBlocklet(did);
|
|
3195
1979
|
|
|
3196
1980
|
// post install
|
|
@@ -3235,17 +2019,15 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
3235
2019
|
|
|
3236
2020
|
await fs.writeFile(path.join(blocklet.env.dataDir, 'logo.svg'), createDidLogo(blocklet.meta.did));
|
|
3237
2021
|
|
|
2022
|
+
await this._updateDependents(did);
|
|
2023
|
+
|
|
3238
2024
|
this.refreshListCache();
|
|
3239
2025
|
|
|
3240
2026
|
try {
|
|
3241
|
-
|
|
3242
|
-
upgrade: BlockletEvents.upgraded,
|
|
3243
|
-
downgrade: BlockletEvents.downgraded,
|
|
3244
|
-
};
|
|
3245
|
-
this.emit(eventNames[action], { blocklet, context });
|
|
2027
|
+
this.emit(BlockletEvents.upgraded, { blocklet, context });
|
|
3246
2028
|
this._createNotification(did, {
|
|
3247
|
-
title:
|
|
3248
|
-
description: `Blocklet ${name}@${version}
|
|
2029
|
+
title: 'Blocklet Upgrade Success',
|
|
2030
|
+
description: `Blocklet ${name}@${version} upgrade successfully. (Source: ${
|
|
3249
2031
|
deployedFrom || fromBlockletSource(source)
|
|
3250
2032
|
})`,
|
|
3251
2033
|
action: `/blocklets/${did}/overview`,
|
|
@@ -3258,26 +2040,25 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
3258
2040
|
}
|
|
3259
2041
|
|
|
3260
2042
|
// Update dynamic component meta in blocklet settings
|
|
3261
|
-
await this.
|
|
2043
|
+
await this._ensureDeletedChildrenInSettings(blocklet);
|
|
3262
2044
|
|
|
3263
2045
|
await this._rollbackCache.remove({ did: blocklet.meta.did });
|
|
3264
2046
|
|
|
3265
2047
|
return blocklet;
|
|
3266
2048
|
} catch (err) {
|
|
3267
|
-
const b = await this._rollback(
|
|
3268
|
-
logger.error(
|
|
2049
|
+
const b = await this._rollback('upgrade', did, oldBlocklet);
|
|
2050
|
+
logger.error('failed to upgrade blocklet', { did, version, name, error: err });
|
|
3269
2051
|
|
|
3270
2052
|
this.emit(BlockletEvents.updated, b);
|
|
3271
2053
|
|
|
3272
|
-
|
|
3273
|
-
|
|
3274
|
-
|
|
3275
|
-
};
|
|
3276
|
-
this.emit(eventNames[action], { blocklet: { ...oldBlocklet, error: { message: err.message } }, context });
|
|
2054
|
+
this.emit(BlockletEvents.upgradeFailed, {
|
|
2055
|
+
blocklet: { ...oldBlocklet, error: { message: err.message } },
|
|
2056
|
+
context,
|
|
2057
|
+
});
|
|
3277
2058
|
|
|
3278
2059
|
this._createNotification(did, {
|
|
3279
|
-
title:
|
|
3280
|
-
description: `Blocklet ${name}@${version}
|
|
2060
|
+
title: 'Blocklet Upgrade Failed',
|
|
2061
|
+
description: `Blocklet ${name}@${version} upgrade failed with error: ${err.message}`,
|
|
3281
2062
|
entityType: 'blocklet',
|
|
3282
2063
|
entityId: did,
|
|
3283
2064
|
severity: 'error',
|
|
@@ -3286,21 +2067,17 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
3286
2067
|
}
|
|
3287
2068
|
}
|
|
3288
2069
|
|
|
3289
|
-
//
|
|
3290
|
-
async
|
|
2070
|
+
// Refresh deleted component in blocklet settings
|
|
2071
|
+
async _ensureDeletedChildrenInSettings(blocklet) {
|
|
3291
2072
|
const { did } = blocklet.meta;
|
|
3292
|
-
const dynamicChildren = blocklet.children
|
|
3293
|
-
.filter((x) => x.dynamic)
|
|
3294
|
-
.map((x) => pick(x, ['meta', 'mountPoint', 'bundleSource', 'source']));
|
|
3295
2073
|
|
|
3296
|
-
//
|
|
3297
|
-
let
|
|
3298
|
-
|
|
2074
|
+
// TODO 不从 settings 中取值, 直接存在 extra 中
|
|
2075
|
+
let deletedChildren = await states.blockletExtras.getSettings(did, 'children', []);
|
|
2076
|
+
deletedChildren = deletedChildren.filter(
|
|
3299
2077
|
(x) => x.status === BlockletStatus.deleted && !blocklet.children.some((y) => y.meta.did === x.meta.did)
|
|
3300
2078
|
);
|
|
3301
|
-
dynamicChildren.push(...deletes);
|
|
3302
2079
|
|
|
3303
|
-
await states.blockletExtras.setSettings(did, { children:
|
|
2080
|
+
await states.blockletExtras.setSettings(did, { children: deletedChildren });
|
|
3304
2081
|
}
|
|
3305
2082
|
|
|
3306
2083
|
/**
|
|
@@ -3483,6 +2260,7 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
3483
2260
|
}
|
|
3484
2261
|
}
|
|
3485
2262
|
|
|
2263
|
+
// to be deleted
|
|
3486
2264
|
async _setAppSk(did, appSk, context) {
|
|
3487
2265
|
if (process.env.NODE_ENV === 'production' && !appSk) {
|
|
3488
2266
|
throw new Error(`appSk for blocklet ${did} is required`);
|
|
@@ -3501,37 +2279,6 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
3501
2279
|
}
|
|
3502
2280
|
}
|
|
3503
2281
|
|
|
3504
|
-
async _findNextCustomBlockletName(leftTimes = 10) {
|
|
3505
|
-
if (leftTimes <= 0) {
|
|
3506
|
-
throw new Error('Generate custom blocklet did too many times');
|
|
3507
|
-
}
|
|
3508
|
-
const number = await states.node.increaseCustomBlockletNumber();
|
|
3509
|
-
const name = `custom-${number}`;
|
|
3510
|
-
// MEMO: 空壳 APP可以保留原有的 did 生成逻辑
|
|
3511
|
-
const did = toBlockletDid(name);
|
|
3512
|
-
const blocklet = await states.blocklet.getBlocklet(did);
|
|
3513
|
-
if (blocklet) {
|
|
3514
|
-
return this._findNextCustomBlockletName(leftTimes - 1);
|
|
3515
|
-
}
|
|
3516
|
-
return { did, name };
|
|
3517
|
-
}
|
|
3518
|
-
|
|
3519
|
-
async _upsertDynamicNavigation(did, child, { skipNavigation } = {}) {
|
|
3520
|
-
const navigation = await states.blockletExtras.getSettings(did, 'navigation', []);
|
|
3521
|
-
const item = { title: child.meta.title, child: child.meta.name };
|
|
3522
|
-
const index = navigation.findIndex((x) => x.child === item.child);
|
|
3523
|
-
if (index > -1) {
|
|
3524
|
-
if (skipNavigation) {
|
|
3525
|
-
navigation.splice(index, 1);
|
|
3526
|
-
} else {
|
|
3527
|
-
navigation.splice(index, 1, item);
|
|
3528
|
-
}
|
|
3529
|
-
} else if (!skipNavigation) {
|
|
3530
|
-
navigation.push(item);
|
|
3531
|
-
}
|
|
3532
|
-
await states.blockletExtras.setSettings(did, { navigation });
|
|
3533
|
-
}
|
|
3534
|
-
|
|
3535
2282
|
async _getBlockletForInstallation(did) {
|
|
3536
2283
|
const blocklet = await states.blocklet.getBlocklet(did, { decryptSk: false });
|
|
3537
2284
|
if (!blocklet) {
|
|
@@ -3544,58 +2291,6 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
3544
2291
|
return blocklet;
|
|
3545
2292
|
}
|
|
3546
2293
|
|
|
3547
|
-
async _getLatestBlockletVersionFromStore({ blocklet, version }) {
|
|
3548
|
-
const { deployedFrom: storeUrl } = blocklet;
|
|
3549
|
-
const { did, bundleDid } = blocklet.meta;
|
|
3550
|
-
|
|
3551
|
-
let versions = this.cachedBlockletVersions.get(did);
|
|
3552
|
-
|
|
3553
|
-
if (!versions) {
|
|
3554
|
-
const item = await StoreUtil.getBlockletMeta({ did: bundleDid, storeUrl });
|
|
3555
|
-
|
|
3556
|
-
if (!item) {
|
|
3557
|
-
return null;
|
|
3558
|
-
}
|
|
3559
|
-
|
|
3560
|
-
versions = [{ did, version: item.version }];
|
|
3561
|
-
|
|
3562
|
-
this.cachedBlockletVersions.set(did, versions);
|
|
3563
|
-
}
|
|
3564
|
-
|
|
3565
|
-
versions = versions.filter((item) => item && semver.gt(item.version, version));
|
|
3566
|
-
|
|
3567
|
-
if (versions.length === 0) {
|
|
3568
|
-
return null;
|
|
3569
|
-
}
|
|
3570
|
-
|
|
3571
|
-
return versions[0];
|
|
3572
|
-
}
|
|
3573
|
-
|
|
3574
|
-
async _getLatestBlockletVersionFromUrl({ blocklet, version }) {
|
|
3575
|
-
const { did } = blocklet.meta;
|
|
3576
|
-
|
|
3577
|
-
let versions = this.cachedBlockletVersions.get(did);
|
|
3578
|
-
|
|
3579
|
-
if (!versions) {
|
|
3580
|
-
try {
|
|
3581
|
-
const item = await getBlockletMetaFromUrl(blocklet.deployedFrom);
|
|
3582
|
-
versions = [{ did, version: item.version }];
|
|
3583
|
-
} catch (error) {
|
|
3584
|
-
logger.error('get blocklet meta from url failed when checking latest version', { did, error });
|
|
3585
|
-
versions = [];
|
|
3586
|
-
}
|
|
3587
|
-
}
|
|
3588
|
-
|
|
3589
|
-
this.cachedBlockletVersions.set(did, versions);
|
|
3590
|
-
versions = versions.filter((item) => item && semver.gt(item.version, version));
|
|
3591
|
-
|
|
3592
|
-
if (versions.length === 0) {
|
|
3593
|
-
return null;
|
|
3594
|
-
}
|
|
3595
|
-
|
|
3596
|
-
return versions[0];
|
|
3597
|
-
}
|
|
3598
|
-
|
|
3599
2294
|
async _runPreInstallHook(blocklet, context) {
|
|
3600
2295
|
const nodeEnvironments = await states.node.getEnvironments();
|
|
3601
2296
|
|
|
@@ -3743,10 +2438,7 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
3743
2438
|
},
|
|
3744
2439
|
nodeInfo.sk
|
|
3745
2440
|
);
|
|
3746
|
-
const didDomain = getDidDomainForBlocklet({
|
|
3747
|
-
blockletAppDid: wallet.address,
|
|
3748
|
-
didDomain: nodeInfo.didDomain,
|
|
3749
|
-
});
|
|
2441
|
+
const didDomain = getDidDomainForBlocklet({ appPid: blocklet.appPid, didDomain: nodeInfo.didDomain });
|
|
3750
2442
|
|
|
3751
2443
|
const domainAliases = (get(blocklet, 'site.domainAliases') || []).filter(
|
|
3752
2444
|
(item) => !item.value.endsWith(nodeInfo.didDomain) && !item.value.endsWith('did.staging.arcblock.io') // did.staging.arcblock.io 是旧 did domain, 但主要存在于比较旧的节点中, 需要做兼容
|
|
@@ -3759,20 +2451,36 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
3759
2451
|
|
|
3760
2452
|
this.emit(BlockletEvents.appDidChanged, blocklet);
|
|
3761
2453
|
|
|
3762
|
-
const blockletWithEnv = await this.getBlocklet(blocklet.meta.did);
|
|
3763
|
-
const appSystemEnvironments = getAppOverwrittenEnvironments(blockletWithEnv, nodeInfo);
|
|
3764
|
-
|
|
3765
2454
|
await didDocument.updateBlockletDocument({
|
|
3766
2455
|
wallet,
|
|
3767
|
-
|
|
2456
|
+
appPid: blocklet.appPid,
|
|
2457
|
+
alsoKnownAs: getBlockletKnownAs(blocklet),
|
|
3768
2458
|
daemonDidDomain: util.getServerDidDomain(nodeInfo),
|
|
3769
2459
|
didRegistryUrl: nodeInfo.didRegistry,
|
|
3770
2460
|
domain: nodeInfo.didDomain,
|
|
3771
2461
|
});
|
|
3772
|
-
logger.info('updated blocklet dns document', {
|
|
3773
|
-
|
|
3774
|
-
|
|
2462
|
+
logger.info('updated blocklet dns document', { appPid: blocklet.appPid, appDid: blocklet.appDid });
|
|
2463
|
+
}
|
|
2464
|
+
|
|
2465
|
+
async _updateDependents(did) {
|
|
2466
|
+
const blocklet = await states.blocklet.getBlocklet(did);
|
|
2467
|
+
const map = {};
|
|
2468
|
+
for (const child of blocklet.children) {
|
|
2469
|
+
child.dependents = [];
|
|
2470
|
+
map[child.meta.did] = child;
|
|
2471
|
+
}
|
|
2472
|
+
|
|
2473
|
+
forEachBlockletSync(blocklet, (x, { id }) => {
|
|
2474
|
+
if (x.dependencies) {
|
|
2475
|
+
x.dependencies.forEach((y) => {
|
|
2476
|
+
if (map[y.did]) {
|
|
2477
|
+
map[y.did].dependents.push({ id, required: y.required });
|
|
2478
|
+
}
|
|
2479
|
+
});
|
|
2480
|
+
}
|
|
3775
2481
|
});
|
|
2482
|
+
|
|
2483
|
+
await states.blocklet.updateBlocklet(blocklet.meta.did, { children: blocklet.children });
|
|
3776
2484
|
}
|
|
3777
2485
|
}
|
|
3778
2486
|
|