@abtnode/core 1.8.68 → 1.8.69-beta-54faead3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,8 +1,8 @@
1
- const { removeSync, outputJsonSync, readFileSync } = require('fs-extra');
1
+ const { removeSync, readFileSync, outputJsonSync } = require('fs-extra');
2
2
  const { isEmpty, cloneDeep } = 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
6
  const states = require('../../../states');
7
7
  const { BaseBackup } = require('./base');
8
8
 
@@ -11,8 +11,8 @@ class BlockletExtrasBackup extends BaseBackup {
11
11
 
12
12
  async export() {
13
13
  const blockletExtra = await this.getBlockletExtra();
14
- removeSync(join(this.blockletBackupDir, this.filename));
15
- outputJsonSync(join(this.blockletBackupDir, this.filename), blockletExtra);
14
+ removeSync(join(this.backupDir, this.filename));
15
+ outputJsonSync(join(this.backupDir, this.filename), blockletExtra);
16
16
  }
17
17
 
18
18
  /**
@@ -32,35 +32,35 @@ class BlockletExtrasBackup extends BaseBackup {
32
32
  throw new Error('blockletExtra cannot be empty');
33
33
  }
34
34
 
35
- return this.cleanDate(blockletExtra);
35
+ return this.cleanData(blockletExtra);
36
36
  }
37
37
 
38
38
  /**
39
39
  *
40
40
  * @description 清理数据并加密
41
- * @param {import('@abtnode/client').BlockletState} blockletExtraInput
42
- * @return {Promise<void>}
41
+ * @param {import('@abtnode/client').BlockletState} raw
42
+ * @return {Promise<any>}
43
43
  * @memberof BlockletExtrasBackup
44
44
  */
45
- async cleanDate(blockletExtraInput) {
46
- const blockletExtra = cloneDeep(blockletExtraInput);
45
+ async cleanData(raw) {
46
+ const blockletExtra = cloneDeep(raw);
47
47
 
48
48
  const queue = [blockletExtra];
49
49
  while (queue.length) {
50
- const currentBlockletExtra = queue.pop();
50
+ const current = queue.pop();
51
51
 
52
52
  // 删除父 blocklet 的某些数据
53
- if (currentBlockletExtra._id) {
54
- delete currentBlockletExtra._id;
55
- delete currentBlockletExtra.createdAt;
56
- delete currentBlockletExtra.updatedAt;
53
+ if (current._id) {
54
+ delete current._id;
55
+ delete current.createdAt;
56
+ delete current.updatedAt;
57
57
  }
58
58
 
59
59
  // 加解密
60
- this.useBlockletEncryptConfigs(currentBlockletExtra.configs);
60
+ this.encrypt(current.configs);
61
61
 
62
- if (currentBlockletExtra?.children) {
63
- queue.push(...currentBlockletExtra.children);
62
+ if (current?.children) {
63
+ queue.push(...current.children);
64
64
  }
65
65
  }
66
66
 
@@ -74,33 +74,16 @@ class BlockletExtrasBackup extends BaseBackup {
74
74
  * @return {void}
75
75
  * @memberof BlockletExtrasBackup
76
76
  */
77
- useBlockletEncryptConfigs(configs) {
77
+ encrypt(configs) {
78
78
  if (isEmpty(configs)) {
79
79
  return;
80
80
  }
81
81
 
82
- // 准备加解密所需的参数
83
- // @see: https://github.com/ArcBlock/blocklet-server/blob/f561ba7290285f2e23dccb6d5323eb4d43c3cc3e/core/state/lib/index.js#L59
84
- const dek = readFileSync(join(this.serverDataDir, '.sock'));
85
-
82
+ const dk = readFileSync(join(this.serverDir, '.sock'));
86
83
  for (const config of configs) {
87
- // 置空 blocklet 的密钥,但是保留其他属性,这很重要
88
- if (config.key === BLOCKLET_CONFIGURABLE_KEY.BLOCKLET_APP_SK) {
89
- config.value = '';
90
- } else if (config.secure) {
91
- // secure 为 true 的配置才需要被加密保存上次到 did spaces
92
-
93
- const encryptByServer = config.value;
94
- // 先从 blocklet server 解密
95
- // @see https://github.com/ArcBlock/blocklet-server/blob/f40338168a66893f325464cea79ae54c43f623b1/core/state/lib/blocklet/extras.js#L139
96
- const decryptByServer = security.decrypt(encryptByServer, this.blocklet.meta.did, dek);
97
- // 再用 blocklet secret 加密,然后才可以上传到 spaces
98
- const encryptByBlocklet = security.encrypt(
99
- decryptByServer,
100
- this.blockletWallet.address,
101
- Buffer.from(this.blockletWallet.secretKey)
102
- );
103
- config.value = encryptByBlocklet;
84
+ if (config.secure) {
85
+ const decrypted = security.decrypt(config.value, this.blocklet.meta.did, dk);
86
+ config.value = this.securityContext.encrypt(decrypted);
104
87
  }
105
88
  }
106
89
  }
@@ -8,45 +8,62 @@ class BlockletBackup extends BaseBackup {
8
8
 
9
9
  async export() {
10
10
  const blocklet = await this.cleanData();
11
- removeSync(join(this.blockletBackupDir, this.filename));
12
- outputJsonSync(join(this.blockletBackupDir, this.filename), blocklet);
11
+ removeSync(join(this.backupDir, this.filename));
12
+ outputJsonSync(join(this.backupDir, this.filename), blocklet);
13
13
  }
14
14
 
15
15
  /**
16
16
  * @description 清理数据
17
17
  * @see blocklet.db 中需要删除哪些字段呢? https://github.com/ArcBlock/blocklet-server/issues/6120#issuecomment-1383798348
18
- * @return {Promise<void>}
18
+ * @return {Promise<import('@abtnode/client').BlockletState>}
19
19
  * @memberof BlockletBackup
20
20
  */
21
21
  async cleanData() {
22
- const originalBlocklet = cloneDeep(this.blocklet);
22
+ const clone = cloneDeep(this.blocklet);
23
23
 
24
24
  /** @type {import('@abtnode/client').ComponentState[]} */
25
- const queue = [originalBlocklet];
25
+ const queue = [clone];
26
26
 
27
27
  // 广度优先遍历
28
28
  while (queue.length) {
29
- const currentBlocklet = queue.pop();
29
+ const current = queue.pop();
30
30
 
31
31
  // 父组件才需要删除的属性
32
- if (currentBlocklet._id) {
33
- delete currentBlocklet._id;
34
- delete currentBlocklet.createdAt;
35
- delete currentBlocklet.startedAt;
36
- delete currentBlocklet.installedAt;
32
+ if (current._id) {
33
+ delete current._id;
34
+ delete current.createdAt;
35
+ delete current.startedAt;
36
+ delete current.installedAt;
37
37
  }
38
38
 
39
39
  // 子组件和父组件都需要删除的属性
40
- delete currentBlocklet.status;
41
- delete currentBlocklet.ports;
42
- delete currentBlocklet.environments;
40
+ delete current.status;
41
+ delete current.ports;
42
+ delete current.environments;
43
43
 
44
- if (currentBlocklet.children) {
45
- queue.push(...currentBlocklet.children);
44
+ if (current.children) {
45
+ queue.push(...current.children);
46
46
  }
47
47
  }
48
48
 
49
- return originalBlocklet;
49
+ return this.encrypt(clone);
50
+ }
51
+
52
+ /**
53
+ *
54
+ * @description 清理数据并加密
55
+ * @param {import('@abtnode/client').BlockletState} info
56
+ * @memberof BlockletExtrasBackup
57
+ */
58
+ encrypt(info) {
59
+ if (Array.isArray(info.migratedFrom)) {
60
+ info.migratedFrom = info.migratedFrom.map((x) => {
61
+ x.appSk = this.securityContext.encrypt(x.appSk);
62
+ return x;
63
+ });
64
+ }
65
+
66
+ return info;
50
67
  }
51
68
  }
52
69
 
@@ -12,7 +12,7 @@ class BlockletsBackup extends BaseBackup {
12
12
  */
13
13
  async export() {
14
14
  const blockletMetas = this.getBlockletMetas(this.blocklet);
15
- const serverBlockletsDir = join(this.serverDataDir, 'blocklets');
15
+ const serverBlockletsDir = join(this.serverDir, 'blocklets');
16
16
 
17
17
  const blockletMetasFromLocal = blockletMetas.filter(
18
18
  (b) => !validUrl.isHttpUri(b.tarball) && !validUrl.isHttpsUri(b.tarball)
@@ -21,7 +21,7 @@ class BlockletsBackup extends BaseBackup {
21
21
  const dirs = [];
22
22
  for (const blockletMeta of blockletMetasFromLocal) {
23
23
  const sourceDir = join(serverBlockletsDir, blockletMeta.name, blockletMeta.version);
24
- const zipPath = join(this.blockletBackupDir, 'blocklets', blockletMeta.name, `${blockletMeta.version}.zip`);
24
+ const zipPath = join(this.backupDir, 'blocklets', blockletMeta.name, `${blockletMeta.version}.zip`);
25
25
  dirs.push({ sourceDir, zipPath });
26
26
  }
27
27
 
@@ -9,8 +9,8 @@ class DataBackup extends BaseBackup {
9
9
  * @memberof BlockletsBackup
10
10
  */
11
11
  async export() {
12
- const blockletDataDir = join(this.serverDataDir, 'data', this.blocklet.meta.name);
13
- const blockletBackupDataDir = join(this.blockletBackupDir, 'data');
12
+ const blockletDataDir = join(this.serverDir, 'data', this.blocklet.meta.name);
13
+ const blockletBackupDataDir = join(this.backupDir, 'data');
14
14
 
15
15
  await copy(blockletDataDir, blockletBackupDataDir, { overwrite: true });
16
16
  }
@@ -5,10 +5,10 @@ const { BaseBackup } = require('./base');
5
5
  // note: 可以不需要备份
6
6
  class LogsBackup extends BaseBackup {
7
7
  async export() {
8
- const sourceLogsDir = join(this.serverDataDir, 'logs', this.blocklet.meta.name);
8
+ const sourceLogsDir = join(this.serverDir, 'logs', this.blocklet.meta.name);
9
9
  ensureDirSync(sourceLogsDir);
10
10
 
11
- const targetLogsDir = join(this.blockletBackupDir, 'logs');
11
+ const targetLogsDir = join(this.backupDir, 'logs');
12
12
 
13
13
  await copy(sourceLogsDir, targetLogsDir, {
14
14
  overwrite: true,
@@ -11,8 +11,8 @@ class RoutingRuleBackup extends BaseBackup {
11
11
  domain: `${this.blocklet.meta.did}.blocklet-domain-group`,
12
12
  });
13
13
 
14
- removeSync(join(this.blockletBackupDir, this.filename));
15
- outputJsonSync(join(this.blockletBackupDir, this.filename), routingRule);
14
+ removeSync(join(this.backupDir, this.filename));
15
+ outputJsonSync(join(this.backupDir, this.filename), routingRule);
16
16
  }
17
17
  }
18
18
 
@@ -1,16 +1,34 @@
1
1
  /**
2
2
  * @typedef {{
3
- * did?: string
3
+ * appPid: string
4
+ * event: import('events').EventEmitter,
5
+ * userDid: string,
6
+ * referrer: string,
4
7
  * }} SpaceBackupInput
8
+ *
9
+ * @typedef {{
10
+ * signer: import('@ocap/wallet').WalletObject
11
+ * encrypt: (v: string) => string,
12
+ * decrypt: (v: string) => string,
13
+ * delegation: string,
14
+ * }} SecurityContext
5
15
  */
6
16
 
7
17
  const { isValid } = require('@arcblock/did');
8
- const { BLOCKLET_CONFIGURABLE_KEY } = require('@blocklet/constant');
9
- const { getBlockletInfo } = require('@blocklet/meta');
10
- const { SpaceClient, SyncFolderPushCommand } = require('@did-space/client');
18
+ const { BLOCKLET_CONFIGURABLE_KEY, BlockletEvents } = require('@blocklet/constant');
19
+ const { SpaceClient, BackupBlockletCommand } = require('@did-space/client');
11
20
  const { ensureDirSync } = require('fs-extra');
12
21
  const { isEmpty } = require('lodash');
13
- const { join } = require('path');
22
+ const { join, basename } = require('path');
23
+ const { signV2 } = require('@arcblock/jwt');
24
+ const { Hasher } = require('@ocap/mcrypto');
25
+ const { toBuffer } = require('@ocap/util');
26
+ const { getAppName, getAppDescription } = require('@blocklet/meta/lib/util');
27
+ const getBlockletInfo = require('@blocklet/meta/lib/info');
28
+ const security = require('@abtnode/util/lib/security');
29
+
30
+ const logger = require('@abtnode/logger')('@abtnode/core:storage:backup');
31
+
14
32
  const states = require('../../../states');
15
33
  const { AuditLogBackup } = require('./audit-log');
16
34
  const { BlockletBackup } = require('./blocklet');
@@ -39,7 +57,7 @@ class SpacesBackup {
39
57
  * @type {string}
40
58
  * @memberof SpacesBackup
41
59
  */
42
- blockletBackupDir;
60
+ backupDir;
43
61
 
44
62
  /**
45
63
  *
@@ -47,7 +65,7 @@ class SpacesBackup {
47
65
  * @type {string}
48
66
  * @memberof SpacesBackup
49
67
  */
50
- serverDataDir;
68
+ serverDir;
51
69
 
52
70
  /**
53
71
  *
@@ -55,15 +73,15 @@ class SpacesBackup {
55
73
  * @type {string}
56
74
  * @memberof SpacesBackup
57
75
  */
58
- spacesEndpoint;
76
+ spaceEndpoint;
59
77
 
60
78
  /**
61
79
  *
62
- * @description spaces 的 endpoint
63
- * @type {import('@ocap/wallet').WalletObject}
80
+ * @description spaces 安全相关的上下文
81
+ * @type SecurityContext
64
82
  * @memberof SpacesBackup
65
83
  */
66
- blockletWallet;
84
+ securityContext;
67
85
 
68
86
  storages;
69
87
 
@@ -91,8 +109,8 @@ class SpacesBackup {
91
109
  * @memberof SpacesBackup
92
110
  */
93
111
  verify(input) {
94
- if (isEmpty(input?.did) || !isValid(input?.did)) {
95
- throw new Error(`input.did(${input?.did}) is not a valid did`);
112
+ if (isEmpty(input?.appPid) || !isValid(input?.appPid)) {
113
+ throw new Error(`input.appPid(${input?.appPid}) is not a valid did`);
96
114
  }
97
115
  }
98
116
 
@@ -108,68 +126,116 @@ class SpacesBackup {
108
126
  }
109
127
 
110
128
  async initialize() {
111
- this.blocklet = await states.blocklet.findOne({
112
- appDid: this.input.did,
113
- });
129
+ this.blocklet = await states.blocklet.getBlocklet(this.input.appPid);
114
130
  if (isEmpty(this.blocklet)) {
115
131
  throw new Error('blocklet cannot be empty');
116
132
  }
117
133
 
118
- this.serverDataDir = process.env.ABT_NODE_DATA_DIR;
134
+ this.serverDir = process.env.ABT_NODE_DATA_DIR;
135
+ this.backupDir = join(this.serverDir, 'tmp/backup', this.blocklet.appPid);
136
+ ensureDirSync(this.backupDir);
119
137
 
120
- this.blockletBackupDir = join(process.env.ABT_NODE_DATA_DIR, 'tmp/backup', this.blocklet.meta.did);
121
- ensureDirSync(this.blockletBackupDir);
122
-
123
- this.spacesEndpoint = this.blocklet.environments.find(
138
+ this.spaceEndpoint = this.blocklet.environments.find(
124
139
  (e) => e.key === BLOCKLET_CONFIGURABLE_KEY.BLOCKLET_APP_SPACE_ENDPOINT
125
140
  )?.value;
126
- if (isEmpty(this.spacesEndpoint)) {
127
- throw new Error('spacesEndpoint cannot be empty');
141
+ if (isEmpty(this.spaceEndpoint)) {
142
+ throw new Error('spaceEndpoint cannot be empty');
128
143
  }
129
144
 
130
- this.blockletWallet = await this.getBlockletWallet();
145
+ this.securityContext = await this.getSecurityContext();
131
146
  }
132
147
 
133
148
  async export() {
149
+ this.input.event.emit(BlockletEvents.backupProgress, {
150
+ appPid: this.input.appPid,
151
+ message: 'Preparing data for backup...',
152
+ progress: 15,
153
+ completed: false,
154
+ });
134
155
  await Promise.all(
135
156
  this.storages.map((storage) => {
136
157
  storage.ensureParams(this);
137
158
  return storage.export();
138
159
  })
139
160
  );
161
+ this.input.event.emit(BlockletEvents.backupProgress, {
162
+ appPid: this.input.appPid,
163
+ message: 'Data ready, start backup...',
164
+ progress: 20,
165
+ completed: false,
166
+ });
140
167
  }
141
168
 
142
169
  async syncToSpaces() {
143
- const wallet = await this.getBlockletWallet();
144
170
  const spaceClient = new SpaceClient({
145
- endpoint: this.spacesEndpoint,
146
- wallet,
171
+ endpoint: this.spaceEndpoint,
172
+ wallet: this.securityContext.signer,
173
+ delegation: this.securityContext.delegation,
147
174
  });
148
175
 
149
- // FIXME: Spaces 里面预览出 blocklet 的样式,需要规划一个好的数据结构
150
- const { errorCount } = await spaceClient.send(
151
- new SyncFolderPushCommand({
152
- source: join(this.blockletBackupDir, '/'),
153
- target: join('.did-objects', this.blocklet.appDid),
176
+ const { errorCount, message } = await spaceClient.send(
177
+ new BackupBlockletCommand({
178
+ appDid: this.blocklet.appPid,
179
+ appName: getAppName(this.blocklet),
180
+ appDescription: getAppDescription(this.blocklet),
181
+ userDid: this.input.userDid,
182
+ referrer: this.input.referrer,
183
+ source: join(this.backupDir, '/'),
154
184
  debug: true,
155
- concurrency: 64,
156
- retryCount: 100,
185
+ concurrency: 32,
186
+ retryCount: 10,
157
187
  filter: (object) => {
188
+ // FIXME: @yejianchao 这里需要更完整的黑名单
158
189
  return object.name !== '.DS_Store';
159
190
  },
191
+ onProgress: (data) => {
192
+ logger.info('backup progress', { appDid: this.input.appPid, data });
193
+ const percent = (data.completed * 100) / data.total;
194
+ this.input.event.emit(BlockletEvents.backupProgress, {
195
+ appPid: this.input.appPid,
196
+ message: `Uploaded file ${basename(data.key)} (${data.completed}/${data.total})`,
197
+ // 0.8 是因为上传文件到 spaces 占进度的 80%,+ 20 是因为需要累加之前的进度
198
+ progress: +Math.ceil(percent * 0.8).toFixed(2) + 20,
199
+ completed: false,
200
+ });
201
+ },
160
202
  })
161
203
  );
162
204
 
163
205
  if (errorCount !== 0) {
164
- throw new Error(`Sync to spaces encountered ${errorCount} error`);
206
+ throw new Error(`Sync to spaces encountered error: ${message}`);
165
207
  }
166
208
  }
167
209
 
168
- async getBlockletWallet() {
169
- const blockletInfo = await states.blocklet.getBlocklet(this.input.did);
210
+ async getSecurityContext() {
211
+ const blocklet = await states.blocklet.getBlocklet(this.input.appPid);
170
212
  const nodeInfo = await states.node.read();
171
- const { wallet } = getBlockletInfo(blockletInfo, nodeInfo.sk);
172
- return wallet;
213
+
214
+ const { wallet, permanentWallet } = getBlockletInfo(blocklet, nodeInfo.sk);
215
+
216
+ // FIXME: @wangshijun migrated apps can not be restored, need to change core/webapp/api/routes/auth/verify-app-ownership.js
217
+ const { secretKey, address } = wallet; // we encrypt using latest wallet, not the permanent wallet
218
+ const password = toBuffer(Hasher.SHA3.hash256(Buffer.concat([secretKey, address].map(toBuffer))));
219
+ const encrypt = (v) => security.encrypt(v, address, password);
220
+ const decrypt = (v) => security.decrypt(v, address, password);
221
+
222
+ const delegation =
223
+ permanentWallet.address !== wallet.address
224
+ ? signV2(permanentWallet.address, permanentWallet.secretKey, {
225
+ from: permanentWallet.address,
226
+ to: wallet.address,
227
+ userPk: permanentWallet.publicKey,
228
+ permissions: [{ role: 'DIDSpaceAgent', spaces: ['*'] }],
229
+ exp: Math.floor(new Date().getTime() / 1000) + 60 * 60,
230
+ })
231
+ : '';
232
+
233
+ return {
234
+ signer: wallet,
235
+ delegation,
236
+ encrypt,
237
+ decrypt,
238
+ };
173
239
  }
174
240
  }
175
241
 
@@ -11,15 +11,7 @@ class BaseRestore {
11
11
  * @type {string}
12
12
  * @memberof BaseRestore
13
13
  */
14
- blockletRestoreDir;
15
-
16
- /**
17
- *
18
- * @description spaces 的 endpoint
19
- * @type {import('@ocap/wallet').WalletObject}
20
- * @memberof BaseRestore
21
- */
22
- blockletWallet;
14
+ restoreDir;
23
15
 
24
16
  /**
25
17
  *
@@ -27,7 +19,7 @@ class BaseRestore {
27
19
  * @type {string}
28
20
  * @memberof BaseRestore
29
21
  */
30
- serverDataDir;
22
+ serverDir;
31
23
 
32
24
  constructor(input) {
33
25
  this.input = input;
@@ -40,14 +32,34 @@ class BaseRestore {
40
32
  * @memberof BaseRestore
41
33
  */
42
34
  ensureParams(spaces) {
43
- this.blockletRestoreDir = spaces.blockletRestoreDir;
44
- this.blockletWallet = spaces.blockletWallet;
45
- this.serverDataDir = spaces.serverDataDir;
35
+ this.restoreDir = spaces.restoreDir;
36
+ this.serverDir = spaces.serverDir;
46
37
  }
47
38
 
48
- async import() {
39
+ // eslint-disable-next-line
40
+ async import(params) {
49
41
  throw new Error('not implemented');
50
42
  }
43
+
44
+ /**
45
+ * Generate params for BlockletManager to install
46
+ *
47
+ * @return {object}
48
+ * @memberof BaseRestore
49
+ */
50
+ getInstallParams() {
51
+ return {};
52
+ }
53
+
54
+ /**
55
+ * Generate params for other restorers
56
+ *
57
+ * @return {object}
58
+ * @memberof BaseRestore
59
+ */
60
+ getImportParams() {
61
+ return {};
62
+ }
51
63
  }
52
64
 
53
65
  module.exports = {
@@ -1,85 +1,85 @@
1
1
  const { removeSync, outputJsonSync, readJSONSync } = require('fs-extra');
2
- const { cloneDeep, isArray } = require('lodash');
2
+ const { cloneDeep, isEmpty } = 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 {
8
9
  filename = 'blocklet-extras.json';
9
10
 
10
- async import() {
11
- const blockletExtra = await this.getBlockletExtra();
12
- removeSync(join(this.blockletRestoreDir, this.filename));
13
- outputJsonSync(join(this.blockletRestoreDir, this.filename), blockletExtra);
11
+ async import(params) {
12
+ const extras = this.getExtras();
13
+ this.cleanExtras(extras, params);
14
+ removeSync(join(this.restoreDir, this.filename));
15
+ outputJsonSync(join(this.restoreDir, this.filename), extras);
14
16
  }
15
17
 
16
18
  /**
17
19
  *
18
20
  * @description
19
- * @return {Promise<import('@abtnode/client').BlockletState>}
21
+ * @return {import('@abtnode/client').BlockletState}
20
22
  * @memberof BlockletExtrasRestore
21
23
  */
22
- async getBlockletExtra() {
24
+ getExtras() {
23
25
  /**
24
26
  * @type {import('@abtnode/client').BlockletState}
25
27
  */
26
- const blockletExtra = readJSONSync(join(this.blockletRestoreDir, this.filename));
27
-
28
- return this.cleanData(blockletExtra);
28
+ return readJSONSync(join(this.restoreDir, this.filename));
29
29
  }
30
30
 
31
31
  /**
32
32
  *
33
33
  * @description 清理数据并加密
34
- * @param {import('@abtnode/client').BlockletState} blockletExtraInput
35
- * @return {Promise<void>}
34
+ * @param {import('@abtnode/client').BlockletState} raw
36
35
  * @memberof BlockletExtrasRestore
37
36
  */
38
- async cleanData(blockletExtraInput) {
39
- const blockletExtra = cloneDeep(blockletExtraInput);
37
+ async cleanExtras(raw, params) {
38
+ const blockletExtra = cloneDeep(raw);
40
39
 
41
40
  const queue = [blockletExtra];
42
41
  while (queue.length) {
43
- const currentBlockletExtra = queue.pop();
42
+ const current = queue.pop();
44
43
 
45
44
  // 加解密
46
- this.useBlockletDecryptConfigs(currentBlockletExtra.configs);
45
+ this.decryptConfigs(current.configs, params);
47
46
 
48
- if (currentBlockletExtra?.children) {
49
- queue.push(...currentBlockletExtra.children);
47
+ if (current?.children) {
48
+ queue.push(...current.children);
50
49
  }
51
50
  }
52
-
53
- return blockletExtra;
54
51
  }
55
52
 
56
53
  /**
57
54
  *
58
- * @description 清理数据并加密
55
+ * @description 解密加密的数据
59
56
  * @param {import('@abtnode/client').ConfigEntry[]} configs
57
+ * @param {object} params
60
58
  * @return {void}
61
59
  * @memberof BlockletExtrasRestore
62
60
  */
63
- useBlockletDecryptConfigs(configs) {
64
- if (!configs || !isArray(configs)) {
61
+ decryptConfigs(configs, params) {
62
+ if (isEmpty(configs)) {
65
63
  return;
66
64
  }
67
65
 
66
+ const { password } = this.input;
68
67
  for (const config of configs) {
69
- // secure 为 true 的配置才需要被加密保存上次到 did spaces
70
68
  if (config.secure) {
71
- const encryptByBlocklet = config.value;
72
-
73
- // 再用 blocklet secret 加密,然后才可以上传到 spaces
74
- const decryptByBlocklet = security.decrypt(
75
- encryptByBlocklet,
76
- this.blockletWallet.address,
77
- Buffer.from(this.blockletWallet.secretKey)
78
- );
79
-
80
- config.value = decryptByBlocklet;
69
+ config.value = security.decrypt(config.value, params.salt, password);
81
70
  }
82
71
  }
72
+
73
+ const skConfig = configs.find((x) => x.key === BLOCKLET_CONFIGURABLE_KEY.BLOCKLET_APP_SK);
74
+ if (skConfig) {
75
+ this.appSk = skConfig.value;
76
+ }
77
+ }
78
+
79
+ getInstallParams() {
80
+ return {
81
+ appSk: this.appSk,
82
+ };
83
83
  }
84
84
  }
85
85