@abtnode/core 1.7.9 → 1.7.12
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 +7 -1
- package/lib/api/team.js +2 -2
- package/lib/blocklet/manager/disk.js +153 -91
- package/lib/blocklet/registry.js +6 -2
- package/lib/cert.js +14 -0
- package/lib/event.js +9 -0
- package/lib/index.js +41 -9
- package/lib/migrations/1.7.12-blocklet-meta.js +51 -0
- package/lib/router/index.js +8 -1
- package/lib/router/manager.js +3 -3
- package/lib/states/README.md +31 -1
- package/lib/states/audit-log.js +382 -0
- package/lib/states/blocklet.js +9 -7
- package/lib/states/index.js +3 -0
- package/lib/states/node.js +8 -0
- package/lib/util/blocklet.js +103 -45
- package/lib/util/index.js +37 -7
- package/lib/util/ip.js +6 -0
- package/lib/util/rpc.js +16 -0
- package/lib/util/ua.js +54 -0
- package/lib/util/upgrade.js +3 -15
- package/lib/validators/node.js +13 -0
- package/package.json +21 -21
- package/lib/util/service.js +0 -81
package/lib/states/blocklet.js
CHANGED
|
@@ -9,7 +9,7 @@ const detectPort = require('detect-port');
|
|
|
9
9
|
const Lock = require('@abtnode/util/lib/lock');
|
|
10
10
|
const security = require('@abtnode/util/lib/security');
|
|
11
11
|
const { fixPerson, fixInterfaces } = require('@blocklet/meta/lib/fix');
|
|
12
|
-
const { getDisplayName } = require('@blocklet/meta/lib/util');
|
|
12
|
+
const { getDisplayName, forEachBlocklet } = require('@blocklet/meta/lib/util');
|
|
13
13
|
const {
|
|
14
14
|
BlockletStatus,
|
|
15
15
|
BlockletSource,
|
|
@@ -21,7 +21,7 @@ const {
|
|
|
21
21
|
const logger = require('@abtnode/logger')('state-blocklet');
|
|
22
22
|
|
|
23
23
|
const BaseState = require('./base');
|
|
24
|
-
const {
|
|
24
|
+
const { checkDuplicateComponents, ensureMeta } = require('../util/blocklet');
|
|
25
25
|
const { validateBlockletMeta } = require('../util');
|
|
26
26
|
|
|
27
27
|
const lock = new Lock('blocklet-port-assign-lock');
|
|
@@ -143,7 +143,6 @@ class BlockletState extends BaseState {
|
|
|
143
143
|
}
|
|
144
144
|
|
|
145
145
|
addBlocklet({
|
|
146
|
-
did,
|
|
147
146
|
meta,
|
|
148
147
|
source = BlockletSource.registry,
|
|
149
148
|
status = BlockletStatus.added,
|
|
@@ -151,7 +150,7 @@ class BlockletState extends BaseState {
|
|
|
151
150
|
mode = BLOCKLET_MODES.PRODUCTION,
|
|
152
151
|
children: rawChildren = [],
|
|
153
152
|
} = {}) {
|
|
154
|
-
return this.getBlocklet(did).then(
|
|
153
|
+
return this.getBlocklet(meta.did).then(
|
|
155
154
|
(doc) =>
|
|
156
155
|
new Promise(async (resolve, reject) => {
|
|
157
156
|
if (doc) {
|
|
@@ -162,7 +161,10 @@ class BlockletState extends BaseState {
|
|
|
162
161
|
try {
|
|
163
162
|
fixPerson(meta);
|
|
164
163
|
fixInterfaces(meta);
|
|
165
|
-
|
|
164
|
+
let sanitized = validateBlockletMeta(meta);
|
|
165
|
+
// bundle info
|
|
166
|
+
sanitized = ensureMeta(sanitized);
|
|
167
|
+
sanitized = omit(sanitized, ['htmlAst']);
|
|
166
168
|
|
|
167
169
|
// get ports
|
|
168
170
|
await lock.acquire();
|
|
@@ -180,7 +182,7 @@ class BlockletState extends BaseState {
|
|
|
180
182
|
{
|
|
181
183
|
appDid: null, // will updated later when updating blocklet environments
|
|
182
184
|
mode,
|
|
183
|
-
meta:
|
|
185
|
+
meta: sanitized,
|
|
184
186
|
status,
|
|
185
187
|
source,
|
|
186
188
|
deployedFrom,
|
|
@@ -530,7 +532,7 @@ class BlockletState extends BaseState {
|
|
|
530
532
|
throw new Error('Cannot add self as a component');
|
|
531
533
|
}
|
|
532
534
|
|
|
533
|
-
checkDuplicateComponents([child
|
|
535
|
+
checkDuplicateComponents([child, ...newChildren]);
|
|
534
536
|
|
|
535
537
|
newChildren.push({
|
|
536
538
|
mountPoint,
|
package/lib/states/index.js
CHANGED
|
@@ -10,6 +10,7 @@ const MigrationState = require('./migration');
|
|
|
10
10
|
const SessionState = require('./session');
|
|
11
11
|
const ExtrasState = require('./blocklet-extras');
|
|
12
12
|
const CacheState = require('./cache');
|
|
13
|
+
const AuditLogState = require('./audit-log');
|
|
13
14
|
|
|
14
15
|
const init = (dataDirs, options) => {
|
|
15
16
|
const notificationState = new NotificationState(dataDirs.core, options);
|
|
@@ -23,6 +24,7 @@ const init = (dataDirs, options) => {
|
|
|
23
24
|
const sessionState = new SessionState(dataDirs.core, options);
|
|
24
25
|
const extrasState = new ExtrasState(dataDirs.core, options);
|
|
25
26
|
const cacheState = new CacheState(dataDirs.core, options);
|
|
27
|
+
const auditLogState = new AuditLogState(dataDirs.core, options);
|
|
26
28
|
|
|
27
29
|
return {
|
|
28
30
|
node: nodeState,
|
|
@@ -36,6 +38,7 @@ const init = (dataDirs, options) => {
|
|
|
36
38
|
session: sessionState,
|
|
37
39
|
blockletExtras: extrasState,
|
|
38
40
|
cache: cacheState,
|
|
41
|
+
auditLog: auditLogState,
|
|
39
42
|
};
|
|
40
43
|
};
|
|
41
44
|
|
package/lib/states/node.js
CHANGED
|
@@ -278,6 +278,14 @@ class NodeState extends BaseState {
|
|
|
278
278
|
await this.update(_id, { $set: { customBlockletNumber: num } });
|
|
279
279
|
return num;
|
|
280
280
|
}
|
|
281
|
+
|
|
282
|
+
async updateGateway(gateway) {
|
|
283
|
+
const [, nodeInfo] = await this.update({}, { $set: { 'routing.requestLimit': gateway.requestLimit } });
|
|
284
|
+
|
|
285
|
+
this.emit(EVENTS.RELOAD_GATEWAY, nodeInfo);
|
|
286
|
+
|
|
287
|
+
return nodeInfo.routing;
|
|
288
|
+
}
|
|
281
289
|
}
|
|
282
290
|
|
|
283
291
|
module.exports = NodeState;
|
package/lib/util/blocklet.js
CHANGED
|
@@ -6,7 +6,6 @@ const os = require('os');
|
|
|
6
6
|
const tar = require('tar');
|
|
7
7
|
const get = require('lodash/get');
|
|
8
8
|
const intersection = require('lodash/intersection');
|
|
9
|
-
const intersectionBy = require('lodash/intersectionBy');
|
|
10
9
|
const streamToPromise = require('stream-to-promise');
|
|
11
10
|
const { Throttle } = require('stream-throttle');
|
|
12
11
|
const ssri = require('ssri');
|
|
@@ -43,12 +42,12 @@ const getBlockletEngine = require('@blocklet/meta/lib/engine');
|
|
|
43
42
|
const getBlockletInfo = require('@blocklet/meta/lib/info');
|
|
44
43
|
const { validateMeta, fixAndValidateService } = require('@blocklet/meta/lib/validate');
|
|
45
44
|
const { forEachBlocklet, isFreeBlocklet, getDisplayName, findWebInterface } = require('@blocklet/meta/lib/util');
|
|
45
|
+
const toBlockletDid = require('@blocklet/meta/lib/did');
|
|
46
46
|
|
|
47
47
|
const { validate: validateEngine, get: getEngine } = require('../blocklet/manager/engine');
|
|
48
48
|
|
|
49
49
|
const isRequirementsSatisfied = require('./requirement');
|
|
50
50
|
const { getDidDomainForBlocklet } = require('./get-domain-for-blocklet');
|
|
51
|
-
const { getServices } = require('./service');
|
|
52
51
|
const {
|
|
53
52
|
isBeforeInstalled,
|
|
54
53
|
expandBundle,
|
|
@@ -104,7 +103,10 @@ const PRIVATE_NODE_ENVS = [
|
|
|
104
103
|
* appMain: app entry file or script (run appMain to start blocklet process)
|
|
105
104
|
* appCwd: cwd of appMain
|
|
106
105
|
*/
|
|
107
|
-
const getBlockletDirs = (
|
|
106
|
+
const getBlockletDirs = (
|
|
107
|
+
blocklet,
|
|
108
|
+
{ rootBlocklet, dataDirs, ensure = false, e2eMode = false, validate = true } = {}
|
|
109
|
+
) => {
|
|
108
110
|
if (!rootBlocklet) {
|
|
109
111
|
// eslint-disable-next-line no-param-reassign
|
|
110
112
|
rootBlocklet = blocklet;
|
|
@@ -134,7 +136,7 @@ const getBlockletDirs = (blocklet, { rootBlocklet, dataDirs, ensure = false, e2e
|
|
|
134
136
|
}
|
|
135
137
|
}
|
|
136
138
|
|
|
137
|
-
if (!main && !startFromDevEntry && group !== BlockletGroup.gateway) {
|
|
139
|
+
if (validate && !main && !startFromDevEntry && group !== BlockletGroup.gateway) {
|
|
138
140
|
throw new Error('Incorrect blocklet manifest: missing `main` field');
|
|
139
141
|
}
|
|
140
142
|
|
|
@@ -164,7 +166,7 @@ const getBlockletDirs = (blocklet, { rootBlocklet, dataDirs, ensure = false, e2e
|
|
|
164
166
|
|
|
165
167
|
mainDir = appDir;
|
|
166
168
|
|
|
167
|
-
if (!startFromDevEntry && !isBeforeInstalled(rootBlocklet.status)) {
|
|
169
|
+
if (validate && !startFromDevEntry && !isBeforeInstalled(rootBlocklet.status)) {
|
|
168
170
|
try {
|
|
169
171
|
validateBlockletEntry(appDir, blocklet.meta);
|
|
170
172
|
} catch (err) {
|
|
@@ -172,7 +174,7 @@ const getBlockletDirs = (blocklet, { rootBlocklet, dataDirs, ensure = false, e2e
|
|
|
172
174
|
}
|
|
173
175
|
}
|
|
174
176
|
|
|
175
|
-
if (!BLOCKLET_GROUPS.includes(group)) {
|
|
177
|
+
if (validate && !BLOCKLET_GROUPS.includes(group)) {
|
|
176
178
|
throw new CustomError('BLOCKLET_CORRUPTED', `Unsupported blocklet type ${group}`);
|
|
177
179
|
}
|
|
178
180
|
|
|
@@ -634,25 +636,30 @@ const reloadProcess = (appId) =>
|
|
|
634
636
|
});
|
|
635
637
|
});
|
|
636
638
|
|
|
639
|
+
const parseChildren = (children, parentMeta = {}, { dynamic } = {}) => {
|
|
640
|
+
const configs = Array.isArray(parentMeta) ? parentMeta : parentMeta.children || [];
|
|
641
|
+
|
|
642
|
+
children.forEach((x) => {
|
|
643
|
+
if (dynamic !== undefined) {
|
|
644
|
+
x.dynamic = !!dynamic;
|
|
645
|
+
}
|
|
646
|
+
});
|
|
647
|
+
|
|
648
|
+
mergeMeta(configs, children);
|
|
649
|
+
return children;
|
|
650
|
+
};
|
|
651
|
+
|
|
637
652
|
/**
|
|
638
653
|
* this function has side effect on children
|
|
639
654
|
*/
|
|
640
|
-
const
|
|
655
|
+
const parseChildrenFromMeta = async (src, { dynamic } = {}) => {
|
|
641
656
|
const configs = Array.isArray(src) ? src : src.children || [];
|
|
642
657
|
|
|
643
|
-
if (children) {
|
|
644
|
-
children.forEach((x) => {
|
|
645
|
-
x.dynamic = !!dynamic;
|
|
646
|
-
});
|
|
647
|
-
mergeMeta(configs, children);
|
|
648
|
-
return children;
|
|
649
|
-
}
|
|
650
|
-
|
|
651
658
|
if (!configs || !configs.length) {
|
|
652
659
|
return [];
|
|
653
660
|
}
|
|
654
661
|
|
|
655
|
-
const
|
|
662
|
+
const children = [];
|
|
656
663
|
|
|
657
664
|
for (const config of configs) {
|
|
658
665
|
const mountPoint = config.mountPoint || config.mountPoints[0].root.prefix;
|
|
@@ -661,15 +668,6 @@ const parseChildren = async (src, { children, dynamic } = {}) => {
|
|
|
661
668
|
}
|
|
662
669
|
|
|
663
670
|
const m = await getBlockletMetaFromUrl(config.resolved);
|
|
664
|
-
if (m.name !== config.name) {
|
|
665
|
-
logger.error('Resolved child blocklet name does not match in the configuration', {
|
|
666
|
-
expected: config.name,
|
|
667
|
-
resolved: m.name,
|
|
668
|
-
});
|
|
669
|
-
throw new Error(
|
|
670
|
-
`Child blocklet name does not match in the configuration. expected: ${config.name}, resolved: ${m.name}`
|
|
671
|
-
);
|
|
672
|
-
}
|
|
673
671
|
validateBlockletMeta(m, { ensureDist: true });
|
|
674
672
|
|
|
675
673
|
const webInterface = findWebInterface(m);
|
|
@@ -682,17 +680,26 @@ const parseChildren = async (src, { children, dynamic } = {}) => {
|
|
|
682
680
|
throw new Error(`Prefix does not match in child ${config.name}. expected: ${rule}, resolved: ${mountPoint}`);
|
|
683
681
|
}
|
|
684
682
|
|
|
685
|
-
|
|
683
|
+
const meta = ensureMeta(m, { name: config.name });
|
|
684
|
+
if (config.title) {
|
|
685
|
+
meta.title = config.title;
|
|
686
|
+
}
|
|
687
|
+
if (config.description) {
|
|
688
|
+
meta.description = config.description;
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
const child = {
|
|
686
692
|
mountPoint,
|
|
687
|
-
meta
|
|
688
|
-
dynamic: !!dynamic,
|
|
693
|
+
meta,
|
|
689
694
|
sourceUrl: config.resolved,
|
|
690
|
-
}
|
|
695
|
+
};
|
|
696
|
+
|
|
697
|
+
children.push(child);
|
|
691
698
|
}
|
|
692
699
|
|
|
693
|
-
|
|
700
|
+
parseChildren(children, configs, { dynamic });
|
|
694
701
|
|
|
695
|
-
return
|
|
702
|
+
return children;
|
|
696
703
|
};
|
|
697
704
|
|
|
698
705
|
const validateBlocklet = (blocklet) =>
|
|
@@ -801,7 +808,7 @@ const pruneBlockletBundle = async ({ blocklets, installDir, blockletSettings })
|
|
|
801
808
|
].includes(blocklet.status)
|
|
802
809
|
) {
|
|
803
810
|
logger.info('There are blocklet activities in progress, abort pruning', {
|
|
804
|
-
|
|
811
|
+
bundleName: blocklet.meta.bundleName,
|
|
805
812
|
status: fromBlockletStatus(blocklet.status),
|
|
806
813
|
});
|
|
807
814
|
return;
|
|
@@ -811,14 +818,16 @@ const pruneBlockletBundle = async ({ blocklets, installDir, blockletSettings })
|
|
|
811
818
|
// blockletMap: { <[scope/]name/version>: true }
|
|
812
819
|
const blockletMap = {};
|
|
813
820
|
for (const blocklet of blocklets) {
|
|
814
|
-
blockletMap[`${blocklet.meta.
|
|
821
|
+
blockletMap[`${blocklet.meta.bundleName}/${blocklet.meta.version}`] = true;
|
|
815
822
|
for (const child of blocklet.children || []) {
|
|
816
|
-
blockletMap[`${child.meta.
|
|
823
|
+
blockletMap[`${child.meta.bundleName}/${child.meta.version}`] = true;
|
|
817
824
|
}
|
|
818
825
|
}
|
|
819
826
|
for (const setting of blockletSettings) {
|
|
820
827
|
for (const child of setting.children || []) {
|
|
821
|
-
|
|
828
|
+
if (child.status !== BlockletStatus.deleted) {
|
|
829
|
+
blockletMap[`${child.meta.bundleName}/${child.meta.version}`] = true;
|
|
830
|
+
}
|
|
822
831
|
}
|
|
823
832
|
}
|
|
824
833
|
|
|
@@ -1007,11 +1016,7 @@ const mergeMeta = (source, childrenMeta = []) => {
|
|
|
1007
1016
|
});
|
|
1008
1017
|
};
|
|
1009
1018
|
|
|
1010
|
-
const
|
|
1011
|
-
if (meta.did !== did) {
|
|
1012
|
-
throw new Error('Invalid blocklet meta: did does not match');
|
|
1013
|
-
}
|
|
1014
|
-
|
|
1019
|
+
const fixAndVerifyMetaFromStore = (meta) => {
|
|
1015
1020
|
const sanitized = validateMeta(meta, { ensureDist: false, schemaOptions: { noDefaults: true, stripUnknown: true } });
|
|
1016
1021
|
|
|
1017
1022
|
try {
|
|
@@ -1024,7 +1029,7 @@ const fixAndVerifyBlockletMeta = (meta, did) => {
|
|
|
1024
1029
|
}
|
|
1025
1030
|
|
|
1026
1031
|
// this step comes last because it has side effects: the meta is changed
|
|
1027
|
-
return fixAndValidateService(meta
|
|
1032
|
+
return fixAndValidateService(meta);
|
|
1028
1033
|
};
|
|
1029
1034
|
|
|
1030
1035
|
const getUpdateMetaList = (oldMetas = [], newMetas = []) => {
|
|
@@ -1058,8 +1063,10 @@ const getSourceFromInstallParams = (params) => {
|
|
|
1058
1063
|
throw new Error('Can only install blocklet from store/url/upload/custom');
|
|
1059
1064
|
};
|
|
1060
1065
|
|
|
1061
|
-
const checkDuplicateComponents = (
|
|
1062
|
-
const duplicates =
|
|
1066
|
+
const checkDuplicateComponents = (components = []) => {
|
|
1067
|
+
const duplicates = components.filter(
|
|
1068
|
+
(item, index) => components.findIndex((x) => x.meta.did === item.meta.did) !== index
|
|
1069
|
+
);
|
|
1063
1070
|
if (duplicates.length) {
|
|
1064
1071
|
throw new Error(
|
|
1065
1072
|
`Cannot add duplicate component${duplicates.length > 1 ? 's' : ''}: ${duplicates
|
|
@@ -1067,6 +1074,13 @@ const checkDuplicateComponents = (dynamicComponents, staticComponents) => {
|
|
|
1067
1074
|
.join(', ')}`
|
|
1068
1075
|
);
|
|
1069
1076
|
}
|
|
1077
|
+
|
|
1078
|
+
const duplicateMountPoints = components.filter(
|
|
1079
|
+
(item, index) => components.findIndex((x) => x.mountPoint === item.mountPoint) !== index
|
|
1080
|
+
);
|
|
1081
|
+
if (duplicateMountPoints.length) {
|
|
1082
|
+
throw new Error(`mountpoint must be unique: ${duplicateMountPoints.map((x) => x.mountPoint).join(', ')}`);
|
|
1083
|
+
}
|
|
1070
1084
|
};
|
|
1071
1085
|
|
|
1072
1086
|
const getDiffFiles = async (inputFiles, sourceDir) => {
|
|
@@ -1110,7 +1124,7 @@ const getDiffFiles = async (inputFiles, sourceDir) => {
|
|
|
1110
1124
|
};
|
|
1111
1125
|
};
|
|
1112
1126
|
|
|
1113
|
-
const getBundleDir = (installDir,
|
|
1127
|
+
const getBundleDir = (installDir, meta) => path.join(installDir, meta.bundleName || meta.name, meta.version);
|
|
1114
1128
|
|
|
1115
1129
|
const needBlockletDownload = (blocklet, oldBlocklet) => {
|
|
1116
1130
|
if ([BlockletSource.upload, BlockletSource.local, BlockletSource.custom].includes(blocklet.source)) {
|
|
@@ -1140,9 +1154,51 @@ const verifyPurchase = (meta, opts = {}) => {
|
|
|
1140
1154
|
throw new Error('Can not install a non-free blocklet directly');
|
|
1141
1155
|
};
|
|
1142
1156
|
|
|
1157
|
+
const findAvailableDid = (meta, siblings) => {
|
|
1158
|
+
const reg = /-(\d+)$/;
|
|
1159
|
+
const match = reg.exec(meta.name);
|
|
1160
|
+
|
|
1161
|
+
const newName = match ? meta.name.replace(reg, `-${Number(match[1]) + 1}`) : `${meta.name}-1`;
|
|
1162
|
+
const newDid = toBlockletDid(newName);
|
|
1163
|
+
const newMeta = { name: newName, did: newDid };
|
|
1164
|
+
|
|
1165
|
+
if (!(siblings || []).some((x) => x.did === newMeta.did)) {
|
|
1166
|
+
return newMeta;
|
|
1167
|
+
}
|
|
1168
|
+
|
|
1169
|
+
return findAvailableDid(newMeta, siblings);
|
|
1170
|
+
};
|
|
1171
|
+
|
|
1172
|
+
const ensureMeta = (meta, { name, did } = {}) => {
|
|
1173
|
+
if (name && did && toBlockletDid(name) !== did) {
|
|
1174
|
+
throw new Error('name does not match with did');
|
|
1175
|
+
}
|
|
1176
|
+
|
|
1177
|
+
const newMeta = {
|
|
1178
|
+
...meta,
|
|
1179
|
+
};
|
|
1180
|
+
|
|
1181
|
+
if (!newMeta.did || !newMeta.name || toBlockletDid(newMeta.name) !== newMeta.did) {
|
|
1182
|
+
throw new Error('name does not match with did in meta');
|
|
1183
|
+
}
|
|
1184
|
+
|
|
1185
|
+
if (!meta.bundleDid) {
|
|
1186
|
+
newMeta.bundleDid = meta.did;
|
|
1187
|
+
newMeta.bundleName = meta.name;
|
|
1188
|
+
}
|
|
1189
|
+
|
|
1190
|
+
if (name) {
|
|
1191
|
+
newMeta.name = name;
|
|
1192
|
+
newMeta.did = did || toBlockletDid(name);
|
|
1193
|
+
}
|
|
1194
|
+
|
|
1195
|
+
return newMeta;
|
|
1196
|
+
};
|
|
1197
|
+
|
|
1143
1198
|
module.exports = {
|
|
1144
1199
|
forEachBlocklet,
|
|
1145
1200
|
getBlockletMetaFromUrl,
|
|
1201
|
+
parseChildrenFromMeta,
|
|
1146
1202
|
parseChildren,
|
|
1147
1203
|
getBlockletDirs,
|
|
1148
1204
|
getRootSystemEnvironments,
|
|
@@ -1167,7 +1223,7 @@ module.exports = {
|
|
|
1167
1223
|
getDiskInfo,
|
|
1168
1224
|
getRuntimeInfo,
|
|
1169
1225
|
mergeMeta,
|
|
1170
|
-
|
|
1226
|
+
fixAndVerifyMetaFromStore,
|
|
1171
1227
|
getUpdateMetaList,
|
|
1172
1228
|
getSourceFromInstallParams,
|
|
1173
1229
|
findWebInterface,
|
|
@@ -1176,4 +1232,6 @@ module.exports = {
|
|
|
1176
1232
|
getBundleDir,
|
|
1177
1233
|
needBlockletDownload,
|
|
1178
1234
|
verifyPurchase,
|
|
1235
|
+
findAvailableDid,
|
|
1236
|
+
ensureMeta,
|
|
1179
1237
|
};
|
package/lib/util/index.js
CHANGED
|
@@ -35,7 +35,6 @@ const DEFAULT_WELLKNOWN_PORT = 8088;
|
|
|
35
35
|
|
|
36
36
|
const logger = require('@abtnode/logger')('@abtnode/core:util');
|
|
37
37
|
|
|
38
|
-
const { getServices } = require('./service');
|
|
39
38
|
const request = require('./request');
|
|
40
39
|
|
|
41
40
|
const validateOwner = (owner) => {
|
|
@@ -475,6 +474,39 @@ const getSafeEnv = (inputEnv, processEnv = process.env) => {
|
|
|
475
474
|
return mergedEnv;
|
|
476
475
|
};
|
|
477
476
|
|
|
477
|
+
// For performance sake, we only support 1 param here, and the cache is flushed every minute
|
|
478
|
+
const memoizeAsync = (fn, flushInterval = 60000) => {
|
|
479
|
+
const cache = new Map();
|
|
480
|
+
setInterval(() => cache.clear(), flushInterval);
|
|
481
|
+
|
|
482
|
+
return (arg) => {
|
|
483
|
+
if (cache.has(arg)) {
|
|
484
|
+
return cache.get(arg);
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
// eslint-disable-next-line prefer-spread
|
|
488
|
+
const result = fn.apply(null, [arg]);
|
|
489
|
+
cache.set(arg, result);
|
|
490
|
+
|
|
491
|
+
return result;
|
|
492
|
+
};
|
|
493
|
+
};
|
|
494
|
+
|
|
495
|
+
const getStateCrons = (states) => [
|
|
496
|
+
{
|
|
497
|
+
name: 'cleanup-audit-logs',
|
|
498
|
+
time: '0 0 6 * * *', // cleanup old logs every day at 6am
|
|
499
|
+
// time: '0 */5 * * * *', // cleanup every 5 minutes
|
|
500
|
+
options: { runOnInit: false },
|
|
501
|
+
fn: async () => {
|
|
502
|
+
const removed = await states.auditLog.remove({
|
|
503
|
+
createdAt: { $lt: new Date(Date.now() - 1000 * 60 * 60 * 24 * 90) },
|
|
504
|
+
});
|
|
505
|
+
logger.info(`Removed ${removed} audit logs`);
|
|
506
|
+
},
|
|
507
|
+
},
|
|
508
|
+
];
|
|
509
|
+
|
|
478
510
|
const lib = {
|
|
479
511
|
validateOwner,
|
|
480
512
|
getProviderFromNodeInfo,
|
|
@@ -496,13 +528,9 @@ const lib = {
|
|
|
496
528
|
ensureDataDirs,
|
|
497
529
|
getDataDirs,
|
|
498
530
|
getBaseUrls,
|
|
499
|
-
getBlockletMeta:
|
|
500
|
-
parseBlockletMeta(dir, {
|
|
501
|
-
serviceMetas: getServices(),
|
|
502
|
-
...opts,
|
|
503
|
-
}),
|
|
531
|
+
getBlockletMeta: parseBlockletMeta,
|
|
504
532
|
validateBlockletMeta: (meta, opts = {}) => {
|
|
505
|
-
fixAndValidateService(meta
|
|
533
|
+
fixAndValidateService(meta);
|
|
506
534
|
return validateMeta(meta, opts);
|
|
507
535
|
},
|
|
508
536
|
getBlockletMetaByUrl,
|
|
@@ -515,6 +543,8 @@ const lib = {
|
|
|
515
543
|
transformIPToDomain,
|
|
516
544
|
getQueueConcurrencyByMem,
|
|
517
545
|
getSafeEnv,
|
|
546
|
+
memoizeAsync,
|
|
547
|
+
getStateCrons,
|
|
518
548
|
};
|
|
519
549
|
|
|
520
550
|
module.exports = lib;
|
package/lib/util/ip.js
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
const getIp = require('@abtnode/util/lib/get-ip');
|
|
2
2
|
const logger = require('@abtnode/logger')('@abtnode/core:ip');
|
|
3
3
|
|
|
4
|
+
const doRpc = require('./rpc');
|
|
5
|
+
const { memoizeAsync } = require('./index');
|
|
6
|
+
|
|
4
7
|
let cache = null;
|
|
5
8
|
const fetch = async () => {
|
|
6
9
|
try {
|
|
@@ -22,6 +25,9 @@ const cron = {
|
|
|
22
25
|
fn: fetch,
|
|
23
26
|
};
|
|
24
27
|
|
|
28
|
+
const lookup = memoizeAsync((ip) => doRpc({ command: 'lookup', ip }));
|
|
29
|
+
|
|
25
30
|
module.exports.fetch = fetch;
|
|
26
31
|
module.exports.get = get;
|
|
27
32
|
module.exports.cron = cron;
|
|
33
|
+
module.exports.lookup = lookup;
|
package/lib/util/rpc.js
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
const axon = require('axon');
|
|
2
|
+
const logger = require('@abtnode/logger')('@abtnode/core:rpc');
|
|
3
|
+
|
|
4
|
+
const sock = axon.socket('req');
|
|
5
|
+
sock.connect(Number(process.env.ABT_NODE_UPDATER_PORT || 40405), '127.0.0.1');
|
|
6
|
+
|
|
7
|
+
const doRpc = async (message) =>
|
|
8
|
+
new Promise((resolve) => {
|
|
9
|
+
logger.info('send rpc request', message);
|
|
10
|
+
sock.send(JSON.stringify(message), (result) => {
|
|
11
|
+
logger.info('receive rpc response', result);
|
|
12
|
+
resolve(result);
|
|
13
|
+
});
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
module.exports = doRpc;
|
package/lib/util/ua.js
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
const semver = require('semver');
|
|
2
|
+
const parser = require('ua-parser-js');
|
|
3
|
+
const packageJson = require('../../package.json');
|
|
4
|
+
|
|
5
|
+
const { memoizeAsync } = require('./index');
|
|
6
|
+
|
|
7
|
+
const parseWalletUA = (userAgent) => {
|
|
8
|
+
const ua = (userAgent || '').toString().toLowerCase();
|
|
9
|
+
let os = '';
|
|
10
|
+
let version = '';
|
|
11
|
+
if (ua.indexOf('android') > -1) {
|
|
12
|
+
os = 'android';
|
|
13
|
+
} else if (ua.indexOf('darwin') > -1) {
|
|
14
|
+
os = 'ios';
|
|
15
|
+
} else if (ua.indexOf('arcwallet') === 0) {
|
|
16
|
+
os = 'web';
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const match = ua.split(/\s+/).find((x) => x.startsWith('arcwallet'));
|
|
20
|
+
if (match) {
|
|
21
|
+
const tmp = match.split('/');
|
|
22
|
+
if (tmp.length > 1 && semver.coerce(tmp[1])) {
|
|
23
|
+
version = semver.coerce(tmp[1]).version;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return { os, version };
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
const parse = memoizeAsync((ua) => {
|
|
31
|
+
let result = parser(ua);
|
|
32
|
+
if (result.browser.name) {
|
|
33
|
+
return result;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
result = parseWalletUA(ua);
|
|
37
|
+
if (result.version) {
|
|
38
|
+
return {
|
|
39
|
+
browser: {
|
|
40
|
+
name: `DID Wallet ${result.os.toUpperCase()}`,
|
|
41
|
+
version: result.version,
|
|
42
|
+
},
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return {
|
|
47
|
+
browser: {
|
|
48
|
+
name: 'CLI',
|
|
49
|
+
version: packageJson.version,
|
|
50
|
+
},
|
|
51
|
+
};
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
module.exports = { parse };
|
package/lib/util/upgrade.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
/* eslint-disable no-param-reassign */
|
|
2
|
-
const axon = require('axon');
|
|
3
2
|
const semver = require('semver');
|
|
4
3
|
const sleep = require('@abtnode/util/lib/sleep');
|
|
5
4
|
const { NODE_MODES, NODE_UPGRADE_PROGRESS, EVENTS } = require('@abtnode/constant');
|
|
@@ -8,21 +7,10 @@ const Lock = require('@abtnode/util/lib/lock');
|
|
|
8
7
|
const logger = require('@abtnode/logger')('@abtnode/core:upgrade');
|
|
9
8
|
|
|
10
9
|
const states = require('../states');
|
|
10
|
+
const doRpc = require('./rpc');
|
|
11
11
|
|
|
12
12
|
const lock = new Lock('node-upgrade-lock');
|
|
13
13
|
|
|
14
|
-
// Connect to updater and send RPC call
|
|
15
|
-
const sock = axon.socket('req');
|
|
16
|
-
sock.connect(Number(process.env.ABT_NODE_UPDATER_PORT), '127.0.0.1');
|
|
17
|
-
const waitUpdaterRpc = async (message) =>
|
|
18
|
-
new Promise((resolve) => {
|
|
19
|
-
logger.info('send command to updater process', message);
|
|
20
|
-
sock.send(JSON.stringify(message), (result) => {
|
|
21
|
-
logger.info('receive response from updater process', result);
|
|
22
|
-
resolve(result);
|
|
23
|
-
});
|
|
24
|
-
});
|
|
25
|
-
|
|
26
14
|
// eslint-disable-next-line no-unused-vars
|
|
27
15
|
const checkNewVersion = async (params, context) => {
|
|
28
16
|
try {
|
|
@@ -120,7 +108,7 @@ const doUpgrade = async (session) => {
|
|
|
120
108
|
|
|
121
109
|
// 2. install specified version
|
|
122
110
|
if (session.stage === NODE_UPGRADE_PROGRESS.INSTALLING) {
|
|
123
|
-
await
|
|
111
|
+
await doRpc({ command: 'install', version: to });
|
|
124
112
|
logger.info('new version installed for upgrading', { from, to, sessionId });
|
|
125
113
|
await goNextState(NODE_UPGRADE_PROGRESS.RESTARTING);
|
|
126
114
|
}
|
|
@@ -131,7 +119,7 @@ const doUpgrade = async (session) => {
|
|
|
131
119
|
await sleep(3000);
|
|
132
120
|
await goNextState(NODE_UPGRADE_PROGRESS.CLEANUP);
|
|
133
121
|
await sleep(3000);
|
|
134
|
-
await
|
|
122
|
+
await doRpc({ command: 'restart', dataDir: process.env.ABT_NODE_DATA_DIR });
|
|
135
123
|
await lock.release();
|
|
136
124
|
return; // we should abort here and resume after restart
|
|
137
125
|
}
|
package/lib/validators/node.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
/* eslint-disable newline-per-chained-call */
|
|
2
|
+
const { GATEWAY_REQ_LIMIT } = require('@abtnode/constant');
|
|
2
3
|
const Joi = require('joi');
|
|
3
4
|
const { getMultipleLangParams } = require('./util');
|
|
4
5
|
|
|
@@ -42,6 +43,18 @@ const nodeInfoSchema = Joi.object({
|
|
|
42
43
|
}),
|
|
43
44
|
}).options({ stripUnknown: true });
|
|
44
45
|
|
|
46
|
+
const updateGatewaySchema = Joi.object({
|
|
47
|
+
requestLimit: Joi.object({
|
|
48
|
+
enabled: Joi.bool().required(),
|
|
49
|
+
rate: Joi.number()
|
|
50
|
+
.min(GATEWAY_REQ_LIMIT.min)
|
|
51
|
+
.max(GATEWAY_REQ_LIMIT.max)
|
|
52
|
+
.when('requestLimit.enabled', { is: true, then: Joi.required() }),
|
|
53
|
+
ipHeader: Joi.string().allow('').trim(),
|
|
54
|
+
}),
|
|
55
|
+
});
|
|
56
|
+
|
|
45
57
|
module.exports = {
|
|
46
58
|
validateNodeInfo: (entity, context) => nodeInfoSchema.validateAsync(entity, getMultipleLangParams(context)),
|
|
59
|
+
validateUpdateGateway: (entity, context) => updateGatewaySchema.validateAsync(entity, getMultipleLangParams(context)),
|
|
47
60
|
};
|