@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 +38 -0
- package/lib/blocklet/manager/disk.js +117 -45
- package/lib/event.js +43 -28
- package/lib/index.js +6 -4
- package/lib/monitor/blocklet-runtime-monitor.js +200 -0
- package/lib/monitor/get-history-list.js +37 -0
- package/lib/monitor/node-runtime-monitor.js +228 -0
- package/lib/router/helper.js +9 -4
- package/lib/states/blocklet.js +16 -0
- package/lib/states/site.js +7 -0
- package/lib/states/user.js +13 -1
- package/lib/util/blocklet.js +12 -3
- package/lib/util/index.js +17 -0
- package/lib/util/sysinfo.js +0 -39
- package/lib/validators/router.js +14 -6
- package/package.json +27 -26
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
|
|
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 (!
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1738
|
-
blocklet.
|
|
1779
|
+
// app runtime info, app status
|
|
1780
|
+
blocklet.appRuntimeInfo = this.runtimeMonitor.getRuntimeInfo(blocklet.meta.did);
|
|
1739
1781
|
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
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 (
|
|
1755
|
-
await
|
|
1756
|
-
|
|
1786
|
+
if (level === 0) {
|
|
1787
|
+
component.diskInfo = await getDiskInfo(component, {
|
|
1788
|
+
useFakeDiskInfo: !diskInfo,
|
|
1789
|
+
});
|
|
1757
1790
|
}
|
|
1758
|
-
}
|
|
1759
1791
|
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
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
|
-
|
|
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
|
-
|
|
40
|
-
|
|
41
|
-
|
|
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
|
-
//
|
|
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
|
-
|
|
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
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
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
|
-
|
|
318
|
-
|
|
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
|
-
|
|
390
|
-
|
|
391
|
-
|
|
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;
|
package/lib/router/helper.js
CHANGED
|
@@ -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 {
|
|
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 =
|
|
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:
|
|
676
|
+
daemonDidDomain: getServerDidDomain(nodeInfo),
|
|
672
677
|
didRegistryUrl: nodeInfo.didRegistry,
|
|
673
678
|
domain: nodeInfo.didDomain,
|
|
674
679
|
})
|
package/lib/states/blocklet.js
CHANGED
|
@@ -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;
|
package/lib/states/site.js
CHANGED
|
@@ -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;
|
package/lib/states/user.js
CHANGED
|
@@ -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
|
-
|
|
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') {
|
package/lib/util/blocklet.js
CHANGED
|
@@ -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 {
|
|
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(
|
|
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 =
|
|
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;
|
package/lib/util/sysinfo.js
CHANGED
|
@@ -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;
|
package/lib/validators/router.js
CHANGED
|
@@ -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
|
|
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.
|
|
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.
|
|
23
|
-
"@abtnode/certificate-manager": "1.8.
|
|
24
|
-
"@abtnode/constant": "1.8.
|
|
25
|
-
"@abtnode/cron": "1.8.
|
|
26
|
-
"@abtnode/db": "1.8.
|
|
27
|
-
"@abtnode/logger": "1.8.
|
|
28
|
-
"@abtnode/queue": "1.8.
|
|
29
|
-
"@abtnode/rbac": "1.8.
|
|
30
|
-
"@abtnode/router-provider": "1.8.
|
|
31
|
-
"@abtnode/static-server": "1.8.
|
|
32
|
-
"@abtnode/timemachine": "1.8.
|
|
33
|
-
"@abtnode/util": "1.8.
|
|
34
|
-
"@arcblock/did": "1.18.
|
|
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.
|
|
37
|
-
"@arcblock/event-hub": "1.18.
|
|
38
|
-
"@arcblock/jwt": "^1.18.
|
|
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.
|
|
41
|
-
"@blocklet/constant": "1.8.
|
|
42
|
-
"@blocklet/meta": "1.8.
|
|
43
|
-
"@blocklet/sdk": "1.8.
|
|
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.
|
|
46
|
-
"@ocap/util": "1.18.
|
|
47
|
-
"@ocap/wallet": "1.18.
|
|
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": "
|
|
86
|
+
"gitHead": "80217320b7cbd8c815172f8a985584586c32e858"
|
|
86
87
|
}
|