@abtnode/core 1.8.68-beta-500af7e5 → 1.8.69-beta-e0666d0d

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -615,23 +615,23 @@ class BlockletManager extends BaseBlockletManager {
615
615
  * @memberof BlockletManager
616
616
  */
617
617
  // eslint-disable-next-line no-unused-vars
618
- async backupToSpaces({ did }, context) {
618
+ async backupToSpaces({ appPid }, context) {
619
619
  // add to queue
620
620
  // const ticket = this.backupQueue.push({
621
621
  // entity: 'blocklet',
622
622
  // action: 'backup-to-space',
623
- // did,
623
+ // appPid,
624
624
  // context,
625
625
  // });
626
626
 
627
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, {
628
+ // logger.error('backup failed', { entity: 'blocklet', appPid, error: err });
629
+ // this.emit('blocklet.backup.failed', { appPid, err });
630
+ // this._createNotification(appPid, {
631
631
  // title: 'Blocklet Backup Failed',
632
632
  // description: `Blocklet backup failed with error: ${err.message || 'queue exception'}`,
633
633
  // entityType: 'blocklet',
634
- // entityId: did,
634
+ // entityId: appPid,
635
635
  // severity: 'error',
636
636
  // });
637
637
  // });
@@ -639,11 +639,10 @@ class BlockletManager extends BaseBlockletManager {
639
639
  const userDid = context.user.did;
640
640
  const { referrer } = context;
641
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 });
642
+ const spacesBackup = new SpacesBackup({ appPid, event: this, userDid, referrer });
643
+ this.emit(BlockletEvents.backupProgress, { appPid, message: 'Start backup...', progress: 10, completed: false });
645
644
  await spacesBackup.backup();
646
- this.emit(BlockletEvents.backupProgress, { did, completed: true, progress: 100 });
645
+ this.emit(BlockletEvents.backupProgress, { appPid, completed: true, progress: 100 });
647
646
  }
648
647
 
649
648
  /**
@@ -654,8 +653,7 @@ class BlockletManager extends BaseBlockletManager {
654
653
  */
655
654
  // eslint-disable-next-line no-unused-vars
656
655
  async restoreFromSpaces(input, context) {
657
- // FIXME: @yejianchao did should be renamed to appDid
658
- this.emit(BlockletEvents.restoreProgress, { did: input.appDid, message: 'Start restore...' });
656
+ this.emit(BlockletEvents.restoreProgress, { appDid: input.appDid, message: 'Start restore...', completed: false });
659
657
 
660
658
  const userDid = context.user.did;
661
659
  const { referrer } = context;
@@ -663,14 +661,14 @@ class BlockletManager extends BaseBlockletManager {
663
661
  const spacesRestore = new SpacesRestore({ ...input, event: this, userDid, referrer });
664
662
  const params = await spacesRestore.restore();
665
663
 
666
- this.emit(BlockletEvents.restoreProgress, { did: input.appDid, message: 'Installing blocklet...' });
664
+ this.emit(BlockletEvents.restoreProgress, { appDid: input.appDid, message: 'Installing blocklet...' });
667
665
  await this._installFromBackup({
668
- url: `file://${spacesRestore.blockletRestoreDir}`,
666
+ url: `file://${spacesRestore.restoreDir}`,
669
667
  moveDir: true,
670
668
  ...merge(...params),
671
669
  });
672
670
 
673
- this.emit(BlockletEvents.restoreProgress, { did: input.appDid, completed: true });
671
+ this.emit(BlockletEvents.restoreProgress, { appDid: input.appDid, completed: true });
674
672
  }
675
673
 
676
674
  async restart({ did }, context) {
@@ -1104,7 +1102,7 @@ class BlockletManager extends BaseBlockletManager {
1104
1102
  if (willAppSkChange) {
1105
1103
  const info = await states.node.read();
1106
1104
  const { wallet } = getBlockletInfo(blocklet, info.sk);
1107
- const migratedFrom = blocklet.migratedFrom || [];
1105
+ const migratedFrom = Array.isArray(blocklet.migratedFrom) ? blocklet.migratedFrom : [];
1108
1106
  await states.blocklet.updateBlocklet(rootDid, {
1109
1107
  migratedFrom: [
1110
1108
  ...migratedFrom,
@@ -1113,6 +1111,11 @@ class BlockletManager extends BaseBlockletManager {
1113
1111
  });
1114
1112
  }
1115
1113
 
1114
+ // Reload nginx to make sure did-space can embed content from this app
1115
+ if (newConfigs.find((x) => x.key === BLOCKLET_CONFIGURABLE_KEY.BLOCKLET_APP_SPACE_ENDPOINT)?.value) {
1116
+ this.emit(BlockletEvents.spaceConnected, blocklet);
1117
+ }
1118
+
1116
1119
  // FIXME: @zhenqiang best way to handle the did document: allow all appDids to resolve
1117
1120
  if (willAppDidChange && !skipDidDocument) {
1118
1121
  await this._updateDidDocument(blocklet);
@@ -2117,7 +2120,7 @@ class BlockletManager extends BaseBlockletManager {
2117
2120
  // put BLOCKLET_APP_ID at root level for indexing
2118
2121
  blocklet.appDid = appSystemEnvironments.BLOCKLET_APP_ID;
2119
2122
 
2120
- if (!blocklet.migratedFrom) {
2123
+ if (!Array.isArray(blocklet.migratedFrom)) {
2121
2124
  blocklet.migratedFrom = [];
2122
2125
  }
2123
2126
  // This can only be set once, can be used for indexing, will not change ever
@@ -2,7 +2,7 @@ const fs = require('fs-extra');
2
2
  const path = require('path');
3
3
  const omit = require('lodash/omit');
4
4
 
5
- const { forEachBlockletSync } = require('@blocklet/meta/lib/util');
5
+ const { forEachBlockletSync, getBlockletAppIdList } = require('@blocklet/meta/lib/util');
6
6
  const getBlockletInfo = require('@blocklet/meta/lib/info');
7
7
 
8
8
  const { BLOCKLET_CONFIGURABLE_KEY } = require('@blocklet/constant');
@@ -90,8 +90,9 @@ module.exports = async ({ url, appSk, moveDir, context = {}, states, manager } =
90
90
 
91
91
  // validate appDid
92
92
  const nodeInfo = await states.node.read();
93
+ const appIdList = getBlockletAppIdList(state);
93
94
  const { wallet } = getBlockletInfo({ meta: state.meta, configs: extra.configs, environments: [] }, nodeInfo.sk);
94
- if (state.appDid !== wallet.address) {
95
+ if (appIdList.includes(wallet.address) === false) {
95
96
  throw new Error('blocklet appDid is different from the previous one');
96
97
  }
97
98
  await checkDuplicateAppSk({ sk: wallet.secretKey, states });
@@ -19,8 +19,8 @@ class AuditLogBackup extends BaseBackup {
19
19
  scope: this.blocklet.meta.did,
20
20
  });
21
21
 
22
- removeSync(join(this.blockletBackupDir, this.filename));
23
- outputJsonSync(join(this.blockletBackupDir, this.filename), auditLogs);
22
+ removeSync(join(this.backupDir, this.filename));
23
+ outputJsonSync(join(this.backupDir, this.filename), auditLogs);
24
24
  }
25
25
  }
26
26
 
@@ -1,6 +1,5 @@
1
1
  class BaseBackup {
2
2
  /**
3
- *
4
3
  * @type {import('./spaces').SpaceBackupInput}
5
4
  * @memberof BaseBackup
6
5
  */
@@ -18,15 +17,15 @@ class BaseBackup {
18
17
  * @type {string}
19
18
  * @memberof BaseBackup
20
19
  */
21
- blockletBackupDir;
20
+ backupDir;
22
21
 
23
22
  /**
24
23
  *
25
24
  * @description spaces 的 endpoint
26
- * @type {import('@ocap/wallet').WalletObject}
25
+ * @type {import('./spaces').SecurityContext}
27
26
  * @memberof BaseBackup
28
27
  */
29
- blockletWallet;
28
+ securityContext;
30
29
 
31
30
  /**
32
31
  *
@@ -34,7 +33,7 @@ class BaseBackup {
34
33
  * @type {string}
35
34
  * @memberof BaseBackup
36
35
  */
37
- serverDataDir;
36
+ serverDir;
38
37
 
39
38
  constructor(input) {
40
39
  this.input = input;
@@ -48,9 +47,9 @@ class BaseBackup {
48
47
  */
49
48
  ensureParams(spacesBackup) {
50
49
  this.blocklet = spacesBackup.blocklet;
51
- this.serverDataDir = spacesBackup.serverDataDir;
52
- this.blockletBackupDir = spacesBackup.blockletBackupDir;
53
- this.blockletWallet = spacesBackup.blockletWallet;
50
+ this.serverDir = spacesBackup.serverDir;
51
+ this.backupDir = spacesBackup.backupDir;
52
+ this.securityContext = spacesBackup.securityContext;
54
53
  }
55
54
 
56
55
  async export() {
@@ -1,9 +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
- const { Hasher } = require('@ocap/mcrypto');
5
- const { toBuffer } = require('@ocap/util');
6
4
  const security = require('@abtnode/util/lib/security');
5
+
7
6
  const states = require('../../../states');
8
7
  const { BaseBackup } = require('./base');
9
8
 
@@ -12,8 +11,8 @@ class BlockletExtrasBackup extends BaseBackup {
12
11
 
13
12
  async export() {
14
13
  const blockletExtra = await this.getBlockletExtra();
15
- removeSync(join(this.blockletBackupDir, this.filename));
16
- outputJsonSync(join(this.blockletBackupDir, this.filename), blockletExtra);
14
+ removeSync(join(this.backupDir, this.filename));
15
+ outputJsonSync(join(this.backupDir, this.filename), blockletExtra);
17
16
  }
18
17
 
19
18
  /**
@@ -39,29 +38,29 @@ class BlockletExtrasBackup extends BaseBackup {
39
38
  /**
40
39
  *
41
40
  * @description 清理数据并加密
42
- * @param {import('@abtnode/client').BlockletState} blockletExtraInput
43
- * @return {Promise<void>}
41
+ * @param {import('@abtnode/client').BlockletState} raw
42
+ * @return {Promise<any>}
44
43
  * @memberof BlockletExtrasBackup
45
44
  */
46
- async cleanData(blockletExtraInput) {
47
- const blockletExtra = cloneDeep(blockletExtraInput);
45
+ async cleanData(raw) {
46
+ const blockletExtra = cloneDeep(raw);
48
47
 
49
48
  const queue = [blockletExtra];
50
49
  while (queue.length) {
51
- const currentBlockletExtra = queue.pop();
50
+ const current = queue.pop();
52
51
 
53
52
  // 删除父 blocklet 的某些数据
54
- if (currentBlockletExtra._id) {
55
- delete currentBlockletExtra._id;
56
- delete currentBlockletExtra.createdAt;
57
- delete currentBlockletExtra.updatedAt;
53
+ if (current._id) {
54
+ delete current._id;
55
+ delete current.createdAt;
56
+ delete current.updatedAt;
58
57
  }
59
58
 
60
59
  // 加解密
61
- this.encryptBlockletConfigs(currentBlockletExtra.configs);
60
+ this.encrypt(current.configs);
62
61
 
63
- if (currentBlockletExtra?.children) {
64
- queue.push(...currentBlockletExtra.children);
62
+ if (current?.children) {
63
+ queue.push(...current.children);
65
64
  }
66
65
  }
67
66
 
@@ -75,28 +74,16 @@ class BlockletExtrasBackup extends BaseBackup {
75
74
  * @return {void}
76
75
  * @memberof BlockletExtrasBackup
77
76
  */
78
- encryptBlockletConfigs(configs) {
77
+ encrypt(configs) {
79
78
  if (isEmpty(configs)) {
80
79
  return;
81
80
  }
82
81
 
83
- const { secretKey, address } = this.blockletWallet;
84
-
85
- // 准备加解密所需的参数
86
- // @see: https://github.com/ArcBlock/blocklet-server/blob/f561ba7290285f2e23dccb6d5323eb4d43c3cc3e/core/state/lib/index.js#L59
87
- const dk = readFileSync(join(this.serverDataDir, '.sock'));
88
- const ek = toBuffer(Hasher.SHA3.hash256(Buffer.concat([secretKey, address].map(toBuffer))));
89
-
82
+ const dk = readFileSync(join(this.serverDir, '.sock'));
90
83
  for (const config of configs) {
91
84
  if (config.secure) {
92
- // secure true 的配置才需要被加密保存上次到 did spaces
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 decrypted = security.decrypt(encryptByServer, this.blocklet.meta.did, dk);
97
- // 再用 blocklet 信息加密,然后才可以上传到 spaces
98
- const encrypted = security.encrypt(decrypted, address, ek);
99
- config.value = encrypted;
85
+ const decrypted = security.decrypt(config.value, this.blocklet.meta.did, dk);
86
+ config.value = this.securityContext.encrypt(decrypted);
100
87
  }
101
88
  }
102
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,19 +1,31 @@
1
1
  /**
2
2
  * @typedef {{
3
- * did: string
3
+ * appPid: string
4
4
  * event: import('events').EventEmitter,
5
5
  * userDid: string,
6
6
  * referrer: string,
7
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
8
15
  */
9
16
 
10
17
  const { isValid } = require('@arcblock/did');
11
18
  const { BLOCKLET_CONFIGURABLE_KEY, BlockletEvents } = require('@blocklet/constant');
12
- const { getBlockletInfo } = require('@blocklet/meta');
13
19
  const { SpaceClient, BackupBlockletCommand } = require('@did-space/client');
14
20
  const { ensureDirSync } = require('fs-extra');
15
21
  const { isEmpty } = require('lodash');
16
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');
17
29
 
18
30
  const logger = require('@abtnode/logger')('@abtnode/core:storage:backup');
19
31
 
@@ -45,7 +57,7 @@ class SpacesBackup {
45
57
  * @type {string}
46
58
  * @memberof SpacesBackup
47
59
  */
48
- blockletBackupDir;
60
+ backupDir;
49
61
 
50
62
  /**
51
63
  *
@@ -53,7 +65,7 @@ class SpacesBackup {
53
65
  * @type {string}
54
66
  * @memberof SpacesBackup
55
67
  */
56
- serverDataDir;
68
+ serverDir;
57
69
 
58
70
  /**
59
71
  *
@@ -61,15 +73,15 @@ class SpacesBackup {
61
73
  * @type {string}
62
74
  * @memberof SpacesBackup
63
75
  */
64
- spacesEndpoint;
76
+ spaceEndpoint;
65
77
 
66
78
  /**
67
79
  *
68
- * @description spaces 的 endpoint
69
- * @type {import('@ocap/wallet').WalletObject}
80
+ * @description spaces 安全相关的上下文
81
+ * @type SecurityContext
70
82
  * @memberof SpacesBackup
71
83
  */
72
- blockletWallet;
84
+ securityContext;
73
85
 
74
86
  storages;
75
87
 
@@ -97,8 +109,8 @@ class SpacesBackup {
97
109
  * @memberof SpacesBackup
98
110
  */
99
111
  verify(input) {
100
- if (isEmpty(input?.did) || !isValid(input?.did)) {
101
- 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`);
102
114
  }
103
115
  }
104
116
 
@@ -114,33 +126,31 @@ class SpacesBackup {
114
126
  }
115
127
 
116
128
  async initialize() {
117
- this.blocklet = await states.blocklet.findOne({
118
- appDid: this.input.did,
119
- });
129
+ this.blocklet = await states.blocklet.getBlocklet(this.input.appPid);
120
130
  if (isEmpty(this.blocklet)) {
121
131
  throw new Error('blocklet cannot be empty');
122
132
  }
123
133
 
124
- 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);
125
137
 
126
- this.blockletBackupDir = join(process.env.ABT_NODE_DATA_DIR, 'tmp/backup', this.blocklet.meta.did);
127
- ensureDirSync(this.blockletBackupDir);
128
-
129
- this.spacesEndpoint = this.blocklet.environments.find(
138
+ this.spaceEndpoint = this.blocklet.environments.find(
130
139
  (e) => e.key === BLOCKLET_CONFIGURABLE_KEY.BLOCKLET_APP_SPACE_ENDPOINT
131
140
  )?.value;
132
- if (isEmpty(this.spacesEndpoint)) {
133
- throw new Error('spacesEndpoint cannot be empty');
141
+ if (isEmpty(this.spaceEndpoint)) {
142
+ throw new Error('spaceEndpoint cannot be empty');
134
143
  }
135
144
 
136
- this.blockletWallet = await this.getBlockletWallet();
145
+ this.securityContext = await this.getSecurityContext();
137
146
  }
138
147
 
139
148
  async export() {
140
149
  this.input.event.emit(BlockletEvents.backupProgress, {
141
- did: this.input.did,
150
+ appPid: this.input.appPid,
142
151
  message: 'Preparing data for backup...',
143
152
  progress: 15,
153
+ completed: false,
144
154
  });
145
155
  await Promise.all(
146
156
  this.storages.map((storage) => {
@@ -149,23 +159,28 @@ class SpacesBackup {
149
159
  })
150
160
  );
151
161
  this.input.event.emit(BlockletEvents.backupProgress, {
152
- did: this.input.did,
162
+ appPid: this.input.appPid,
153
163
  message: 'Data ready, start backup...',
154
164
  progress: 20,
165
+ completed: false,
155
166
  });
156
167
  }
157
168
 
158
169
  async syncToSpaces() {
159
- const wallet = await this.getBlockletWallet();
160
170
  const spaceClient = new SpaceClient({
161
- endpoint: this.spacesEndpoint,
162
- wallet,
171
+ endpoint: this.spaceEndpoint,
172
+ wallet: this.securityContext.signer,
173
+ delegation: this.securityContext.delegation,
163
174
  });
164
175
 
165
176
  const { errorCount, message } = await spaceClient.send(
166
177
  new BackupBlockletCommand({
167
- appDid: this.blocklet.appDid,
168
- source: join(this.blockletBackupDir, '/'),
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, '/'),
169
184
  debug: true,
170
185
  concurrency: 32,
171
186
  retryCount: 10,
@@ -174,30 +189,53 @@ class SpacesBackup {
174
189
  return object.name !== '.DS_Store';
175
190
  },
176
191
  onProgress: (data) => {
177
- logger.info('backup progress', { appDid: this.input.did, data });
192
+ logger.info('backup progress', { appDid: this.input.appPid, data });
178
193
  const percent = (data.completed * 100) / data.total;
179
194
  this.input.event.emit(BlockletEvents.backupProgress, {
180
- did: this.input.did,
195
+ appPid: this.input.appPid,
181
196
  message: `Uploaded file ${basename(data.key)} (${data.completed}/${data.total})`,
182
197
  // 0.8 是因为上传文件到 spaces 占进度的 80%,+ 20 是因为需要累加之前的进度
183
198
  progress: +Math.ceil(percent * 0.8).toFixed(2) + 20,
199
+ completed: false,
184
200
  });
185
201
  },
186
- userDid: this.input.userDid,
187
- referrer: this.input.referrer,
188
202
  })
189
203
  );
190
204
 
191
205
  if (errorCount !== 0) {
192
- throw new Error(`Sync to spaces encountered ${errorCount} error: ${message}`);
206
+ throw new Error(`Sync to spaces encountered error: ${message}`);
193
207
  }
194
208
  }
195
209
 
196
- async getBlockletWallet() {
197
- const blockletInfo = await states.blocklet.getBlocklet(this.input.did);
210
+ async getSecurityContext() {
211
+ const blocklet = await states.blocklet.getBlocklet(this.input.appPid);
198
212
  const nodeInfo = await states.node.read();
199
- const { wallet } = getBlockletInfo(blockletInfo, nodeInfo.sk);
200
- 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
+ };
201
239
  }
202
240
  }
203
241
 
@@ -11,7 +11,7 @@ class BaseRestore {
11
11
  * @type {string}
12
12
  * @memberof BaseRestore
13
13
  */
14
- blockletRestoreDir;
14
+ restoreDir;
15
15
 
16
16
  /**
17
17
  *
@@ -19,7 +19,7 @@ class BaseRestore {
19
19
  * @type {string}
20
20
  * @memberof BaseRestore
21
21
  */
22
- serverDataDir;
22
+ serverDir;
23
23
 
24
24
  constructor(input) {
25
25
  this.input = input;
@@ -32,11 +32,12 @@ class BaseRestore {
32
32
  * @memberof BaseRestore
33
33
  */
34
34
  ensureParams(spaces) {
35
- this.blockletRestoreDir = spaces.blockletRestoreDir;
36
- this.serverDataDir = spaces.serverDataDir;
35
+ this.restoreDir = spaces.restoreDir;
36
+ this.serverDir = spaces.serverDir;
37
37
  }
38
38
 
39
- async import() {
39
+ // eslint-disable-next-line
40
+ async import(params) {
40
41
  throw new Error('not implemented');
41
42
  }
42
43
 
@@ -49,6 +50,16 @@ class BaseRestore {
49
50
  getInstallParams() {
50
51
  return {};
51
52
  }
53
+
54
+ /**
55
+ * Generate params for other restorers
56
+ *
57
+ * @return {object}
58
+ * @memberof BaseRestore
59
+ */
60
+ getImportParams() {
61
+ return {};
62
+ }
52
63
  }
53
64
 
54
65
  module.exports = {
@@ -1,5 +1,5 @@
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
5
  const { BLOCKLET_CONFIGURABLE_KEY } = require('@blocklet/constant');
@@ -8,77 +8,72 @@ const { BaseRestore } = require('./base');
8
8
  class BlockletExtrasRestore extends BaseRestore {
9
9
  filename = 'blocklet-extras.json';
10
10
 
11
- async import() {
12
- const blockletExtra = await this.getBlockletExtra();
13
- removeSync(join(this.blockletRestoreDir, this.filename));
14
- 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);
15
16
  }
16
17
 
17
18
  /**
18
19
  *
19
20
  * @description
20
- * @return {Promise<import('@abtnode/client').BlockletState>}
21
+ * @return {import('@abtnode/client').BlockletState}
21
22
  * @memberof BlockletExtrasRestore
22
23
  */
23
- async getBlockletExtra() {
24
+ getExtras() {
24
25
  /**
25
26
  * @type {import('@abtnode/client').BlockletState}
26
27
  */
27
- const blockletExtra = readJSONSync(join(this.blockletRestoreDir, this.filename));
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
34
+ * @param {import('@abtnode/client').BlockletState} raw
35
35
  * @memberof BlockletExtrasRestore
36
36
  */
37
- async cleanData(blockletExtraInput) {
38
- const blockletExtra = cloneDeep(blockletExtraInput);
37
+ async cleanExtras(raw, params) {
38
+ const blockletExtra = cloneDeep(raw);
39
39
 
40
40
  const queue = [blockletExtra];
41
41
  while (queue.length) {
42
- const currentBlockletExtra = queue.pop();
42
+ const current = queue.pop();
43
43
 
44
44
  // 加解密
45
- this.decrypt(currentBlockletExtra.configs);
45
+ this.decryptConfigs(current.configs, params);
46
46
 
47
- if (currentBlockletExtra?.children) {
48
- queue.push(...currentBlockletExtra.children);
47
+ if (current?.children) {
48
+ queue.push(...current.children);
49
49
  }
50
50
  }
51
-
52
- return blockletExtra;
53
51
  }
54
52
 
55
53
  /**
56
54
  *
57
55
  * @description 解密加密的数据
58
56
  * @param {import('@abtnode/client').ConfigEntry[]} configs
57
+ * @param {object} params
59
58
  * @return {void}
60
59
  * @memberof BlockletExtrasRestore
61
60
  */
62
- decrypt(configs) {
63
- if (!configs || !isArray(configs)) {
61
+ decryptConfigs(configs, params) {
62
+ if (isEmpty(configs)) {
64
63
  return;
65
64
  }
66
65
 
67
- const { appDid, password } = this.input;
66
+ const { password } = this.input;
68
67
  for (const config of configs) {
69
68
  if (config.secure) {
70
- const encrypted = config.value;
71
- const decrypted = security.decrypt(encrypted, appDid, Buffer.from(password));
72
- config.value = decrypted;
69
+ config.value = security.decrypt(config.value, params.salt, password);
73
70
  }
74
71
  }
75
72
 
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`);
73
+ const skConfig = configs.find((x) => x.key === BLOCKLET_CONFIGURABLE_KEY.BLOCKLET_APP_SK);
74
+ if (skConfig) {
75
+ this.appSk = skConfig.value;
79
76
  }
80
-
81
- this.appSk = config.value;
82
77
  }
83
78
 
84
79
  getInstallParams() {
@@ -0,0 +1,49 @@
1
+ const { removeSync, outputJsonSync, readJSONSync } = require('fs-extra');
2
+ const { join } = require('path');
3
+ const security = require('@abtnode/util/lib/security');
4
+ const { BaseRestore } = require('./base');
5
+
6
+ class BlockletRestore extends BaseRestore {
7
+ filename = 'blocklet.json';
8
+
9
+ async import(params) {
10
+ const blocklet = this.decrypt(this.getBlocklet(), params);
11
+ removeSync(join(this.restoreDir, this.filename));
12
+ outputJsonSync(join(this.restoreDir, this.filename), blocklet);
13
+ }
14
+
15
+ /**
16
+ *
17
+ * @description
18
+ * @return {import('@abtnode/client').BlockletState}
19
+ * @memberof BlockletRestore
20
+ */
21
+ getBlocklet() {
22
+ return readJSONSync(join(this.restoreDir, this.filename));
23
+ }
24
+
25
+ /**
26
+ *
27
+ * @description 解密加密的数据
28
+ * @param {import('@abtnode/client').BlockletState} blocklet
29
+ * @return {import('@abtnode/client').BlockletState}
30
+ * @memberof BlockletRestore
31
+ */
32
+ decrypt(blocklet, params) {
33
+ const { password } = this.input;
34
+ if (Array.isArray(blocklet.migratedFrom)) {
35
+ blocklet.migratedFrom = blocklet.migratedFrom.map((x) => {
36
+ x.appSk = security.decrypt(x.appSk, params.salt, password);
37
+ return x;
38
+ });
39
+ }
40
+
41
+ return blocklet;
42
+ }
43
+
44
+ getImportParams() {
45
+ return { salt: this.getBlocklet().appDid };
46
+ }
47
+ }
48
+
49
+ module.exports = { BlockletRestore };
@@ -8,7 +8,7 @@ class BlockletsRestore extends BaseRestore {
8
8
  filename = 'blocklets';
9
9
 
10
10
  async import() {
11
- const blockletsDir = join(this.blockletRestoreDir, this.filename);
11
+ const blockletsDir = join(this.restoreDir, this.filename);
12
12
 
13
13
  // blockletsDir 可以不存在, 因为还原的 blocklet 可能所有的组件都是来自 store 的
14
14
  if (!existsSync(blockletsDir)) {
@@ -7,13 +7,13 @@ class LogsRestore extends BaseRestore {
7
7
  filename = 'logs.zip';
8
8
 
9
9
  async import() {
10
- const blockletZipPath = join(this.blockletRestoreDir, this.filename);
10
+ const blockletZipPath = join(this.restoreDir, this.filename);
11
11
 
12
12
  if (!existsSync(blockletZipPath)) {
13
13
  throw new Error(`file not found: ${blockletZipPath}`);
14
14
  }
15
15
 
16
- await zipToDir(blockletZipPath, join(this.blockletRestoreDir, 'logs'));
16
+ await zipToDir(blockletZipPath, join(this.restoreDir, 'logs'));
17
17
  removeSync(blockletZipPath);
18
18
  }
19
19
  }
@@ -12,6 +12,7 @@
12
12
  */
13
13
 
14
14
  const validUrl = require('valid-url');
15
+ const merge = require('lodash/merge');
15
16
  const { BlockletEvents } = require('@blocklet/constant');
16
17
  const { SpaceClient, RestoreBlockletCommand } = require('@did-space/client');
17
18
  const { ensureDirSync, existsSync, rmdirSync } = require('fs-extra');
@@ -20,6 +21,7 @@ const { join, basename } = require('path');
20
21
  const logger = require('@abtnode/logger')('@abtnode/core:storage:restore');
21
22
 
22
23
  const { BlockletExtrasRestore } = require('./blocklet-extras');
24
+ const { BlockletRestore } = require('./blocklet');
23
25
  const { BlockletsRestore } = require('./blocklets');
24
26
 
25
27
  class SpacesRestore {
@@ -35,7 +37,7 @@ class SpacesRestore {
35
37
  * @type {string}
36
38
  * @memberof SpacesRestore
37
39
  */
38
- blockletRestoreDir;
40
+ restoreDir;
39
41
 
40
42
  /**
41
43
  *
@@ -43,7 +45,7 @@ class SpacesRestore {
43
45
  * @type {string}
44
46
  * @memberof SpacesRestore
45
47
  */
46
- serverDataDir;
48
+ serverDir;
47
49
 
48
50
  storages;
49
51
 
@@ -55,7 +57,11 @@ class SpacesRestore {
55
57
  constructor(input) {
56
58
  this.verify(input);
57
59
  this.input = input;
58
- this.storages = [new BlockletExtrasRestore(this.input), new BlockletsRestore(this.input)];
60
+ this.storages = [
61
+ new BlockletExtrasRestore(this.input),
62
+ new BlockletRestore(this.input),
63
+ new BlockletsRestore(this.input),
64
+ ];
59
65
  }
60
66
 
61
67
  /**
@@ -68,26 +74,28 @@ class SpacesRestore {
68
74
  if (!validUrl.isWebUri(input.endpoint)) {
69
75
  throw new Error(`endpoint(${input.endpoint}) must be a WebUri`);
70
76
  }
77
+ if (!input.endpoint.includes(input.appDid)) {
78
+ throw new Error(`endpoint and blocklet.appDid(${input.appDid}) do not match`);
79
+ }
71
80
  }
72
81
 
73
82
  async initialize() {
74
- this.serverDataDir = process.env.ABT_NODE_DATA_DIR;
75
-
76
- if (!this.input.endpoint.includes(this.input.appDid)) {
77
- throw new Error(`endpoint and blocklet.appDid(${this.input.appDid}) do not match`);
83
+ this.serverDir = process.env.ABT_NODE_DATA_DIR;
84
+ this.restoreDir = join(this.serverDir, 'tmp/restore', this.input.appDid);
85
+ if (existsSync(this.restoreDir)) {
86
+ rmdirSync(this.restoreDir, { recursive: true });
78
87
  }
88
+ ensureDirSync(this.restoreDir);
79
89
 
80
- this.blockletRestoreDir = join(process.env.ABT_NODE_DATA_DIR, 'tmp/restore', this.input.appDid);
81
- if (existsSync(this.blockletRestoreDir)) {
82
- rmdirSync(this.blockletRestoreDir, { recursive: true });
83
- }
84
- ensureDirSync(this.blockletRestoreDir);
90
+ this.storages.map((x) => x.ensureParams(this));
85
91
  }
86
92
 
87
93
  async restore() {
88
94
  await this.initialize();
89
95
  await this.syncFromSpaces();
90
- await this.import();
96
+
97
+ const params = await Promise.all(this.storages.map((x) => x.getImportParams()));
98
+ await this.import(merge(...params));
91
99
 
92
100
  return this.storages.map((x) => x.getInstallParams());
93
101
  }
@@ -104,14 +112,14 @@ class SpacesRestore {
104
112
  const { errorCount, message } = await spaceClient.send(
105
113
  new RestoreBlockletCommand({
106
114
  appDid: this.input.appDid,
107
- target: join(this.blockletRestoreDir, '/'),
115
+ target: join(this.restoreDir, '/'),
108
116
  debug: true,
109
117
  concurrency: 32,
110
118
  retryCount: 10,
111
119
  onProgress: (data) => {
112
120
  logger.info('restore progress', { appDid: this.input.appDid, data });
113
121
  this.input.event.emit(BlockletEvents.restoreProgress, {
114
- did: this.input.appDid,
122
+ appDid: this.input.appDid,
115
123
  message: `Downloaded file ${basename(data.key)} (${data.completed}/${data.total})`,
116
124
  });
117
125
  },
@@ -122,23 +130,22 @@ class SpacesRestore {
122
130
  );
123
131
 
124
132
  if (errorCount !== 0) {
125
- throw new Error(`Sync from spaces encountered ${errorCount} error: ${message}`);
133
+ throw new Error(`Sync from spaces encountered error: ${message}`);
126
134
  }
127
135
  }
128
136
 
129
- async import() {
137
+ async import(params) {
130
138
  this.input.event.emit(BlockletEvents.restoreProgress, {
131
- did: this.input.appDid,
139
+ appDid: this.input.appDid,
132
140
  message: 'Preparing to import data...',
133
141
  });
134
142
  await Promise.all(
135
143
  this.storages.map((storage) => {
136
- storage.ensureParams(this);
137
- return storage.import();
144
+ return storage.import(params);
138
145
  })
139
146
  );
140
147
  this.input.event.emit(BlockletEvents.restoreProgress, {
141
- did: this.input.appDid,
148
+ appDid: this.input.appDid,
142
149
  message: 'Importing data successfully...',
143
150
  });
144
151
  }
package/lib/event.js CHANGED
@@ -242,6 +242,14 @@ module.exports = ({
242
242
  );
243
243
 
244
244
  logger.info('take snapshot after updated blocklet app', { event: eventName, did: blocklet.meta.did, hash });
245
+ } else if (BlockletEvents.spaceConnected === eventName) {
246
+ nodeState
247
+ .read()
248
+ .then((info) => handleRouting(info))
249
+ .catch((err) => {
250
+ logger.error('Reload gateway failed on blocklet.connectedSpace', { error: err });
251
+ });
252
+ logger.info('Reload gateway after blocklet connected to space', { event: eventName, did: blocklet.appDid });
245
253
  }
246
254
 
247
255
  if (
@@ -307,6 +315,8 @@ module.exports = ({
307
315
 
308
316
  BlockletEvents.backupProgress,
309
317
  BlockletEvents.restoreProgress,
318
+
319
+ BlockletEvents.spaceConnected,
310
320
  ].forEach((eventName) => {
311
321
  listen(blockletManager, eventName, handleBlockletEvent);
312
322
  });
@@ -3,6 +3,7 @@
3
3
  const fs = require('fs-extra');
4
4
  const path = require('path');
5
5
  const tar = require('tar');
6
+ const isUrl = require('is-url');
6
7
  const get = require('lodash/get');
7
8
  const cloneDeep = require('lodash/cloneDeep');
8
9
  const isEqual = require('lodash/isEqual');
@@ -43,6 +44,7 @@ const {
43
44
  BLOCKLET_INTERFACE_PUBLIC,
44
45
  BLOCKLET_INTERFACE_WELLKNOWN,
45
46
  BLOCKLET_INTERFACE_TYPE_WELLKNOWN,
47
+ BLOCKLET_CONFIGURABLE_KEY,
46
48
  BlockletEvents,
47
49
  BLOCKLET_MODES,
48
50
  } = require('@blocklet/constant');
@@ -122,7 +124,7 @@ const addCorsToSite = (site, rawUrl) => {
122
124
  site.corsAllowedOrigins.push(url.hostname);
123
125
  }
124
126
  } catch (err) {
125
- // Do nothing
127
+ console.error('addCorsToSite', err);
126
128
  }
127
129
  };
128
130
 
@@ -336,6 +338,22 @@ const ensureCorsForWebWallet = async (sites) => {
336
338
  return sites;
337
339
  };
338
340
 
341
+ const ensureCorsForDidSpace = async (sites = [], blocklets) => {
342
+ return sites.map((site) => {
343
+ const blocklet = blocklets.find((x) => x.meta.did === site.blockletDid);
344
+ if (blocklet) {
345
+ const endpoint = blocklet.environments.find(
346
+ (x) => x.key === BLOCKLET_CONFIGURABLE_KEY.BLOCKLET_APP_SPACE_ENDPOINT
347
+ );
348
+ if (endpoint && isUrl(endpoint.value)) {
349
+ addCorsToSite(site, endpoint.value);
350
+ }
351
+ }
352
+
353
+ return site;
354
+ });
355
+ };
356
+
339
357
  const filterSitesForRemovedBlocklets = async (sites = [], blocklets) => {
340
358
  return sites.filter((site) => {
341
359
  if (!site.domain.endsWith(BLOCKLET_SITE_GROUP_SUFFIX)) {
@@ -407,6 +425,7 @@ const ensureLatestInfo = async (sites = [], { withDefaultCors = true } = {}) =>
407
425
  result = await ensureBlockletCache(result, blocklets);
408
426
  result = await ensureWellknownRule(result);
409
427
  result = await ensureCorsForWebWallet(result);
428
+ result = await ensureCorsForDidSpace(result, blocklets);
410
429
  result = await ensureLatestInterfaceInfo(result);
411
430
 
412
431
  return result;
@@ -49,7 +49,7 @@ const formatBlocklet = (blocklet, phase, dek) => {
49
49
  return;
50
50
  }
51
51
 
52
- (b.migratedFrom || []).forEach((x) => {
52
+ (Array.isArray(b.migratedFrom) ? b.migratedFrom : []).forEach((x) => {
53
53
  if (phase === 'onUpdate' && isHex(x.appSk) === true) {
54
54
  x.appSk = security.encrypt(x.appSk, b.meta.did, dek);
55
55
  }
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "1.8.68-beta-500af7e5",
6
+ "version": "1.8.69-beta-e0666d0d",
7
7
  "description": "",
8
8
  "main": "lib/index.js",
9
9
  "files": [
@@ -19,18 +19,18 @@
19
19
  "author": "wangshijun <wangshijun2010@gmail.com> (http://github.com/wangshijun)",
20
20
  "license": "MIT",
21
21
  "dependencies": {
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",
22
+ "@abtnode/auth": "1.8.69-beta-e0666d0d",
23
+ "@abtnode/certificate-manager": "1.8.69-beta-e0666d0d",
24
+ "@abtnode/constant": "1.8.69-beta-e0666d0d",
25
+ "@abtnode/cron": "1.8.69-beta-e0666d0d",
26
+ "@abtnode/db": "1.8.69-beta-e0666d0d",
27
+ "@abtnode/logger": "1.8.69-beta-e0666d0d",
28
+ "@abtnode/queue": "1.8.69-beta-e0666d0d",
29
+ "@abtnode/rbac": "1.8.69-beta-e0666d0d",
30
+ "@abtnode/router-provider": "1.8.69-beta-e0666d0d",
31
+ "@abtnode/static-server": "1.8.69-beta-e0666d0d",
32
+ "@abtnode/timemachine": "1.8.69-beta-e0666d0d",
33
+ "@abtnode/util": "1.8.69-beta-e0666d0d",
34
34
  "@arcblock/did": "1.18.57",
35
35
  "@arcblock/did-motif": "^1.1.10",
36
36
  "@arcblock/did-util": "1.18.57",
@@ -38,10 +38,10 @@
38
38
  "@arcblock/jwt": "^1.18.57",
39
39
  "@arcblock/pm2-events": "^0.0.5",
40
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",
41
+ "@blocklet/constant": "1.8.69-beta-e0666d0d",
42
+ "@blocklet/meta": "1.8.69-beta-e0666d0d",
43
+ "@blocklet/sdk": "1.8.69-beta-e0666d0d",
44
+ "@did-space/client": "0.2.25",
45
45
  "@fidm/x509": "^1.2.1",
46
46
  "@ocap/mcrypto": "1.18.57",
47
47
  "@ocap/util": "1.18.57",
@@ -89,5 +89,5 @@
89
89
  "express": "^4.18.2",
90
90
  "jest": "^27.5.1"
91
91
  },
92
- "gitHead": "9070621373f317a10ff0d289323bf725e30d3521"
92
+ "gitHead": "acf0373591eaa3aff76483edc4e648afc543f1f7"
93
93
  }