@abtnode/core 1.16.17-beta-2ac96448 → 1.16.17-beta-3232a7af

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.
@@ -91,6 +91,7 @@ const normalizePathPrefix = require('@abtnode/util/lib/normalize-path-prefix');
91
91
  const pLimit = require('p-limit');
92
92
  const pRetry = require('p-retry');
93
93
 
94
+ const isFunction = require('lodash/isFunction');
94
95
  const { consumeServerlessNFT, consumeLauncherSession } = require('../../util/launcher');
95
96
  const util = require('../../util');
96
97
  const {
@@ -219,6 +220,18 @@ class DiskBlockletManager extends BaseBlockletManager {
219
220
  this.restoreQueue = restoreQueue;
220
221
  this.teamManager = teamManager;
221
222
 
223
+ if (isFunction(this.backupQueue.on)) {
224
+ const handleBackupComplete = async ({ id, job }) => {
225
+ await this.backupQueue.delete(id);
226
+
227
+ const autoBackup = await this.getAutoBackup({ did: id });
228
+ if (autoBackup?.enabled) {
229
+ this.backupQueue.push(job, id, true, BACKUPS.JOB.INTERVAL);
230
+ }
231
+ };
232
+ this.backupQueue.on('finished', handleBackupComplete).on('failed', handleBackupComplete);
233
+ }
234
+
222
235
  // cached installed blocklets for performance
223
236
  this.cachedBlocklets = new LRU({
224
237
  max: 100,
@@ -1887,13 +1900,14 @@ class DiskBlockletManager extends BaseBlockletManager {
1887
1900
 
1888
1901
  await this.backupQueue.delete(did);
1889
1902
 
1903
+ logger.info('updateAutoBackup.$value', value);
1904
+
1890
1905
  if (value.enabled) {
1891
1906
  const blocklet = await states.blocklet.getBlocklet(did);
1892
1907
  this.backupQueue.push(
1893
1908
  {
1894
1909
  entity: 'blocklet',
1895
1910
  action: 'backupToSpaces',
1896
- id: did,
1897
1911
  blocklet,
1898
1912
  context,
1899
1913
  },
@@ -2385,22 +2399,6 @@ class DiskBlockletManager extends BaseBlockletManager {
2385
2399
  blocklet,
2386
2400
  });
2387
2401
  throw error;
2388
- } finally {
2389
- const autoBackup = await this.getAutoBackup({ did: appPid });
2390
- if (autoBackup.enabled) {
2391
- this.backupQueue.push(
2392
- {
2393
- entity: 'blocklet',
2394
- action: 'backupToSpaces',
2395
- id: appPid,
2396
- blocklet,
2397
- context,
2398
- },
2399
- appPid,
2400
- true,
2401
- BACKUPS.JOB.INTERVAL
2402
- );
2403
- }
2404
2402
  }
2405
2403
  }
2406
2404
 
@@ -3576,7 +3574,6 @@ class DiskBlockletManager extends BaseBlockletManager {
3576
3574
  {
3577
3575
  entity: 'blocklet',
3578
3576
  action: 'backupToSpaces',
3579
- id: did,
3580
3577
  blocklet,
3581
3578
  context,
3582
3579
  backupState: {
@@ -0,0 +1,49 @@
1
+ const debounce = require('lodash/debounce');
2
+ const logger = require('@abtnode/logger')('@abtnode/core:event/auto-backup-handler');
3
+
4
+ /**
5
+ * @description
6
+ * @param {string} eventName
7
+ * @param {import('@abtnode/client').BlockletState & {context: {}} } payload
8
+ * @param {import('../blocklet/manager/disk')} blockletManager
9
+ */
10
+ async function autoBackupHandler(eventName, payload, blockletManager) {
11
+ const { did } = payload.meta;
12
+ const autoBackup = await blockletManager.getAutoBackup({ did });
13
+
14
+ logger.info('autoBackupHandler.$autoBackup', autoBackup);
15
+ logger.info('autoBackupHandler.$Boolean(payload.context)', Boolean(payload.context));
16
+
17
+ if (autoBackup.enabled && payload.context) {
18
+ await blockletManager.backupQueue.delete(did);
19
+
20
+ blockletManager.backupQueue.push(
21
+ {
22
+ entity: 'blocklet',
23
+ action: 'backupToSpaces',
24
+ blocklet: payload,
25
+ context: payload.context,
26
+ },
27
+ did
28
+ );
29
+ }
30
+ }
31
+
32
+ const debouncedFuncs = {};
33
+ /**
34
+ * @description
35
+ * @param {string} id
36
+ * @param {Function} fn
37
+ * @return {Function}
38
+ */
39
+ function autoBackupHandlerFactory(id, fn) {
40
+ if (!debouncedFuncs[id]) {
41
+ debouncedFuncs[id] = debounce(fn, 3000);
42
+ }
43
+ return debouncedFuncs[id];
44
+ }
45
+
46
+ module.exports = {
47
+ autoBackupHandler,
48
+ autoBackupHandlerFactory,
49
+ };
@@ -11,22 +11,23 @@ const {
11
11
  BlockletInternalEvents,
12
12
  } = require('@blocklet/constant');
13
13
  const { EVENTS } = require('@abtnode/constant');
14
- const { NodeMonitSender } = require('./monitor/node-monit-sender');
15
- const handleInstanceInStore = require('./util/public-to-store');
16
- const { isCLI } = require('./util');
14
+ const { NodeMonitSender } = require('../monitor/node-monit-sender');
15
+ const handleInstanceInStore = require('../util/public-to-store');
16
+ const { isCLI } = require('../util');
17
17
 
18
18
  const eventHub =
19
19
  process.env.NODE_ENV === 'test' ? require('@arcblock/event-hub/single') : require('@arcblock/event-hub');
20
20
 
21
- const states = require('./states');
22
- const { getBackupEndpoint, getBackupFilesUrlFromEndpoint, getDIDSpacesUrlFromEndpoint } = require('./util/spaces');
21
+ const states = require('../states');
22
+ const { getBackupEndpoint, getBackupFilesUrlFromEndpoint, getDIDSpacesUrlFromEndpoint } = require('../util/spaces');
23
+ const { autoBackupHandlerFactory, autoBackupHandler } = require('./auto-backup-handler');
23
24
 
24
25
  const routingSnapshotPrefix = (blocklet) => (blocklet.mode === BLOCKLET_MODES.DEVELOPMENT ? '[DEV] ' : '');
25
26
 
26
27
  /**
27
28
  *
28
29
  * @param {{
29
- * blockletManager: import('./blocklet/manager/disk')
30
+ * blockletManager: import('../blocklet/manager/disk')
30
31
  * }} param0
31
32
  * @returns
32
33
  */
@@ -400,38 +401,26 @@ module.exports = ({
400
401
  listen(blockletManager, eventName, handleBlockletEvent);
401
402
  });
402
403
 
403
- /**
404
- * @description
405
- * @param {string} eventName
406
- * @param {import('@abtnode/client').BlockletState & {context: {}} } payload
407
- */
408
- async function autoBackupHandler(eventName, payload) {
409
- const { did } = payload.meta;
410
- const autoBackup = await blockletManager.getAutoBackup({ did });
411
-
412
- if (autoBackup.enabled && payload.context) {
413
- await blockletManager.backupQueue.delete(did);
414
-
415
- blockletManager.backupQueue.push(
416
- {
417
- entity: 'blocklet',
418
- action: 'backupToSpaces',
419
- id: did,
420
- blocklet: payload,
421
- context: payload.context,
422
- },
423
- did
424
- );
425
- }
426
- }
427
-
428
404
  [
429
405
  BlockletEvents.componentInstalled,
430
406
  BlockletEvents.componentUpgraded,
431
407
  BlockletEvents.componentRemoved,
432
408
  BlockletEvents.updated,
433
409
  ].forEach((eventName) => {
434
- listen(blockletManager, eventName, autoBackupHandler);
410
+ listen(
411
+ blockletManager,
412
+ eventName,
413
+ /**
414
+ *
415
+ * @param {string} eventName
416
+ * @param {import('@abtnode/client').BlockletState & {context: {}} } payload
417
+ * @returns
418
+ */
419
+ (_eventName, payload, ...args) => {
420
+ const id = payload.meta.did;
421
+ return autoBackupHandlerFactory(id, autoBackupHandler)(eventName, payload, blockletManager, ...args);
422
+ }
423
+ );
435
424
  });
436
425
 
437
426
  Object.keys(BlockletInternalEvents).forEach((key) => {
@@ -18,6 +18,7 @@ const normalizePathPrefix = require('@abtnode/util/lib/normalize-path-prefix');
18
18
  const logger = require('@abtnode/logger')('@abtnode/core:router:manager');
19
19
  const { getProvider } = require('@abtnode/router-provider');
20
20
  const checkDomainMatch = require('@abtnode/util/lib/check-domain-match');
21
+ const { isDidDomain } = require('@abtnode/util/lib/url-evaluation');
21
22
  const {
22
23
  DOMAIN_FOR_IP_SITE,
23
24
  DOMAIN_FOR_DEFAULT_SITE,
@@ -44,6 +45,7 @@ const { findWebInterface } = require('../util/blocklet');
44
45
  const { attachRuntimeDomainAliases, ensureLatestInfo } = require('./helper');
45
46
  const Router = require('./index');
46
47
  const states = require('../states');
48
+ const { getDidFromDomainGroupName, updateNFTDomainRecord, revokeAndDeleteNFTDomainRecord } = require('../util/router');
47
49
 
48
50
  const checkPathPrefixInBlackList = (pathPrefix, extraBlackList = []) => {
49
51
  const blacklist = [
@@ -184,7 +186,7 @@ class RouterManager extends EventEmitter {
184
186
  return dbSite;
185
187
  }
186
188
 
187
- async addDomainAlias({ id, domainAlias: tmpAlias, force }, context = {}) {
189
+ async addDomainAlias({ id, domainAlias: tmpAlias, force, type, nftDid, chainHost }, context = {}) {
188
190
  const domainAlias = await validateAddDomainAlias(tmpAlias, context);
189
191
  const dbSite = await states.site.findOne({ id });
190
192
  if (!dbSite) {
@@ -215,12 +217,26 @@ class RouterManager extends EventEmitter {
215
217
  }
216
218
 
217
219
  const doc = await states.site.findOne({ id });
218
- const updateResult = await states.site.update(
219
- { id },
220
- { $set: { domainAliases: [...doc.domainAliases, { value: domainAlias, isProtected: false }] } }
221
- );
220
+ const item = { value: domainAlias, isProtected: false };
221
+ if (type === 'nft-domain') {
222
+ item.type = type;
223
+ item.nftDid = nftDid;
224
+ item.chainHost = chainHost;
225
+ }
226
+
227
+ const updateResult = await states.site.update({ id }, { $set: { domainAliases: [...doc.domainAliases, item] } });
222
228
  logger.debug('add domain alias update result', { id, updateResult, domainAlias });
223
229
 
230
+ if (type === 'nft-domain') {
231
+ const did = getDidFromDomainGroupName(doc.domain); // TODO: 是不是可靠?
232
+ const didDomain = doc.domainAliases.find((x) => isDidDomain(x.value));
233
+ const blocklet = await states.blocklet.getBlocklet(did);
234
+ const nodeInfo = await states.node.read();
235
+
236
+ await updateNFTDomainRecord({ name: domainAlias, value: didDomain.value, blocklet, nodeInfo });
237
+ logger.info('update nft domain record', { domain: domainAlias, didDomain: '', nftDid, id });
238
+ }
239
+
224
240
  const newSite = await states.site.findOne({ id });
225
241
  await attachRuntimeDomainAliases({ sites: newSite, context, node: states.node });
226
242
 
@@ -228,12 +244,16 @@ class RouterManager extends EventEmitter {
228
244
  }
229
245
 
230
246
  async deleteDomainAlias({ id, domainAlias: tmpAlias }, context = {}) {
231
- const domainAlias = await validateAddDomainAlias(tmpAlias, context);
247
+ let domainAlias = await validateAddDomainAlias(tmpAlias, context);
248
+ domainAlias = toLower(domainAlias);
249
+
232
250
  const dbSite = await states.site.findOne({ id });
233
251
  if (!dbSite) {
234
252
  throw new Error(`site ${id} does not exist`);
235
253
  }
236
254
 
255
+ const toDelete = dbSite.domainAliases.find((x) => toLower(x.value) === domainAlias);
256
+
237
257
  dbSite.domainAliases = dbSite.domainAliases.filter((x) => {
238
258
  if (typeof x === 'string') {
239
259
  return toLower(x) !== domainAlias;
@@ -247,6 +267,18 @@ class RouterManager extends EventEmitter {
247
267
 
248
268
  await attachRuntimeDomainAliases({ sites: dbSite, context, node: states.node });
249
269
 
270
+ if (toDelete?.type === 'nft-domain') {
271
+ const blockletDid = getDidFromDomainGroupName(dbSite.domain); // TODO: 是不是可靠?
272
+ const blocklet = await states.blocklet.getBlocklet(blockletDid);
273
+ const nodeInfo = await states.node.read();
274
+
275
+ revokeAndDeleteNFTDomainRecord({ name: domainAlias, blocklet, nodeInfo })
276
+ .then(() => logger.info('revoke and delete nft domain record', { domain: domainAlias, blockletDid, id }))
277
+ .catch((error) =>
278
+ logger.error('revoke and delete nft domain record failed', { error, domain: domainAlias, blockletDid, id })
279
+ );
280
+ }
281
+
250
282
  return dbSite;
251
283
  }
252
284
 
@@ -67,6 +67,8 @@ const init = (dataDirs, config = {}) => {
67
67
  * backup: import('./backup'),
68
68
  * blocklet: import('./blocklet'),
69
69
  * blockletExtras: import('./blocklet-extras'),
70
+ * notification: import('./notification'),
71
+ * job: import('./job'),
70
72
  * [key: string]: any
71
73
  * }}
72
74
  */
@@ -125,6 +125,7 @@ class NodeState extends BaseState {
125
125
  trustedPassports,
126
126
  customBlockletNumber: 0,
127
127
  webWalletUrl,
128
+ nftDomainUrl: '', // TODO: Default domain url
128
129
  });
129
130
 
130
131
  if (dek) {
@@ -274,6 +274,7 @@ const getAppSystemEnvironments = (blocklet, nodeInfo, dataDirs) => {
274
274
 
275
275
  const { wallet } = result;
276
276
  const appSk = toHex(wallet.secretKey);
277
+ const appPk = toHex(wallet.publicKey);
277
278
  const appId = wallet.address;
278
279
  const appName = title || name || result.name;
279
280
  const appDescription = description || result.description;
@@ -302,6 +303,7 @@ const getAppSystemEnvironments = (blocklet, nodeInfo, dataDirs) => {
302
303
 
303
304
  return {
304
305
  BLOCKLET_DID: did, // BLOCKLET_DID is always same as BLOCKLET_APP_PID in structV2 application
306
+ BLOCKLET_APP_PK: appPk,
305
307
  BLOCKLET_APP_SK: appSk,
306
308
  BLOCKLET_APP_ID: appId,
307
309
  BLOCKLET_APP_PSK: appPsk, // permanent sk even the blocklet has been migrated
@@ -1,4 +1,10 @@
1
1
  const { BLOCKLET_SITE_GROUP_SUFFIX } = require('@abtnode/constant');
2
+ const axios = require('@abtnode/util/lib/axios');
3
+ const getBlockletInfo = require('@blocklet/meta/lib/info');
4
+ const { stableStringify } = require('@arcblock/vc');
5
+ const { toBase58 } = require('@ocap/util');
6
+ const joinURL = require('url-join');
7
+ const logger = require('@abtnode/logger')('@abtnode/core:router:util');
2
8
 
3
9
  const getBlockletDomainGroupName = (did) => `${did}${BLOCKLET_SITE_GROUP_SUFFIX}`;
4
10
 
@@ -7,7 +13,74 @@ const getDidFromDomainGroupName = (name) => {
7
13
  return did;
8
14
  };
9
15
 
16
+ const getNFTDomainHeaders = ({ wallet, payload }) => ({
17
+ 'x-blocklet-sig': toBase58(wallet.sign(stableStringify(payload))),
18
+ });
19
+
20
+ const updateNFTDomainRecord = async ({ name, value, blocklet, nodeInfo }) => {
21
+ const { wallet } = getBlockletInfo(blocklet, nodeInfo.sk);
22
+
23
+ const payload = {
24
+ delegatee: wallet.address,
25
+ domain: name,
26
+ record: {
27
+ name,
28
+ type: 'CNAME',
29
+ value,
30
+ },
31
+ };
32
+ try {
33
+ const { data } = await axios({
34
+ method: 'POST',
35
+ url: joinURL(nodeInfo.nftDomainUrl, '/api/domains'), // TODO: 替换为真实地址
36
+ data: payload,
37
+ headers: getNFTDomainHeaders({ wallet, payload }),
38
+ });
39
+
40
+ return data;
41
+ } catch (error) {
42
+ logger.error('updateNFTDomainRecord error', {
43
+ error,
44
+ name,
45
+ value,
46
+ delegatee: wallet.address,
47
+ appPid: blocklet.appPid,
48
+ resp: error.response?.data,
49
+ });
50
+ throw new Error('update nft domain record failed');
51
+ }
52
+ };
53
+
54
+ const revokeAndDeleteNFTDomainRecord = async ({ name, blocklet, nodeInfo }) => {
55
+ const { wallet } = getBlockletInfo(blocklet, nodeInfo.sk);
56
+
57
+ const payload = {
58
+ delegatee: wallet.address,
59
+ domain: name,
60
+ };
61
+ try {
62
+ const { data } = await axios({
63
+ method: 'DELETE',
64
+ url: joinURL(nodeInfo.nftDomainUrl, '/api/domains'),
65
+ data: payload,
66
+ headers: getNFTDomainHeaders({ wallet, payload }),
67
+ });
68
+
69
+ return data;
70
+ } catch (error) {
71
+ logger.error('revokeAndDeleteNFTDomainRecord error', {
72
+ error,
73
+ name,
74
+ appPid: blocklet.appPid,
75
+ resp: error.response?.data,
76
+ });
77
+ throw new Error('revoke nft domain record failed');
78
+ }
79
+ };
80
+
10
81
  module.exports = {
11
82
  getBlockletDomainGroupName,
12
83
  getDidFromDomainGroupName,
84
+ updateNFTDomainRecord,
85
+ revokeAndDeleteNFTDomainRecord,
13
86
  };
@@ -28,6 +28,15 @@ const nodeInfoSchema = Joi.object({
28
28
  zh: { 'string.uriCustomScheme': 'Web Wallet 必须是合法的 URL' },
29
29
  en: { 'string.uriCustomScheme': 'Web Wallet must be a valid URL' },
30
30
  }),
31
+ nftDomainUrl: Joi.string()
32
+ .uri({ scheme: [/https?/] })
33
+ .label('web wallet url')
34
+ .allow('')
35
+ .optional()
36
+ .messages({
37
+ zh: { 'string.uriCustomScheme': 'NFT Domain 必须是合法的 URL' },
38
+ en: { 'string.uriCustomScheme': 'NFT Domain must be a valid URL' },
39
+ }),
31
40
  autoUpgrade: Joi.boolean(),
32
41
  enableWelcomePage: Joi.boolean(),
33
42
  diskAlertThreshold: Joi.number()
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "1.16.17-beta-2ac96448",
6
+ "version": "1.16.17-beta-3232a7af",
7
7
  "description": "",
8
8
  "main": "lib/index.js",
9
9
  "files": [
@@ -19,19 +19,19 @@
19
19
  "author": "wangshijun <wangshijun2010@gmail.com> (http://github.com/wangshijun)",
20
20
  "license": "Apache-2.0",
21
21
  "dependencies": {
22
- "@abtnode/analytics": "1.16.17-beta-2ac96448",
23
- "@abtnode/auth": "1.16.17-beta-2ac96448",
24
- "@abtnode/certificate-manager": "1.16.17-beta-2ac96448",
25
- "@abtnode/constant": "1.16.17-beta-2ac96448",
26
- "@abtnode/cron": "1.16.17-beta-2ac96448",
27
- "@abtnode/logger": "1.16.17-beta-2ac96448",
28
- "@abtnode/models": "1.16.17-beta-2ac96448",
29
- "@abtnode/queue": "1.16.17-beta-2ac96448",
30
- "@abtnode/rbac": "1.16.17-beta-2ac96448",
31
- "@abtnode/router-provider": "1.16.17-beta-2ac96448",
32
- "@abtnode/static-server": "1.16.17-beta-2ac96448",
33
- "@abtnode/timemachine": "1.16.17-beta-2ac96448",
34
- "@abtnode/util": "1.16.17-beta-2ac96448",
22
+ "@abtnode/analytics": "1.16.17-beta-3232a7af",
23
+ "@abtnode/auth": "1.16.17-beta-3232a7af",
24
+ "@abtnode/certificate-manager": "1.16.17-beta-3232a7af",
25
+ "@abtnode/constant": "1.16.17-beta-3232a7af",
26
+ "@abtnode/cron": "1.16.17-beta-3232a7af",
27
+ "@abtnode/logger": "1.16.17-beta-3232a7af",
28
+ "@abtnode/models": "1.16.17-beta-3232a7af",
29
+ "@abtnode/queue": "1.16.17-beta-3232a7af",
30
+ "@abtnode/rbac": "1.16.17-beta-3232a7af",
31
+ "@abtnode/router-provider": "1.16.17-beta-3232a7af",
32
+ "@abtnode/static-server": "1.16.17-beta-3232a7af",
33
+ "@abtnode/timemachine": "1.16.17-beta-3232a7af",
34
+ "@abtnode/util": "1.16.17-beta-3232a7af",
35
35
  "@arcblock/did": "1.18.92",
36
36
  "@arcblock/did-auth": "1.18.92",
37
37
  "@arcblock/did-ext": "^1.18.92",
@@ -42,11 +42,11 @@
42
42
  "@arcblock/pm2-events": "^0.0.5",
43
43
  "@arcblock/validator": "^1.18.92",
44
44
  "@arcblock/vc": "1.18.92",
45
- "@blocklet/constant": "1.16.17-beta-2ac96448",
46
- "@blocklet/env": "1.16.17-beta-2ac96448",
47
- "@blocklet/meta": "1.16.17-beta-2ac96448",
48
- "@blocklet/resolver": "1.16.17-beta-2ac96448",
49
- "@blocklet/sdk": "1.16.17-beta-2ac96448",
45
+ "@blocklet/constant": "1.16.17-beta-3232a7af",
46
+ "@blocklet/env": "1.16.17-beta-3232a7af",
47
+ "@blocklet/meta": "1.16.17-beta-3232a7af",
48
+ "@blocklet/resolver": "1.16.17-beta-3232a7af",
49
+ "@blocklet/sdk": "1.16.17-beta-3232a7af",
50
50
  "@did-space/client": "^0.3.11",
51
51
  "@fidm/x509": "^1.2.1",
52
52
  "@ocap/mcrypto": "1.18.92",
@@ -101,5 +101,5 @@
101
101
  "jest": "^27.5.1",
102
102
  "unzipper": "^0.10.11"
103
103
  },
104
- "gitHead": "3f18c2e1dd086879b22faf92b6b6954898ed72ef"
104
+ "gitHead": "af4067dd2e418c51a0ed96b1ee242ca812904056"
105
105
  }