@abtnode/core 1.8.50 → 1.8.52

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/api/node.js CHANGED
@@ -14,6 +14,14 @@ const IP = require('../util/ip');
14
14
  const { validateNodeInfo, validateUpdateGateway } = require('../validators/node');
15
15
  const { getAll } = require('../blocklet/manager/engine');
16
16
  const { getDelegateState } = require('../util');
17
+ const { NodeRuntimeMonitor } = require('../monitor/node-runtime-monitor');
18
+ const getHistoryList = require('../monitor/get-history-list');
19
+
20
+ // 10s 上报统计一次
21
+ const MONITOR_RECORD_INTERVAL_SEC = 10;
22
+
23
+ // 保存当天数据, 每天上报 8640 次
24
+ const MONITOR_HISTORY_LENGTH = 86400 / MONITOR_RECORD_INTERVAL_SEC;
17
25
 
18
26
  class NodeAPI {
19
27
  /**
@@ -24,6 +32,10 @@ class NodeAPI {
24
32
  assert.notStrictEqual(state, undefined, 'argument state can not be undefined');
25
33
  assert.notStrictEqual(state, null, 'argument state can not be null');
26
34
 
35
+ this.runtimeMonitor = new NodeRuntimeMonitor({
36
+ historyLength: MONITOR_HISTORY_LENGTH,
37
+ });
38
+
27
39
  this.state = state;
28
40
  }
29
41
 
@@ -115,6 +127,32 @@ class NodeAPI {
115
127
  const transferV2Delegation = (state.ops || []).find(({ key }) => key === 'fg:t:transfer_v2');
116
128
  return { delegated: !!transferV2Delegation };
117
129
  }
130
+
131
+ // eslint-disable-next-line no-unused-vars
132
+ getHistory({ hours = 1 } = {}, context) {
133
+ const history = this.runtimeMonitor.getHistory();
134
+
135
+ return getHistoryList({
136
+ history,
137
+ hours,
138
+ recordIntervalSec: MONITOR_RECORD_INTERVAL_SEC,
139
+ props: ['date', 'cpu', 'mem', 'daemonMem', 'serviceMem', 'dbMem'],
140
+ });
141
+ }
142
+
143
+ getRealtimeData() {
144
+ return this.runtimeMonitor.getRealtimeData();
145
+ }
146
+
147
+ getCrons() {
148
+ return [
149
+ {
150
+ name: 'record-node-runtime-info',
151
+ time: `*/${MONITOR_RECORD_INTERVAL_SEC} * * * * *`,
152
+ fn: () => this.runtimeMonitor.monit(),
153
+ },
154
+ ];
155
+ }
118
156
  }
119
157
 
120
158
  module.exports = NodeAPI;
@@ -4,7 +4,6 @@ const fs = require('fs-extra');
4
4
  const { fileURLToPath } = require('url');
5
5
  const path = require('path');
6
6
  const flat = require('flat');
7
- const urlHttp = require('url-http');
8
7
  const get = require('lodash/get');
9
8
  const pick = require('lodash/pick');
10
9
  const cloneDeep = require('lodash/cloneDeep');
@@ -13,8 +12,9 @@ const capitalize = require('lodash/capitalize');
13
12
  const { Throttle } = require('stream-throttle');
14
13
  const LRU = require('lru-cache');
15
14
  const joi = require('joi');
15
+ const isUrl = require('is-url');
16
16
  const { isNFTExpired } = require('@abtnode/util/lib/nft');
17
- const { disableDNS } = require('@abtnode/util/lib/did-document');
17
+ const didDocument = require('@abtnode/util/lib/did-document');
18
18
  const { sign } = require('@arcblock/jwt');
19
19
  const { isValid: isValidDid } = require('@arcblock/did');
20
20
  const { verifyPresentation } = require('@arcblock/vc');
@@ -45,7 +45,6 @@ const {
45
45
  forEachBlockletSync,
46
46
  forEachChildSync,
47
47
  forEachBlocklet,
48
- forEachChild,
49
48
  getComponentId,
50
49
  getComponentBundleId,
51
50
  isPreferenceKey,
@@ -112,7 +111,6 @@ const {
112
111
  verifyIntegrity,
113
112
  pruneBlockletBundle,
114
113
  getDiskInfo,
115
- getRuntimeInfo,
116
114
  getUpdateMetaList,
117
115
  getRuntimeEnvironments,
118
116
  getSourceFromInstallParams,
@@ -136,9 +134,11 @@ const blockletPm2Events = require('./pm2-events');
136
134
  const { getFactoryState } = require('../../util/chain');
137
135
  const runMigrationScripts = require('../migration');
138
136
  const hooks = require('../hooks');
139
- const { formatName } = require('../../util/get-domain-for-blocklet');
137
+ const { formatName, getDidDomainForBlocklet } = require('../../util/get-domain-for-blocklet');
140
138
  const handleInstanceInStore = require('../../util/public-to-store');
141
- const { getNFTState } = require('../../util');
139
+ const { getNFTState, getServerDidDomain } = require('../../util');
140
+ const { BlockletRuntimeMonitor } = require('../../monitor/blocklet-runtime-monitor');
141
+ const getHistoryList = require('../../monitor/get-history-list');
142
142
 
143
143
  const {
144
144
  isInProgress,
@@ -194,6 +194,12 @@ const getSkippedProcessIds = ({ newBlocklet, oldBlocklet, context = {} }) => {
194
194
  const getBlockletIndex = (meta, controller) =>
195
195
  controller ? toExternalBlocklet(meta.name, controller.nftId) : { did: meta.did, name: meta.name };
196
196
 
197
+ // 10s 上报统计一次
198
+ const MONITOR_RECORD_INTERVAL_SEC = 10;
199
+
200
+ // 保存当天数据, 每天上报 8640 次
201
+ const MONITOR_HISTORY_LENGTH = 86400 / MONITOR_RECORD_INTERVAL_SEC;
202
+
197
203
  class BlockletManager extends BaseBlockletManager {
198
204
  /**
199
205
  * @param {*} dataDirs generate by ../../util:getDataDirs
@@ -225,6 +231,8 @@ class BlockletManager extends BaseBlockletManager {
225
231
  maxAge: process.env.NODE_ENV === 'test' ? 500 : 0.5 * 60 * 1000, // cache for 0.5 minute
226
232
  });
227
233
 
234
+ this.runtimeMonitor = new BlockletRuntimeMonitor({ historyLength: MONITOR_HISTORY_LENGTH, states });
235
+
228
236
  if (daemon) {
229
237
  blockletPm2Events.on('online', (data) => this._syncPm2Status('online', data.blockletDid));
230
238
  blockletPm2Events.on('stop', (data) => this._syncPm2Status('stop', data.blockletDid));
@@ -1037,7 +1045,7 @@ class BlockletManager extends BaseBlockletManager {
1037
1045
  throw new Error(`${x.key} can not be empty`);
1038
1046
  }
1039
1047
 
1040
- if (!urlHttp(x.value)) {
1048
+ if (!isUrl(x.value)) {
1041
1049
  throw new Error(`${x.key}(${x.value}) is not a valid http address`);
1042
1050
  }
1043
1051
  }
@@ -1065,6 +1073,15 @@ class BlockletManager extends BaseBlockletManager {
1065
1073
 
1066
1074
  // update db
1067
1075
  await states.blockletExtras.setConfigs(dids, newConfigs);
1076
+
1077
+ const isSkOrWalletTypeChanged = newConfigs.find((item) =>
1078
+ [BLOCKLET_CONFIGURABLE_KEY.BLOCKLET_APP_SK, BLOCKLET_CONFIGURABLE_KEY.BLOCKLET_WALLET_TYPE].includes(item.key)
1079
+ );
1080
+
1081
+ if (isSkOrWalletTypeChanged) {
1082
+ await this._updateDidDocument(blocklet);
1083
+ }
1084
+
1068
1085
  await this.updateBlockletEnvironment(rootDid);
1069
1086
 
1070
1087
  // response
@@ -1486,6 +1503,20 @@ class BlockletManager extends BaseBlockletManager {
1486
1503
  return newBlocklet;
1487
1504
  }
1488
1505
 
1506
+ // eslint-disable-next-line no-unused-vars
1507
+ async getRuntimeHistory({ did, hours }, context) {
1508
+ const metaDid = await states.blocklet.getBlockletMetaDid(did);
1509
+
1510
+ const history = this.runtimeMonitor.getHistory(metaDid);
1511
+
1512
+ return getHistoryList({
1513
+ history,
1514
+ hours,
1515
+ recordIntervalSec: MONITOR_RECORD_INTERVAL_SEC,
1516
+ props: ['date', 'cpu', 'mem'],
1517
+ });
1518
+ }
1519
+
1489
1520
  // ============================================================================================
1490
1521
  // Internal API that are used by public APIs and called from CLI
1491
1522
  // ============================================================================================
@@ -1711,7 +1742,7 @@ class BlockletManager extends BaseBlockletManager {
1711
1742
  }
1712
1743
 
1713
1744
  try {
1714
- let blocklet = await this.ensureBlocklet(did, { throwOnNotExist: false });
1745
+ const blocklet = await this.ensureBlocklet(did, { throwOnNotExist: false });
1715
1746
 
1716
1747
  if (!blocklet) {
1717
1748
  return null;
@@ -1721,7 +1752,18 @@ class BlockletManager extends BaseBlockletManager {
1721
1752
 
1722
1753
  // if from cached data, only use cache data of runtime info (engine, diskInfo, runtimeInfo...)
1723
1754
  if (fromCache) {
1724
- blocklet = { ...cachedBlocklet, ...blocklet };
1755
+ const cached = {};
1756
+ forEachBlockletSync(cachedBlocklet, (component, { id }) => {
1757
+ cached[id] = component;
1758
+ });
1759
+
1760
+ Object.assign(blocklet, pick(cachedBlocklet, ['appRuntimeInfo', 'diskInfo']));
1761
+
1762
+ forEachBlockletSync(blocklet, (component, { id }) => {
1763
+ if (cached[id]) {
1764
+ Object.assign(component, pick(cached[id], ['runtimeInfo']));
1765
+ }
1766
+ });
1725
1767
  }
1726
1768
 
1727
1769
  // 处理 domainAliases#value SLOT_FOR_IP_DNS_SITE
@@ -1734,45 +1776,23 @@ class BlockletManager extends BaseBlockletManager {
1734
1776
  }
1735
1777
 
1736
1778
  if (!fromCache) {
1737
- blocklet.engine = getEngine(getBlockletEngineNameByPlatform(blocklet.meta)).describe();
1738
- blocklet.diskInfo = await getDiskInfo(blocklet, { useFakeDiskInfo: !diskInfo });
1779
+ // app runtime info, app status
1780
+ blocklet.appRuntimeInfo = this.runtimeMonitor.getRuntimeInfo(blocklet.meta.did);
1739
1781
 
1740
- try {
1741
- const app = blocklet.meta.group === BlockletGroup.gateway ? blocklet.children[0] : blocklet;
1742
- if (app) {
1743
- const { processId } = app.env;
1744
- blocklet.runtimeInfo = await getRuntimeInfo(processId);
1745
- if (blocklet.runtimeInfo.status && shouldUpdateBlockletStatus(blocklet.status)) {
1746
- blocklet.status = statusMap[blocklet.runtimeInfo.status];
1747
- }
1748
- }
1749
- } catch (err) {
1750
- if (err.code !== 'BLOCKLET_PROCESS_404') {
1751
- logger.error('failed to construct blocklet runtime info', { did, error: err });
1752
- }
1782
+ // app disk info, component runtime info, component status, component engine
1783
+ await forEachBlocklet(blocklet, async (component, { level }) => {
1784
+ component.engine = getEngine(getBlockletEngineNameByPlatform(component.meta)).describe();
1753
1785
 
1754
- if (blocklet.status === BlockletStatus.running) {
1755
- await states.blocklet.setBlockletStatus(did, BlockletStatus.stopped);
1756
- blocklet.status = BlockletStatus.stopped;
1786
+ if (level === 0) {
1787
+ component.diskInfo = await getDiskInfo(component, {
1788
+ useFakeDiskInfo: !diskInfo,
1789
+ });
1757
1790
  }
1758
- }
1759
1791
 
1760
- await forEachChild(blocklet, async (child) => {
1761
- child.engine = getEngine(getBlockletEngineNameByPlatform(child.meta)).describe();
1762
- child.diskInfo = await getDiskInfo(child, {
1763
- useFakeDiskInfo: !diskInfo,
1764
- });
1765
- if (child.meta.group !== BlockletGroup.gateway) {
1766
- try {
1767
- child.runtimeInfo = await getRuntimeInfo(child.env.processId);
1768
- if (child.runtimeInfo.status && shouldUpdateBlockletStatus(child.status)) {
1769
- child.status = statusMap[child.runtimeInfo.status];
1770
- }
1771
- } catch (err) {
1772
- if (err.code !== 'BLOCKLET_PROCESS_404') {
1773
- logger.error('failed to construct blocklet runtime info', { did, error: err });
1774
- }
1775
- }
1792
+ component.runtimeInfo = this.runtimeMonitor.getRuntimeInfo(blocklet.meta.did, component.env.id);
1793
+
1794
+ if (component.runtimeInfo?.status && shouldUpdateBlockletStatus(component.status)) {
1795
+ component.status = statusMap[component.runtimeInfo.status];
1776
1796
  }
1777
1797
  });
1778
1798
  }
@@ -2639,6 +2659,11 @@ class BlockletManager extends BaseBlockletManager {
2639
2659
  time: '0 */30 * * * *', // 30min
2640
2660
  fn: () => this._deleteExpiredExternalBlocklet(),
2641
2661
  },
2662
+ {
2663
+ name: 'record-blocklet-runtime-history',
2664
+ time: `*/${MONITOR_RECORD_INTERVAL_SEC} * * * * *`, // 10s
2665
+ fn: () => this.runtimeMonitor.monitAll(),
2666
+ },
2642
2667
  ];
2643
2668
  }
2644
2669
 
@@ -3607,7 +3632,8 @@ class BlockletManager extends BaseBlockletManager {
3607
3632
 
3608
3633
  const nodeInfo = await states.node.read();
3609
3634
  const { wallet } = getBlockletInfo(blocklet, nodeInfo.sk);
3610
- disableDNS({ wallet, didRegistryUrl: nodeInfo.didRegistry })
3635
+ didDocument
3636
+ .disableDNS({ wallet, didRegistryUrl: nodeInfo.didRegistry })
3611
3637
  .then(() => {
3612
3638
  logger.info(`disabled blocklet ${blocklet.appDid} dns`);
3613
3639
  })
@@ -3623,6 +3649,8 @@ class BlockletManager extends BaseBlockletManager {
3623
3649
  keepRouting = false;
3624
3650
  }
3625
3651
 
3652
+ this.runtimeMonitor.delete(blocklet.meta.did);
3653
+
3626
3654
  this.emit(BlockletEvents.removed, {
3627
3655
  blocklet: result,
3628
3656
  context: {
@@ -3840,6 +3868,50 @@ class BlockletManager extends BaseBlockletManager {
3840
3868
  logger.info('check expired external blocklet failed', { error });
3841
3869
  }
3842
3870
  }
3871
+
3872
+ async _updateDidDocument(blocklet) {
3873
+ const nodeInfo = await states.node.read();
3874
+
3875
+ const { wallet } = getBlockletInfo(
3876
+ {
3877
+ meta: blocklet.meta,
3878
+ environments: [BLOCKLET_CONFIGURABLE_KEY.BLOCKLET_APP_SK, BLOCKLET_CONFIGURABLE_KEY.BLOCKLET_WALLET_TYPE]
3879
+ .map((key) => ({ key, value: blocklet.configObj[key] }))
3880
+ .filter((x) => x.value),
3881
+ },
3882
+ nodeInfo.sk
3883
+ );
3884
+ const didDomain = getDidDomainForBlocklet({
3885
+ blockletAppDid: wallet.address,
3886
+ didDomain: nodeInfo.didDomain,
3887
+ });
3888
+
3889
+ const domainAliases = (get(blocklet, 'site.domainAliases') || []).filter(
3890
+ (item) => !item.value.endsWith(nodeInfo.didDomain) && !item.value.endsWith('did.staging.arcblock.io') // did.staging.arcblock.io 是旧 did domain, 但主要存在于比较旧的节点中, 需要做兼容
3891
+ );
3892
+
3893
+ domainAliases.push({ value: didDomain, isProtected: true });
3894
+
3895
+ // 先更新 routing rule db 中的 domain aliases, 这一步的目的是为了后面用
3896
+ await states.site.updateDomainAliasList(blocklet.site.id, domainAliases);
3897
+
3898
+ this.emit(BlockletEvents.appDidChanged, blocklet);
3899
+
3900
+ const blockletWithEnv = await this.ensureBlocklet(blocklet.meta.did);
3901
+ const appSystemEnvironments = getAppOverwrittenEnvironments(blockletWithEnv, nodeInfo);
3902
+
3903
+ await didDocument.updateBlockletDocument({
3904
+ wallet,
3905
+ blockletAppDid: appSystemEnvironments.BLOCKLET_APP_ID,
3906
+ daemonDidDomain: getServerDidDomain(nodeInfo),
3907
+ didRegistryUrl: nodeInfo.didRegistry,
3908
+ domain: nodeInfo.didDomain,
3909
+ });
3910
+ logger.info('updated blocklet dns document', {
3911
+ did: blocklet.meta.did,
3912
+ currentAppDid: appSystemEnvironments.BLOCKLET_APP_ID,
3913
+ });
3914
+ }
3843
3915
  }
3844
3916
 
3845
3917
  module.exports = BlockletManager;
package/lib/event.js CHANGED
@@ -10,7 +10,6 @@ const handleInstanceInStore = require('./util/public-to-store');
10
10
  const eventHub =
11
11
  process.env.NODE_ENV === 'test' ? require('@arcblock/event-hub/single') : require('@arcblock/event-hub');
12
12
 
13
- const { isCLI } = require('./util');
14
13
  const states = require('./states');
15
14
 
16
15
  const routingSnapshotPrefix = (blocklet) => (blocklet.mode === BLOCKLET_MODES.DEVELOPMENT ? '[DEV] ' : '');
@@ -29,6 +28,7 @@ module.exports = ({
29
28
  certManager,
30
29
  routerManager,
31
30
  node,
31
+ nodeRuntimeMonitor,
32
32
  }) => {
33
33
  const notificationState = states.notification;
34
34
  const nodeState = states.node;
@@ -36,20 +36,14 @@ module.exports = ({
36
36
  const events = new EventEmitter();
37
37
  events.setMaxListeners(0);
38
38
 
39
- // HACK: do not emit any events from CLI
40
- if (isCLI() && process.env.NODE_ENV !== 'test') {
41
- events.emit = (name) => logger.debug('stopped core state event in CLI', name);
42
- }
43
-
44
- // Push events to event hub for other process to consume
45
- const originEmit = events.emit.bind(events);
46
- events.emit = (name, ...args) => {
47
- logger.debug('proxy event to event hub', { name });
48
- eventHub.broadcast(name, ...args);
49
- originEmit(name, ...args);
39
+ let eventHandler = null;
40
+ events.setEventHandler = (handler) => {
41
+ if (typeof handler === 'function') {
42
+ eventHandler = handler;
43
+ }
50
44
  };
51
45
 
52
- // Subscribe events from eventHub and proxy to eventHandler
46
+ // Listen events from eventHub and call eventHandler
53
47
  [...Object.values(BlockletEvents), ...Object.values(EVENTS)].forEach((name) => {
54
48
  eventHub.on(name, (data) => {
55
49
  if (name === BlockletEvents.removed) {
@@ -63,16 +57,29 @@ module.exports = ({
63
57
  });
64
58
  });
65
59
 
66
- let eventHandler = null;
67
-
60
+ // Wipe sensitive data
61
+ // Emit events to event hub
62
+ // Emit events to node listener
68
63
  const onEvent = (name, data) => {
69
64
  let safeData = data;
70
65
  if (get(data, 'meta.did', '')) {
71
66
  safeData = wipeSensitiveData(cloneDeep(data));
72
67
  }
68
+
69
+ logger.debug('proxy event to event hub', { name });
70
+ eventHub.broadcast(name, safeData);
73
71
  events.emit(name, safeData);
74
72
  };
75
73
 
74
+ // Emit events to node listener
75
+ // Call eventHandler
76
+ const onInternalEvent = (name, data) => {
77
+ events.emit(name, data);
78
+ if (typeof eventHandler === 'function') {
79
+ eventHandler({ name, data });
80
+ }
81
+ };
82
+
76
83
  const handleBlockletAdd = async (name, { blocklet, context }) => {
77
84
  try {
78
85
  const changed = await ensureBlockletRouting(blocklet, context);
@@ -199,14 +206,11 @@ module.exports = ({
199
206
  } else if ([BlockletEvents.removed].includes(eventName)) {
200
207
  await handleBlockletRemove(eventName, payload);
201
208
  } else if ([BlockletEvents.started].includes(eventName)) {
202
- try {
203
- const { publicToStore } = blocklet.settings || {};
204
- // 如果一个 blocklet 没有设置 publicToStore,启动成功后不应给 store 发请求
205
- if (publicToStore) {
206
- await handleInstanceInStore(blocklet, { publicToStore });
207
- }
208
- } catch (error) {
209
- logger.error('handleInstanceInStore failed', { error });
209
+ const { publicToStore } = blocklet.settings || {};
210
+ if (publicToStore) {
211
+ handleInstanceInStore(blocklet, { publicToStore }).catch((error) => {
212
+ logger.error('handleInstanceInStore failed', { message: error.message });
213
+ });
210
214
  }
211
215
  } else if ([BlockletEvents.upgradeFailed, BlockletEvents.downgradeFailed].includes(eventName)) {
212
216
  try {
@@ -224,6 +228,19 @@ module.exports = ({
224
228
  } catch (error) {
225
229
  logger.error('Failed to createAuditLog for upgradeBlocklet failed', { error });
226
230
  }
231
+ } else if (BlockletEvents.appDidChanged === eventName) {
232
+ const hash = await takeRoutingSnapshot(
233
+ { message: `${routingSnapshotPrefix(blocklet)}Update blocklet ${blocklet.meta.name} app did`, dryRun: false },
234
+ get(payload, 'context') || {}
235
+ );
236
+
237
+ logger.info('take snapshot after updated blocklet app', { event: eventName, did: blocklet.meta.did, hash });
238
+ }
239
+
240
+ try {
241
+ await blockletManager.runtimeMonitor.monit(blocklet.meta.did);
242
+ } catch (error) {
243
+ logger.error('monit runtime info failed', { error });
227
244
  }
228
245
 
229
246
  if (payload.blocklet && !payload.meta) {
@@ -266,6 +283,7 @@ module.exports = ({
266
283
  BlockletEvents.started,
267
284
  BlockletEvents.startFailed,
268
285
  BlockletEvents.stopped,
286
+ BlockletEvents.appDidChanged,
269
287
  ].forEach((eventName) => {
270
288
  listen(blockletManager, eventName, handleBlockletEvent);
271
289
  });
@@ -314,11 +332,8 @@ module.exports = ({
314
332
 
315
333
  listen(routerManager, BlockletEvents.updated, onEvent);
316
334
 
317
- events.setEventHandler = (handler) => {
318
- if (typeof handler === 'function') {
319
- eventHandler = handler;
320
- }
321
- };
335
+ listen(nodeRuntimeMonitor, EVENTS.NODE_RUNTIME_INFO, onInternalEvent);
336
+ listen(blockletManager.runtimeMonitor, EVENTS.BLOCKLETS_RUNTIME_INFO, onInternalEvent);
322
337
 
323
338
  events.handleServerEvent = handleServerEvent;
324
339
  events.handleBlockletEvent = handleBlockletEvent;
package/lib/index.js CHANGED
@@ -30,7 +30,6 @@ const createQueue = require('./queue');
30
30
  const createEvents = require('./event');
31
31
  const pm2Events = require('./blocklet/manager/pm2-events');
32
32
  const { createStateReadyQueue, createStateReadyHandler } = require('./util/ready');
33
- const { getSysInfo, SysInfoEmitter } = require('./util/sysinfo');
34
33
  const { toStatus, fromStatus, ensureDataDirs, getQueueConcurrencyByMem, getStateCrons } = require('./util');
35
34
 
36
35
  /**
@@ -227,6 +226,7 @@ function ABTNode(options) {
227
226
  hasBlocklet: blockletManager.hasBlocklet.bind(blockletManager),
228
227
  updateAllBlockletEnvironment: blockletManager.updateAllBlockletEnvironment.bind(blockletManager),
229
228
  setBlockletInitialized: blockletManager.setInitialized.bind(blockletManager),
229
+ getBlockletRuntimeHistory: blockletManager.getRuntimeHistory.bind(blockletManager),
230
230
 
231
231
  // Store
232
232
  getBlockletMeta: StoreUtil.getBlockletMeta,
@@ -386,9 +386,9 @@ function ABTNode(options) {
386
386
  ),
387
387
  endSession: (params, context) => states.session.end(params.id, context),
388
388
 
389
- // Utilities: moved here because some deps require native build
390
- getSysInfo,
391
- getSysInfoEmitter: (interval) => new SysInfoEmitter(interval),
389
+ getNodeRuntimeHistory: nodeAPI.getHistory.bind(nodeAPI),
390
+ getNodeRuntimeInfo: nodeAPI.getRealtimeData.bind(nodeAPI),
391
+
392
392
  getRouterProvider,
393
393
  };
394
394
 
@@ -405,6 +405,7 @@ function ABTNode(options) {
405
405
  certManager,
406
406
  routerManager,
407
407
  node: instance,
408
+ nodeRuntimeMonitor: nodeAPI.runtimeMonitor,
408
409
  });
409
410
 
410
411
  const webhook = WebHook({ events, dataDirs, instance });
@@ -419,6 +420,7 @@ function ABTNode(options) {
419
420
  ...blockletManager.getCrons(),
420
421
  DiskMonitor.getCron(),
421
422
  ...getStateCrons(states),
423
+ ...nodeAPI.getCrons(),
422
424
  ],
423
425
  onError: (error, name) => {
424
426
  states.notification.create({
@@ -0,0 +1,200 @@
1
+ const EventEmitter = require('events');
2
+
3
+ const cloneDeep = require('lodash/cloneDeep');
4
+ const pLimit = require('p-limit');
5
+ const { forEachBlocklet } = require('@blocklet/meta/lib/util');
6
+ const getComponentProcessId = require('@blocklet/meta/lib/get-component-process-id');
7
+ const { EVENTS } = require('@abtnode/constant');
8
+ const { BlockletStatus, BlockletGroup } = require('@blocklet/constant');
9
+ const defaultLogger = require('@abtnode/logger')('blocklet-runtime-monitor');
10
+
11
+ const { getRuntimeInfo } = require('../util/blocklet');
12
+
13
+ // type BlockletHistoryItem = {
14
+ // date: number;
15
+ // cpu: number;
16
+ // mem: number;
17
+ // };
18
+
19
+ // type History = Array<BlockletHistoryItem>;
20
+
21
+ // type RuntimeInfo = {
22
+ // pid: number;
23
+ // uptime: number;
24
+ // memoryUsage: number;
25
+ // cpuUsage: number;
26
+ // status: string;
27
+ // };
28
+
29
+ // type ComponentMonitorData = {
30
+ // runtimeInfo: RuntimeInfo;
31
+ // }
32
+
33
+ // type AppMonitorData = ComponentMonitorData & {
34
+ // history: History;
35
+ // }
36
+
37
+ // type BlockletMonitorData = {
38
+ // app: AppMonitorData;
39
+ // [componentId: string]: ComponentMonitorData;
40
+ // }
41
+
42
+ // type MonitorData = {
43
+ // [blockletDid: string]: BlockletMonitorData;
44
+ // }
45
+
46
+ class BlockletRuntimeMonitor extends EventEmitter {
47
+ constructor({ historyLength = 100, states, logger = defaultLogger } = {}) {
48
+ super();
49
+
50
+ /**
51
+ * private
52
+ */
53
+ this.historyLength = historyLength;
54
+ this.states = states;
55
+ this.data = {};
56
+ this.logger = logger;
57
+ this.inProgress = false;
58
+ }
59
+
60
+ getHistory(blockletDid) {
61
+ if (!this.data[blockletDid]) {
62
+ return [];
63
+ }
64
+
65
+ return this.data[blockletDid].app.history || [];
66
+ }
67
+
68
+ getRuntimeInfo(blockletDid, componentId = 'app') {
69
+ if (!this.data[blockletDid]) {
70
+ return null;
71
+ }
72
+
73
+ if (!this.data[blockletDid][componentId]) {
74
+ return null;
75
+ }
76
+
77
+ return this.data[blockletDid][componentId].runtimeInfo;
78
+ }
79
+
80
+ async monit(did) {
81
+ const blocklet = await this.states.blocklet.getBlocklet(did);
82
+ await this._monit(blocklet);
83
+ }
84
+
85
+ async _monit(blocklet, { addToHistory } = {}) {
86
+ const {
87
+ meta: { did: blockletDid },
88
+ status,
89
+ } = blocklet;
90
+
91
+ if (status !== BlockletStatus.running) {
92
+ if (this.data[blockletDid]) {
93
+ Object.keys(this.data[blockletDid]).forEach((key) => {
94
+ delete this.data[blockletDid][key].runtimeInfo;
95
+ });
96
+ }
97
+ return;
98
+ }
99
+
100
+ this.data[blockletDid] = this.data[blockletDid] || {
101
+ app: {
102
+ runtimeInfo: {
103
+ memoryUsage: 0,
104
+ cpuUsage: 0,
105
+ pid: -1,
106
+ uptime: 0,
107
+ status: '',
108
+ },
109
+ history: [],
110
+ },
111
+ };
112
+
113
+ let appCpu = 0;
114
+ let appMem = 0;
115
+
116
+ await forEachBlocklet(
117
+ blocklet,
118
+ async (component, { id: componentId, ancestors }) => {
119
+ if (component.meta.group !== BlockletGroup.gateway) {
120
+ const processId = getComponentProcessId(component, ancestors);
121
+ try {
122
+ const runtimeInfo = await getRuntimeInfo(processId);
123
+
124
+ this.data[blockletDid][componentId] = { runtimeInfo };
125
+
126
+ if (!component.mountPoint || component.mountPoint === '/') {
127
+ this.data[blockletDid].app.runtimeInfo = cloneDeep(runtimeInfo);
128
+ }
129
+
130
+ appCpu += runtimeInfo.cpuUsage || 0;
131
+ appMem += runtimeInfo.memoryUsage || 0;
132
+ } catch (err) {
133
+ this.logger.error('failed to get blocklet runtime info', { processId, error: err });
134
+ }
135
+ }
136
+ },
137
+ { parallel: true }
138
+ );
139
+
140
+ this.data[blockletDid].app.runtimeInfo.cpuUsage = appCpu;
141
+ this.data[blockletDid].app.runtimeInfo.memoryUsage = appMem;
142
+
143
+ // push to history
144
+ if (addToHistory) {
145
+ this._push(blockletDid, { date: Date.now(), cpu: appCpu, mem: appMem });
146
+ }
147
+ }
148
+
149
+ async monitAll() {
150
+ if (this.inProgress) {
151
+ this.logger.error('monitoring is in progress');
152
+ return;
153
+ }
154
+
155
+ this.inProgress = true;
156
+
157
+ try {
158
+ const blocklets = await this.states.blocklet.getBlocklets();
159
+
160
+ const limit = pLimit(5);
161
+ await Promise.allSettled(
162
+ blocklets.map((blocklet) =>
163
+ limit(async () => {
164
+ await this._monit(blocklet, { addToHistory: true });
165
+ })
166
+ )
167
+ );
168
+
169
+ this.inProgress = false;
170
+
171
+ // emit event
172
+ const eventData = [];
173
+ Object.entries(this.data).forEach(([did, x]) => {
174
+ Object.entries(x).forEach(([componentId, { runtimeInfo }]) => {
175
+ if (componentId !== 'app') {
176
+ eventData.push({ did, componentId, runtimeInfo });
177
+ }
178
+ });
179
+ });
180
+ this.emit(EVENTS.BLOCKLETS_RUNTIME_INFO, eventData);
181
+ } catch (error) {
182
+ this.inProgress = false;
183
+ this.logger.error('monit blocklet runtime failed', { error });
184
+ }
185
+ }
186
+
187
+ delete(blockletDid) {
188
+ delete this.data[blockletDid];
189
+ }
190
+
191
+ _push(blockletDid, value) {
192
+ const arr = this.data[blockletDid].app.history;
193
+ arr.push(value);
194
+ if (arr.length > this.historyLength) {
195
+ arr.shift();
196
+ }
197
+ }
198
+ }
199
+
200
+ module.exports.BlockletRuntimeMonitor = BlockletRuntimeMonitor;
@@ -0,0 +1,37 @@
1
+ const maxBy = require('lodash/maxBy');
2
+
3
+ const getHistoryList = ({ history, hours, recordIntervalSec, props = [] }) => {
4
+ if (hours < 1 || hours > 24) {
5
+ throw new Error('hours should between 1 to 24');
6
+ }
7
+ const intHours = Math.floor(hours);
8
+
9
+ const nPerHour = (60 / recordIntervalSec) * 60;
10
+ const n = nPerHour * intHours;
11
+ const list = history.slice(-n);
12
+
13
+ let i = list.length;
14
+
15
+ const res = [];
16
+
17
+ while (i > 0 && res.length < nPerHour) {
18
+ const arr = list.slice(Math.max(0, i - intHours), i);
19
+
20
+ // 取一段时间的最大值
21
+ if (props && props.length) {
22
+ const obj = {};
23
+ props.forEach((p) => {
24
+ obj[p] = (maxBy(arr, (x) => x[p]) || {})[p];
25
+ });
26
+ res.unshift(obj);
27
+ } else {
28
+ res.unshift(Math.max(...arr));
29
+ }
30
+
31
+ i -= intHours;
32
+ }
33
+
34
+ return res;
35
+ };
36
+
37
+ module.exports = getHistoryList;
@@ -0,0 +1,228 @@
1
+ const EventEmitter = require('events');
2
+ const pick = require('lodash/pick');
3
+ const cloneDeep = require('lodash/cloneDeep');
4
+
5
+ const { PROCESS_NAME_DAEMON, PROCESS_NAME_PROXY, PROCESS_NAME_SERVICE, EVENTS } = require('@abtnode/constant');
6
+ const defaultLogger = require('@abtnode/logger')('@abtnode/util:node-runtime-info');
7
+ const pm2 = require('@abtnode/util/lib/async-pm2');
8
+
9
+ const { getSysInfo } = require('../util/sysinfo');
10
+
11
+ const getProcessInfo = (processId, { returnList } = {}) =>
12
+ new Promise((resolve, reject) => {
13
+ pm2.describe(processId, (err, infos) => {
14
+ if (err) {
15
+ return reject(err);
16
+ }
17
+
18
+ if (!infos || !infos[0]) {
19
+ return resolve(null);
20
+ }
21
+
22
+ if (returnList) {
23
+ return resolve(infos);
24
+ }
25
+
26
+ return resolve(infos[0]);
27
+ });
28
+ });
29
+
30
+ // type RuntimeInfo = {
31
+ // pid: number;
32
+ // uptime: number;
33
+ // memoryUsage: number;
34
+ // cpuUsage: number;
35
+ // status: string;
36
+ // };
37
+
38
+ // type ServiceRuntimeInfo = RuntimeInfo & {
39
+ // instanceCount: number;
40
+ // };
41
+
42
+ // type NodeHistoryItem = {
43
+ // date: number;
44
+ // cpu: number;
45
+ // mem: number;
46
+ // daemonMem: number;
47
+ // serviceMem: number;
48
+ // dbMem: number;
49
+ // };
50
+
51
+ // type History = Array<NodeHistoryItem>;
52
+
53
+ // type Realtime = {
54
+ // cpu: {};
55
+ // mem: {};
56
+ // os: {};
57
+ // disks: [];
58
+ // daemon: RuntimeInfo
59
+ // service: ServiceRuntimeInfo
60
+ // db: RuntimeInfo
61
+
62
+ // type MonitorData = {
63
+ // realtime: Realtime;
64
+ // history: History;
65
+ // }
66
+
67
+ const DEFAULT_DATA = {
68
+ realtime: {
69
+ cpu: {},
70
+ mem: {},
71
+ os: {},
72
+ disks: [],
73
+ daemon: {},
74
+ service: {},
75
+ db: {},
76
+ },
77
+ history: [],
78
+ };
79
+
80
+ class NodeRuntimeMonitor extends EventEmitter {
81
+ constructor({ historyLength = 100, logger = defaultLogger } = {}) {
82
+ super();
83
+
84
+ /**
85
+ * private
86
+ */
87
+ this.historyLength = historyLength;
88
+ this.logger = logger;
89
+ this.data = cloneDeep(DEFAULT_DATA);
90
+ this.inProgress = false;
91
+ }
92
+
93
+ emitRealtimeData() {
94
+ this.emit(EVENTS.NODE_RUNTIME_INFO, this.getRealtimeData());
95
+ }
96
+
97
+ getRealtimeData() {
98
+ return this.data.realtime;
99
+ }
100
+
101
+ getHistory() {
102
+ return this.data.history;
103
+ }
104
+
105
+ async monit() {
106
+ if (this.inProgress) {
107
+ this.logger.error('monitoring is in progress');
108
+ return;
109
+ }
110
+
111
+ this.inProgress = true;
112
+
113
+ const date = Date.now();
114
+ await Promise.allSettled([
115
+ getSysInfo().catch((error) => {
116
+ this.logger.error(`failed to getSysInfo: ${error.message}`);
117
+ }),
118
+ getProcessInfo(PROCESS_NAME_SERVICE, { returnList: true }).catch((error) => {
119
+ this.logger.error(`failed to get blocklet-service info: ${error.message}`);
120
+ }),
121
+ process.env.NODE_ENV !== 'development'
122
+ ? getProcessInfo(PROCESS_NAME_DAEMON).catch((error) => {
123
+ this.logger.error(`failed to get daemon info: ${error.message}`);
124
+ })
125
+ : Promise.resolve(),
126
+ process.env.NODE_ENV !== 'development'
127
+ ? getProcessInfo(PROCESS_NAME_PROXY).catch((error) => {
128
+ this.logger.error(`failed to get db info: ${error.message}`);
129
+ })
130
+ : Promise.resolve(),
131
+ ])
132
+ .then(([{ value: sysInfo }, { value: serviceInfos }, { value: daemonInfo }, { value: dbInfo }]) => {
133
+ this.inProgress = false;
134
+
135
+ const historyItem = {
136
+ date,
137
+ cpu: 0,
138
+ mem: 0,
139
+ daemonMem: 0,
140
+ serviceMem: 0,
141
+ dbMem: 0,
142
+ };
143
+
144
+ if (sysInfo) {
145
+ Object.assign(this.data.realtime, pick(sysInfo, ['cpu', 'mem', 'os', 'disks']));
146
+
147
+ historyItem.cpu = Number(sysInfo.cpu.currentLoad.toFixed(2));
148
+ historyItem.mem = sysInfo.mem.total - sysInfo.mem.available;
149
+ }
150
+
151
+ if (serviceInfos) {
152
+ const [proc] = serviceInfos;
153
+
154
+ const runtimeInfo = {
155
+ pid: proc.pid,
156
+ uptime: proc.pm2_env ? +new Date() - Number(proc.pm2_env.pm_uptime) : 0,
157
+ // FIXME: 将 services 进程聚合在一起不利于排查问题
158
+ memoryUsage: serviceInfos.reduce((total, info) => total + info.monit.memory, 0),
159
+ cpuUsage: serviceInfos.reduce((total, info) => total + info.monit.cpu, 0),
160
+ status: proc.pm2_env ? proc.pm2_env.status : null,
161
+ instanceCount: serviceInfos.length,
162
+ };
163
+
164
+ this.data.realtime.service = runtimeInfo;
165
+
166
+ historyItem.serviceMem = runtimeInfo.memoryUsage;
167
+ } else {
168
+ this.data.realtime.service = {};
169
+ }
170
+
171
+ if (daemonInfo) {
172
+ const proc = daemonInfo;
173
+
174
+ const runtimeInfo = {
175
+ pid: proc.pid,
176
+ uptime: proc.pm2_env ? +new Date() - Number(proc.pm2_env.pm_uptime) : 0,
177
+ memoryUsage: proc.monit.memory,
178
+ cpuUsage: proc.monit.cpu,
179
+ status: proc.pm2_env ? proc.pm2_env.status : null,
180
+ };
181
+
182
+ this.data.realtime.daemon = runtimeInfo;
183
+
184
+ historyItem.daemonMem = runtimeInfo.memoryUsage;
185
+ } else {
186
+ this.data.realtime.daemon = {};
187
+ }
188
+
189
+ if (dbInfo) {
190
+ const proc = dbInfo;
191
+
192
+ const runtimeInfo = {
193
+ pid: proc.pid,
194
+ uptime: proc.pm2_env ? +new Date() - Number(proc.pm2_env.pm_uptime) : 0,
195
+ memoryUsage: proc.monit.memory,
196
+ cpuUsage: proc.monit.cpu,
197
+ status: proc.pm2_env ? proc.pm2_env.status : null,
198
+ };
199
+
200
+ this.data.realtime.db = runtimeInfo;
201
+
202
+ historyItem.dbMem = runtimeInfo.memoryUsage;
203
+ } else {
204
+ this.data.realtime.db = {};
205
+ }
206
+
207
+ this.logger.info('server runtime info', historyItem);
208
+
209
+ this._push(historyItem);
210
+
211
+ this.emitRealtimeData();
212
+ })
213
+ .catch((err) => {
214
+ this.inProgress = false;
215
+ this.logger.error(err.message);
216
+ });
217
+ }
218
+
219
+ _push(value) {
220
+ const arr = this.data.history;
221
+ arr.push(value);
222
+ if (arr.length > this.historyLength) {
223
+ arr.shift();
224
+ }
225
+ }
226
+ }
227
+
228
+ module.exports.NodeRuntimeMonitor = NodeRuntimeMonitor;
@@ -12,7 +12,6 @@ const { getProvider } = require('@abtnode/router-provider');
12
12
  const normalizePathPrefix = require('@abtnode/util/lib/normalize-path-prefix');
13
13
  const getTmpDir = require('@abtnode/util/lib/get-tmp-directory');
14
14
  const downloadFile = require('@abtnode/util/lib/download-file');
15
- const { encode: encodeBase32 } = require('@abtnode/util/lib/base32');
16
15
  const { updateBlockletDocument } = require('@abtnode/util/lib/did-document');
17
16
  const getBlockletInfo = require('@blocklet/meta/lib/info');
18
17
  const {
@@ -45,7 +44,13 @@ const {
45
44
 
46
45
  // eslint-disable-next-line global-require
47
46
  const logger = require('@abtnode/logger')(`${require('../../package.json').name}:router:helper`);
48
- const { getProviderFromNodeInfo, getHttpsCertInfo, findInterfacePortByName, getWellknownSitePort } = require('../util');
47
+ const {
48
+ getProviderFromNodeInfo,
49
+ getHttpsCertInfo,
50
+ findInterfacePortByName,
51
+ getWellknownSitePort,
52
+ getServerDidDomain,
53
+ } = require('../util');
49
54
  const { getIpDnsDomainForBlocklet, getDidDomainForBlocklet } = require('../util/get-domain-for-blocklet');
50
55
  const { getFromCache: getAccessibleExternalNodeIp } = require('../util/get-accessible-external-node-ip');
51
56
 
@@ -573,7 +578,7 @@ module.exports = function getRouterHelpers({ dataDirs, routingSnapshot, routerMa
573
578
  (item) => !item.value.endsWith(ipWildcardDomain) && !item.value.endsWith(info.didDomain)
574
579
  );
575
580
 
576
- const didDomain = `${encodeBase32(info.did)}.${info.didDomain}`;
581
+ const didDomain = getServerDidDomain(info);
577
582
  const dashboardAliasDomains = [
578
583
  { value: ipWildcardDomain, isProtected: true },
579
584
  { value: didDomain, isProtected: true },
@@ -668,7 +673,7 @@ module.exports = function getRouterHelpers({ dataDirs, routingSnapshot, routerMa
668
673
  updateBlockletDocument({
669
674
  wallet,
670
675
  blockletAppDid: blocklet.appDid,
671
- daemonDidDomain: `${encodeBase32(nodeInfo.did)}.${nodeInfo.didDomain}`,
676
+ daemonDidDomain: getServerDidDomain(nodeInfo),
672
677
  didRegistryUrl: nodeInfo.didRegistry,
673
678
  domain: nodeInfo.didDomain,
674
679
  })
@@ -106,6 +106,22 @@ class BlockletState extends BaseState {
106
106
  });
107
107
  }
108
108
 
109
+ getBlockletMetaDid(did) {
110
+ return new Promise((resolve, reject) => {
111
+ if (!did) {
112
+ resolve(null);
113
+ }
114
+
115
+ this.findOne({ $or: [{ 'meta.did': did }, { appDid: did }] }, (err, doc) => {
116
+ if (err) {
117
+ return reject(err);
118
+ }
119
+
120
+ return resolve(doc ? doc.meta.did : null);
121
+ });
122
+ });
123
+ }
124
+
109
125
  async isExternalBlocklet(did) {
110
126
  if (!did) {
111
127
  return false;
@@ -3,6 +3,7 @@ const { toSlotDomain } = require('@abtnode/router-provider/lib/util');
3
3
 
4
4
  const BaseState = require('./base');
5
5
  const { getBlockletDomainGroupName } = require('../util/router');
6
+ const { validateUpdateDomainAliases } = require('../validators/router');
6
7
 
7
8
  class SiteState extends BaseState {
8
9
  constructor(baseDir, config = {}) {
@@ -83,6 +84,12 @@ class SiteState extends BaseState {
83
84
  const site = await this.findOneByBlocklet(did);
84
85
  return site.domainAliases.map((x) => x.value).filter(Boolean);
85
86
  }
87
+
88
+ async updateDomainAliasList(id, domainAliases) {
89
+ await validateUpdateDomainAliases(domainAliases);
90
+
91
+ return super.update({ _id: id }, { $set: { domainAliases } });
92
+ }
86
93
  }
87
94
 
88
95
  module.exports = SiteState;
@@ -1,6 +1,8 @@
1
1
  const pickBy = require('lodash/pickBy');
2
+ const escapeStringRegexp = require('escape-string-regexp');
2
3
 
3
4
  const logger = require('@abtnode/logger')('@abtnode/core:states:user');
5
+ const { isValid } = require('@arcblock/did');
4
6
  const { PASSPORT_STATUS } = require('@abtnode/constant');
5
7
  const BaseState = require('./base');
6
8
  const { validateOwner } = require('../util');
@@ -98,7 +100,17 @@ class User extends BaseState {
98
100
  }
99
101
 
100
102
  if (search) {
101
- queryParam.$or = [{ fullName: search }, { did: search }];
103
+ if (search.length > 50) {
104
+ throw new Error('the length of search text should not more than 50');
105
+ }
106
+
107
+ if (isValid(search)) {
108
+ queryParam.did = search;
109
+ } else {
110
+ const reg = new RegExp(escapeStringRegexp(search), 'i');
111
+
112
+ queryParam.fullName = { $regex: reg };
113
+ }
102
114
  }
103
115
 
104
116
  if (role && role !== '$all') {
@@ -78,7 +78,13 @@ const { validate: validateEngine, get: getEngine } = require('../blocklet/manage
78
78
 
79
79
  const isRequirementsSatisfied = require('./requirement');
80
80
  const { getDidDomainForBlocklet } = require('./get-domain-for-blocklet');
81
- const { isBeforeInstalled, expandBundle, findInterfacePortByName, validateBlockletMeta } = require('./index');
81
+ const {
82
+ isBeforeInstalled,
83
+ expandBundle,
84
+ findInterfacePortByName,
85
+ validateBlockletMeta,
86
+ prettyURL,
87
+ } = require('./index');
82
88
 
83
89
  const getComponentConfig = (meta) => meta.components || meta.children;
84
90
 
@@ -274,9 +280,12 @@ const getAppSystemEnvironments = (blocklet, nodeInfo) => {
274
280
  let appUrl = '';
275
281
 
276
282
  const domainAliases = get(blocklet, 'site.domainAliases') || [];
277
- const didDomainAlias = domainAliases.find((item) => item.value.endsWith(nodeInfo.didDomain));
283
+ const didDomainAlias = domainAliases.find(
284
+ (item) => item.value.endsWith(nodeInfo.didDomain) || item.value.endsWith('did.staging.arcblock.io') // did.staging.arcblock.io 是旧 did domain, 但主要存在于比较旧的节点中, 需要做兼容
285
+ );
286
+
278
287
  if (didDomainAlias) {
279
- appUrl = domainAliases.value;
288
+ appUrl = prettyURL(didDomainAlias.value, true);
280
289
  } else {
281
290
  appUrl = `https://${getDidDomainForBlocklet({
282
291
  blockletAppDid: appId,
package/lib/util/index.js CHANGED
@@ -14,6 +14,7 @@ const getPortLib = require('get-port');
14
14
  const v8 = require('v8');
15
15
  const normalizePathPrefix = require('@abtnode/util/lib/normalize-path-prefix');
16
16
  const axios = require('@abtnode/util/lib/axios');
17
+ const { encode: encodeBase32 } = require('@abtnode/util/lib/base32');
17
18
  const parseBlockletMeta = require('@blocklet/meta/lib/parse');
18
19
  const { validateMeta, fixAndValidateService } = require('@blocklet/meta/lib/validate');
19
20
  const { BlockletStatus } = require('@blocklet/constant');
@@ -437,6 +438,20 @@ const getNFTState = async (chainHost, nftId) => {
437
438
  return state;
438
439
  };
439
440
 
441
+ const getServerDidDomain = (nodeInfo) => `${encodeBase32(nodeInfo.did)}.${nodeInfo.didDomain}`;
442
+
443
+ const prettyURL = (url, isHttps = true) => {
444
+ if (typeof url !== 'string') {
445
+ return url;
446
+ }
447
+
448
+ if (url.toLowerCase().startsWith('http')) {
449
+ return url;
450
+ }
451
+
452
+ return isHttps ? `https://${url}` : `http://${url}`;
453
+ };
454
+
440
455
  const lib = {
441
456
  validateOwner,
442
457
  getProviderFromNodeInfo,
@@ -472,6 +487,8 @@ const lib = {
472
487
  getStateCrons,
473
488
  getDelegateState,
474
489
  getNFTState,
490
+ getServerDidDomain,
491
+ prettyURL,
475
492
  };
476
493
 
477
494
  module.exports = lib;
@@ -1,4 +1,3 @@
1
- const EventEmitter = require('events');
2
1
  const maxBy = require('lodash/maxBy');
3
2
  const SysInfo = require('systeminformation');
4
3
 
@@ -42,42 +41,4 @@ const getSysInfo = async () => {
42
41
  };
43
42
  };
44
43
 
45
- class SysInfoEmitter extends EventEmitter {
46
- constructor(interval = 3000) {
47
- super();
48
- this.timer = null;
49
- this.polling = false;
50
- this.interval = interval;
51
- }
52
-
53
- startPolling() {
54
- if (this.polling) {
55
- return;
56
- }
57
-
58
- if (this.timer) {
59
- clearInterval(this.timer);
60
- this.timer = null;
61
- }
62
-
63
- this.polling = true;
64
- this.timer = setInterval(() => {
65
- getSysInfo().then((info) => {
66
- if (info) {
67
- this.emit('info', info);
68
- }
69
- });
70
- }, this.interval);
71
- }
72
-
73
- stopPolling() {
74
- if (this.timer) {
75
- clearInterval(this.timer);
76
- this.timer = null;
77
- this.polling = false;
78
- }
79
- }
80
- }
81
-
82
44
  module.exports.getSysInfo = getSysInfo;
83
- module.exports.SysInfoEmitter = SysInfoEmitter;
@@ -96,6 +96,15 @@ const addDomainAlias = Joi.alternatives()
96
96
  )
97
97
  .required();
98
98
 
99
+ const domainAliases = Joi.array().items(
100
+ Joi.object({
101
+ value: addDomainAlias,
102
+ isProtected: Joi.boolean(),
103
+ })
104
+ );
105
+
106
+ const updateAliases = domainAliases.required();
107
+
99
108
  const addSiteSchema = Joi.object({
100
109
  domain: Joi.alternatives()
101
110
  .try(
@@ -105,12 +114,7 @@ const addSiteSchema = Joi.object({
105
114
  )
106
115
  .required()
107
116
  .messages(domainMessages),
108
- domainAliases: Joi.array().items(
109
- Joi.object({
110
- value: addDomainAlias,
111
- isProtected: Joi.boolean(),
112
- })
113
- ),
117
+ domainAliases,
114
118
  isProtected: Joi.boolean(),
115
119
  rules: Joi.array().items(ruleJoiSchema),
116
120
  corsAllowedOrigins: corsSchema,
@@ -148,10 +152,14 @@ const validateUpdateSite = (entity, context) => updateSite.validateAsync(entity,
148
152
  const validateAddRule = (entity, context) => addRuleSchema.validateAsync(entity, getMultipleLangParams(context));
149
153
  const validateEditRule = (entity, context) => editRuleSchema.validateAsync(entity, getMultipleLangParams(context));
150
154
 
155
+ const validateUpdateDomainAliases = (entity, context) =>
156
+ updateAliases.validateAsync(entity, getMultipleLangParams(context));
157
+
151
158
  module.exports = {
152
159
  validateAddRule,
153
160
  validateAddSite,
154
161
  validateEditRule,
155
162
  validateAddDomainAlias,
156
163
  validateUpdateSite,
164
+ validateUpdateDomainAliases,
157
165
  };
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "1.8.50",
6
+ "version": "1.8.52",
7
7
  "description": "",
8
8
  "main": "lib/index.js",
9
9
  "files": [
@@ -19,38 +19,39 @@
19
19
  "author": "wangshijun <wangshijun2010@gmail.com> (http://github.com/wangshijun)",
20
20
  "license": "MIT",
21
21
  "dependencies": {
22
- "@abtnode/auth": "1.8.50",
23
- "@abtnode/certificate-manager": "1.8.50",
24
- "@abtnode/constant": "1.8.50",
25
- "@abtnode/cron": "1.8.50",
26
- "@abtnode/db": "1.8.50",
27
- "@abtnode/logger": "1.8.50",
28
- "@abtnode/queue": "1.8.50",
29
- "@abtnode/rbac": "1.8.50",
30
- "@abtnode/router-provider": "1.8.50",
31
- "@abtnode/static-server": "1.8.50",
32
- "@abtnode/timemachine": "1.8.50",
33
- "@abtnode/util": "1.8.50",
34
- "@arcblock/did": "1.18.32",
22
+ "@abtnode/auth": "1.8.52",
23
+ "@abtnode/certificate-manager": "1.8.52",
24
+ "@abtnode/constant": "1.8.52",
25
+ "@abtnode/cron": "1.8.52",
26
+ "@abtnode/db": "1.8.52",
27
+ "@abtnode/logger": "1.8.52",
28
+ "@abtnode/queue": "1.8.52",
29
+ "@abtnode/rbac": "1.8.52",
30
+ "@abtnode/router-provider": "1.8.52",
31
+ "@abtnode/static-server": "1.8.52",
32
+ "@abtnode/timemachine": "1.8.52",
33
+ "@abtnode/util": "1.8.52",
34
+ "@arcblock/did": "1.18.34",
35
35
  "@arcblock/did-motif": "^1.1.10",
36
- "@arcblock/did-util": "1.18.32",
37
- "@arcblock/event-hub": "1.18.32",
38
- "@arcblock/jwt": "^1.18.32",
36
+ "@arcblock/did-util": "1.18.34",
37
+ "@arcblock/event-hub": "1.18.34",
38
+ "@arcblock/jwt": "^1.18.34",
39
39
  "@arcblock/pm2-events": "^0.0.5",
40
- "@arcblock/vc": "1.18.32",
41
- "@blocklet/constant": "1.8.50",
42
- "@blocklet/meta": "1.8.50",
43
- "@blocklet/sdk": "1.8.50",
40
+ "@arcblock/vc": "1.18.34",
41
+ "@blocklet/constant": "1.8.52",
42
+ "@blocklet/meta": "1.8.52",
43
+ "@blocklet/sdk": "1.8.52",
44
44
  "@fidm/x509": "^1.2.1",
45
- "@ocap/mcrypto": "1.18.32",
46
- "@ocap/util": "1.18.32",
47
- "@ocap/wallet": "1.18.32",
45
+ "@ocap/mcrypto": "1.18.34",
46
+ "@ocap/util": "1.18.34",
47
+ "@ocap/wallet": "1.18.34",
48
48
  "@slack/webhook": "^5.0.4",
49
49
  "axios": "^0.27.2",
50
50
  "axon": "^2.0.3",
51
51
  "chalk": "^4.1.2",
52
52
  "deep-diff": "^1.0.2",
53
53
  "detect-port": "^1.5.1",
54
+ "escape-string-regexp": "^4.0.0",
54
55
  "flat": "^5.0.2",
55
56
  "fs-extra": "^10.1.0",
56
57
  "get-port": "^5.1.1",
@@ -61,6 +62,7 @@
61
62
  "js-yaml": "^4.1.0",
62
63
  "lodash": "^4.17.21",
63
64
  "lru-cache": "^6.0.0",
65
+ "p-limit": "^3.1.0",
64
66
  "pm2": "^5.2.0",
65
67
  "semver": "^7.3.8",
66
68
  "shelljs": "^0.8.5",
@@ -72,7 +74,6 @@
72
74
  "tar": "^6.1.11",
73
75
  "ua-parser-js": "^1.0.2",
74
76
  "unzipper": "^0.10.11",
75
- "url-http": "^1.0.7",
76
77
  "url-join": "^4.0.1",
77
78
  "uuid": "7.0.3"
78
79
  },
@@ -82,5 +83,5 @@
82
83
  "express": "^4.18.2",
83
84
  "jest": "^27.5.1"
84
85
  },
85
- "gitHead": "7f3ccb957659a6791b93b6fc4fbcbbd2c42f1efd"
86
+ "gitHead": "80217320b7cbd8c815172f8a985584586c32e858"
86
87
  }