@abtnode/core 1.16.0-beta-8ee536d7 → 1.16.0-beta-62b42401
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 +51 -0
- package/lib/blocklet/manager/disk.js +129 -28
- package/lib/blocklet/manager/helper/install-application-from-backup.js +101 -16
- package/lib/blocklet/manager/helper/install-application-from-dev.js +7 -1
- package/lib/blocklet/manager/helper/install-application-from-general.js +23 -17
- package/lib/blocklet/manager/helper/install-component-from-upload.js +1 -1
- package/lib/blocklet/manager/helper/migrate-application-to-struct-v2.js +32 -7
- package/lib/blocklet/manager/helper/upgrade-components.js +6 -2
- package/lib/blocklet/storage/backup/base.js +64 -10
- package/lib/blocklet/storage/backup/blocklet.js +64 -3
- package/lib/blocklet/storage/backup/disk.js +132 -0
- package/lib/blocklet/storage/backup/spaces.js +11 -33
- package/lib/blocklet/storage/restore/base.js +13 -6
- package/lib/blocklet/storage/restore/blocklet-extras.js +5 -3
- package/lib/blocklet/storage/restore/blocklet.js +1 -1
- package/lib/blocklet/storage/restore/disk.js +104 -0
- package/lib/blocklet/storage/restore/spaces.js +10 -6
- package/lib/blocklet/storage/utils/disk.js +61 -0
- package/lib/event.js +7 -1
- package/lib/index.js +4 -2
- package/lib/states/audit-log.js +3 -0
- package/lib/states/user.js +88 -1
- package/lib/util/blocklet.js +37 -0
- package/package.json +27 -27
package/lib/api/team.js
CHANGED
|
@@ -12,6 +12,7 @@ const {
|
|
|
12
12
|
WELLKNOWN_SERVICE_PATH_PREFIX,
|
|
13
13
|
MAX_USER_PAGE_SIZE,
|
|
14
14
|
STORE_DETAIL_PAGE_PATH_PREFIX,
|
|
15
|
+
USER_TYPE,
|
|
15
16
|
} = require('@abtnode/constant');
|
|
16
17
|
const { isValid: isValidDid } = require('@arcblock/did');
|
|
17
18
|
const { BlockletEvents } = require('@blocklet/constant');
|
|
@@ -182,6 +183,10 @@ class TeamAPI extends EventEmitter {
|
|
|
182
183
|
'remark',
|
|
183
184
|
'avatar',
|
|
184
185
|
'locale',
|
|
186
|
+
// oauth relate fields
|
|
187
|
+
'source',
|
|
188
|
+
'derivedAccount',
|
|
189
|
+
'connectedAccounts',
|
|
185
190
|
])
|
|
186
191
|
// eslint-disable-next-line function-paren-newline
|
|
187
192
|
),
|
|
@@ -592,6 +597,13 @@ class TeamAPI extends EventEmitter {
|
|
|
592
597
|
|
|
593
598
|
// Issue Passport
|
|
594
599
|
|
|
600
|
+
/**
|
|
601
|
+
* 为指定用户颁发 passport
|
|
602
|
+
* @param {string} param.teamDid - 应用的 did
|
|
603
|
+
* @param {string} param.ownerDid - 指定用户的 did
|
|
604
|
+
* @param {string} param.name - passport 的 role
|
|
605
|
+
* @returns {object} passport-info
|
|
606
|
+
*/
|
|
595
607
|
async createPassportIssuance({ teamDid, ownerDid, name }) {
|
|
596
608
|
if (!name) {
|
|
597
609
|
throw new Error('Passport cannot be empty');
|
|
@@ -613,6 +625,45 @@ class TeamAPI extends EventEmitter {
|
|
|
613
625
|
const expireDate = Date.now() + this.memberInviteExpireTime;
|
|
614
626
|
|
|
615
627
|
const state = await this.getSessionState(teamDid);
|
|
628
|
+
|
|
629
|
+
const currentUser = await this.getUser({ teamDid, user: { did: ownerDid } });
|
|
630
|
+
if (currentUser?.source === USER_TYPE.DERIVED) {
|
|
631
|
+
const userDid = currentUser.did;
|
|
632
|
+
const blocklet = await getBlocklet({ did: teamDid, states: this.states, dataDirs: this.dataDirs });
|
|
633
|
+
const nodeInfo = await this.node.read();
|
|
634
|
+
const blockletInfo = getBlockletInfo(blocklet, nodeInfo.sk);
|
|
635
|
+
const { wallet, passportColor, appUrl, name: issuerName } = blockletInfo;
|
|
636
|
+
const vc = createPassportVC({
|
|
637
|
+
issuerName,
|
|
638
|
+
issuerWallet: wallet,
|
|
639
|
+
ownerDid: userDid,
|
|
640
|
+
passport: await createPassport({
|
|
641
|
+
role,
|
|
642
|
+
}),
|
|
643
|
+
endpoint: getPassportStatusEndpoint({
|
|
644
|
+
baseUrl: joinUrl(appUrl, WELLKNOWN_SERVICE_PATH_PREFIX),
|
|
645
|
+
userDid,
|
|
646
|
+
teamDid,
|
|
647
|
+
}),
|
|
648
|
+
ownerProfile: {
|
|
649
|
+
...currentUser,
|
|
650
|
+
// avatar: await parseUserAvatar(currentUser.avatar, { dataDir: blocklet.env.dataDir }),
|
|
651
|
+
},
|
|
652
|
+
preferredColor: passportColor,
|
|
653
|
+
});
|
|
654
|
+
|
|
655
|
+
// write passport to db
|
|
656
|
+
const passport = createUserPassport(vc, { role: role.name });
|
|
657
|
+
const passports = upsertToPassports(currentUser.passports || [], passport);
|
|
658
|
+
await this.updateUser({
|
|
659
|
+
teamDid,
|
|
660
|
+
user: {
|
|
661
|
+
did: ownerDid,
|
|
662
|
+
passports,
|
|
663
|
+
},
|
|
664
|
+
});
|
|
665
|
+
return undefined;
|
|
666
|
+
}
|
|
616
667
|
const { id } = await state.start({
|
|
617
668
|
type: 'passport-issuance',
|
|
618
669
|
expireDate, // session expireDate
|
|
@@ -11,9 +11,11 @@ const cloneDeep = require('lodash/cloneDeep');
|
|
|
11
11
|
const { isNFTExpired, getNftExpirationDate } = require('@abtnode/util/lib/nft');
|
|
12
12
|
const didDocument = require('@abtnode/util/lib/did-document');
|
|
13
13
|
const { sign } = require('@arcblock/jwt');
|
|
14
|
+
const { isEthereumDid } = require('@arcblock/did');
|
|
14
15
|
const { toSvg: createDidLogo } =
|
|
15
16
|
process.env.NODE_ENV !== 'test' ? require('@arcblock/did-motif') : require('@arcblock/did-motif/dist/did-motif.cjs');
|
|
16
17
|
const getBlockletInfo = require('@blocklet/meta/lib/info');
|
|
18
|
+
const { createBlockiesSvg } = require('@blocklet/meta/lib/blockies');
|
|
17
19
|
const sleep = require('@abtnode/util/lib/sleep');
|
|
18
20
|
|
|
19
21
|
const logger = require('@abtnode/logger')('@abtnode/core:blocklet:manager');
|
|
@@ -60,6 +62,7 @@ const {
|
|
|
60
62
|
BLOCKLET_LATEST_SPEC_VERSION,
|
|
61
63
|
BLOCKLET_META_FILE,
|
|
62
64
|
BLOCKLET_CONFIGURABLE_KEY,
|
|
65
|
+
RESTORE_PROGRESS_STATUS,
|
|
63
66
|
} = require('@blocklet/constant');
|
|
64
67
|
const util = require('../../util');
|
|
65
68
|
const {
|
|
@@ -109,7 +112,10 @@ const handleInstanceInStore = require('../../util/public-to-store');
|
|
|
109
112
|
const { BlockletRuntimeMonitor } = require('../../monitor/blocklet-runtime-monitor');
|
|
110
113
|
const getHistoryList = require('../../monitor/get-history-list');
|
|
111
114
|
const { SpacesBackup } = require('../storage/backup/spaces');
|
|
115
|
+
const { DiskBackup } = require('../storage/backup/disk');
|
|
116
|
+
const { getBackupList, removeBackup } = require('../storage/utils/disk');
|
|
112
117
|
const { SpacesRestore } = require('../storage/restore/spaces');
|
|
118
|
+
const { DiskRestore } = require('../storage/restore/disk');
|
|
113
119
|
const { installApplicationFromGeneral } = require('./helper/install-application-from-general');
|
|
114
120
|
const { installApplicationFromDev } = require('./helper/install-application-from-dev');
|
|
115
121
|
const { installApplicationFromBackup } = require('./helper/install-application-from-backup');
|
|
@@ -566,51 +572,45 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
566
572
|
}
|
|
567
573
|
|
|
568
574
|
/**
|
|
569
|
-
*
|
|
570
|
-
* @param {import('@abtnode/client').RequestBackupToSpacesInput} input
|
|
575
|
+
* @param {import('@abtnode/client').RequestBackupBlockletInput} input
|
|
571
576
|
* @memberof BlockletManager
|
|
572
577
|
*/
|
|
573
578
|
// eslint-disable-next-line no-unused-vars
|
|
574
|
-
async
|
|
579
|
+
async backup({ appDid, to }, context) {
|
|
575
580
|
const blocklet = await states.blocklet.getBlocklet(appDid);
|
|
576
581
|
if (blocklet.structVersion !== APP_STRUCT_VERSION) {
|
|
577
582
|
throw new Error('Only new version app can be backup to spaces, please migrate this app first');
|
|
578
583
|
}
|
|
579
584
|
|
|
580
|
-
|
|
581
|
-
|
|
585
|
+
if (to === 'spaces') {
|
|
586
|
+
return this._backupToSpaces({ blocklet }, context);
|
|
587
|
+
}
|
|
582
588
|
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
589
|
+
if (to === 'disk') {
|
|
590
|
+
return this._backupToDisk({ blocklet }, context);
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
throw new Error('Can only backup to spaces or disk');
|
|
587
594
|
}
|
|
588
595
|
|
|
589
596
|
/**
|
|
590
597
|
* FIXME: @linchen support cancel
|
|
591
598
|
* FIXME: @wangshijun create audit log for this
|
|
592
|
-
* @param {import('@abtnode/client').
|
|
599
|
+
* @param {import('@abtnode/client').RequestRestoreBlockletInput} input
|
|
593
600
|
* @memberof BlockletManager
|
|
594
601
|
*/
|
|
595
602
|
// eslint-disable-next-line no-unused-vars
|
|
596
|
-
async
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
const spacesRestore = new SpacesRestore({ ...input, event: this, userDid, referrer: context.referrer });
|
|
602
|
-
const params = await spacesRestore.restore();
|
|
603
|
+
async restore(input, context) {
|
|
604
|
+
const { from, ...param } = input;
|
|
605
|
+
if (from === 'spaces') {
|
|
606
|
+
return this._restoreFromSpaces(param, context);
|
|
607
|
+
}
|
|
603
608
|
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
moveDir: true,
|
|
608
|
-
...merge(...params),
|
|
609
|
-
manager: this,
|
|
610
|
-
states,
|
|
611
|
-
});
|
|
609
|
+
if (from === 'disk') {
|
|
610
|
+
return this._restoreFromDisk(param, context);
|
|
611
|
+
}
|
|
612
612
|
|
|
613
|
-
|
|
613
|
+
throw new Error('Can only restore from spaces or disk');
|
|
614
614
|
}
|
|
615
615
|
|
|
616
616
|
/**
|
|
@@ -982,6 +982,10 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
982
982
|
return blocklets;
|
|
983
983
|
}
|
|
984
984
|
|
|
985
|
+
async listBackups() {
|
|
986
|
+
return getBackupList(this.dataDirs.data);
|
|
987
|
+
}
|
|
988
|
+
|
|
985
989
|
// CAUTION: this method currently only support config by blocklet.meta.did
|
|
986
990
|
// eslint-disable-next-line no-unused-vars
|
|
987
991
|
async config({ did, configs: newConfigs, skipHook, skipDidDocument }, context) {
|
|
@@ -1150,6 +1154,15 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
1150
1154
|
return blocklet;
|
|
1151
1155
|
}
|
|
1152
1156
|
|
|
1157
|
+
async configOAuth({ did, oauth = {} }) {
|
|
1158
|
+
const oldOAuth = await states.blockletExtras.getSettings(did, 'oauth', {});
|
|
1159
|
+
const mergeConfig = { ...oldOAuth, ...JSON.parse(oauth) };
|
|
1160
|
+
await states.blockletExtras.setSettings(did, { oauth: mergeConfig });
|
|
1161
|
+
const newState = await this.getBlocklet(did);
|
|
1162
|
+
this.emit(BlockletEvents.updated, newState);
|
|
1163
|
+
return newState;
|
|
1164
|
+
}
|
|
1165
|
+
|
|
1153
1166
|
async updateComponentTitle({ did, rootDid: inputRootDid, title }) {
|
|
1154
1167
|
await titleSchema.validateAsync(title);
|
|
1155
1168
|
|
|
@@ -1402,6 +1415,7 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
1402
1415
|
throwOnError,
|
|
1403
1416
|
skipCheckStatusBeforeDownload,
|
|
1404
1417
|
selectedComponentDids,
|
|
1418
|
+
skipCheckIntegrity,
|
|
1405
1419
|
} = params;
|
|
1406
1420
|
const { meta } = blocklet;
|
|
1407
1421
|
const { name, did, version } = meta;
|
|
@@ -1443,7 +1457,10 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
1443
1457
|
return x;
|
|
1444
1458
|
}),
|
|
1445
1459
|
};
|
|
1446
|
-
const { isCancelled } = await this._downloadBlocklet(
|
|
1460
|
+
const { isCancelled } = await this._downloadBlocklet(
|
|
1461
|
+
blockletForDownload,
|
|
1462
|
+
Object.assign({}, context, { skipCheckIntegrity })
|
|
1463
|
+
);
|
|
1447
1464
|
|
|
1448
1465
|
if (isCancelled) {
|
|
1449
1466
|
logger.info('Download was canceled', { name, did, version });
|
|
@@ -1869,7 +1886,11 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
1869
1886
|
await fs.copy(src, dist);
|
|
1870
1887
|
}
|
|
1871
1888
|
|
|
1872
|
-
|
|
1889
|
+
if (isEthereumDid(blocklet.meta.did)) {
|
|
1890
|
+
await fs.writeFile(path.join(blocklet.env.dataDir, 'logo.svg'), createBlockiesSvg(blocklet.meta.did));
|
|
1891
|
+
} else {
|
|
1892
|
+
await fs.writeFile(path.join(blocklet.env.dataDir, 'logo.svg'), createDidLogo(blocklet.meta.did));
|
|
1893
|
+
}
|
|
1873
1894
|
|
|
1874
1895
|
// Init db
|
|
1875
1896
|
await this.teamManager.initTeam(blocklet.meta.did);
|
|
@@ -1910,6 +1931,7 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
1910
1931
|
}
|
|
1911
1932
|
|
|
1912
1933
|
await this._rollbackCache.remove({ did: blocklet.meta.did });
|
|
1934
|
+
removeBackup(this.dataDirs.data, blocklet.appDid);
|
|
1913
1935
|
return blocklet;
|
|
1914
1936
|
} catch (err) {
|
|
1915
1937
|
const { meta } = await states.blocklet.getBlocklet(did);
|
|
@@ -2473,6 +2495,85 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
2473
2495
|
|
|
2474
2496
|
await states.blocklet.updateBlocklet(blocklet.meta.did, { children: blocklet.children });
|
|
2475
2497
|
}
|
|
2498
|
+
|
|
2499
|
+
/**
|
|
2500
|
+
* FIXME: @wangshijun create audit log for this
|
|
2501
|
+
* @param {import('@abtnode/client').BlockletState} blocklet
|
|
2502
|
+
* @memberof BlockletManager
|
|
2503
|
+
*/
|
|
2504
|
+
// eslint-disable-next-line no-unused-vars
|
|
2505
|
+
async _backupToSpaces({ blocklet }, context) {
|
|
2506
|
+
const userDid = context.user.did;
|
|
2507
|
+
const { referrer } = context;
|
|
2508
|
+
const { appDid } = blocklet;
|
|
2509
|
+
|
|
2510
|
+
const spacesBackup = new SpacesBackup({ appDid, event: this, userDid, referrer });
|
|
2511
|
+
this.emit(BlockletEvents.backupProgress, { appDid, message: 'Start backup...', progress: 10, completed: false });
|
|
2512
|
+
await spacesBackup.backup();
|
|
2513
|
+
this.emit(BlockletEvents.backupProgress, { appDid, completed: true, progress: 100 });
|
|
2514
|
+
}
|
|
2515
|
+
|
|
2516
|
+
/**
|
|
2517
|
+
* @param {import('@abtnode/client').BlockletState} blocklet
|
|
2518
|
+
*/
|
|
2519
|
+
// eslint-disable-next-line no-unused-vars
|
|
2520
|
+
async _backupToDisk({ blocklet }, context) {
|
|
2521
|
+
const { appDid } = blocklet;
|
|
2522
|
+
|
|
2523
|
+
const diskBackup = new DiskBackup({ appDid, event: this });
|
|
2524
|
+
await diskBackup.backup();
|
|
2525
|
+
}
|
|
2526
|
+
|
|
2527
|
+
/**
|
|
2528
|
+
* FIXME: @linchen support cancel
|
|
2529
|
+
* FIXME: @wangshijun create audit log for this
|
|
2530
|
+
* @param {import('@abtnode/client').RequestRestoreBlockletInput} input
|
|
2531
|
+
* @memberof BlockletManager
|
|
2532
|
+
*/
|
|
2533
|
+
// eslint-disable-next-line no-unused-vars
|
|
2534
|
+
async _restoreFromSpaces(input, context) {
|
|
2535
|
+
if (input.delay) {
|
|
2536
|
+
await sleep(input.delay);
|
|
2537
|
+
}
|
|
2538
|
+
|
|
2539
|
+
this.emit(BlockletEvents.restoreProgress, { appDid: input.appDid, status: RESTORE_PROGRESS_STATUS.start });
|
|
2540
|
+
|
|
2541
|
+
const userDid = context.user.did;
|
|
2542
|
+
|
|
2543
|
+
const spacesRestore = new SpacesRestore({ ...input, event: this, userDid, referrer: context.referrer });
|
|
2544
|
+
const params = await spacesRestore.restore();
|
|
2545
|
+
|
|
2546
|
+
this.emit(BlockletEvents.restoreProgress, { appDid: input.appDid, status: RESTORE_PROGRESS_STATUS.installing });
|
|
2547
|
+
await installApplicationFromBackup({
|
|
2548
|
+
url: `file://${spacesRestore.restoreDir}`,
|
|
2549
|
+
moveDir: true,
|
|
2550
|
+
...merge(...params),
|
|
2551
|
+
manager: this,
|
|
2552
|
+
states,
|
|
2553
|
+
controller: input.controller,
|
|
2554
|
+
context: { ...context, startImmediately: true },
|
|
2555
|
+
});
|
|
2556
|
+
|
|
2557
|
+
this.emit(BlockletEvents.restoreProgress, { appDid: input.appDid, status: RESTORE_PROGRESS_STATUS.completed });
|
|
2558
|
+
}
|
|
2559
|
+
|
|
2560
|
+
async _restoreFromDisk(input) {
|
|
2561
|
+
if (input.delay) {
|
|
2562
|
+
await sleep(input.delay);
|
|
2563
|
+
}
|
|
2564
|
+
|
|
2565
|
+
const diskRestore = new DiskRestore({ ...input, event: this });
|
|
2566
|
+
const params = await diskRestore.restore();
|
|
2567
|
+
|
|
2568
|
+
await installApplicationFromBackup({
|
|
2569
|
+
url: `file://${diskRestore.restoreDir}`,
|
|
2570
|
+
...merge(...params),
|
|
2571
|
+
manager: this,
|
|
2572
|
+
states,
|
|
2573
|
+
move: true,
|
|
2574
|
+
sync: false, // use queue to download and install application
|
|
2575
|
+
});
|
|
2576
|
+
}
|
|
2476
2577
|
}
|
|
2477
2578
|
|
|
2478
2579
|
module.exports = BlockletManager;
|
|
@@ -5,7 +5,7 @@ const omit = require('lodash/omit');
|
|
|
5
5
|
const { forEachBlockletSync, getBlockletAppIdList } = require('@blocklet/meta/lib/util');
|
|
6
6
|
const getBlockletInfo = require('@blocklet/meta/lib/info');
|
|
7
7
|
|
|
8
|
-
const { BLOCKLET_CONFIGURABLE_KEY } = require('@blocklet/constant');
|
|
8
|
+
const { BLOCKLET_CONFIGURABLE_KEY, BlockletEvents, BlockletStatus } = require('@blocklet/constant');
|
|
9
9
|
|
|
10
10
|
const logger = require('@abtnode/logger')('@abtnode/core:install-app-backup');
|
|
11
11
|
|
|
@@ -18,12 +18,21 @@ const { validateBlocklet, checkDuplicateAppSk, getAppDirs } = require('../../../
|
|
|
18
18
|
* /blocklets/<name3>version>
|
|
19
19
|
* /data
|
|
20
20
|
* /blocklet.json
|
|
21
|
-
* /
|
|
21
|
+
* /blocklet-extras.json
|
|
22
22
|
* @see Blocklet 端到端备份方案的设计与实现(一期) https://team.arcblock.io/comment/discussions/e62084d5-fafa-489e-91d5-defcd6e93223
|
|
23
23
|
* @param {{ url: string, appSk: string, moveDir: boolean, manager: import('../disk'), states: import('../../../states/index'), context: Record<string, string> }}
|
|
24
24
|
* @return {Promise<any>}
|
|
25
25
|
*/
|
|
26
|
-
const installApplicationFromBackup = async ({
|
|
26
|
+
const installApplicationFromBackup = async ({
|
|
27
|
+
url,
|
|
28
|
+
appSk,
|
|
29
|
+
moveDir,
|
|
30
|
+
context = {},
|
|
31
|
+
states,
|
|
32
|
+
manager,
|
|
33
|
+
sync = true,
|
|
34
|
+
controller,
|
|
35
|
+
} = {}) => {
|
|
27
36
|
// TODO: support more url schema feature (http, did-spaces)
|
|
28
37
|
if (!url.startsWith('file://')) {
|
|
29
38
|
throw new Error('url must starts with file://');
|
|
@@ -41,7 +50,7 @@ const installApplicationFromBackup = async ({ url, appSk, moveDir, context = {},
|
|
|
41
50
|
const srcDataDir = path.join(dir, 'data');
|
|
42
51
|
|
|
43
52
|
/** @type {import('@abtnode/client').BlockletState} */
|
|
44
|
-
const
|
|
53
|
+
const blockletState = omit(fs.readJSONSync(path.join(dir, 'blocklet.json')), [
|
|
45
54
|
'_id',
|
|
46
55
|
'createdAt',
|
|
47
56
|
'updatedAt',
|
|
@@ -49,22 +58,24 @@ const installApplicationFromBackup = async ({ url, appSk, moveDir, context = {},
|
|
|
49
58
|
'startedAt',
|
|
50
59
|
]);
|
|
51
60
|
|
|
52
|
-
if (!
|
|
61
|
+
if (!blockletState.structVersion) {
|
|
53
62
|
throw new Error(
|
|
54
63
|
'Only application of structVersion 2 can be restored on this server. Please migrate you application and re-backup your application on your previous server.'
|
|
55
64
|
);
|
|
56
65
|
}
|
|
57
66
|
|
|
58
|
-
const { meta } =
|
|
67
|
+
const { meta } = blockletState;
|
|
59
68
|
const { did, name: appName } = meta;
|
|
60
69
|
|
|
61
70
|
const extra = omit(fs.readJSONSync(path.join(dir, 'blocklet-extras.json')), ['_id', 'createdAt', 'updatedAt']);
|
|
62
71
|
|
|
63
|
-
if (
|
|
64
|
-
throw new Error(
|
|
72
|
+
if (blockletState.meta.did !== extra.did) {
|
|
73
|
+
throw new Error(
|
|
74
|
+
`did does not match in blocklet.json (${blockletState.meta.did}) and blocklet_extra.json ${extra.did}`
|
|
75
|
+
);
|
|
65
76
|
}
|
|
66
77
|
|
|
67
|
-
forEachBlockletSync(
|
|
78
|
+
forEachBlockletSync(blockletState, (component) => {
|
|
68
79
|
delete component.status;
|
|
69
80
|
delete component.ports;
|
|
70
81
|
delete component.environments;
|
|
@@ -98,8 +109,11 @@ const installApplicationFromBackup = async ({ url, appSk, moveDir, context = {},
|
|
|
98
109
|
|
|
99
110
|
// validate appDid
|
|
100
111
|
const nodeInfo = await states.node.read();
|
|
101
|
-
const appIdList = getBlockletAppIdList(
|
|
102
|
-
const { wallet } = getBlockletInfo(
|
|
112
|
+
const appIdList = getBlockletAppIdList(blockletState);
|
|
113
|
+
const { wallet } = getBlockletInfo(
|
|
114
|
+
{ meta: blockletState.meta, configs: extra.configs, environments: [] },
|
|
115
|
+
nodeInfo.sk
|
|
116
|
+
);
|
|
103
117
|
if (appIdList.includes(wallet.address) === false) {
|
|
104
118
|
throw new Error('blocklet appDid is different from the previous one');
|
|
105
119
|
}
|
|
@@ -117,11 +131,15 @@ const installApplicationFromBackup = async ({ url, appSk, moveDir, context = {},
|
|
|
117
131
|
logger.error('old extra state exists and will be force removed', { existExtra });
|
|
118
132
|
await states.blockletExtras.remove({ did });
|
|
119
133
|
}
|
|
134
|
+
if (controller) {
|
|
135
|
+
extra.controller = controller;
|
|
136
|
+
}
|
|
137
|
+
|
|
120
138
|
await states.blockletExtras.insert(extra);
|
|
121
139
|
logger.info('blocklet extra is copied successfully');
|
|
122
140
|
|
|
123
141
|
// add blocklet
|
|
124
|
-
await states.blocklet.addBlocklet(
|
|
142
|
+
await states.blocklet.addBlocklet(blockletState);
|
|
125
143
|
logger.info('blocklet state is added successfully');
|
|
126
144
|
|
|
127
145
|
// copy bundle
|
|
@@ -145,9 +163,8 @@ const installApplicationFromBackup = async ({ url, appSk, moveDir, context = {},
|
|
|
145
163
|
logger.info(`bundle is ${moveDir ? 'moved' : 'copied'} successfully`, { installDir });
|
|
146
164
|
})
|
|
147
165
|
);
|
|
148
|
-
// 从 store 下载 blocklet
|
|
149
|
-
await manager.blockletDownloader.download(state, { skipCheckIntegrity: true });
|
|
150
166
|
|
|
167
|
+
// copy data
|
|
151
168
|
const dataDir = path.join(manager.dataDirs.data, appName);
|
|
152
169
|
if (fs.existsSync(dataDir)) {
|
|
153
170
|
logger.error('old data exists and will be force removed', { dataDir });
|
|
@@ -170,8 +187,76 @@ const installApplicationFromBackup = async ({ url, appSk, moveDir, context = {},
|
|
|
170
187
|
throw error;
|
|
171
188
|
}
|
|
172
189
|
|
|
173
|
-
|
|
174
|
-
|
|
190
|
+
if (sync) {
|
|
191
|
+
try {
|
|
192
|
+
logger.info('start download blocklet', { did });
|
|
193
|
+
// 从 store 下载 blocklet
|
|
194
|
+
await manager.blockletDownloader.download(blockletState, { skipCheckIntegrity: true });
|
|
195
|
+
} catch (error) {
|
|
196
|
+
logger.error('download blocklet failed', { did, error });
|
|
197
|
+
|
|
198
|
+
await manager._rollback('install', did);
|
|
199
|
+
|
|
200
|
+
throw error;
|
|
201
|
+
}
|
|
202
|
+
logger.info('start install blocklet', { did });
|
|
203
|
+
return manager._installBlocklet({ did, context });
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
try {
|
|
207
|
+
const blocklet1 = await states.blocklet.setBlockletStatus(did, BlockletStatus.waiting);
|
|
208
|
+
manager.emit(BlockletEvents.added, blocklet1);
|
|
209
|
+
|
|
210
|
+
const downloadParams = {
|
|
211
|
+
blocklet: { ...blocklet1 },
|
|
212
|
+
skipCheckIntegrity: true,
|
|
213
|
+
context,
|
|
214
|
+
postAction: 'install',
|
|
215
|
+
};
|
|
216
|
+
|
|
217
|
+
// backup rollback data
|
|
218
|
+
await manager._rollbackCache.backup({ did, action: 'install' });
|
|
219
|
+
|
|
220
|
+
const ticket = manager.installQueue.push(
|
|
221
|
+
{
|
|
222
|
+
entity: 'blocklet',
|
|
223
|
+
action: 'download',
|
|
224
|
+
id: did,
|
|
225
|
+
...downloadParams,
|
|
226
|
+
},
|
|
227
|
+
did
|
|
228
|
+
);
|
|
229
|
+
ticket.on('failed', async (err) => {
|
|
230
|
+
logger.error('failed to install blocklet', { did, error: err });
|
|
231
|
+
try {
|
|
232
|
+
await manager._rollback('install', did, {});
|
|
233
|
+
} catch (e) {
|
|
234
|
+
logger.error('failed to remove blocklet on install error', { did, error: e });
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
manager._createNotification(did, {
|
|
238
|
+
title: 'Blocklet Install Failed',
|
|
239
|
+
description: `Blocklet ${blockletState?.meta?.title} install failed with error: ${
|
|
240
|
+
err.message || 'queue exception'
|
|
241
|
+
}`,
|
|
242
|
+
entityType: 'blocklet',
|
|
243
|
+
entityId: did,
|
|
244
|
+
severity: 'error',
|
|
245
|
+
});
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
return blocklet1;
|
|
249
|
+
} catch (err) {
|
|
250
|
+
logger.error('failed to install blocklet', { did, error: err });
|
|
251
|
+
|
|
252
|
+
try {
|
|
253
|
+
await manager._rollback('install', did, {});
|
|
254
|
+
} catch (e) {
|
|
255
|
+
logger.error('failed to remove blocklet on install error', { did, error: e });
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
throw err;
|
|
259
|
+
}
|
|
175
260
|
};
|
|
176
261
|
|
|
177
262
|
module.exports = { installApplicationFromBackup };
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
const path = require('path');
|
|
2
2
|
const fs = require('fs-extra');
|
|
3
|
+
const { isEthereumDid } = require('@arcblock/did');
|
|
4
|
+
const { createBlockiesSvg } = require('@blocklet/meta/lib/blockies');
|
|
3
5
|
|
|
4
6
|
const { toSvg: createDidLogo } =
|
|
5
7
|
process.env.NODE_ENV !== 'test' ? require('@arcblock/did-motif') : require('@arcblock/did-motif/dist/did-motif.cjs');
|
|
@@ -86,7 +88,11 @@ const installApplicationFromDev = async ({ folder, meta, states, manager } = {})
|
|
|
86
88
|
|
|
87
89
|
blocklet = await manager.getBlocklet(did);
|
|
88
90
|
|
|
89
|
-
|
|
91
|
+
if (isEthereumDid(blocklet.meta.did)) {
|
|
92
|
+
await fs.writeFile(path.join(blocklet.env.dataDir, 'logo.svg'), createBlockiesSvg(blocklet.meta.did));
|
|
93
|
+
} else {
|
|
94
|
+
await fs.writeFile(path.join(blocklet.env.dataDir, 'logo.svg'), createDidLogo(blocklet.meta.did));
|
|
95
|
+
}
|
|
90
96
|
|
|
91
97
|
return blocklet;
|
|
92
98
|
};
|
|
@@ -63,28 +63,16 @@ const installApplicationFromGeneral = async ({
|
|
|
63
63
|
}
|
|
64
64
|
}
|
|
65
65
|
|
|
66
|
-
|
|
67
|
-
const wallet = getApplicationWallet(appSk);
|
|
68
|
-
const did = wallet.address;
|
|
69
|
-
const name = wallet.address;
|
|
70
|
-
|
|
71
|
-
// check exist
|
|
72
|
-
const exist = await states.blocklet.hasBlocklet(did);
|
|
73
|
-
if (exist) {
|
|
74
|
-
throw new Error(`blocklet ${did} already exists`);
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
// remove dirty data
|
|
78
|
-
const oldExtraState = await states.blockletExtras.findOne({ did });
|
|
79
|
-
if (oldExtraState) {
|
|
80
|
-
logger.error('find dirty data in blocklet extra', { did });
|
|
81
|
-
await states.blockletExtras.remove({ did });
|
|
82
|
-
}
|
|
66
|
+
let blockletWalletType;
|
|
83
67
|
|
|
84
68
|
// create component
|
|
85
69
|
let component;
|
|
86
70
|
if (componentSourceUrl) {
|
|
87
71
|
const meta = await getBlockletMetaFromUrl(componentSourceUrl);
|
|
72
|
+
const blockletWalletTypeEnv = (meta.environments || []).find((x) => x.name === 'BLOCKLET_WALLET_TYPE');
|
|
73
|
+
if (blockletWalletTypeEnv) {
|
|
74
|
+
blockletWalletType = blockletWalletTypeEnv.default;
|
|
75
|
+
}
|
|
88
76
|
|
|
89
77
|
if (nodeInfo.mode === NODE_MODES.SERVERLESS) {
|
|
90
78
|
validateInServerless({ blockletMeta: meta });
|
|
@@ -97,6 +85,24 @@ const installApplicationFromGeneral = async ({
|
|
|
97
85
|
};
|
|
98
86
|
}
|
|
99
87
|
|
|
88
|
+
// app wallet
|
|
89
|
+
const wallet = getApplicationWallet(appSk, undefined, blockletWalletType);
|
|
90
|
+
const did = wallet.address;
|
|
91
|
+
const name = wallet.address;
|
|
92
|
+
|
|
93
|
+
// check exist
|
|
94
|
+
const exist = await states.blocklet.hasBlocklet(did);
|
|
95
|
+
if (exist) {
|
|
96
|
+
throw new Error(`blocklet ${did} already exists`);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// remove dirty data
|
|
100
|
+
const oldExtraState = await states.blockletExtras.findOne({ did });
|
|
101
|
+
if (oldExtraState) {
|
|
102
|
+
logger.error('find dirty data in blocklet extra', { did });
|
|
103
|
+
await states.blockletExtras.remove({ did });
|
|
104
|
+
}
|
|
105
|
+
|
|
100
106
|
// create app
|
|
101
107
|
const blocklet = await manager._addBlocklet({ component, name, did, title, description });
|
|
102
108
|
logger.info('blocklet added to database', { did: blocklet.meta.did });
|
|
@@ -138,7 +138,7 @@ const diff = async ({ did, hashFiles: clientFiles, rootDid: inputRootDid, states
|
|
|
138
138
|
|
|
139
139
|
const rootBlocklet = await states.blocklet.getBlocklet(rootDid);
|
|
140
140
|
if (childDid && !rootBlocklet) {
|
|
141
|
-
throw new Error(
|
|
141
|
+
throw new Error(`Root blocklet does not exist: ${rootDid}`);
|
|
142
142
|
}
|
|
143
143
|
|
|
144
144
|
const state = childDid ? await (rootBlocklet.children || []).find((x) => x.meta.did === childDid) : rootBlocklet;
|