@abtnode/core 1.8.69-beta-76f8a46f → 1.8.69-beta-650a290b

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.
@@ -357,7 +357,7 @@ class BlockletManager extends BaseBlockletManager {
357
357
  }
358
358
 
359
359
  async diff({ did, hashFiles, rootDid }) {
360
- return diff({ did, hashFiles, rootDid, states });
360
+ return diff({ did, hashFiles, rootDid, states, manager: this });
361
361
  }
362
362
 
363
363
  /**
@@ -34,7 +34,11 @@ const installComponentFromDev = async ({ folder, meta, rootDid, mountPoint, mana
34
34
  await manager.deleteComponent({ did, rootDid });
35
35
  }
36
36
 
37
- const defaultPath = formatName(meta.name);
37
+ let defaultPath = formatName(meta.name);
38
+ if (!existRoot.children.map((x) => x.mountPoint).includes('/')) {
39
+ defaultPath = '';
40
+ }
41
+
38
42
  const component = {
39
43
  meta: ensureMeta(meta),
40
44
  mountPoint: mountPoint || `/${defaultPath}`,
@@ -118,7 +118,7 @@ const installComponentFromUpload = async ({
118
118
  return manager.getBlocklet(newBlocklet.meta.did);
119
119
  };
120
120
 
121
- const diff = async ({ did, hashFiles: clientFiles, rootDid: inputRootDid, states }) => {
121
+ const diff = async ({ did, hashFiles: clientFiles, rootDid: inputRootDid, states, manager }) => {
122
122
  if (!did) {
123
123
  throw new Error('did is empty');
124
124
  }
@@ -154,7 +154,7 @@ const diff = async ({ did, hashFiles: clientFiles, rootDid: inputRootDid, states
154
154
  }
155
155
 
156
156
  const { version } = state.meta;
157
- const bundleDir = getBundleDir(this.installDir, state.meta);
157
+ const bundleDir = getBundleDir(manager.installDir, state.meta);
158
158
  const { addSet, changeSet, deleteSet } = await getDiffFiles(clientFiles, bundleDir);
159
159
 
160
160
  logger.info('Diff files', {
@@ -1,10 +1,12 @@
1
1
  const path = require('path');
2
2
  const fs = require('fs-extra');
3
3
  const pick = require('lodash/pick');
4
+ const pRetry = require('p-retry');
5
+ const Client = require('@ocap/client');
4
6
 
5
7
  const logger = require('@abtnode/logger')('@abtnode/core:migrate-application-to-struct-v2');
6
8
 
7
- const { forEachBlockletSync, getSharedConfigObj } = require('@blocklet/meta/lib/util');
9
+ const { forEachBlockletSync, getSharedConfigObj, getChainInfo } = require('@blocklet/meta/lib/util');
8
10
  const { SLOT_FOR_IP_DNS_SITE } = require('@abtnode/constant');
9
11
 
10
12
  const {
@@ -25,7 +27,9 @@ const {
25
27
  BLOCKLET_UPLOADS_DIR,
26
28
  } = require('@blocklet/constant');
27
29
  const { update: updateMetaFile } = require('@blocklet/meta/lib/file');
30
+ const getBlockletWallet = require('@blocklet/meta/lib/wallet');
28
31
  const { cloneDeep } = require('lodash');
32
+
29
33
  const { isInProgress } = require('../../../util');
30
34
  const { getBlockletDomainGroupName } = require('../../../util/router');
31
35
  const { getIpDnsDomainForBlocklet } = require('../../../util/get-domain-for-blocklet');
@@ -87,23 +91,79 @@ const fillBlockletData = (data, app, id) => {
87
91
 
88
92
  const appSystemFiles = ['logo.svg', 'rbac.db', 'session.db', 'user.db', '.assets', BLOCKLET_UPLOADS_DIR];
89
93
 
94
+ const migrateAppOnChain = async (blocklet, oldSk, newSk) => {
95
+ logger.info('Preparing for on-chain migration', { did: blocklet.meta.did });
96
+ if (!oldSk) {
97
+ logger.info('on-chain migration aborted because oldSk is empty', { did: blocklet.meta.did });
98
+ return;
99
+ }
100
+
101
+ if (!newSk) {
102
+ logger.info('on-chain migration aborted because newSk is empty', { did: blocklet.meta.did });
103
+ return;
104
+ }
105
+
106
+ // ensure chain host
107
+ const chainInfo = getChainInfo(blocklet.configObj);
108
+ if (chainInfo.host === 'none') {
109
+ logger.info('on-chain migration aborted because CHAIN_HOST is empty', { did: blocklet.meta.did });
110
+ return;
111
+ }
112
+
113
+ logger.info('on-chain migration for chain ', { did: blocklet.meta.did, host: chainInfo.host });
114
+
115
+ // ensure account changed
116
+ const type = blocklet.configObj.BLOCKLET_WALLET_TYPE;
117
+ const oldWallet = getBlockletWallet(oldSk, undefined, type);
118
+ const newWallet = getBlockletWallet(newSk, undefined, type);
119
+ if (oldWallet.address === newWallet.address) {
120
+ logger.info('on-chain migration aborted because newSk same with oldSk', { did: blocklet.meta.did });
121
+ return;
122
+ }
123
+
124
+ // ensure old account exist on chain
125
+ const client = new Client(chainInfo.host);
126
+ const oldResult = await client.getAccountState({ address: oldWallet.address });
127
+ if (!oldResult.state) {
128
+ logger.info('on-chain migration aborted because oldSk not declared on chain', { did: blocklet.meta.did });
129
+ return;
130
+ }
131
+
132
+ // ensure new account not exist on chain
133
+ const newResult = await client.getAccountState({ address: newWallet.address });
134
+ if (newResult.state) {
135
+ logger.info('on-chain migration aborted because newSk declared on chain', { did: blocklet.meta.did });
136
+ return;
137
+ }
138
+
139
+ logger.info('on-chain migration for wallets', { oldAddress: oldWallet.address, newAddress: newWallet.address });
140
+
141
+ // migrate old account to new account
142
+ const tx = await client.signAccountMigrateTx({
143
+ tx: {
144
+ itx: {
145
+ address: newWallet.address,
146
+ pk: newWallet.publicKey,
147
+ },
148
+ },
149
+ wallet: oldWallet,
150
+ });
151
+ const hash = await client.sendAccountMigrateTx({ tx, wallet: oldWallet });
152
+ logger.info('on-chain migration done', { did: blocklet.meta.did, hash });
153
+ };
154
+
90
155
  const migrateApplicationToStructV2 = async ({ did, appSk: newAppSk, context = {}, manager, states }) => {
91
156
  logger.info('Preparing data for migration', { did });
92
157
 
93
158
  if (!newAppSk) {
94
- throw new Error('appSk is required');
159
+ throw new Error('New key pair is required when migrate application');
95
160
  }
96
161
 
97
162
  const oldBlocklet = await manager.getBlocklet(did, { throwOnNotExist: true, ensureIntegrity: true });
98
-
99
- if (oldBlocklet.structVersion || oldBlocklet.externalSk) {
163
+ if (oldBlocklet.structVersion) {
100
164
  throw new Error('Blocklet already migrated', pick(oldBlocklet, ['structVersion', 'externalSk']));
101
165
  }
102
166
 
103
- if (oldBlocklet.migratedFrom?.length) {
104
- throw new Error('Blocklet already migrated');
105
- }
106
-
107
167
  if (isInProgress(oldBlocklet.status) || oldBlocklet.status === BlockletStatus.running) {
108
168
  throw new Error('Please stop blocklet before migration');
109
169
  }
@@ -115,25 +175,26 @@ const migrateApplicationToStructV2 = async ({ did, appSk: newAppSk, context = {}
115
175
  const backupExtra = cloneDeep(extraData);
116
176
  const backupSite = await states.site.findOne({ _id: siteData.id });
117
177
 
118
- const permanentAppDid = oldBlocklet.appDid;
119
- const permanentAppSk = oldBlocklet.environmentObj.BLOCKLET_APP_SK;
178
+ const { appPid } = oldBlocklet;
120
179
 
121
180
  // change index of extraData
122
- extraData.did = permanentAppDid;
181
+ extraData.did = appPid;
123
182
  if (extraData.meta) {
124
- extraData.meta.did = permanentAppDid;
125
- extraData.meta.name = permanentAppDid;
183
+ extraData.meta.did = appPid;
184
+ extraData.meta.name = appPid;
126
185
  }
127
186
  // fork root component's configs to container
128
- // FIXME: the configs in container should be managed in dashboard ?
129
- // set permanent appSk first and then migrate to new appSk
130
- extraData.configs = (extraData.configs || []).filter((x) => x.name !== BLOCKLET_CONFIGURABLE_KEY.BLOCKLET_APP_SK);
187
+ // FIXME: @linchen the configs in container should be managed in dashboard ?
188
+ extraData.configs = (extraData.configs || []).filter((x) => x.key !== BLOCKLET_CONFIGURABLE_KEY.BLOCKLET_APP_SK);
189
+
190
+ // If appSk not configured before, then set derived appSk as permanent appSk
131
191
  extraData.configs.push({
132
192
  key: BLOCKLET_CONFIGURABLE_KEY.BLOCKLET_APP_SK,
133
- value: permanentAppSk,
193
+ value: oldBlocklet.environmentObj.BLOCKLET_APP_SK,
134
194
  secure: true,
135
195
  shared: false,
136
196
  });
197
+
137
198
  // clean children configs
138
199
  extraData.children = [];
139
200
  // clean dirty data in settings
@@ -145,14 +206,16 @@ const migrateApplicationToStructV2 = async ({ did, appSk: newAppSk, context = {}
145
206
  // delete system generated rules in siteData
146
207
  siteData.rules = siteData.rules.filter((rule) => !rule.isProtected && rule.to.did !== oldBlocklet.meta.did);
147
208
  // change index of siteData
148
- siteData.domain = getBlockletDomainGroupName(permanentAppDid);
209
+ siteData.domain = getBlockletDomainGroupName(appPid);
149
210
 
150
211
  const blockletData = {
212
+ appPid,
151
213
  children: [],
214
+ migratedFrom: Array.isArray(oldBlocklet.migratedFrom) ? cloneDeep(oldBlocklet.migratedFrom) : [],
152
215
  };
153
216
 
154
217
  const dataDirSrc = path.join(oldBlocklet.env.dataDir);
155
- const dataDirDist = path.join(manager.dataDirs.data, permanentAppDid);
218
+ const dataDirDist = path.join(manager.dataDirs.data, appPid);
156
219
  if (fs.existsSync(dataDirDist)) {
157
220
  throw new Error(`blocklet data dir already exist: ${dataDirDist}`);
158
221
  }
@@ -176,13 +239,13 @@ const migrateApplicationToStructV2 = async ({ did, appSk: newAppSk, context = {}
176
239
 
177
240
  // If old root component is a container, just skip it
178
241
  if (level === 0 && component.meta.group === BlockletGroup.gateway) {
179
- fillBlockletData(blockletData, component, permanentAppDid);
242
+ fillBlockletData(blockletData, component, appPid);
180
243
  return;
181
244
  }
182
245
 
183
246
  // If old root component is a blocklet, make it to be level one component
184
247
  if (level === 0 && component.meta.group !== BlockletGroup.gateway) {
185
- fillBlockletData(blockletData, component, permanentAppDid);
248
+ fillBlockletData(blockletData, component, appPid);
186
249
 
187
250
  // add root component to blockletData
188
251
  const { source, deployedFrom } = component;
@@ -291,14 +354,14 @@ const migrateApplicationToStructV2 = async ({ did, appSk: newAppSk, context = {}
291
354
  // update state
292
355
  let newBlocklet;
293
356
  try {
294
- logger.info('Start migrate application state', { did, appDid: permanentAppDid });
357
+ logger.info('Start migrate application state', { did, appPid });
295
358
  // delete old db in db proxy
296
359
  await manager.teamManager.deleteTeam(oldBlocklet.meta.did);
297
360
 
298
361
  // re create blocklet
299
- await states.blocklet.remove({ appDid: permanentAppDid });
362
+ await states.blocklet.remove({ appPid });
300
363
  await states.blocklet.addBlocklet(blockletData);
301
- await states.blocklet.updateStructV1Did(permanentAppDid, oldBlocklet.meta.did);
364
+ await states.blocklet.updateStructV1Did(appPid, oldBlocklet.meta.did);
302
365
 
303
366
  // fake install bundle
304
367
  const bundleDir = getBundleDir(manager.installDir, blockletData.meta);
@@ -317,11 +380,11 @@ const migrateApplicationToStructV2 = async ({ did, appSk: newAppSk, context = {}
317
380
  await states.blockletExtras.update({ did: oldBlocklet.meta.did }, extraData);
318
381
 
319
382
  // update environment, generate appDid and appPid
320
- await manager._updateBlockletEnvironment(permanentAppDid);
383
+ await manager._updateBlockletEnvironment(appPid);
321
384
 
322
385
  // rotate to newAppSk
323
386
  await manager.config({
324
- did: permanentAppDid,
387
+ did: appPid,
325
388
  configs: [{ key: 'BLOCKLET_APP_SK', value: newAppSk, secure: true }],
326
389
  skipHook: true,
327
390
  skipDidDocument: true,
@@ -333,7 +396,7 @@ const migrateApplicationToStructV2 = async ({ did, appSk: newAppSk, context = {}
333
396
  { $set: { domain: siteData.domain, domainAliases: siteData.domainAliases, rules: siteData.rules } }
334
397
  );
335
398
 
336
- newBlocklet = await manager.getBlocklet(permanentAppDid);
399
+ newBlocklet = await manager.getBlocklet(appPid);
337
400
 
338
401
  logger.info('Start migrate application data', { sortedDataMoveList });
339
402
 
@@ -344,6 +407,12 @@ const migrateApplicationToStructV2 = async ({ did, appSk: newAppSk, context = {}
344
407
  fs.moveSync(src, dist);
345
408
  currentMoveIndex = i;
346
409
  }
410
+
411
+ // migrate on-chain account
412
+ await pRetry(() => migrateAppOnChain(oldBlocklet, oldBlocklet.environmentObj.BLOCKLET_APP_SK, newAppSk), {
413
+ retries: 3,
414
+ onFailedAttempt: console.error,
415
+ });
347
416
  } catch (error) {
348
417
  logger.error('Migrate application state failed: ', { did, error });
349
418
 
@@ -10,9 +10,7 @@ class BlockletsRestore extends BaseRestore {
10
10
  async import() {
11
11
  const blockletsDir = join(this.restoreDir, this.filename);
12
12
 
13
- // blockletsDir 可以不存在, 因为还原的 blocklet 可能所有的组件都是来自 store 的
14
13
  if (!existsSync(blockletsDir)) {
15
- // FIXME: 如果文件夹不存在,需要创建文件夹,因为 installFromBackup 未做容错处理
16
14
  ensureDirSync(blockletsDir);
17
15
  return;
18
16
  }
@@ -23,10 +21,7 @@ class BlockletsRestore extends BaseRestore {
23
21
  absolute: true,
24
22
  });
25
23
 
26
- /**
27
- * @type {{source: string, target: string}}
28
- */
29
- const zipConfigs = paths.map((path) => {
24
+ const configs = paths.map((path) => {
30
25
  return {
31
26
  source: path,
32
27
  target: path.replace(/.zip$/, ''),
@@ -34,9 +29,12 @@ class BlockletsRestore extends BaseRestore {
34
29
  });
35
30
 
36
31
  await Promise.all(
37
- zipConfigs.map(async (zipConfig) => {
38
- await zipToDir(zipConfig.source, zipConfig.target);
39
- await remove(zipConfig.source);
32
+ configs.map(async ({ source, target }) => {
33
+ if (!existsSync(target)) {
34
+ ensureDirSync(target);
35
+ }
36
+ await zipToDir(source, target);
37
+ await remove(source);
40
38
  })
41
39
  );
42
40
  }
@@ -1,10 +1,10 @@
1
1
  /**
2
2
  * @typedef {{
3
- * appDid: string;
3
+ * appDid: string; // --> appPid
4
4
  * endpoint: string;
5
- * password: Buffer;
6
- * delegation: string;
7
- * wallet: import('@ocap/wallet').WalletObject;
5
+ * password: Buffer; // derived from (appSk, appDid)
6
+ * delegation: string; // from appPid --> serverDid for downloading
7
+ * wallet: import('@ocap/wallet').WalletObject; // server wallet
8
8
  * event: import('events').EventEmitter,
9
9
  * userDid: string,
10
10
  * referrer: string,
@@ -15,7 +15,7 @@ const validUrl = require('valid-url');
15
15
  const merge = require('lodash/merge');
16
16
  const { BlockletEvents } = require('@blocklet/constant');
17
17
  const { SpaceClient, RestoreBlockletCommand } = require('@did-space/client');
18
- const { ensureDirSync, existsSync, rmdirSync } = require('fs-extra');
18
+ const { ensureDirSync, existsSync, rmSync } = require('fs-extra');
19
19
  const { join, basename } = require('path');
20
20
 
21
21
  const logger = require('@abtnode/logger')('@abtnode/core:storage:restore');
@@ -83,7 +83,7 @@ class SpacesRestore {
83
83
  this.serverDir = process.env.ABT_NODE_DATA_DIR;
84
84
  this.restoreDir = join(this.serverDir, 'tmp/restore', this.input.appDid);
85
85
  if (existsSync(this.restoreDir)) {
86
- rmdirSync(this.restoreDir, { recursive: true });
86
+ rmSync(this.restoreDir, { recursive: true });
87
87
  }
88
88
  ensureDirSync(this.restoreDir);
89
89
 
@@ -623,6 +623,11 @@ const deleteBlockletProcess = async (blocklet, { preDelete = noop, skippedProces
623
623
  await forEachBlocklet(
624
624
  blocklet,
625
625
  async (b, { ancestors }) => {
626
+ // NOTICE: 如果不判断 group, 在 github action 中测试 disk.spec.js 时会报错, 但是在 mac 中跑测试不会报错
627
+ if (b.meta?.group === BlockletGroup.gateway) {
628
+ return;
629
+ }
630
+
626
631
  if (skippedProcessIds.includes(b.env.processId)) {
627
632
  logger.info(`skip delete process ${b.env.processId}`);
628
633
  return;
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "1.8.69-beta-76f8a46f",
6
+ "version": "1.8.69-beta-650a290b",
7
7
  "description": "",
8
8
  "main": "lib/index.js",
9
9
  "files": [
@@ -19,33 +19,34 @@
19
19
  "author": "wangshijun <wangshijun2010@gmail.com> (http://github.com/wangshijun)",
20
20
  "license": "MIT",
21
21
  "dependencies": {
22
- "@abtnode/auth": "1.8.69-beta-76f8a46f",
23
- "@abtnode/certificate-manager": "1.8.69-beta-76f8a46f",
24
- "@abtnode/constant": "1.8.69-beta-76f8a46f",
25
- "@abtnode/cron": "1.8.69-beta-76f8a46f",
26
- "@abtnode/db": "1.8.69-beta-76f8a46f",
27
- "@abtnode/logger": "1.8.69-beta-76f8a46f",
28
- "@abtnode/queue": "1.8.69-beta-76f8a46f",
29
- "@abtnode/rbac": "1.8.69-beta-76f8a46f",
30
- "@abtnode/router-provider": "1.8.69-beta-76f8a46f",
31
- "@abtnode/static-server": "1.8.69-beta-76f8a46f",
32
- "@abtnode/timemachine": "1.8.69-beta-76f8a46f",
33
- "@abtnode/util": "1.8.69-beta-76f8a46f",
34
- "@arcblock/did": "1.18.58",
22
+ "@abtnode/auth": "1.8.69-beta-650a290b",
23
+ "@abtnode/certificate-manager": "1.8.69-beta-650a290b",
24
+ "@abtnode/constant": "1.8.69-beta-650a290b",
25
+ "@abtnode/cron": "1.8.69-beta-650a290b",
26
+ "@abtnode/db": "1.8.69-beta-650a290b",
27
+ "@abtnode/logger": "1.8.69-beta-650a290b",
28
+ "@abtnode/queue": "1.8.69-beta-650a290b",
29
+ "@abtnode/rbac": "1.8.69-beta-650a290b",
30
+ "@abtnode/router-provider": "1.8.69-beta-650a290b",
31
+ "@abtnode/static-server": "1.8.69-beta-650a290b",
32
+ "@abtnode/timemachine": "1.8.69-beta-650a290b",
33
+ "@abtnode/util": "1.8.69-beta-650a290b",
34
+ "@arcblock/did": "1.18.59",
35
35
  "@arcblock/did-motif": "^1.1.10",
36
- "@arcblock/did-util": "1.18.58",
37
- "@arcblock/event-hub": "1.18.58",
38
- "@arcblock/jwt": "^1.18.58",
36
+ "@arcblock/did-util": "1.18.59",
37
+ "@arcblock/event-hub": "1.18.59",
38
+ "@arcblock/jwt": "^1.18.59",
39
39
  "@arcblock/pm2-events": "^0.0.5",
40
- "@arcblock/vc": "1.18.58",
41
- "@blocklet/constant": "1.8.69-beta-76f8a46f",
42
- "@blocklet/meta": "1.8.69-beta-76f8a46f",
43
- "@blocklet/sdk": "1.8.69-beta-76f8a46f",
40
+ "@arcblock/vc": "1.18.59",
41
+ "@blocklet/constant": "1.8.69-beta-650a290b",
42
+ "@blocklet/meta": "1.8.69-beta-650a290b",
43
+ "@blocklet/sdk": "1.8.69-beta-650a290b",
44
44
  "@did-space/client": "^0.2.33",
45
45
  "@fidm/x509": "^1.2.1",
46
- "@ocap/mcrypto": "1.18.58",
47
- "@ocap/util": "1.18.58",
48
- "@ocap/wallet": "1.18.58",
46
+ "@ocap/client": "1.18.59",
47
+ "@ocap/mcrypto": "1.18.59",
48
+ "@ocap/util": "1.18.59",
49
+ "@ocap/wallet": "1.18.59",
49
50
  "@slack/webhook": "^5.0.4",
50
51
  "archiver": "^5.3.1",
51
52
  "axios": "^0.27.2",
@@ -69,6 +70,7 @@
69
70
  "lru-cache": "^6.0.0",
70
71
  "node-stream-zip": "^1.15.0",
71
72
  "p-limit": "^3.1.0",
73
+ "p-retry": "4.6.1",
72
74
  "pm2": "^5.2.0",
73
75
  "semver": "^7.3.8",
74
76
  "shelljs": "^0.8.5",
@@ -89,5 +91,5 @@
89
91
  "express": "^4.18.2",
90
92
  "jest": "^27.5.1"
91
93
  },
92
- "gitHead": "07415e674443b85648ea80b734f7a3690abfc15e"
94
+ "gitHead": "a33c9ce6a52b8d522d13860e85809dcbba236712"
93
95
  }