@abtnode/core 1.8.67-beta-794a8082 → 1.8.68-beta-500af7e5
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 +82 -19
- package/lib/blocklet/storage/backup/blocklet-extras.js +15 -19
- package/lib/blocklet/storage/backup/spaces.js +37 -9
- package/lib/blocklet/storage/restore/base.js +10 -9
- package/lib/blocklet/storage/restore/blocklet-extras.js +21 -16
- package/lib/blocklet/storage/restore/spaces.js +42 -35
- package/lib/event.js +16 -0
- package/lib/index.js +21 -2
- package/lib/states/blocklet.js +21 -5
- package/lib/util/blocklet.js +65 -9
- package/lib/webhook/index.js +1 -1
- package/package.json +26 -26
- /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');
|
|
@@ -111,6 +112,8 @@ const {
|
|
|
111
112
|
checkDuplicateMountPoint,
|
|
112
113
|
validateStore,
|
|
113
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
|
|
@@ -266,6 +270,7 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
266
270
|
|
|
267
271
|
const info = await states.node.read();
|
|
268
272
|
|
|
273
|
+
// Note: if you added new header here, please change core/state/lib/blocklet/downloader/bundle-downloader.js to use that header
|
|
269
274
|
context.headers = Object.assign(context?.headers || {}, {
|
|
270
275
|
'x-server-did': info.did,
|
|
271
276
|
'x-server-public-key': info.pk,
|
|
@@ -605,32 +610,67 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
605
610
|
}
|
|
606
611
|
|
|
607
612
|
/**
|
|
608
|
-
*
|
|
609
|
-
*
|
|
610
|
-
* @param {import('@abtnode/client').BackupToSpacesParams} input
|
|
613
|
+
* FIXME: @wangshijun create audit log for this
|
|
614
|
+
* @param {import('@abtnode/client').RequestBackupToSpacesInput} input
|
|
611
615
|
* @memberof BlockletManager
|
|
612
616
|
*/
|
|
613
|
-
|
|
614
|
-
|
|
617
|
+
// eslint-disable-next-line no-unused-vars
|
|
618
|
+
async backupToSpaces({ did }, context) {
|
|
619
|
+
// add to queue
|
|
620
|
+
// const ticket = this.backupQueue.push({
|
|
621
|
+
// entity: 'blocklet',
|
|
622
|
+
// action: 'backup-to-space',
|
|
623
|
+
// did,
|
|
624
|
+
// context,
|
|
625
|
+
// });
|
|
626
|
+
|
|
627
|
+
// ticket.on('failed', async (err) => {
|
|
628
|
+
// logger.error('backup failed', { entity: 'blocklet', did, error: err });
|
|
629
|
+
// this.emit('blocklet.backup.failed', { did, err });
|
|
630
|
+
// this._createNotification(did, {
|
|
631
|
+
// title: 'Blocklet Backup Failed',
|
|
632
|
+
// description: `Blocklet backup failed with error: ${err.message || 'queue exception'}`,
|
|
633
|
+
// entityType: 'blocklet',
|
|
634
|
+
// entityId: did,
|
|
635
|
+
// severity: 'error',
|
|
636
|
+
// });
|
|
637
|
+
// });
|
|
638
|
+
|
|
639
|
+
const userDid = context.user.did;
|
|
640
|
+
const { referrer } = context;
|
|
641
|
+
|
|
642
|
+
// FIXME: @yejianchao did should be renamed to appDid
|
|
643
|
+
const spacesBackup = new SpacesBackup({ did, event: this, userDid, referrer });
|
|
644
|
+
this.emit(BlockletEvents.backupProgress, { did, message: 'Start backup...', progress: 10 });
|
|
615
645
|
await spacesBackup.backup();
|
|
646
|
+
this.emit(BlockletEvents.backupProgress, { did, completed: true, progress: 100 });
|
|
616
647
|
}
|
|
617
648
|
|
|
618
649
|
/**
|
|
619
|
-
*
|
|
620
|
-
*
|
|
650
|
+
* FIXME: @linchen support cancel
|
|
651
|
+
* FIXME: @wangshijun create audit log for this
|
|
621
652
|
* @param {import('@abtnode/client').RequestRestoreFromSpacesInput} input
|
|
622
653
|
* @memberof BlockletManager
|
|
623
654
|
*/
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
655
|
+
// eslint-disable-next-line no-unused-vars
|
|
656
|
+
async restoreFromSpaces(input, context) {
|
|
657
|
+
// FIXME: @yejianchao did should be renamed to appDid
|
|
658
|
+
this.emit(BlockletEvents.restoreProgress, { did: input.appDid, message: 'Start restore...' });
|
|
659
|
+
|
|
660
|
+
const userDid = context.user.did;
|
|
661
|
+
const { referrer } = context;
|
|
662
|
+
|
|
663
|
+
const spacesRestore = new SpacesRestore({ ...input, event: this, userDid, referrer });
|
|
664
|
+
const params = await spacesRestore.restore();
|
|
627
665
|
|
|
628
|
-
|
|
666
|
+
this.emit(BlockletEvents.restoreProgress, { did: input.appDid, message: 'Installing blocklet...' });
|
|
629
667
|
await this._installFromBackup({
|
|
630
668
|
url: `file://${spacesRestore.blockletRestoreDir}`,
|
|
631
|
-
appSk: spacesRestore.blockletWallet.secretKey,
|
|
632
669
|
moveDir: true,
|
|
670
|
+
...merge(...params),
|
|
633
671
|
});
|
|
672
|
+
|
|
673
|
+
this.emit(BlockletEvents.restoreProgress, { did: input.appDid, completed: true });
|
|
634
674
|
}
|
|
635
675
|
|
|
636
676
|
async restart({ did }, context) {
|
|
@@ -1000,6 +1040,7 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
1000
1040
|
return blocklets;
|
|
1001
1041
|
}
|
|
1002
1042
|
|
|
1043
|
+
// CAUTION: this method currently only support config by blocklet.meta.did
|
|
1003
1044
|
// eslint-disable-next-line no-unused-vars
|
|
1004
1045
|
async config({ did, configs: newConfigs, skipHook, skipDidDocument }, context) {
|
|
1005
1046
|
if (!Array.isArray(newConfigs)) {
|
|
@@ -1031,7 +1072,7 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
1031
1072
|
throw new Error(`Cannot set ${x.key} to child blocklet`);
|
|
1032
1073
|
}
|
|
1033
1074
|
|
|
1034
|
-
await validateAppConfig(x,
|
|
1075
|
+
await validateAppConfig(x, states);
|
|
1035
1076
|
} else if (!BLOCKLET_CONFIGURABLE_KEY[x.key] && !isPreferenceKey(x)) {
|
|
1036
1077
|
if (!(blocklet.meta.environments || []).some((y) => y.name === x.key)) {
|
|
1037
1078
|
// forbid unknown format key
|
|
@@ -1054,14 +1095,26 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
1054
1095
|
});
|
|
1055
1096
|
}
|
|
1056
1097
|
|
|
1098
|
+
const willAppSkChange = isRotatingAppSk(newConfigs, blocklet.configs, blocklet.externalSk);
|
|
1099
|
+
const willAppDidChange = isRotatingAppDid(newConfigs, blocklet.configs, blocklet.externalSk);
|
|
1100
|
+
|
|
1057
1101
|
// update db
|
|
1058
1102
|
await states.blockletExtras.setConfigs(dids, newConfigs);
|
|
1059
1103
|
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1104
|
+
if (willAppSkChange) {
|
|
1105
|
+
const info = await states.node.read();
|
|
1106
|
+
const { wallet } = getBlockletInfo(blocklet, info.sk);
|
|
1107
|
+
const migratedFrom = blocklet.migratedFrom || [];
|
|
1108
|
+
await states.blocklet.updateBlocklet(rootDid, {
|
|
1109
|
+
migratedFrom: [
|
|
1110
|
+
...migratedFrom,
|
|
1111
|
+
{ appSk: wallet.secretKey, appDid: wallet.address, at: new Date().toISOString() },
|
|
1112
|
+
],
|
|
1113
|
+
});
|
|
1114
|
+
}
|
|
1063
1115
|
|
|
1064
|
-
|
|
1116
|
+
// FIXME: @zhenqiang best way to handle the did document: allow all appDids to resolve
|
|
1117
|
+
if (willAppDidChange && !skipDidDocument) {
|
|
1065
1118
|
await this._updateDidDocument(blocklet);
|
|
1066
1119
|
}
|
|
1067
1120
|
|
|
@@ -2064,6 +2117,14 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
2064
2117
|
// put BLOCKLET_APP_ID at root level for indexing
|
|
2065
2118
|
blocklet.appDid = appSystemEnvironments.BLOCKLET_APP_ID;
|
|
2066
2119
|
|
|
2120
|
+
if (!blocklet.migratedFrom) {
|
|
2121
|
+
blocklet.migratedFrom = [];
|
|
2122
|
+
}
|
|
2123
|
+
// This can only be set once, can be used for indexing, will not change ever
|
|
2124
|
+
if (!blocklet.appPid) {
|
|
2125
|
+
blocklet.appPid = appSystemEnvironments.BLOCKLET_APP_PID;
|
|
2126
|
+
}
|
|
2127
|
+
|
|
2067
2128
|
// update state to db
|
|
2068
2129
|
return states.blocklet.updateBlocklet(did, blocklet);
|
|
2069
2130
|
}
|
|
@@ -2344,7 +2405,7 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
2344
2405
|
};
|
|
2345
2406
|
const meta = validateMeta(rawMeta);
|
|
2346
2407
|
|
|
2347
|
-
await states.blocklet.addBlocklet({ meta, source: BlockletSource.custom });
|
|
2408
|
+
await states.blocklet.addBlocklet({ meta, source: BlockletSource.custom, externalSk: !!appSk });
|
|
2348
2409
|
await this._setConfigsFromMeta(did);
|
|
2349
2410
|
await this._setAppSk(did, appSk);
|
|
2350
2411
|
|
|
@@ -2478,6 +2539,7 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
2478
2539
|
source: BlockletSource.upload,
|
|
2479
2540
|
deployedFrom: `Upload by ${context.user.fullName}`,
|
|
2480
2541
|
children,
|
|
2542
|
+
externalSk: !!appSk,
|
|
2481
2543
|
});
|
|
2482
2544
|
|
|
2483
2545
|
const action = 'install';
|
|
@@ -2812,6 +2874,7 @@ class BlockletManager extends BaseBlockletManager {
|
|
|
2812
2874
|
source,
|
|
2813
2875
|
deployedFrom,
|
|
2814
2876
|
children,
|
|
2877
|
+
externalSk: !!appSk,
|
|
2815
2878
|
});
|
|
2816
2879
|
|
|
2817
2880
|
await validateBlocklet(blocklet);
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
const { removeSync, outputJsonSync, readFileSync } = require('fs-extra');
|
|
2
2
|
const { isEmpty, cloneDeep } = require('lodash');
|
|
3
3
|
const { join } = require('path');
|
|
4
|
+
const { Hasher } = require('@ocap/mcrypto');
|
|
5
|
+
const { toBuffer } = require('@ocap/util');
|
|
4
6
|
const security = require('@abtnode/util/lib/security');
|
|
5
|
-
const { BLOCKLET_CONFIGURABLE_KEY } = require('@blocklet/constant');
|
|
6
7
|
const states = require('../../../states');
|
|
7
8
|
const { BaseBackup } = require('./base');
|
|
8
9
|
|
|
@@ -32,7 +33,7 @@ class BlockletExtrasBackup extends BaseBackup {
|
|
|
32
33
|
throw new Error('blockletExtra cannot be empty');
|
|
33
34
|
}
|
|
34
35
|
|
|
35
|
-
return this.
|
|
36
|
+
return this.cleanData(blockletExtra);
|
|
36
37
|
}
|
|
37
38
|
|
|
38
39
|
/**
|
|
@@ -42,7 +43,7 @@ class BlockletExtrasBackup extends BaseBackup {
|
|
|
42
43
|
* @return {Promise<void>}
|
|
43
44
|
* @memberof BlockletExtrasBackup
|
|
44
45
|
*/
|
|
45
|
-
async
|
|
46
|
+
async cleanData(blockletExtraInput) {
|
|
46
47
|
const blockletExtra = cloneDeep(blockletExtraInput);
|
|
47
48
|
|
|
48
49
|
const queue = [blockletExtra];
|
|
@@ -57,7 +58,7 @@ class BlockletExtrasBackup extends BaseBackup {
|
|
|
57
58
|
}
|
|
58
59
|
|
|
59
60
|
// 加解密
|
|
60
|
-
this.
|
|
61
|
+
this.encryptBlockletConfigs(currentBlockletExtra.configs);
|
|
61
62
|
|
|
62
63
|
if (currentBlockletExtra?.children) {
|
|
63
64
|
queue.push(...currentBlockletExtra.children);
|
|
@@ -74,33 +75,28 @@ class BlockletExtrasBackup extends BaseBackup {
|
|
|
74
75
|
* @return {void}
|
|
75
76
|
* @memberof BlockletExtrasBackup
|
|
76
77
|
*/
|
|
77
|
-
|
|
78
|
+
encryptBlockletConfigs(configs) {
|
|
78
79
|
if (isEmpty(configs)) {
|
|
79
80
|
return;
|
|
80
81
|
}
|
|
81
82
|
|
|
83
|
+
const { secretKey, address } = this.blockletWallet;
|
|
84
|
+
|
|
82
85
|
// 准备加解密所需的参数
|
|
83
86
|
// @see: https://github.com/ArcBlock/blocklet-server/blob/f561ba7290285f2e23dccb6d5323eb4d43c3cc3e/core/state/lib/index.js#L59
|
|
84
|
-
const
|
|
87
|
+
const dk = readFileSync(join(this.serverDataDir, '.sock'));
|
|
88
|
+
const ek = toBuffer(Hasher.SHA3.hash256(Buffer.concat([secretKey, address].map(toBuffer))));
|
|
85
89
|
|
|
86
90
|
for (const config of configs) {
|
|
87
|
-
|
|
88
|
-
if (config.key === BLOCKLET_CONFIGURABLE_KEY.BLOCKLET_APP_SK) {
|
|
89
|
-
config.value = '';
|
|
90
|
-
} else if (config.secure) {
|
|
91
|
+
if (config.secure) {
|
|
91
92
|
// secure 为 true 的配置才需要被加密保存上次到 did spaces
|
|
92
|
-
|
|
93
93
|
const encryptByServer = config.value;
|
|
94
94
|
// 先从 blocklet server 解密
|
|
95
95
|
// @see https://github.com/ArcBlock/blocklet-server/blob/f40338168a66893f325464cea79ae54c43f623b1/core/state/lib/blocklet/extras.js#L139
|
|
96
|
-
const
|
|
97
|
-
// 再用 blocklet
|
|
98
|
-
const
|
|
99
|
-
|
|
100
|
-
this.blockletWallet.address,
|
|
101
|
-
Buffer.from(this.blockletWallet.secretKey)
|
|
102
|
-
);
|
|
103
|
-
config.value = encryptByBlocklet;
|
|
96
|
+
const decrypted = security.decrypt(encryptByServer, this.blocklet.meta.did, dk);
|
|
97
|
+
// 再用 blocklet 信息加密,然后才可以上传到 spaces
|
|
98
|
+
const encrypted = security.encrypt(decrypted, address, ek);
|
|
99
|
+
config.value = encrypted;
|
|
104
100
|
}
|
|
105
101
|
}
|
|
106
102
|
}
|
|
@@ -1,16 +1,22 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @typedef {{
|
|
3
|
-
* did
|
|
3
|
+
* did: string
|
|
4
|
+
* event: import('events').EventEmitter,
|
|
5
|
+
* userDid: string,
|
|
6
|
+
* referrer: string,
|
|
4
7
|
* }} SpaceBackupInput
|
|
5
8
|
*/
|
|
6
9
|
|
|
7
10
|
const { isValid } = require('@arcblock/did');
|
|
8
|
-
const { BLOCKLET_CONFIGURABLE_KEY } = require('@blocklet/constant');
|
|
11
|
+
const { BLOCKLET_CONFIGURABLE_KEY, BlockletEvents } = require('@blocklet/constant');
|
|
9
12
|
const { getBlockletInfo } = require('@blocklet/meta');
|
|
10
|
-
const { SpaceClient,
|
|
13
|
+
const { SpaceClient, BackupBlockletCommand } = require('@did-space/client');
|
|
11
14
|
const { ensureDirSync } = require('fs-extra');
|
|
12
15
|
const { isEmpty } = require('lodash');
|
|
13
|
-
const { join } = require('path');
|
|
16
|
+
const { join, basename } = require('path');
|
|
17
|
+
|
|
18
|
+
const logger = require('@abtnode/logger')('@abtnode/core:storage:backup');
|
|
19
|
+
|
|
14
20
|
const states = require('../../../states');
|
|
15
21
|
const { AuditLogBackup } = require('./audit-log');
|
|
16
22
|
const { BlockletBackup } = require('./blocklet');
|
|
@@ -131,12 +137,22 @@ class SpacesBackup {
|
|
|
131
137
|
}
|
|
132
138
|
|
|
133
139
|
async export() {
|
|
140
|
+
this.input.event.emit(BlockletEvents.backupProgress, {
|
|
141
|
+
did: this.input.did,
|
|
142
|
+
message: 'Preparing data for backup...',
|
|
143
|
+
progress: 15,
|
|
144
|
+
});
|
|
134
145
|
await Promise.all(
|
|
135
146
|
this.storages.map((storage) => {
|
|
136
147
|
storage.ensureParams(this);
|
|
137
148
|
return storage.export();
|
|
138
149
|
})
|
|
139
150
|
);
|
|
151
|
+
this.input.event.emit(BlockletEvents.backupProgress, {
|
|
152
|
+
did: this.input.did,
|
|
153
|
+
message: 'Data ready, start backup...',
|
|
154
|
+
progress: 20,
|
|
155
|
+
});
|
|
140
156
|
}
|
|
141
157
|
|
|
142
158
|
async syncToSpaces() {
|
|
@@ -146,22 +162,34 @@ class SpacesBackup {
|
|
|
146
162
|
wallet,
|
|
147
163
|
});
|
|
148
164
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
165
|
+
const { errorCount, message } = await spaceClient.send(
|
|
166
|
+
new BackupBlockletCommand({
|
|
167
|
+
appDid: this.blocklet.appDid,
|
|
152
168
|
source: join(this.blockletBackupDir, '/'),
|
|
153
|
-
target: join('.did-objects', this.blocklet.appDid, '/'),
|
|
154
169
|
debug: true,
|
|
155
170
|
concurrency: 32,
|
|
156
171
|
retryCount: 10,
|
|
157
172
|
filter: (object) => {
|
|
173
|
+
// FIXME: @yejianchao 这里需要更完整的黑名单
|
|
158
174
|
return object.name !== '.DS_Store';
|
|
159
175
|
},
|
|
176
|
+
onProgress: (data) => {
|
|
177
|
+
logger.info('backup progress', { appDid: this.input.did, data });
|
|
178
|
+
const percent = (data.completed * 100) / data.total;
|
|
179
|
+
this.input.event.emit(BlockletEvents.backupProgress, {
|
|
180
|
+
did: this.input.did,
|
|
181
|
+
message: `Uploaded file ${basename(data.key)} (${data.completed}/${data.total})`,
|
|
182
|
+
// 0.8 是因为上传文件到 spaces 占进度的 80%,+ 20 是因为需要累加之前的进度
|
|
183
|
+
progress: +Math.ceil(percent * 0.8).toFixed(2) + 20,
|
|
184
|
+
});
|
|
185
|
+
},
|
|
186
|
+
userDid: this.input.userDid,
|
|
187
|
+
referrer: this.input.referrer,
|
|
160
188
|
})
|
|
161
189
|
);
|
|
162
190
|
|
|
163
191
|
if (errorCount !== 0) {
|
|
164
|
-
throw new Error(`Sync to spaces encountered ${errorCount} error`);
|
|
192
|
+
throw new Error(`Sync to spaces encountered ${errorCount} error: ${message}`);
|
|
165
193
|
}
|
|
166
194
|
}
|
|
167
195
|
|
|
@@ -13,14 +13,6 @@ class BaseRestore {
|
|
|
13
13
|
*/
|
|
14
14
|
blockletRestoreDir;
|
|
15
15
|
|
|
16
|
-
/**
|
|
17
|
-
*
|
|
18
|
-
* @description spaces 的 endpoint
|
|
19
|
-
* @type {import('@ocap/wallet').WalletObject}
|
|
20
|
-
* @memberof BaseRestore
|
|
21
|
-
*/
|
|
22
|
-
blockletWallet;
|
|
23
|
-
|
|
24
16
|
/**
|
|
25
17
|
*
|
|
26
18
|
* @description server 的数据目录
|
|
@@ -41,13 +33,22 @@ class BaseRestore {
|
|
|
41
33
|
*/
|
|
42
34
|
ensureParams(spaces) {
|
|
43
35
|
this.blockletRestoreDir = spaces.blockletRestoreDir;
|
|
44
|
-
this.blockletWallet = spaces.blockletWallet;
|
|
45
36
|
this.serverDataDir = spaces.serverDataDir;
|
|
46
37
|
}
|
|
47
38
|
|
|
48
39
|
async import() {
|
|
49
40
|
throw new Error('not implemented');
|
|
50
41
|
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Generate params for BlockletManager to install
|
|
45
|
+
*
|
|
46
|
+
* @return {object}
|
|
47
|
+
* @memberof BaseRestore
|
|
48
|
+
*/
|
|
49
|
+
getInstallParams() {
|
|
50
|
+
return {};
|
|
51
|
+
}
|
|
51
52
|
}
|
|
52
53
|
|
|
53
54
|
module.exports = {
|
|
@@ -2,6 +2,7 @@ const { removeSync, outputJsonSync, readJSONSync } = require('fs-extra');
|
|
|
2
2
|
const { cloneDeep, isArray } = require('lodash');
|
|
3
3
|
const { join } = require('path');
|
|
4
4
|
const security = require('@abtnode/util/lib/security');
|
|
5
|
+
const { BLOCKLET_CONFIGURABLE_KEY } = require('@blocklet/constant');
|
|
5
6
|
const { BaseRestore } = require('./base');
|
|
6
7
|
|
|
7
8
|
class BlockletExtrasRestore extends BaseRestore {
|
|
@@ -24,7 +25,6 @@ class BlockletExtrasRestore extends BaseRestore {
|
|
|
24
25
|
* @type {import('@abtnode/client').BlockletState}
|
|
25
26
|
*/
|
|
26
27
|
const blockletExtra = readJSONSync(join(this.blockletRestoreDir, this.filename));
|
|
27
|
-
|
|
28
28
|
return this.cleanData(blockletExtra);
|
|
29
29
|
}
|
|
30
30
|
|
|
@@ -32,7 +32,6 @@ class BlockletExtrasRestore extends BaseRestore {
|
|
|
32
32
|
*
|
|
33
33
|
* @description 清理数据并加密
|
|
34
34
|
* @param {import('@abtnode/client').BlockletState} blockletExtraInput
|
|
35
|
-
* @return {Promise<void>}
|
|
36
35
|
* @memberof BlockletExtrasRestore
|
|
37
36
|
*/
|
|
38
37
|
async cleanData(blockletExtraInput) {
|
|
@@ -43,7 +42,7 @@ class BlockletExtrasRestore extends BaseRestore {
|
|
|
43
42
|
const currentBlockletExtra = queue.pop();
|
|
44
43
|
|
|
45
44
|
// 加解密
|
|
46
|
-
this.
|
|
45
|
+
this.decrypt(currentBlockletExtra.configs);
|
|
47
46
|
|
|
48
47
|
if (currentBlockletExtra?.children) {
|
|
49
48
|
queue.push(...currentBlockletExtra.children);
|
|
@@ -55,31 +54,37 @@ class BlockletExtrasRestore extends BaseRestore {
|
|
|
55
54
|
|
|
56
55
|
/**
|
|
57
56
|
*
|
|
58
|
-
* @description
|
|
57
|
+
* @description 解密加密的数据
|
|
59
58
|
* @param {import('@abtnode/client').ConfigEntry[]} configs
|
|
60
59
|
* @return {void}
|
|
61
60
|
* @memberof BlockletExtrasRestore
|
|
62
61
|
*/
|
|
63
|
-
|
|
62
|
+
decrypt(configs) {
|
|
64
63
|
if (!configs || !isArray(configs)) {
|
|
65
64
|
return;
|
|
66
65
|
}
|
|
67
66
|
|
|
67
|
+
const { appDid, password } = this.input;
|
|
68
68
|
for (const config of configs) {
|
|
69
|
-
// secure 为 true 的配置才需要被加密保存上次到 did spaces
|
|
70
69
|
if (config.secure) {
|
|
71
|
-
const
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
const decryptByBlocklet = security.decrypt(
|
|
75
|
-
encryptByBlocklet,
|
|
76
|
-
this.blockletWallet.address,
|
|
77
|
-
Buffer.from(this.blockletWallet.secretKey)
|
|
78
|
-
);
|
|
79
|
-
|
|
80
|
-
config.value = decryptByBlocklet;
|
|
70
|
+
const encrypted = config.value;
|
|
71
|
+
const decrypted = security.decrypt(encrypted, appDid, Buffer.from(password));
|
|
72
|
+
config.value = decrypted;
|
|
81
73
|
}
|
|
82
74
|
}
|
|
75
|
+
|
|
76
|
+
const config = configs.find((x) => x.key === BLOCKLET_CONFIGURABLE_KEY.BLOCKLET_APP_SK);
|
|
77
|
+
if (!config) {
|
|
78
|
+
throw new Error(`Invalid blocklet backup file, no ${BLOCKLET_CONFIGURABLE_KEY.BLOCKLET_APP_SK} found`);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
this.appSk = config.value;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
getInstallParams() {
|
|
85
|
+
return {
|
|
86
|
+
appSk: this.appSk,
|
|
87
|
+
};
|
|
83
88
|
}
|
|
84
89
|
}
|
|
85
90
|
|
|
@@ -1,16 +1,24 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @typedef {{
|
|
3
|
+
* appDid: string;
|
|
3
4
|
* endpoint: string;
|
|
4
|
-
*
|
|
5
|
+
* password: Buffer;
|
|
6
|
+
* delegation: string;
|
|
7
|
+
* wallet: import('@ocap/wallet').WalletObject;
|
|
8
|
+
* event: import('events').EventEmitter,
|
|
9
|
+
* userDid: string,
|
|
10
|
+
* referrer: string,
|
|
5
11
|
* }} SpaceRestoreInput
|
|
6
12
|
*/
|
|
7
13
|
|
|
8
|
-
const { SpaceClient, SyncFolderPullCommand } = require('@did-space/client');
|
|
9
|
-
const { types } = require('@ocap/mcrypto');
|
|
10
|
-
const { fromSecretKey, WalletType } = require('@ocap/wallet');
|
|
11
|
-
const { ensureDirSync, existsSync, rmdirSync } = require('fs-extra');
|
|
12
|
-
const { join } = require('path');
|
|
13
14
|
const validUrl = require('valid-url');
|
|
15
|
+
const { BlockletEvents } = require('@blocklet/constant');
|
|
16
|
+
const { SpaceClient, RestoreBlockletCommand } = require('@did-space/client');
|
|
17
|
+
const { ensureDirSync, existsSync, rmdirSync } = require('fs-extra');
|
|
18
|
+
const { join, basename } = require('path');
|
|
19
|
+
|
|
20
|
+
const logger = require('@abtnode/logger')('@abtnode/core:storage:restore');
|
|
21
|
+
|
|
14
22
|
const { BlockletExtrasRestore } = require('./blocklet-extras');
|
|
15
23
|
const { BlockletsRestore } = require('./blocklets');
|
|
16
24
|
|
|
@@ -37,14 +45,6 @@ class SpacesRestore {
|
|
|
37
45
|
*/
|
|
38
46
|
serverDataDir;
|
|
39
47
|
|
|
40
|
-
/**
|
|
41
|
-
*
|
|
42
|
-
* @description spaces 的 endpoint
|
|
43
|
-
* @type {import('@ocap/wallet').WalletObject}
|
|
44
|
-
* @memberof SpacesRestore
|
|
45
|
-
*/
|
|
46
|
-
blockletWallet;
|
|
47
|
-
|
|
48
48
|
storages;
|
|
49
49
|
|
|
50
50
|
/**
|
|
@@ -72,68 +72,75 @@ class SpacesRestore {
|
|
|
72
72
|
|
|
73
73
|
async initialize() {
|
|
74
74
|
this.serverDataDir = process.env.ABT_NODE_DATA_DIR;
|
|
75
|
-
this.blockletWallet = await this.getBlockletWallet();
|
|
76
75
|
|
|
77
|
-
if (!this.input.endpoint.includes(this.
|
|
78
|
-
throw new Error(`endpoint and blocklet.appDid(${this.
|
|
76
|
+
if (!this.input.endpoint.includes(this.input.appDid)) {
|
|
77
|
+
throw new Error(`endpoint and blocklet.appDid(${this.input.appDid}) do not match`);
|
|
79
78
|
}
|
|
80
79
|
|
|
81
|
-
this.blockletRestoreDir = join(process.env.ABT_NODE_DATA_DIR, 'tmp/restore', this.
|
|
80
|
+
this.blockletRestoreDir = join(process.env.ABT_NODE_DATA_DIR, 'tmp/restore', this.input.appDid);
|
|
82
81
|
if (existsSync(this.blockletRestoreDir)) {
|
|
83
82
|
rmdirSync(this.blockletRestoreDir, { recursive: true });
|
|
84
83
|
}
|
|
85
84
|
ensureDirSync(this.blockletRestoreDir);
|
|
86
85
|
}
|
|
87
86
|
|
|
88
|
-
/**
|
|
89
|
-
*
|
|
90
|
-
* @returns {Promise<void>}
|
|
91
|
-
* @memberof SpacesRestore
|
|
92
|
-
*/
|
|
93
87
|
async restore() {
|
|
94
88
|
await this.initialize();
|
|
95
89
|
await this.syncFromSpaces();
|
|
96
90
|
await this.import();
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
async getBlockletWallet() {
|
|
100
|
-
// @FIXME: blocklet 钱包类型如何得知呢?
|
|
101
|
-
const wallet = fromSecretKey(this.input.appSk, WalletType({ role: types.RoleType.ROLE_APPLICATION }));
|
|
102
91
|
|
|
103
|
-
return
|
|
92
|
+
return this.storages.map((x) => x.getInstallParams());
|
|
104
93
|
}
|
|
105
94
|
|
|
106
95
|
async syncFromSpaces() {
|
|
107
|
-
const { endpoint } = this.input;
|
|
108
|
-
const wallet = await this.getBlockletWallet();
|
|
96
|
+
const { endpoint, wallet, delegation } = this.input;
|
|
109
97
|
|
|
110
98
|
const spaceClient = new SpaceClient({
|
|
111
99
|
endpoint,
|
|
100
|
+
delegation,
|
|
112
101
|
wallet,
|
|
113
102
|
});
|
|
114
103
|
|
|
115
|
-
const { errorCount } = await spaceClient.send(
|
|
116
|
-
new
|
|
117
|
-
|
|
104
|
+
const { errorCount, message } = await spaceClient.send(
|
|
105
|
+
new RestoreBlockletCommand({
|
|
106
|
+
appDid: this.input.appDid,
|
|
118
107
|
target: join(this.blockletRestoreDir, '/'),
|
|
119
108
|
debug: true,
|
|
120
109
|
concurrency: 32,
|
|
121
110
|
retryCount: 10,
|
|
111
|
+
onProgress: (data) => {
|
|
112
|
+
logger.info('restore progress', { appDid: this.input.appDid, data });
|
|
113
|
+
this.input.event.emit(BlockletEvents.restoreProgress, {
|
|
114
|
+
did: this.input.appDid,
|
|
115
|
+
message: `Downloaded file ${basename(data.key)} (${data.completed}/${data.total})`,
|
|
116
|
+
});
|
|
117
|
+
},
|
|
118
|
+
|
|
119
|
+
userDid: this.input.userDid,
|
|
120
|
+
referrer: this.input.referrer,
|
|
122
121
|
})
|
|
123
122
|
);
|
|
124
123
|
|
|
125
124
|
if (errorCount !== 0) {
|
|
126
|
-
throw new Error(`Sync from spaces encountered ${errorCount} error`);
|
|
125
|
+
throw new Error(`Sync from spaces encountered ${errorCount} error: ${message}`);
|
|
127
126
|
}
|
|
128
127
|
}
|
|
129
128
|
|
|
130
129
|
async import() {
|
|
130
|
+
this.input.event.emit(BlockletEvents.restoreProgress, {
|
|
131
|
+
did: this.input.appDid,
|
|
132
|
+
message: 'Preparing to import data...',
|
|
133
|
+
});
|
|
131
134
|
await Promise.all(
|
|
132
135
|
this.storages.map((storage) => {
|
|
133
136
|
storage.ensureParams(this);
|
|
134
137
|
return storage.import();
|
|
135
138
|
})
|
|
136
139
|
);
|
|
140
|
+
this.input.event.emit(BlockletEvents.restoreProgress, {
|
|
141
|
+
did: this.input.appDid,
|
|
142
|
+
message: 'Importing data successfully...',
|
|
143
|
+
});
|
|
137
144
|
}
|
|
138
145
|
}
|
|
139
146
|
|
package/lib/event.js
CHANGED
|
@@ -169,6 +169,12 @@ module.exports = ({
|
|
|
169
169
|
});
|
|
170
170
|
};
|
|
171
171
|
|
|
172
|
+
/**
|
|
173
|
+
*
|
|
174
|
+
* @description 事件必须注册在这里才能被发布出去
|
|
175
|
+
* @param {string} eventName
|
|
176
|
+
* @param {any} payload
|
|
177
|
+
*/
|
|
172
178
|
const handleBlockletEvent = async (eventName, payload) => {
|
|
173
179
|
const blocklet = payload.blocklet || payload;
|
|
174
180
|
|
|
@@ -273,6 +279,13 @@ module.exports = ({
|
|
|
273
279
|
}
|
|
274
280
|
};
|
|
275
281
|
|
|
282
|
+
/**
|
|
283
|
+
*
|
|
284
|
+
*
|
|
285
|
+
* @param {*} subject
|
|
286
|
+
* @param {string} event
|
|
287
|
+
* @param {(event: string, data: any) => Promise<void> | void} handler
|
|
288
|
+
*/
|
|
276
289
|
const listen = (subject, event, handler) => subject.on(event, (data) => handler(event, data));
|
|
277
290
|
|
|
278
291
|
[
|
|
@@ -291,6 +304,9 @@ module.exports = ({
|
|
|
291
304
|
BlockletEvents.startFailed,
|
|
292
305
|
BlockletEvents.stopped,
|
|
293
306
|
BlockletEvents.appDidChanged,
|
|
307
|
+
|
|
308
|
+
BlockletEvents.backupProgress,
|
|
309
|
+
BlockletEvents.restoreProgress,
|
|
294
310
|
].forEach((eventName) => {
|
|
295
311
|
listen(blockletManager, eventName, handleBlockletEvent);
|
|
296
312
|
});
|
package/lib/index.js
CHANGED
|
@@ -26,7 +26,7 @@ const Maintain = require('./util/maintain');
|
|
|
26
26
|
const resetNode = require('./util/reset-node');
|
|
27
27
|
const DiskMonitor = require('./util/disk-monitor');
|
|
28
28
|
const StoreUtil = require('./util/store');
|
|
29
|
-
const createQueue = require('./queue');
|
|
29
|
+
const createQueue = require('./util/queue');
|
|
30
30
|
const createEvents = require('./event');
|
|
31
31
|
const pm2Events = require('./blocklet/manager/pm2-events');
|
|
32
32
|
const { createStateReadyQueue, createStateReadyHandler } = require('./util/ready');
|
|
@@ -99,7 +99,25 @@ function ABTNode(options) {
|
|
|
99
99
|
concurrency,
|
|
100
100
|
maxRetries: 3,
|
|
101
101
|
retryDelay: 5000, // retry after 5 seconds
|
|
102
|
-
maxTimeout: 60 * 1000 * 15, // throw timeout error after
|
|
102
|
+
maxTimeout: 60 * 1000 * 15, // throw timeout error after 15 minutes
|
|
103
|
+
id: (job) => (job ? md5(`${job.entity}-${job.action}-${job.id}`) : ''),
|
|
104
|
+
},
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
const backupQueue = createQueue({
|
|
108
|
+
daemon: options.daemon,
|
|
109
|
+
name: 'backup_queue',
|
|
110
|
+
dataDir: dataDirs.core,
|
|
111
|
+
onJob: async (job) => {
|
|
112
|
+
if (typeof blockletManager.onJob === 'function') {
|
|
113
|
+
await blockletManager.onJob(job);
|
|
114
|
+
}
|
|
115
|
+
},
|
|
116
|
+
options: {
|
|
117
|
+
concurrency,
|
|
118
|
+
maxRetries: 3,
|
|
119
|
+
retryDelay: 10000, // retry after 10 seconds
|
|
120
|
+
maxTimeout: 60 * 1000 * 30, // throw timeout error after 30 minutes
|
|
103
121
|
id: (job) => (job ? md5(`${job.entity}-${job.action}-${job.id}`) : ''),
|
|
104
122
|
},
|
|
105
123
|
});
|
|
@@ -129,6 +147,7 @@ function ABTNode(options) {
|
|
|
129
147
|
dataDirs,
|
|
130
148
|
startQueue,
|
|
131
149
|
installQueue,
|
|
150
|
+
backupQueue,
|
|
132
151
|
daemon: options.daemon,
|
|
133
152
|
teamManager,
|
|
134
153
|
});
|
package/lib/states/blocklet.js
CHANGED
|
@@ -28,6 +28,7 @@ const lock = new Lock('blocklet-port-assign-lock');
|
|
|
28
28
|
|
|
29
29
|
const isHex = (str) => /^0x[0-9a-f]+$/i.test(str);
|
|
30
30
|
const getMaxPort = (ports = {}) => Math.max(Object.values(ports).map(Number));
|
|
31
|
+
const getConditions = (did) => [{ 'meta.did': did }, { appDid: did }, { appPid: did }];
|
|
31
32
|
|
|
32
33
|
const getExternalPortsFromMeta = (meta) =>
|
|
33
34
|
(meta.interfaces || []).map((x) => x.port && x.port.external).filter(Boolean);
|
|
@@ -48,7 +49,16 @@ const formatBlocklet = (blocklet, phase, dek) => {
|
|
|
48
49
|
return;
|
|
49
50
|
}
|
|
50
51
|
|
|
51
|
-
[
|
|
52
|
+
(b.migratedFrom || []).forEach((x) => {
|
|
53
|
+
if (phase === 'onUpdate' && isHex(x.appSk) === true) {
|
|
54
|
+
x.appSk = security.encrypt(x.appSk, b.meta.did, dek);
|
|
55
|
+
}
|
|
56
|
+
if (phase === 'onRead' && isHex(x.appSk) === false) {
|
|
57
|
+
x.appSk = security.decrypt(x.appSk, b.meta.did, dek);
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
['BLOCKLET_APP_SK', 'BLOCKLET_APP_PSK'].forEach((key) => {
|
|
52
62
|
const env = b.environments.find((x) => x.key === key);
|
|
53
63
|
if (!env) {
|
|
54
64
|
return;
|
|
@@ -98,7 +108,7 @@ class BlockletState extends BaseState {
|
|
|
98
108
|
resolve(null);
|
|
99
109
|
}
|
|
100
110
|
|
|
101
|
-
this.findOne({ $or:
|
|
111
|
+
this.findOne({ $or: getConditions(did) }, (err, doc) => {
|
|
102
112
|
if (err) {
|
|
103
113
|
return reject(err);
|
|
104
114
|
}
|
|
@@ -114,7 +124,7 @@ class BlockletState extends BaseState {
|
|
|
114
124
|
resolve(null);
|
|
115
125
|
}
|
|
116
126
|
|
|
117
|
-
this.findOne({ $or:
|
|
127
|
+
this.findOne({ $or: getConditions(did) }, (err, doc) => {
|
|
118
128
|
if (err) {
|
|
119
129
|
return reject(err);
|
|
120
130
|
}
|
|
@@ -130,7 +140,7 @@ class BlockletState extends BaseState {
|
|
|
130
140
|
resolve(null);
|
|
131
141
|
}
|
|
132
142
|
|
|
133
|
-
this.findOne({ $or:
|
|
143
|
+
this.findOne({ $or: getConditions(did) }, { status: 1 }, (err, doc) => {
|
|
134
144
|
if (err) {
|
|
135
145
|
return reject(err);
|
|
136
146
|
}
|
|
@@ -146,7 +156,7 @@ class BlockletState extends BaseState {
|
|
|
146
156
|
resolve(false);
|
|
147
157
|
}
|
|
148
158
|
|
|
149
|
-
this.count({ $or:
|
|
159
|
+
this.count({ $or: getConditions(did) }, (err, count) => {
|
|
150
160
|
if (err) {
|
|
151
161
|
return reject(err);
|
|
152
162
|
}
|
|
@@ -199,6 +209,9 @@ class BlockletState extends BaseState {
|
|
|
199
209
|
deployedFrom = '',
|
|
200
210
|
mode = BLOCKLET_MODES.PRODUCTION,
|
|
201
211
|
children: rawChildren = [],
|
|
212
|
+
appPid = null, // the permanent appDid, which will not change after initial set
|
|
213
|
+
migratedFrom = [], // the complete migrate history
|
|
214
|
+
externalSk = false, // whether sk is managed by some party beside server, such as did-wallet
|
|
202
215
|
} = {}) {
|
|
203
216
|
return this.getBlocklet(meta.did).then(
|
|
204
217
|
(exist) =>
|
|
@@ -229,6 +242,7 @@ class BlockletState extends BaseState {
|
|
|
229
242
|
|
|
230
243
|
const data = {
|
|
231
244
|
appDid: null, // will updated later when updating blocklet environments
|
|
245
|
+
appPid,
|
|
232
246
|
mode,
|
|
233
247
|
meta: sanitized,
|
|
234
248
|
status,
|
|
@@ -237,6 +251,8 @@ class BlockletState extends BaseState {
|
|
|
237
251
|
ports,
|
|
238
252
|
environments: [],
|
|
239
253
|
children,
|
|
254
|
+
migratedFrom,
|
|
255
|
+
externalSk,
|
|
240
256
|
};
|
|
241
257
|
|
|
242
258
|
// add to db
|
package/lib/util/blocklet.js
CHANGED
|
@@ -305,10 +305,16 @@ const getAppSystemEnvironments = (blocklet, nodeInfo) => {
|
|
|
305
305
|
})}`;
|
|
306
306
|
}
|
|
307
307
|
|
|
308
|
+
const isMigrated = Array.isArray(blocklet.migratedFrom) && blocklet.migratedFrom.length > 0;
|
|
309
|
+
const appPid = blocklet.appPid || appId;
|
|
310
|
+
const appPsk = isMigrated ? blocklet.migratedFrom[0].appSk : appSk;
|
|
311
|
+
|
|
308
312
|
return {
|
|
309
313
|
BLOCKLET_DID: did,
|
|
310
314
|
BLOCKLET_APP_SK: appSk,
|
|
311
315
|
BLOCKLET_APP_ID: appId,
|
|
316
|
+
BLOCKLET_APP_PSK: appPsk, // permanent sk even the blocklet has been migrated
|
|
317
|
+
BLOCKLET_APP_PID: appPid, // permanent did even the blocklet has been migrated
|
|
312
318
|
BLOCKLET_APP_NAME: appName,
|
|
313
319
|
BLOCKLET_APP_DESCRIPTION: appDescription,
|
|
314
320
|
BLOCKLET_APP_URL: appUrl,
|
|
@@ -1555,11 +1561,63 @@ const createDataArchive = (dataDir, fileName) => {
|
|
|
1555
1561
|
});
|
|
1556
1562
|
};
|
|
1557
1563
|
|
|
1564
|
+
const isBlockletAppSkUsed = ({ environments, migratedFrom = [] }, appSk) => {
|
|
1565
|
+
const isUsedInEnv = environments.find((e) => e.key === BLOCKLET_CONFIGURABLE_KEY.BLOCKLET_APP_SK)?.value === appSk;
|
|
1566
|
+
const isUsedInHistory = migratedFrom.some((x) => x.appSk === appSk);
|
|
1567
|
+
return isUsedInEnv || isUsedInHistory;
|
|
1568
|
+
};
|
|
1569
|
+
|
|
1570
|
+
const isRotatingAppSk = (newConfigs, oldConfigs, externalSk) => {
|
|
1571
|
+
const newSk = newConfigs.find((x) => BLOCKLET_CONFIGURABLE_KEY.BLOCKLET_APP_SK === x.key);
|
|
1572
|
+
if (!newSk) {
|
|
1573
|
+
// If no newSk found, we are not rotating the appSk
|
|
1574
|
+
return false;
|
|
1575
|
+
}
|
|
1576
|
+
|
|
1577
|
+
const oldSk = oldConfigs.find((x) => BLOCKLET_CONFIGURABLE_KEY.BLOCKLET_APP_SK === x.key);
|
|
1578
|
+
if (!oldSk) {
|
|
1579
|
+
// If we have no oldSk, we are setting the initial appSk for external managed apps
|
|
1580
|
+
// If we have no oldSk, but we are not external managed apps, we are rotating the appSk
|
|
1581
|
+
return !externalSk;
|
|
1582
|
+
}
|
|
1583
|
+
|
|
1584
|
+
// Otherwise, we must be rotating the appSk
|
|
1585
|
+
// eslint-disable-next-line sonarjs/prefer-single-boolean-return
|
|
1586
|
+
if (oldSk.value !== newSk.value) {
|
|
1587
|
+
return true;
|
|
1588
|
+
}
|
|
1589
|
+
|
|
1590
|
+
return false;
|
|
1591
|
+
};
|
|
1592
|
+
|
|
1593
|
+
const isRotatingAppDid = (newConfigs, oldConfigs, externalSk) => {
|
|
1594
|
+
if (isRotatingAppSk(newConfigs, oldConfigs, externalSk)) {
|
|
1595
|
+
return true;
|
|
1596
|
+
}
|
|
1597
|
+
|
|
1598
|
+
const newType = newConfigs.find((x) => BLOCKLET_CONFIGURABLE_KEY.BLOCKLET_WALLET_TYPE === x.key);
|
|
1599
|
+
const oldType = oldConfigs.find((x) => BLOCKLET_CONFIGURABLE_KEY.BLOCKLET_WALLET_TYPE === x.key);
|
|
1600
|
+
if (!newType) {
|
|
1601
|
+
return false;
|
|
1602
|
+
}
|
|
1603
|
+
|
|
1604
|
+
if (!oldType) {
|
|
1605
|
+
return true;
|
|
1606
|
+
}
|
|
1607
|
+
|
|
1608
|
+
// eslint-disable-next-line sonarjs/prefer-single-boolean-return
|
|
1609
|
+
if (oldType !== newType) {
|
|
1610
|
+
return true;
|
|
1611
|
+
}
|
|
1612
|
+
|
|
1613
|
+
return false;
|
|
1614
|
+
};
|
|
1615
|
+
|
|
1558
1616
|
/**
|
|
1559
1617
|
* this function has side effect on config.value
|
|
1560
1618
|
* @param {{ key: string, value?: string }} config
|
|
1561
1619
|
*/
|
|
1562
|
-
const validateAppConfig = async (config,
|
|
1620
|
+
const validateAppConfig = async (config, states) => {
|
|
1563
1621
|
const x = config;
|
|
1564
1622
|
|
|
1565
1623
|
// sk should be force secured while other app prop should not be secured
|
|
@@ -1577,15 +1635,10 @@ const validateAppConfig = async (config, blockletDid, states) => {
|
|
|
1577
1635
|
}
|
|
1578
1636
|
}
|
|
1579
1637
|
|
|
1580
|
-
// Ensure sk is not used by
|
|
1638
|
+
// Ensure sk is not used by existing blocklets, otherwise we may encounter appDid collision
|
|
1581
1639
|
const blocklets = await states.blocklet.getBlocklets({});
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
others.some(
|
|
1585
|
-
(b) => b.environments.find((e) => e.key === BLOCKLET_CONFIGURABLE_KEY.BLOCKLET_APP_SK).value === x.value
|
|
1586
|
-
)
|
|
1587
|
-
) {
|
|
1588
|
-
throw new Error('Invalid custom blocklet secret key: already used by another blocklet');
|
|
1640
|
+
if (blocklets.some((b) => isBlockletAppSkUsed(b, x.value))) {
|
|
1641
|
+
throw new Error('Invalid custom blocklet secret key: already used by existing blocklet');
|
|
1589
1642
|
}
|
|
1590
1643
|
} else {
|
|
1591
1644
|
delete x.value;
|
|
@@ -1769,6 +1822,9 @@ module.exports = {
|
|
|
1769
1822
|
getConfigFromPreferences,
|
|
1770
1823
|
createDataArchive,
|
|
1771
1824
|
validateAppConfig,
|
|
1825
|
+
isBlockletAppSkUsed,
|
|
1826
|
+
isRotatingAppSk,
|
|
1827
|
+
isRotatingAppDid,
|
|
1772
1828
|
checkDuplicateAppSk,
|
|
1773
1829
|
checkDuplicateMountPoint,
|
|
1774
1830
|
validateStore,
|
package/lib/webhook/index.js
CHANGED
|
@@ -5,7 +5,7 @@ const checkURLAccessible = require('@abtnode/util/lib/url-evaluation/check-acces
|
|
|
5
5
|
const { EVENTS } = require('@abtnode/constant');
|
|
6
6
|
const WebHookSender = require('./sender');
|
|
7
7
|
const WalletSender = require('./sender/wallet');
|
|
8
|
-
const createQueue = require('../queue');
|
|
8
|
+
const createQueue = require('../util/queue');
|
|
9
9
|
const IP = require('../util/ip');
|
|
10
10
|
const states = require('../states');
|
|
11
11
|
const { getBaseUrls } = require('../util');
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"publishConfig": {
|
|
4
4
|
"access": "public"
|
|
5
5
|
},
|
|
6
|
-
"version": "1.8.
|
|
6
|
+
"version": "1.8.68-beta-500af7e5",
|
|
7
7
|
"description": "",
|
|
8
8
|
"main": "lib/index.js",
|
|
9
9
|
"files": [
|
|
@@ -19,33 +19,33 @@
|
|
|
19
19
|
"author": "wangshijun <wangshijun2010@gmail.com> (http://github.com/wangshijun)",
|
|
20
20
|
"license": "MIT",
|
|
21
21
|
"dependencies": {
|
|
22
|
-
"@abtnode/auth": "1.8.
|
|
23
|
-
"@abtnode/certificate-manager": "1.8.
|
|
24
|
-
"@abtnode/constant": "1.8.
|
|
25
|
-
"@abtnode/cron": "1.8.
|
|
26
|
-
"@abtnode/db": "1.8.
|
|
27
|
-
"@abtnode/logger": "1.8.
|
|
28
|
-
"@abtnode/queue": "1.8.
|
|
29
|
-
"@abtnode/rbac": "1.8.
|
|
30
|
-
"@abtnode/router-provider": "1.8.
|
|
31
|
-
"@abtnode/static-server": "1.8.
|
|
32
|
-
"@abtnode/timemachine": "1.8.
|
|
33
|
-
"@abtnode/util": "1.8.
|
|
34
|
-
"@arcblock/did": "1.18.
|
|
22
|
+
"@abtnode/auth": "1.8.68-beta-500af7e5",
|
|
23
|
+
"@abtnode/certificate-manager": "1.8.68-beta-500af7e5",
|
|
24
|
+
"@abtnode/constant": "1.8.68-beta-500af7e5",
|
|
25
|
+
"@abtnode/cron": "1.8.68-beta-500af7e5",
|
|
26
|
+
"@abtnode/db": "1.8.68-beta-500af7e5",
|
|
27
|
+
"@abtnode/logger": "1.8.68-beta-500af7e5",
|
|
28
|
+
"@abtnode/queue": "1.8.68-beta-500af7e5",
|
|
29
|
+
"@abtnode/rbac": "1.8.68-beta-500af7e5",
|
|
30
|
+
"@abtnode/router-provider": "1.8.68-beta-500af7e5",
|
|
31
|
+
"@abtnode/static-server": "1.8.68-beta-500af7e5",
|
|
32
|
+
"@abtnode/timemachine": "1.8.68-beta-500af7e5",
|
|
33
|
+
"@abtnode/util": "1.8.68-beta-500af7e5",
|
|
34
|
+
"@arcblock/did": "1.18.57",
|
|
35
35
|
"@arcblock/did-motif": "^1.1.10",
|
|
36
|
-
"@arcblock/did-util": "1.18.
|
|
37
|
-
"@arcblock/event-hub": "1.18.
|
|
38
|
-
"@arcblock/jwt": "^1.18.
|
|
36
|
+
"@arcblock/did-util": "1.18.57",
|
|
37
|
+
"@arcblock/event-hub": "1.18.57",
|
|
38
|
+
"@arcblock/jwt": "^1.18.57",
|
|
39
39
|
"@arcblock/pm2-events": "^0.0.5",
|
|
40
|
-
"@arcblock/vc": "1.18.
|
|
41
|
-
"@blocklet/constant": "1.8.
|
|
42
|
-
"@blocklet/meta": "1.8.
|
|
43
|
-
"@blocklet/sdk": "1.8.
|
|
44
|
-
"@did-space/client": "
|
|
40
|
+
"@arcblock/vc": "1.18.57",
|
|
41
|
+
"@blocklet/constant": "1.8.68-beta-500af7e5",
|
|
42
|
+
"@blocklet/meta": "1.8.68-beta-500af7e5",
|
|
43
|
+
"@blocklet/sdk": "1.8.68-beta-500af7e5",
|
|
44
|
+
"@did-space/client": "0.1.87-beta-1",
|
|
45
45
|
"@fidm/x509": "^1.2.1",
|
|
46
|
-
"@ocap/mcrypto": "1.18.
|
|
47
|
-
"@ocap/util": "1.18.
|
|
48
|
-
"@ocap/wallet": "1.18.
|
|
46
|
+
"@ocap/mcrypto": "1.18.57",
|
|
47
|
+
"@ocap/util": "1.18.57",
|
|
48
|
+
"@ocap/wallet": "1.18.57",
|
|
49
49
|
"@slack/webhook": "^5.0.4",
|
|
50
50
|
"archiver": "^5.3.1",
|
|
51
51
|
"axios": "^0.27.2",
|
|
@@ -89,5 +89,5 @@
|
|
|
89
89
|
"express": "^4.18.2",
|
|
90
90
|
"jest": "^27.5.1"
|
|
91
91
|
},
|
|
92
|
-
"gitHead": "
|
|
92
|
+
"gitHead": "9070621373f317a10ff0d289323bf725e30d3521"
|
|
93
93
|
}
|
|
File without changes
|