@abtnode/core 1.8.68 → 1.8.69-beta-e0666d0d
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/blocklet/downloader/bundle-downloader.js +6 -1
- package/lib/blocklet/manager/disk.js +162 -65
- package/lib/blocklet/manager/helper/install-from-backup.js +7 -6
- package/lib/blocklet/storage/backup/audit-log.js +2 -2
- package/lib/blocklet/storage/backup/base.js +7 -8
- package/lib/blocklet/storage/backup/blocklet-extras.js +22 -39
- package/lib/blocklet/storage/backup/blocklet.js +34 -17
- package/lib/blocklet/storage/backup/blocklets.js +2 -2
- package/lib/blocklet/storage/backup/data.js +2 -2
- package/lib/blocklet/storage/backup/logs.js +2 -2
- package/lib/blocklet/storage/backup/routing-rule.js +2 -2
- package/lib/blocklet/storage/backup/spaces.js +105 -39
- package/lib/blocklet/storage/restore/base.js +26 -14
- package/lib/blocklet/storage/restore/blocklet-extras.js +34 -34
- package/lib/blocklet/storage/restore/blocklet.js +49 -0
- package/lib/blocklet/storage/restore/blocklets.js +6 -3
- package/lib/blocklet/storage/restore/logs.js +2 -2
- package/lib/blocklet/storage/restore/spaces.js +64 -50
- package/lib/event.js +28 -2
- package/lib/index.js +21 -3
- package/lib/router/helper.js +27 -6
- package/lib/router/index.js +31 -0
- package/lib/states/audit-log.js +5 -3
- package/lib/states/blocklet.js +21 -5
- package/lib/states/node.js +5 -2
- package/lib/util/blocklet.js +111 -19
- package/lib/util/index.js +24 -0
- package/lib/validators/node.js +1 -0
- package/lib/webhook/index.js +1 -1
- package/package.json +26 -27
- /package/lib/{queue.js → util/queue.js} +0 -0
|
@@ -2,6 +2,7 @@ const { EventEmitter } = require('events');
|
|
|
2
2
|
const fs = require('fs-extra');
|
|
3
3
|
const { fileURLToPath } = require('url');
|
|
4
4
|
const path = require('path');
|
|
5
|
+
const pick = require('lodash/pick');
|
|
5
6
|
const cloneDeep = require('lodash/cloneDeep');
|
|
6
7
|
const { toBase58 } = require('@ocap/util');
|
|
7
8
|
|
|
@@ -161,7 +162,11 @@ class BundleDownloader extends EventEmitter {
|
|
|
161
162
|
}
|
|
162
163
|
ctrlStore[rootDid].set(did, cancelCtrl);
|
|
163
164
|
|
|
164
|
-
const headers = context.headers ? cloneDeep(context.headers) : {}
|
|
165
|
+
const headers = pick(context.headers ? cloneDeep(context.headers) : {}, [
|
|
166
|
+
'x-server-did',
|
|
167
|
+
'x-server-public-key',
|
|
168
|
+
'x-server-signature',
|
|
169
|
+
]);
|
|
165
170
|
const exist = (context.downloadTokenList || []).find((x) => x.did === did);
|
|
166
171
|
if (exist) {
|
|
167
172
|
headers['x-download-token'] = exist.token;
|
|
@@ -4,6 +4,7 @@ const fs = require('fs-extra');
|
|
|
4
4
|
const path = require('path');
|
|
5
5
|
const flat = require('flat');
|
|
6
6
|
const get = require('lodash/get');
|
|
7
|
+
const merge = require('lodash/merge');
|
|
7
8
|
const pick = require('lodash/pick');
|
|
8
9
|
const cloneDeep = require('lodash/cloneDeep');
|
|
9
10
|
const semver = require('semver');
|
|
@@ -15,20 +16,18 @@ const { isNFTExpired, getNftExpirationDate } = require('@abtnode/util/lib/nft');
|
|
|
15
16
|
const didDocument = require('@abtnode/util/lib/did-document');
|
|
16
17
|
const { sign } = require('@arcblock/jwt');
|
|
17
18
|
const { isValid: isValidDid } = require('@arcblock/did');
|
|
18
|
-
const { verifyPresentation } = require('@arcblock/vc');
|
|
19
19
|
const { toSvg: createDidLogo } =
|
|
20
20
|
process.env.NODE_ENV !== 'test' ? require('@arcblock/did-motif') : require('@arcblock/did-motif/dist/did-motif.cjs');
|
|
21
21
|
const getBlockletInfo = require('@blocklet/meta/lib/info');
|
|
22
22
|
const sleep = require('@abtnode/util/lib/sleep');
|
|
23
23
|
|
|
24
24
|
const logger = require('@abtnode/logger')('@abtnode/core:blocklet:manager');
|
|
25
|
-
const { getVcFromPresentation } = require('@abtnode/util/lib/vc');
|
|
26
25
|
const {
|
|
27
|
-
VC_TYPE_BLOCKLET_PURCHASE,
|
|
28
26
|
WHO_CAN_ACCESS,
|
|
29
27
|
SERVER_ROLES,
|
|
30
28
|
WHO_CAN_ACCESS_PREFIX_ROLES,
|
|
31
29
|
BLOCKLET_INSTALL_TYPE,
|
|
30
|
+
NODE_MODES,
|
|
32
31
|
} = require('@abtnode/constant');
|
|
33
32
|
|
|
34
33
|
const getBlockletEngine = require('@blocklet/meta/lib/engine');
|
|
@@ -49,7 +48,7 @@ const getComponentProcessId = require('@blocklet/meta/lib/get-component-process-
|
|
|
49
48
|
const toBlockletDid = require('@blocklet/meta/lib/did');
|
|
50
49
|
const { validateMeta } = require('@blocklet/meta/lib/validate');
|
|
51
50
|
const { update: updateMetaFile } = require('@blocklet/meta/lib/file');
|
|
52
|
-
const { titleSchema,
|
|
51
|
+
const { titleSchema, updateMountPointSchema, environmentNameSchema } = require('@blocklet/meta/lib/schema');
|
|
53
52
|
const hasReservedKey = require('@blocklet/meta/lib/has-reserved-key');
|
|
54
53
|
const Lock = require('@abtnode/util/lib/lock');
|
|
55
54
|
|
|
@@ -111,6 +110,10 @@ const {
|
|
|
111
110
|
validateAppConfig,
|
|
112
111
|
checkDuplicateAppSk,
|
|
113
112
|
checkDuplicateMountPoint,
|
|
113
|
+
validateStore,
|
|
114
|
+
validateInServerless,
|
|
115
|
+
isRotatingAppSk,
|
|
116
|
+
isRotatingAppDid,
|
|
114
117
|
} = require('../../util/blocklet');
|
|
115
118
|
const StoreUtil = require('../../util/store');
|
|
116
119
|
const states = require('../../states');
|
|
@@ -193,13 +196,14 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
193
196
|
/**
|
|
194
197
|
* @param {*} dataDirs generate by ../../util:getDataDirs
|
|
195
198
|
*/
|
|
196
|
-
constructor({ dataDirs, startQueue, installQueue, daemon = false, teamManager }) {
|
|
199
|
+
constructor({ dataDirs, startQueue, installQueue, backupQueue, daemon = false, teamManager }) {
|
|
197
200
|
super();
|
|
198
201
|
|
|
199
202
|
this.dataDirs = dataDirs;
|
|
200
203
|
this.installDir = dataDirs.blocklets;
|
|
201
204
|
this.startQueue = startQueue;
|
|
202
205
|
this.installQueue = installQueue;
|
|
206
|
+
this.backupQueue = backupQueue;
|
|
203
207
|
this.teamManager = teamManager;
|
|
204
208
|
|
|
205
209
|
// cached installed blocklets for performance
|
|
@@ -242,6 +246,8 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
242
246
|
* did: string;
|
|
243
247
|
* title: string;
|
|
244
248
|
* description: string;
|
|
249
|
+
* storeUrl: string;
|
|
250
|
+
* appSk: string;
|
|
245
251
|
* sync: boolean = false; // download synchronously, not use queue
|
|
246
252
|
* delay: number; // push download task to queue after a delay
|
|
247
253
|
* downloadTokenList: Array<{did: string, token: string}>;
|
|
@@ -264,6 +270,7 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
264
270
|
|
|
265
271
|
const info = await states.node.read();
|
|
266
272
|
|
|
273
|
+
// Note: if you added new header here, please change core/state/lib/blocklet/downloader/bundle-downloader.js to use that header
|
|
267
274
|
context.headers = Object.assign(context?.headers || {}, {
|
|
268
275
|
'x-server-did': info.did,
|
|
269
276
|
'x-server-public-key': info.pk,
|
|
@@ -278,28 +285,31 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
278
285
|
context.startImmediately = !!params.startImmediately;
|
|
279
286
|
}
|
|
280
287
|
|
|
288
|
+
const { appSk } = params;
|
|
289
|
+
|
|
281
290
|
if (type === BLOCKLET_INSTALL_TYPE.URL) {
|
|
282
291
|
const { url, controller, sync, delay } = params;
|
|
283
|
-
return this._installFromUrl({ url, controller, sync, delay }, context);
|
|
292
|
+
return this._installFromUrl({ url, controller, sync, delay, appSk }, context);
|
|
284
293
|
}
|
|
285
294
|
|
|
286
295
|
if (type === BLOCKLET_INSTALL_TYPE.UPLOAD) {
|
|
287
296
|
const { file, did, diffVersion, deleteSet } = params;
|
|
288
|
-
return this._installFromUpload({ file, did, diffVersion, deleteSet,
|
|
297
|
+
return this._installFromUpload({ file, did, diffVersion, deleteSet, appSk }, context);
|
|
289
298
|
}
|
|
290
299
|
|
|
291
300
|
if (type === BLOCKLET_INSTALL_TYPE.STORE) {
|
|
292
301
|
const { did, controller, sync, delay, storeUrl } = params;
|
|
293
|
-
return this._installFromStore({ did, controller, sync, delay, storeUrl }, context);
|
|
302
|
+
return this._installFromStore({ did, controller, sync, delay, storeUrl, appSk }, context);
|
|
294
303
|
}
|
|
295
304
|
|
|
296
305
|
if (type === BLOCKLET_INSTALL_TYPE.CREATE) {
|
|
297
|
-
|
|
306
|
+
const { title, description } = params;
|
|
307
|
+
return this._installFromCreate({ title, description, appSk }, context);
|
|
298
308
|
}
|
|
299
309
|
|
|
300
310
|
if (type === BLOCKLET_INSTALL_TYPE.RESTORE) {
|
|
301
|
-
const { url
|
|
302
|
-
return this._installFromBackup({ url,
|
|
311
|
+
const { url } = params;
|
|
312
|
+
return this._installFromBackup({ url, appSk }, context);
|
|
303
313
|
}
|
|
304
314
|
|
|
305
315
|
// should not be here
|
|
@@ -343,10 +353,16 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
343
353
|
},
|
|
344
354
|
context = {}
|
|
345
355
|
) {
|
|
346
|
-
const mountPoint = await
|
|
356
|
+
const mountPoint = await updateMountPointSchema.validateAsync(tmpMountPoint);
|
|
347
357
|
logger.debug('start install component', { rootDid, mountPoint, url });
|
|
348
358
|
|
|
349
359
|
if (file) {
|
|
360
|
+
// TODO: 如何触发这种场景?
|
|
361
|
+
const info = await states.node.read();
|
|
362
|
+
if (info.mode === NODE_MODES.SERVERLESS) {
|
|
363
|
+
throw new Error("Can't install component in serverless-mode server via upload");
|
|
364
|
+
}
|
|
365
|
+
|
|
350
366
|
return this._installComponentFromUpload({
|
|
351
367
|
rootDid,
|
|
352
368
|
mountPoint,
|
|
@@ -360,6 +376,11 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
360
376
|
}
|
|
361
377
|
|
|
362
378
|
if (url) {
|
|
379
|
+
const info = await states.node.read();
|
|
380
|
+
if (info.mode === NODE_MODES.SERVERLESS) {
|
|
381
|
+
validateStore(info, url);
|
|
382
|
+
}
|
|
383
|
+
|
|
363
384
|
return this._installComponentFromUrl({
|
|
364
385
|
rootDid,
|
|
365
386
|
mountPoint,
|
|
@@ -431,25 +452,6 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
431
452
|
return { isInstalled: !!blocklet, isRunning, blockletDid, isExternal };
|
|
432
453
|
}
|
|
433
454
|
|
|
434
|
-
async installBlockletFromVc({ vcPresentation, challenge }, context) {
|
|
435
|
-
logger.info('Install from vc');
|
|
436
|
-
const vc = getVcFromPresentation(vcPresentation);
|
|
437
|
-
|
|
438
|
-
// FIXME: 这里的 trustedIssuers 相当于相信任何 VC,需要想更安全的方法
|
|
439
|
-
verifyPresentation({ presentation: vcPresentation, trustedIssuers: [get(vc, 'issuer.id')], challenge });
|
|
440
|
-
|
|
441
|
-
if (!vc.type.includes(VC_TYPE_BLOCKLET_PURCHASE)) {
|
|
442
|
-
throw new Error(`Expect ${VC_TYPE_BLOCKLET_PURCHASE} VC type`);
|
|
443
|
-
}
|
|
444
|
-
|
|
445
|
-
const blockletUrl = get(vc, 'credentialSubject.purchased.blocklet.url');
|
|
446
|
-
const urlObject = new URL(blockletUrl);
|
|
447
|
-
const did = get(vc, 'credentialSubject.purchased.blocklet.id');
|
|
448
|
-
const registry = urlObject.origin;
|
|
449
|
-
|
|
450
|
-
return this._installFromStore({ did, registry }, context);
|
|
451
|
-
}
|
|
452
|
-
|
|
453
455
|
async start({ did, throwOnError, checkHealthImmediately = false, e2eMode = false }, context) {
|
|
454
456
|
logger.info('start blocklet', { did });
|
|
455
457
|
// should check blocklet integrity
|
|
@@ -608,32 +610,65 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
608
610
|
}
|
|
609
611
|
|
|
610
612
|
/**
|
|
611
|
-
*
|
|
612
|
-
*
|
|
613
|
-
* @param {import('@abtnode/client').BackupToSpacesParams} input
|
|
613
|
+
* FIXME: @wangshijun create audit log for this
|
|
614
|
+
* @param {import('@abtnode/client').RequestBackupToSpacesInput} input
|
|
614
615
|
* @memberof BlockletManager
|
|
615
616
|
*/
|
|
616
|
-
|
|
617
|
-
|
|
617
|
+
// eslint-disable-next-line no-unused-vars
|
|
618
|
+
async backupToSpaces({ appPid }, context) {
|
|
619
|
+
// add to queue
|
|
620
|
+
// const ticket = this.backupQueue.push({
|
|
621
|
+
// entity: 'blocklet',
|
|
622
|
+
// action: 'backup-to-space',
|
|
623
|
+
// appPid,
|
|
624
|
+
// context,
|
|
625
|
+
// });
|
|
626
|
+
|
|
627
|
+
// ticket.on('failed', async (err) => {
|
|
628
|
+
// logger.error('backup failed', { entity: 'blocklet', appPid, error: err });
|
|
629
|
+
// this.emit('blocklet.backup.failed', { appPid, err });
|
|
630
|
+
// this._createNotification(appPid, {
|
|
631
|
+
// title: 'Blocklet Backup Failed',
|
|
632
|
+
// description: `Blocklet backup failed with error: ${err.message || 'queue exception'}`,
|
|
633
|
+
// entityType: 'blocklet',
|
|
634
|
+
// entityId: appPid,
|
|
635
|
+
// severity: 'error',
|
|
636
|
+
// });
|
|
637
|
+
// });
|
|
638
|
+
|
|
639
|
+
const userDid = context.user.did;
|
|
640
|
+
const { referrer } = context;
|
|
641
|
+
|
|
642
|
+
const spacesBackup = new SpacesBackup({ appPid, event: this, userDid, referrer });
|
|
643
|
+
this.emit(BlockletEvents.backupProgress, { appPid, message: 'Start backup...', progress: 10, completed: false });
|
|
618
644
|
await spacesBackup.backup();
|
|
645
|
+
this.emit(BlockletEvents.backupProgress, { appPid, completed: true, progress: 100 });
|
|
619
646
|
}
|
|
620
647
|
|
|
621
648
|
/**
|
|
622
|
-
*
|
|
623
|
-
*
|
|
649
|
+
* FIXME: @linchen support cancel
|
|
650
|
+
* FIXME: @wangshijun create audit log for this
|
|
624
651
|
* @param {import('@abtnode/client').RequestRestoreFromSpacesInput} input
|
|
625
652
|
* @memberof BlockletManager
|
|
626
653
|
*/
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
654
|
+
// eslint-disable-next-line no-unused-vars
|
|
655
|
+
async restoreFromSpaces(input, context) {
|
|
656
|
+
this.emit(BlockletEvents.restoreProgress, { appDid: input.appDid, message: 'Start restore...', completed: false });
|
|
657
|
+
|
|
658
|
+
const userDid = context.user.did;
|
|
659
|
+
const { referrer } = context;
|
|
660
|
+
|
|
661
|
+
const spacesRestore = new SpacesRestore({ ...input, event: this, userDid, referrer });
|
|
662
|
+
const params = await spacesRestore.restore();
|
|
630
663
|
|
|
631
|
-
|
|
664
|
+
this.emit(BlockletEvents.restoreProgress, { appDid: input.appDid, message: 'Installing blocklet...' });
|
|
632
665
|
await this._installFromBackup({
|
|
633
|
-
url: `file://${spacesRestore.
|
|
634
|
-
blockletSecretKey: spacesRestore.blockletWallet.secretKey,
|
|
666
|
+
url: `file://${spacesRestore.restoreDir}`,
|
|
635
667
|
moveDir: true,
|
|
668
|
+
...merge(...params),
|
|
636
669
|
});
|
|
670
|
+
|
|
671
|
+
this.emit(BlockletEvents.restoreProgress, { appDid: input.appDid, completed: true });
|
|
637
672
|
}
|
|
638
673
|
|
|
639
674
|
async restart({ did }, context) {
|
|
@@ -1003,8 +1038,9 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
1003
1038
|
return blocklets;
|
|
1004
1039
|
}
|
|
1005
1040
|
|
|
1041
|
+
// CAUTION: this method currently only support config by blocklet.meta.did
|
|
1006
1042
|
// eslint-disable-next-line no-unused-vars
|
|
1007
|
-
async config({ did, configs: newConfigs, skipHook }, context) {
|
|
1043
|
+
async config({ did, configs: newConfigs, skipHook, skipDidDocument }, context) {
|
|
1008
1044
|
if (!Array.isArray(newConfigs)) {
|
|
1009
1045
|
throw new Error('configs list is not an array');
|
|
1010
1046
|
}
|
|
@@ -1034,7 +1070,7 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
1034
1070
|
throw new Error(`Cannot set ${x.key} to child blocklet`);
|
|
1035
1071
|
}
|
|
1036
1072
|
|
|
1037
|
-
await validateAppConfig(x,
|
|
1073
|
+
await validateAppConfig(x, states);
|
|
1038
1074
|
} else if (!BLOCKLET_CONFIGURABLE_KEY[x.key] && !isPreferenceKey(x)) {
|
|
1039
1075
|
if (!(blocklet.meta.environments || []).some((y) => y.name === x.key)) {
|
|
1040
1076
|
// forbid unknown format key
|
|
@@ -1057,14 +1093,31 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
1057
1093
|
});
|
|
1058
1094
|
}
|
|
1059
1095
|
|
|
1096
|
+
const willAppSkChange = isRotatingAppSk(newConfigs, blocklet.configs, blocklet.externalSk);
|
|
1097
|
+
const willAppDidChange = isRotatingAppDid(newConfigs, blocklet.configs, blocklet.externalSk);
|
|
1098
|
+
|
|
1060
1099
|
// update db
|
|
1061
1100
|
await states.blockletExtras.setConfigs(dids, newConfigs);
|
|
1062
1101
|
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1102
|
+
if (willAppSkChange) {
|
|
1103
|
+
const info = await states.node.read();
|
|
1104
|
+
const { wallet } = getBlockletInfo(blocklet, info.sk);
|
|
1105
|
+
const migratedFrom = Array.isArray(blocklet.migratedFrom) ? blocklet.migratedFrom : [];
|
|
1106
|
+
await states.blocklet.updateBlocklet(rootDid, {
|
|
1107
|
+
migratedFrom: [
|
|
1108
|
+
...migratedFrom,
|
|
1109
|
+
{ appSk: wallet.secretKey, appDid: wallet.address, at: new Date().toISOString() },
|
|
1110
|
+
],
|
|
1111
|
+
});
|
|
1112
|
+
}
|
|
1066
1113
|
|
|
1067
|
-
|
|
1114
|
+
// Reload nginx to make sure did-space can embed content from this app
|
|
1115
|
+
if (newConfigs.find((x) => x.key === BLOCKLET_CONFIGURABLE_KEY.BLOCKLET_APP_SPACE_ENDPOINT)?.value) {
|
|
1116
|
+
this.emit(BlockletEvents.spaceConnected, blocklet);
|
|
1117
|
+
}
|
|
1118
|
+
|
|
1119
|
+
// FIXME: @zhenqiang best way to handle the did document: allow all appDids to resolve
|
|
1120
|
+
if (willAppDidChange && !skipDidDocument) {
|
|
1068
1121
|
await this._updateDidDocument(blocklet);
|
|
1069
1122
|
}
|
|
1070
1123
|
|
|
@@ -1198,7 +1251,7 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
1198
1251
|
}
|
|
1199
1252
|
|
|
1200
1253
|
async updateComponentMountPoint({ did, rootDid: inputRootDid, mountPoint: tmpMountPoint }, context) {
|
|
1201
|
-
const mountPoint = await
|
|
1254
|
+
const mountPoint = await updateMountPointSchema.validateAsync(tmpMountPoint);
|
|
1202
1255
|
|
|
1203
1256
|
const blocklet = await states.blocklet.getBlocklet(inputRootDid);
|
|
1204
1257
|
|
|
@@ -1679,13 +1732,13 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
1679
1732
|
* /blocklet.json
|
|
1680
1733
|
* /blocklet_extras.json
|
|
1681
1734
|
* @see Blocklet 端到端备份方案的设计与实现(一期) https://team.arcblock.io/comment/discussions/e62084d5-fafa-489e-91d5-defcd6e93223
|
|
1682
|
-
* @param {{ url: string,
|
|
1735
|
+
* @param {{ url: string, appSk: string, moveDir: boolean}} [{ url }={}]
|
|
1683
1736
|
* @param {Record<string, string>} [context={}]
|
|
1684
1737
|
* @return {Promise<any>}
|
|
1685
1738
|
* @memberof BlockletManager
|
|
1686
1739
|
*/
|
|
1687
|
-
async _installFromBackup({ url,
|
|
1688
|
-
return installFromBackup({ url,
|
|
1740
|
+
async _installFromBackup({ url, appSk, moveDir } = {}, context = {}) {
|
|
1741
|
+
return installFromBackup({ url, appSk, moveDir, context, manager: this, states });
|
|
1689
1742
|
}
|
|
1690
1743
|
|
|
1691
1744
|
async ensureBlocklet(did, opts = {}) {
|
|
@@ -2067,6 +2120,14 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
2067
2120
|
// put BLOCKLET_APP_ID at root level for indexing
|
|
2068
2121
|
blocklet.appDid = appSystemEnvironments.BLOCKLET_APP_ID;
|
|
2069
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
|
+
|
|
2070
2131
|
// update state to db
|
|
2071
2132
|
return states.blocklet.updateBlocklet(did, blocklet);
|
|
2072
2133
|
}
|
|
@@ -2084,17 +2145,18 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
2084
2145
|
*
|
|
2085
2146
|
* @param {{
|
|
2086
2147
|
* did: string;
|
|
2087
|
-
* registry: string;
|
|
2088
2148
|
* sync: boolean;
|
|
2089
2149
|
* delay: number;
|
|
2090
2150
|
* controller: Controller;
|
|
2151
|
+
* appSk: string;
|
|
2152
|
+
* storeUrl: string;
|
|
2091
2153
|
* }} params
|
|
2092
2154
|
* @param {*} context
|
|
2093
2155
|
* @return {*}
|
|
2094
2156
|
* @memberof BlockletManager
|
|
2095
2157
|
*/
|
|
2096
2158
|
async _installFromStore(params, context) {
|
|
2097
|
-
const { did, storeUrl, sync, delay, controller } = params;
|
|
2159
|
+
const { did, storeUrl, sync, delay, controller, appSk } = params;
|
|
2098
2160
|
|
|
2099
2161
|
logger.debug('start install blocklet', { did });
|
|
2100
2162
|
if (!isValidDid(did)) {
|
|
@@ -2138,6 +2200,7 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
2138
2200
|
sync,
|
|
2139
2201
|
delay,
|
|
2140
2202
|
controller,
|
|
2203
|
+
appSk,
|
|
2141
2204
|
context,
|
|
2142
2205
|
});
|
|
2143
2206
|
}
|
|
@@ -2154,13 +2217,14 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
2154
2217
|
* sync: boolean;
|
|
2155
2218
|
* delay: number;
|
|
2156
2219
|
* controller: Controller;
|
|
2220
|
+
* appSk: string;
|
|
2157
2221
|
* }} params
|
|
2158
2222
|
* @param {{}} context
|
|
2159
2223
|
* @return {*}
|
|
2160
2224
|
* @memberof BlockletManager
|
|
2161
2225
|
*/
|
|
2162
2226
|
async _installFromUrl(params, context) {
|
|
2163
|
-
const { url, sync, delay, controller } = params;
|
|
2227
|
+
const { url, sync, delay, controller, appSk } = params;
|
|
2164
2228
|
|
|
2165
2229
|
logger.debug('start install blocklet', { url });
|
|
2166
2230
|
|
|
@@ -2174,13 +2238,18 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
2174
2238
|
|
|
2175
2239
|
// install from store if url is a store url
|
|
2176
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
|
+
|
|
2177
2246
|
if (inStore) {
|
|
2178
2247
|
const exist = await states.blocklet.getBlocklet(blockletDid);
|
|
2179
2248
|
if (exist) {
|
|
2180
2249
|
return this.upgrade({ did: blockletDid, storeUrl: registryUrl, sync, delay }, context);
|
|
2181
2250
|
}
|
|
2182
2251
|
|
|
2183
|
-
return this._installFromStore({ did: bundleDid, storeUrl: registryUrl, controller, sync, delay }, context);
|
|
2252
|
+
return this._installFromStore({ did: bundleDid, storeUrl: registryUrl, controller, sync, delay, appSk }, context);
|
|
2184
2253
|
}
|
|
2185
2254
|
|
|
2186
2255
|
const meta = ensureMeta(bundleMeta, { name: blockletName, did: blockletDid });
|
|
@@ -2206,6 +2275,7 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
2206
2275
|
sync,
|
|
2207
2276
|
delay,
|
|
2208
2277
|
controller,
|
|
2278
|
+
appSk,
|
|
2209
2279
|
context,
|
|
2210
2280
|
});
|
|
2211
2281
|
}
|
|
@@ -2310,7 +2380,7 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
2310
2380
|
);
|
|
2311
2381
|
}
|
|
2312
2382
|
|
|
2313
|
-
async _installFromCreate({ title, description }, context = {}) {
|
|
2383
|
+
async _installFromCreate({ title, description, appSk }, context = {}) {
|
|
2314
2384
|
logger.debug('create blocklet', { title, description });
|
|
2315
2385
|
|
|
2316
2386
|
await joi.string().label('title').max(20).required().validateAsync(title);
|
|
@@ -2338,11 +2408,9 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
2338
2408
|
};
|
|
2339
2409
|
const meta = validateMeta(rawMeta);
|
|
2340
2410
|
|
|
2341
|
-
await states.blocklet.addBlocklet({
|
|
2342
|
-
meta,
|
|
2343
|
-
source: BlockletSource.custom,
|
|
2344
|
-
});
|
|
2411
|
+
await states.blocklet.addBlocklet({ meta, source: BlockletSource.custom, externalSk: !!appSk });
|
|
2345
2412
|
await this._setConfigsFromMeta(did);
|
|
2413
|
+
await this._setAppSk(did, appSk);
|
|
2346
2414
|
|
|
2347
2415
|
// check duplicate appSk
|
|
2348
2416
|
await checkDuplicateAppSk({ did, states });
|
|
@@ -2382,7 +2450,7 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
2382
2450
|
return { cwd, tarFile };
|
|
2383
2451
|
}
|
|
2384
2452
|
|
|
2385
|
-
async _installFromUpload({ file, did, diffVersion, deleteSet,
|
|
2453
|
+
async _installFromUpload({ file, did, diffVersion, deleteSet, appSk }, context) {
|
|
2386
2454
|
logger.info('install blocklet', { from: 'upload file' });
|
|
2387
2455
|
const { tarFile } = await this._downloadFromUpload(file);
|
|
2388
2456
|
|
|
@@ -2474,12 +2542,14 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
2474
2542
|
source: BlockletSource.upload,
|
|
2475
2543
|
deployedFrom: `Upload by ${context.user.fullName}`,
|
|
2476
2544
|
children,
|
|
2545
|
+
externalSk: !!appSk,
|
|
2477
2546
|
});
|
|
2478
2547
|
|
|
2479
2548
|
const action = 'install';
|
|
2480
2549
|
const oldState = { extraState: oldExtraState };
|
|
2481
2550
|
try {
|
|
2482
2551
|
await this._setConfigsFromMeta(meta.did);
|
|
2552
|
+
await this._setAppSk(appSk);
|
|
2483
2553
|
await validateBlocklet(blocklet);
|
|
2484
2554
|
|
|
2485
2555
|
// check duplicate appSk
|
|
@@ -2776,6 +2846,7 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
2776
2846
|
* meta: {}; // blocklet meta
|
|
2777
2847
|
* source: number; // example: BlockletSource.registry,
|
|
2778
2848
|
* deployedFrom: string;
|
|
2849
|
+
* appSk: string;
|
|
2779
2850
|
* context: {}
|
|
2780
2851
|
* sync: boolean = false;
|
|
2781
2852
|
* delay: number;
|
|
@@ -2785,10 +2856,15 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
2785
2856
|
* @memberof BlockletManager
|
|
2786
2857
|
*/
|
|
2787
2858
|
async _install(params) {
|
|
2788
|
-
const { meta, source, deployedFrom, context, sync, delay, controller } = params;
|
|
2859
|
+
const { meta, source, deployedFrom, context, sync, delay, controller, appSk } = params;
|
|
2789
2860
|
|
|
2790
2861
|
validateBlockletMeta(meta, { ensureDist: true });
|
|
2791
2862
|
|
|
2863
|
+
const info = await states.node.read();
|
|
2864
|
+
if (info.mode === NODE_MODES.SERVERLESS) {
|
|
2865
|
+
validateInServerless({ blockletMeta: meta });
|
|
2866
|
+
}
|
|
2867
|
+
|
|
2792
2868
|
const { name, did, version } = meta;
|
|
2793
2869
|
|
|
2794
2870
|
const oldExtraState = await states.blockletExtras.findOne({ did: meta.did });
|
|
@@ -2801,6 +2877,7 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
2801
2877
|
source,
|
|
2802
2878
|
deployedFrom,
|
|
2803
2879
|
children,
|
|
2880
|
+
externalSk: !!appSk,
|
|
2804
2881
|
});
|
|
2805
2882
|
|
|
2806
2883
|
await validateBlocklet(blocklet);
|
|
@@ -2808,6 +2885,7 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
2808
2885
|
await states.blockletExtras.addMeta({ did, meta: { did, name }, controller });
|
|
2809
2886
|
|
|
2810
2887
|
await this._setConfigsFromMeta(did);
|
|
2888
|
+
await this._setAppSk(did, appSk);
|
|
2811
2889
|
|
|
2812
2890
|
// check duplicate appSk
|
|
2813
2891
|
await checkDuplicateAppSk({ did, states });
|
|
@@ -3405,12 +3483,31 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
3405
3483
|
}
|
|
3406
3484
|
}
|
|
3407
3485
|
|
|
3486
|
+
async _setAppSk(did, appSk, context) {
|
|
3487
|
+
if (process.env.NODE_ENV === 'production' && !appSk) {
|
|
3488
|
+
throw new Error(`appSk for blocklet ${did} is required`);
|
|
3489
|
+
}
|
|
3490
|
+
|
|
3491
|
+
if (appSk) {
|
|
3492
|
+
await this.config(
|
|
3493
|
+
{
|
|
3494
|
+
did,
|
|
3495
|
+
configs: [{ key: 'BLOCKLET_APP_SK', value: appSk, secure: true }],
|
|
3496
|
+
skipHook: true,
|
|
3497
|
+
skipDidDocument: true,
|
|
3498
|
+
},
|
|
3499
|
+
context
|
|
3500
|
+
);
|
|
3501
|
+
}
|
|
3502
|
+
}
|
|
3503
|
+
|
|
3408
3504
|
async _findNextCustomBlockletName(leftTimes = 10) {
|
|
3409
3505
|
if (leftTimes <= 0) {
|
|
3410
3506
|
throw new Error('Generate custom blocklet did too many times');
|
|
3411
3507
|
}
|
|
3412
3508
|
const number = await states.node.increaseCustomBlockletNumber();
|
|
3413
3509
|
const name = `custom-${number}`;
|
|
3510
|
+
// MEMO: 空壳 APP可以保留原有的 did 生成逻辑
|
|
3414
3511
|
const did = toBlockletDid(name);
|
|
3415
3512
|
const blocklet = await states.blocklet.getBlocklet(did);
|
|
3416
3513
|
if (blocklet) {
|
|
@@ -2,7 +2,7 @@ const fs = require('fs-extra');
|
|
|
2
2
|
const path = require('path');
|
|
3
3
|
const omit = require('lodash/omit');
|
|
4
4
|
|
|
5
|
-
const { forEachBlockletSync } = require('@blocklet/meta/lib/util');
|
|
5
|
+
const { forEachBlockletSync, getBlockletAppIdList } = require('@blocklet/meta/lib/util');
|
|
6
6
|
const getBlockletInfo = require('@blocklet/meta/lib/info');
|
|
7
7
|
|
|
8
8
|
const { BLOCKLET_CONFIGURABLE_KEY } = require('@blocklet/constant');
|
|
@@ -19,7 +19,7 @@ const { validateBlocklet, checkDuplicateAppSk, getAppDirs } = require('../../../
|
|
|
19
19
|
* }} param0
|
|
20
20
|
* @returns
|
|
21
21
|
*/
|
|
22
|
-
module.exports = async ({ url,
|
|
22
|
+
module.exports = async ({ url, appSk, moveDir, context = {}, states, manager } = {}) => {
|
|
23
23
|
// TODO: support more url schema feature (http, did-spaces)
|
|
24
24
|
if (!url.startsWith('file://')) {
|
|
25
25
|
throw new Error('url must starts with file://');
|
|
@@ -71,17 +71,17 @@ module.exports = async ({ url, blockletSecretKey, moveDir, context = {}, states,
|
|
|
71
71
|
throw new Error('blocklet is already exist');
|
|
72
72
|
}
|
|
73
73
|
|
|
74
|
-
if (
|
|
74
|
+
if (appSk) {
|
|
75
75
|
extra.configs = extra.configs || [];
|
|
76
76
|
const skConfig = extra.configs.find((x) => x.key === BLOCKLET_CONFIGURABLE_KEY.BLOCKLET_APP_SK);
|
|
77
77
|
if (skConfig) {
|
|
78
|
-
skConfig.value =
|
|
78
|
+
skConfig.value = appSk;
|
|
79
79
|
skConfig.secure = true;
|
|
80
80
|
skConfig.shared = false;
|
|
81
81
|
} else {
|
|
82
82
|
extra.configs.push({
|
|
83
83
|
key: BLOCKLET_CONFIGURABLE_KEY.BLOCKLET_APP_SK,
|
|
84
|
-
value:
|
|
84
|
+
value: appSk,
|
|
85
85
|
secure: true,
|
|
86
86
|
shared: false,
|
|
87
87
|
});
|
|
@@ -90,8 +90,9 @@ module.exports = async ({ url, blockletSecretKey, moveDir, context = {}, states,
|
|
|
90
90
|
|
|
91
91
|
// validate appDid
|
|
92
92
|
const nodeInfo = await states.node.read();
|
|
93
|
+
const appIdList = getBlockletAppIdList(state);
|
|
93
94
|
const { wallet } = getBlockletInfo({ meta: state.meta, configs: extra.configs, environments: [] }, nodeInfo.sk);
|
|
94
|
-
if (
|
|
95
|
+
if (appIdList.includes(wallet.address) === false) {
|
|
95
96
|
throw new Error('blocklet appDid is different from the previous one');
|
|
96
97
|
}
|
|
97
98
|
await checkDuplicateAppSk({ sk: wallet.secretKey, states });
|
|
@@ -19,8 +19,8 @@ class AuditLogBackup extends BaseBackup {
|
|
|
19
19
|
scope: this.blocklet.meta.did,
|
|
20
20
|
});
|
|
21
21
|
|
|
22
|
-
removeSync(join(this.
|
|
23
|
-
outputJsonSync(join(this.
|
|
22
|
+
removeSync(join(this.backupDir, this.filename));
|
|
23
|
+
outputJsonSync(join(this.backupDir, this.filename), auditLogs);
|
|
24
24
|
}
|
|
25
25
|
}
|
|
26
26
|
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
class BaseBackup {
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
4
3
|
* @type {import('./spaces').SpaceBackupInput}
|
|
5
4
|
* @memberof BaseBackup
|
|
6
5
|
*/
|
|
@@ -18,15 +17,15 @@ class BaseBackup {
|
|
|
18
17
|
* @type {string}
|
|
19
18
|
* @memberof BaseBackup
|
|
20
19
|
*/
|
|
21
|
-
|
|
20
|
+
backupDir;
|
|
22
21
|
|
|
23
22
|
/**
|
|
24
23
|
*
|
|
25
24
|
* @description spaces 的 endpoint
|
|
26
|
-
* @type {import('
|
|
25
|
+
* @type {import('./spaces').SecurityContext}
|
|
27
26
|
* @memberof BaseBackup
|
|
28
27
|
*/
|
|
29
|
-
|
|
28
|
+
securityContext;
|
|
30
29
|
|
|
31
30
|
/**
|
|
32
31
|
*
|
|
@@ -34,7 +33,7 @@ class BaseBackup {
|
|
|
34
33
|
* @type {string}
|
|
35
34
|
* @memberof BaseBackup
|
|
36
35
|
*/
|
|
37
|
-
|
|
36
|
+
serverDir;
|
|
38
37
|
|
|
39
38
|
constructor(input) {
|
|
40
39
|
this.input = input;
|
|
@@ -48,9 +47,9 @@ class BaseBackup {
|
|
|
48
47
|
*/
|
|
49
48
|
ensureParams(spacesBackup) {
|
|
50
49
|
this.blocklet = spacesBackup.blocklet;
|
|
51
|
-
this.
|
|
52
|
-
this.
|
|
53
|
-
this.
|
|
50
|
+
this.serverDir = spacesBackup.serverDir;
|
|
51
|
+
this.backupDir = spacesBackup.backupDir;
|
|
52
|
+
this.securityContext = spacesBackup.securityContext;
|
|
54
53
|
}
|
|
55
54
|
|
|
56
55
|
async export() {
|