@abtnode/core 1.5.13 → 1.15.17
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/blocklet/extras.js +1 -1
- package/lib/blocklet/hooks.js +4 -1
- package/lib/blocklet/manager/disk.js +30 -8
- package/lib/blocklet/migration.js +95 -47
- package/lib/blocklet/registry.js +5 -5
- package/lib/event.js +2 -2
- package/lib/migrations/1.5.15-site.js +184 -0
- package/lib/router/helper.js +124 -92
- package/lib/router/index.js +10 -2
- package/lib/router/manager.js +7 -7
- package/lib/states/site.js +1 -1
- package/lib/team/manager.js +1 -1
- package/lib/util/default-node-config.js +6 -6
- package/lib/util/index.js +29 -14
- package/lib/util/ready.js +5 -5
- package/lib/util/upgrade.js +4 -4
- package/lib/validators/router.js +13 -7
- package/lib/webhook/index.js +2 -2
- package/lib/webhook/sender/slack/index.js +1 -1
- package/package.json +18 -18
package/lib/router/helper.js
CHANGED
|
@@ -28,11 +28,14 @@ const {
|
|
|
28
28
|
CERTIFICATE_EXPIRES_WARNING_OFFSET,
|
|
29
29
|
DEFAULT_DAEMON_PORT,
|
|
30
30
|
DEFAULT_SERVICE_PATH,
|
|
31
|
+
SLOT_FOR_IP_DNS_SITE,
|
|
32
|
+
BLOCKLET_SITE_GROUP_SUFFIX,
|
|
31
33
|
} = require('@abtnode/constant');
|
|
32
34
|
const {
|
|
33
35
|
BLOCKLET_DYNAMIC_PATH_PREFIX,
|
|
34
36
|
BLOCKLET_INTERFACE_TYPE_WEB,
|
|
35
37
|
BLOCKLET_INTERFACE_WELLKNOWN,
|
|
38
|
+
BLOCKLET_INTERFACE_TYPE_WELLKNOWN,
|
|
36
39
|
} = require('@blocklet/meta/lib/constants');
|
|
37
40
|
|
|
38
41
|
// eslint-disable-next-line global-require
|
|
@@ -48,15 +51,58 @@ const {
|
|
|
48
51
|
} = require('../util');
|
|
49
52
|
const { getServicesFromBlockletInterface } = require('../util/service');
|
|
50
53
|
const getIpDnsDomainForBlocklet = require('../util/get-ip-dns-domain-for-blocklet');
|
|
54
|
+
const { getFromCache: getAccessibleExternalNodeIp } = require('../util/get-accessible-external-node-ip');
|
|
51
55
|
|
|
52
56
|
const Router = require('./index');
|
|
53
57
|
const states = require('../states');
|
|
54
58
|
|
|
55
|
-
|
|
59
|
+
/**
|
|
60
|
+
* replace 888-888-888-888 with accessible ip for domain
|
|
61
|
+
*/
|
|
62
|
+
const attachRuntimeDomainAliases = async ({ sites = [], context = {}, node }) => {
|
|
63
|
+
let ip;
|
|
64
|
+
const ipRegex = /\d+[-.]\d+[-.]\d+[-.]\d+/;
|
|
65
|
+
const match = ipRegex.exec(context.hostname);
|
|
66
|
+
if (match) {
|
|
67
|
+
ip = match[0];
|
|
68
|
+
} else if (node) {
|
|
69
|
+
const nodeInfo = await node.read();
|
|
70
|
+
const nodeIp = await getAccessibleExternalNodeIp(nodeInfo);
|
|
71
|
+
if (nodeIp) {
|
|
72
|
+
ip = nodeIp;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const getDomainAliases = (site) =>
|
|
77
|
+
(site.domainAliases || []).map((domain) => {
|
|
78
|
+
if (!domain.value) {
|
|
79
|
+
return domain;
|
|
80
|
+
}
|
|
81
|
+
if (domain.value.includes(SLOT_FOR_IP_DNS_SITE) && ip) {
|
|
82
|
+
domain.value = domain.value.replace(SLOT_FOR_IP_DNS_SITE, ip.replace(/\./g, '-'));
|
|
83
|
+
}
|
|
84
|
+
return domain;
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
if (!Array.isArray(sites)) {
|
|
88
|
+
sites.domainAliases = getDomainAliases(sites);
|
|
89
|
+
return sites;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return sites.map((site) => {
|
|
93
|
+
site.domainAliases = getDomainAliases(site);
|
|
94
|
+
|
|
95
|
+
return site;
|
|
96
|
+
});
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
const attachInterfaceUrls = async ({ sites = [], context, node }) => {
|
|
56
100
|
if (!sites) {
|
|
57
101
|
return [];
|
|
58
102
|
}
|
|
59
103
|
|
|
104
|
+
attachRuntimeDomainAliases({ sites, context, node });
|
|
105
|
+
|
|
60
106
|
const getUrl = (rule, domain = '') => {
|
|
61
107
|
const host = getBlockletHost({ domain, context });
|
|
62
108
|
const prefix = trimSlash(rule.from.pathPrefix);
|
|
@@ -340,7 +386,7 @@ const ensureAuthService = async (sites = [], blockletManager) => {
|
|
|
340
386
|
// we should only use rule.to.realInterfaceName, rule.to.interfaceName is for backward compatible
|
|
341
387
|
const interfaceName = rule.to.realInterfaceName || rule.to.interfaceName;
|
|
342
388
|
const { interfaces } = blocklet.meta;
|
|
343
|
-
const _interface = interfaces.find((x) => x.type ===
|
|
389
|
+
const _interface = interfaces.find((x) => x.type === BLOCKLET_INTERFACE_TYPE_WEB && x.name === interfaceName);
|
|
344
390
|
if (_interface) {
|
|
345
391
|
rule.services = rule.services || [];
|
|
346
392
|
rule.services.unshift(...getServicesFromBlockletInterface(_interface, { logError: logger.error }));
|
|
@@ -596,10 +642,13 @@ module.exports = function getRouterHelpers({ dataDirs, routingSnapshot, routerMa
|
|
|
596
642
|
/**
|
|
597
643
|
* Add system sites for blocklet
|
|
598
644
|
*
|
|
599
|
-
* @returns {boolean} if routing changed
|
|
645
|
+
* @returns {boolean} if routing state db changed
|
|
600
646
|
*/
|
|
601
647
|
const _ensureBlockletSites = async (blocklet, sites, nodeInfo, context = {}) => {
|
|
602
|
-
const
|
|
648
|
+
const webInterface = (blocklet.meta.interfaces || []).find((x) => x.type === BLOCKLET_INTERFACE_TYPE_WEB);
|
|
649
|
+
if (!webInterface) {
|
|
650
|
+
return false;
|
|
651
|
+
}
|
|
603
652
|
|
|
604
653
|
const getPrefix = (str) => {
|
|
605
654
|
if (!str || str === '*') {
|
|
@@ -608,68 +657,59 @@ module.exports = function getRouterHelpers({ dataDirs, routingSnapshot, routerMa
|
|
|
608
657
|
return `/${str}`.replace(/^\/+/, '/');
|
|
609
658
|
};
|
|
610
659
|
|
|
611
|
-
const
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
};
|
|
660
|
+
const domainGroup = `${blocklet.meta.did}${BLOCKLET_SITE_GROUP_SUFFIX}`;
|
|
661
|
+
const domain = getIpDnsDomainForBlocklet(blocklet, webInterface);
|
|
662
|
+
const pathPrefix = getPrefix(webInterface.prefix);
|
|
663
|
+
const rule = {
|
|
664
|
+
from: { pathPrefix },
|
|
665
|
+
to: {
|
|
666
|
+
port: findInterfacePortByName(blocklet, webInterface.name),
|
|
667
|
+
did: blocklet.meta.did,
|
|
668
|
+
type: ROUTING_RULE_TYPES.BLOCKLET,
|
|
669
|
+
interfaceName: webInterface.name, // root blocklet interface
|
|
670
|
+
},
|
|
671
|
+
isProtected: true,
|
|
672
|
+
};
|
|
625
673
|
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
context
|
|
642
|
-
);
|
|
643
|
-
logger.info('add routing site', { site: domain });
|
|
674
|
+
const existSite = await states.site.findOne({ domain: domainGroup });
|
|
675
|
+
if (!existSite) {
|
|
676
|
+
await routerManager.addRoutingSite(
|
|
677
|
+
{
|
|
678
|
+
site: {
|
|
679
|
+
domain: domainGroup,
|
|
680
|
+
domainAliases: [{ value: domain, isProtected: true }],
|
|
681
|
+
isProtected: true,
|
|
682
|
+
rules: [rule],
|
|
683
|
+
},
|
|
684
|
+
skipCheckDynamicBlacklist: true,
|
|
685
|
+
},
|
|
686
|
+
context
|
|
687
|
+
);
|
|
688
|
+
logger.info('add routing site', { site: domain });
|
|
644
689
|
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
const existRule = (exist.rules || []).find((y) => get(y, 'from.pathPrefix') === pathPrefix);
|
|
648
|
-
if (existRule) {
|
|
649
|
-
await routerManager.updateRoutingRule({
|
|
650
|
-
id: exist.id,
|
|
651
|
-
rule: {
|
|
652
|
-
...rule,
|
|
653
|
-
id: exist.id,
|
|
654
|
-
},
|
|
655
|
-
skipProtectedRuleChecking: true,
|
|
656
|
-
});
|
|
657
|
-
logger.info('update routing rule for site', { site: domain });
|
|
658
|
-
} else {
|
|
659
|
-
await routerManager.addRoutingRule({
|
|
660
|
-
id: exist.id,
|
|
661
|
-
rule,
|
|
662
|
-
});
|
|
663
|
-
logger.info('add routing rule for site', { site: domain });
|
|
664
|
-
}
|
|
665
|
-
changed = true;
|
|
666
|
-
}
|
|
690
|
+
return true;
|
|
691
|
+
}
|
|
667
692
|
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
693
|
+
const existRule = (existSite.rules || []).find((y) => get(y, 'from.pathPrefix') === pathPrefix);
|
|
694
|
+
if (existRule) {
|
|
695
|
+
await routerManager.updateRoutingRule({
|
|
696
|
+
id: existSite.id,
|
|
697
|
+
rule: {
|
|
698
|
+
...rule,
|
|
699
|
+
id: existRule.id,
|
|
700
|
+
},
|
|
701
|
+
skipProtectedRuleChecking: true,
|
|
702
|
+
});
|
|
703
|
+
logger.info('update routing rule for site', { site: domain });
|
|
704
|
+
} else {
|
|
705
|
+
await routerManager.addRoutingRule({
|
|
706
|
+
id: existSite.id,
|
|
707
|
+
rule,
|
|
708
|
+
});
|
|
709
|
+
logger.info('add routing rule for site', { site: domain });
|
|
710
|
+
}
|
|
671
711
|
|
|
672
|
-
return
|
|
712
|
+
return true;
|
|
673
713
|
};
|
|
674
714
|
|
|
675
715
|
/**
|
|
@@ -684,7 +724,7 @@ module.exports = function getRouterHelpers({ dataDirs, routingSnapshot, routerMa
|
|
|
684
724
|
}
|
|
685
725
|
|
|
686
726
|
const tasks = (blocklet.meta.interfaces || [])
|
|
687
|
-
.filter((x) => x.
|
|
727
|
+
.filter((x) => x.type === BLOCKLET_INTERFACE_TYPE_WELLKNOWN)
|
|
688
728
|
.map(async (x) => {
|
|
689
729
|
const pathPrefix = normalizePathPrefix(x.prefix);
|
|
690
730
|
if (!pathPrefix.startsWith(WELLKNOWN_PATH_PREFIX)) {
|
|
@@ -811,30 +851,15 @@ module.exports = function getRouterHelpers({ dataDirs, routingSnapshot, routerMa
|
|
|
811
851
|
};
|
|
812
852
|
|
|
813
853
|
const _removeBlockletSites = async (blocklet, nodeInfo, context = {}) => {
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
const changes = await Promise.all(
|
|
817
|
-
interfaces.map(async (x) => {
|
|
818
|
-
let changed = false;
|
|
819
|
-
|
|
820
|
-
const domain = getIpDnsDomainForBlocklet(blocklet, x);
|
|
821
|
-
|
|
822
|
-
const site = await states.site.findOne({ domain });
|
|
823
|
-
if (
|
|
824
|
-
site &&
|
|
825
|
-
site.isProtected &&
|
|
826
|
-
(!site.domainAliases || !site.domainAliases.length) &&
|
|
827
|
-
(!site.rules || !site.rules.length)
|
|
828
|
-
) {
|
|
829
|
-
await routerManager.deleteRoutingSite({ id: site.id }, context);
|
|
830
|
-
changed = true;
|
|
831
|
-
}
|
|
854
|
+
let changed = false;
|
|
832
855
|
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
856
|
+
const site = await states.site.findOne({ domain: `${blocklet.meta.did}${BLOCKLET_SITE_GROUP_SUFFIX}` });
|
|
857
|
+
if (site) {
|
|
858
|
+
await routerManager.deleteRoutingSite({ id: site.id }, context);
|
|
859
|
+
changed = true;
|
|
860
|
+
}
|
|
836
861
|
|
|
837
|
-
return
|
|
862
|
+
return changed;
|
|
838
863
|
};
|
|
839
864
|
|
|
840
865
|
/**
|
|
@@ -854,8 +879,8 @@ module.exports = function getRouterHelpers({ dataDirs, routingSnapshot, routerMa
|
|
|
854
879
|
return false;
|
|
855
880
|
}
|
|
856
881
|
|
|
857
|
-
const
|
|
858
|
-
if (
|
|
882
|
+
const hasWebInterface = (blocklet.meta.interfaces || []).some((x) => x.type === BLOCKLET_INTERFACE_TYPE_WEB);
|
|
883
|
+
if (!hasWebInterface) {
|
|
859
884
|
return false;
|
|
860
885
|
}
|
|
861
886
|
|
|
@@ -886,9 +911,11 @@ module.exports = function getRouterHelpers({ dataDirs, routingSnapshot, routerMa
|
|
|
886
911
|
* @returns {boolean} if routing changed
|
|
887
912
|
*/
|
|
888
913
|
const ensureBlockletCustomRouting = async (blocklet) => {
|
|
914
|
+
// Only one blocklet web interface can be declared since router 2.0
|
|
889
915
|
const interfaces = (blocklet.meta.interfaces || []).filter((x) => x.type === BLOCKLET_INTERFACE_TYPE_WEB);
|
|
890
916
|
const hasInterface = (name) => interfaces.some((x) => x.name === name);
|
|
891
|
-
|
|
917
|
+
|
|
918
|
+
const sites = await siteState.getSitesByBlocklet(blocklet.meta.did);
|
|
892
919
|
let changed = false;
|
|
893
920
|
|
|
894
921
|
for (const site of sites) {
|
|
@@ -1164,7 +1191,7 @@ module.exports = function getRouterHelpers({ dataDirs, routingSnapshot, routerMa
|
|
|
1164
1191
|
if (msg.length < 5) {
|
|
1165
1192
|
throw new Error('Message cannot be less than 5 characters');
|
|
1166
1193
|
}
|
|
1167
|
-
if (msg.length >
|
|
1194
|
+
if (msg.length > 150) {
|
|
1168
1195
|
throw new Error('Message cannot exceed 100 characters');
|
|
1169
1196
|
}
|
|
1170
1197
|
}
|
|
@@ -1216,12 +1243,17 @@ module.exports = function getRouterHelpers({ dataDirs, routingSnapshot, routerMa
|
|
|
1216
1243
|
return attachInterfaceUrls({
|
|
1217
1244
|
sites: await ensureLatestInfo(sites, { withDefaultCors }),
|
|
1218
1245
|
context,
|
|
1246
|
+
node: nodeState,
|
|
1219
1247
|
});
|
|
1220
1248
|
};
|
|
1221
1249
|
|
|
1222
1250
|
const getSnapshotSites = async ({ hash }, context = {}, { withDefaultCors = true } = {}) => {
|
|
1223
1251
|
const sites = await routingSnapshot.readSnapshotSites(hash);
|
|
1224
|
-
return attachInterfaceUrls({
|
|
1252
|
+
return attachInterfaceUrls({
|
|
1253
|
+
sites: await ensureLatestInfo(sites, { withDefaultCors }),
|
|
1254
|
+
context,
|
|
1255
|
+
node: nodeState,
|
|
1256
|
+
});
|
|
1225
1257
|
};
|
|
1226
1258
|
|
|
1227
1259
|
const getCertificates = async () => {
|
|
@@ -1262,7 +1294,7 @@ module.exports = function getRouterHelpers({ dataDirs, routingSnapshot, routerMa
|
|
|
1262
1294
|
logger.info('send certificate expire notification', { domain: cert.domain });
|
|
1263
1295
|
notification.create({
|
|
1264
1296
|
title: 'SSL Certificate Expired',
|
|
1265
|
-
description: `Your SSL certificate for domain ${cert.domain} has expired, please update it in
|
|
1297
|
+
description: `Your SSL certificate for domain ${cert.domain} has expired, please update it in Blocklet Server`,
|
|
1266
1298
|
severity: 'error',
|
|
1267
1299
|
entityType: 'certificate',
|
|
1268
1300
|
entityId: cert._id, // eslint-disable-line no-underscore-dangle
|
|
@@ -1276,7 +1308,7 @@ module.exports = function getRouterHelpers({ dataDirs, routingSnapshot, routerMa
|
|
|
1276
1308
|
cert.domain
|
|
1277
1309
|
} will expire in ${expireInDays} days (on ${new Date(
|
|
1278
1310
|
cert.validTo
|
|
1279
|
-
).toLocaleString()}), please remember to update it in
|
|
1311
|
+
).toLocaleString()}), please remember to update it in Blocklet Server`,
|
|
1280
1312
|
severity: 'warning',
|
|
1281
1313
|
entityType: 'certificate',
|
|
1282
1314
|
entityId: cert._id, // eslint-disable-line no-underscore-dangle
|
package/lib/router/index.js
CHANGED
|
@@ -6,6 +6,7 @@ const {
|
|
|
6
6
|
ROUTING_RULE_TYPES,
|
|
7
7
|
DEFAULT_DASHBOARD_DOMAIN,
|
|
8
8
|
BLOCKLET_PROXY_PATH_PREFIX,
|
|
9
|
+
BLOCKLET_SITE_GROUP_SUFFIX,
|
|
9
10
|
} = require('@abtnode/constant');
|
|
10
11
|
const { BLOCKLET_UI_INTERFACES } = require('@blocklet/meta/lib/constants');
|
|
11
12
|
const { pick } = require('lodash');
|
|
@@ -26,7 +27,11 @@ const expandSites = (sites = []) => {
|
|
|
26
27
|
});
|
|
27
28
|
|
|
28
29
|
delete site.domainAliases;
|
|
29
|
-
|
|
30
|
+
|
|
31
|
+
// skip site if domain is BLOCKLET_SITE_GROUP
|
|
32
|
+
if (!site.domain.endsWith(BLOCKLET_SITE_GROUP_SUFFIX)) {
|
|
33
|
+
result.push(site);
|
|
34
|
+
}
|
|
30
35
|
});
|
|
31
36
|
|
|
32
37
|
const defaultSite = result.find((site) => site.domain === DOMAIN_FOR_DEFAULT_SITE);
|
|
@@ -45,7 +50,10 @@ const mergeAllowedOrigins = (domain, allowedOrigins) => {
|
|
|
45
50
|
origins.push(domain);
|
|
46
51
|
}
|
|
47
52
|
|
|
48
|
-
|
|
53
|
+
// skip site if domain is BLOCKLET_SITE_GROUP
|
|
54
|
+
const res = origins.filter((x) => !x.endsWith(BLOCKLET_SITE_GROUP_SUFFIX));
|
|
55
|
+
|
|
56
|
+
return res;
|
|
49
57
|
};
|
|
50
58
|
|
|
51
59
|
const getRoutingTable = ({ sites, nodeInfo }) => {
|
package/lib/router/manager.js
CHANGED
|
@@ -121,7 +121,7 @@ class RouterManager extends EventEmitter {
|
|
|
121
121
|
await this.validateRouterConfig('addRoutingSite', { site: newSite });
|
|
122
122
|
|
|
123
123
|
const result = await states.site.add(newSite);
|
|
124
|
-
await attachInterfaceUrls({ sites: result, context });
|
|
124
|
+
await attachInterfaceUrls({ sites: result, context, node: states.node });
|
|
125
125
|
|
|
126
126
|
this.emit('router.site.created', result);
|
|
127
127
|
return result;
|
|
@@ -181,7 +181,7 @@ class RouterManager extends EventEmitter {
|
|
|
181
181
|
this.emit('router.site.updated', params.id);
|
|
182
182
|
|
|
183
183
|
const dbSite = await states.site.findOne({ _id: params.id });
|
|
184
|
-
await attachInterfaceUrls({ sites: dbSite, context });
|
|
184
|
+
await attachInterfaceUrls({ sites: dbSite, context, node: states.node });
|
|
185
185
|
return dbSite;
|
|
186
186
|
}
|
|
187
187
|
|
|
@@ -207,7 +207,7 @@ class RouterManager extends EventEmitter {
|
|
|
207
207
|
logger.debug('add domain alias update result', { id, updateResult, domainAlias });
|
|
208
208
|
|
|
209
209
|
const newSite = await states.site.findOne({ _id: id });
|
|
210
|
-
await attachInterfaceUrls({ sites: newSite, context });
|
|
210
|
+
await attachInterfaceUrls({ sites: newSite, context, node: states.node });
|
|
211
211
|
|
|
212
212
|
return newSite;
|
|
213
213
|
}
|
|
@@ -230,7 +230,7 @@ class RouterManager extends EventEmitter {
|
|
|
230
230
|
const updateResult = await states.site.update({ _id: id }, { $set: { domainAliases: dbSite.domainAliases } });
|
|
231
231
|
logger.debug('remove domain alias update result', { id, updateResult, domainAlias });
|
|
232
232
|
|
|
233
|
-
await attachInterfaceUrls({ sites: dbSite, context });
|
|
233
|
+
await attachInterfaceUrls({ sites: dbSite, context, node: states.node });
|
|
234
234
|
|
|
235
235
|
return dbSite;
|
|
236
236
|
}
|
|
@@ -265,7 +265,7 @@ class RouterManager extends EventEmitter {
|
|
|
265
265
|
}
|
|
266
266
|
|
|
267
267
|
const newSite = await states.site.findOne({ _id: id });
|
|
268
|
-
await attachInterfaceUrls({ sites: newSite, context });
|
|
268
|
+
await attachInterfaceUrls({ sites: newSite, context, node: states.node });
|
|
269
269
|
|
|
270
270
|
this.emit('router.rule.created', newSite);
|
|
271
271
|
return newSite;
|
|
@@ -306,7 +306,7 @@ class RouterManager extends EventEmitter {
|
|
|
306
306
|
logger.info('update result', { updateResult });
|
|
307
307
|
const newSite = await states.site.findOne({ _id: id });
|
|
308
308
|
|
|
309
|
-
await attachInterfaceUrls({ sites: newSite, context });
|
|
309
|
+
await attachInterfaceUrls({ sites: newSite, context, node: states.node });
|
|
310
310
|
|
|
311
311
|
this.emit('router.rule.updated', newSite);
|
|
312
312
|
|
|
@@ -335,7 +335,7 @@ class RouterManager extends EventEmitter {
|
|
|
335
335
|
logger.info('router.rule.removed', { id, ruleId });
|
|
336
336
|
const newSite = await states.site.findOne({ _id: id });
|
|
337
337
|
|
|
338
|
-
await attachInterfaceUrls({ sites: newSite, context });
|
|
338
|
+
await attachInterfaceUrls({ sites: newSite, context, node: states.node });
|
|
339
339
|
|
|
340
340
|
this.emit('router.rule.removed', newSite);
|
|
341
341
|
return newSite;
|
package/lib/states/site.js
CHANGED
|
@@ -33,7 +33,7 @@ class SiteState extends BaseState {
|
|
|
33
33
|
return SiteState.renameIdFiledName(result);
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
-
async
|
|
36
|
+
async getSitesByBlocklet(did) {
|
|
37
37
|
const rules = await this.asyncDB.find({ 'rules.to.did': did });
|
|
38
38
|
return SiteState.renameIdFiledName(rules);
|
|
39
39
|
}
|
package/lib/team/manager.js
CHANGED
|
@@ -190,7 +190,7 @@ class TeamManager extends EventEmitter {
|
|
|
190
190
|
// first getRBAC after blocklet added
|
|
191
191
|
if (!this.cache[did].rbac) {
|
|
192
192
|
if (this.isNodeTeam(did)) {
|
|
193
|
-
throw new Error('
|
|
193
|
+
throw new Error('Blocklet Server rbac instance has not been initialized');
|
|
194
194
|
}
|
|
195
195
|
|
|
196
196
|
// FIXME: cross process lock
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
const {
|
|
2
2
|
NODE_REGISTER_URL,
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
BLOCKLET_STORE_URL,
|
|
4
|
+
BLOCKLET_STORE_URL_DEV,
|
|
5
5
|
WEB_WALLET_URL,
|
|
6
6
|
NODE_PACKAGE_NAME,
|
|
7
7
|
NODE_COMMAND_NAME,
|
|
@@ -23,18 +23,18 @@ const defaultNodeConfigs = {
|
|
|
23
23
|
blockletRegistryList: {
|
|
24
24
|
getDefaultValue: () => [
|
|
25
25
|
{
|
|
26
|
-
name: 'Official
|
|
26
|
+
name: 'Official Store',
|
|
27
27
|
description: 'ArcBlock official blocklet registry',
|
|
28
|
-
url:
|
|
28
|
+
url: BLOCKLET_STORE_URL,
|
|
29
29
|
logoUrl: '/logo.png',
|
|
30
30
|
maintainer: 'arcblock',
|
|
31
31
|
selected: true,
|
|
32
32
|
protected: true,
|
|
33
33
|
},
|
|
34
34
|
{
|
|
35
|
-
name: 'Dev
|
|
35
|
+
name: 'Dev Store',
|
|
36
36
|
description: 'ArcBlock dev registry that contains demo and example blocklets',
|
|
37
|
-
url:
|
|
37
|
+
url: BLOCKLET_STORE_URL_DEV,
|
|
38
38
|
maintainer: 'arcblock',
|
|
39
39
|
logoUrl: '/logo.png',
|
|
40
40
|
selected: false,
|
package/lib/util/index.js
CHANGED
|
@@ -7,6 +7,7 @@ const crypto = require('crypto');
|
|
|
7
7
|
const shell = require('shelljs');
|
|
8
8
|
const get = require('lodash/get');
|
|
9
9
|
const uniqBy = require('lodash/uniqBy');
|
|
10
|
+
const pickBy = require('lodash/pickBy');
|
|
10
11
|
const { isFromPublicKey } = require('@arcblock/did');
|
|
11
12
|
const joinUrl = require('url-join');
|
|
12
13
|
const { Certificate } = require('@fidm/x509');
|
|
@@ -28,7 +29,9 @@ const {
|
|
|
28
29
|
ROUTING_RULE_TYPES,
|
|
29
30
|
SLOT_FOR_IP_DNS_SITE,
|
|
30
31
|
DEFAULT_IP_DNS_DOMAIN_SUFFIX,
|
|
32
|
+
BLOCKLET_SITE_GROUP_SUFFIX,
|
|
31
33
|
} = require('@abtnode/constant');
|
|
34
|
+
const { BLOCKLET_INTERFACE_TYPE_WEB } = require('@blocklet/meta/lib/constants');
|
|
32
35
|
|
|
33
36
|
const DEFAULT_WELLKNOWN_PORT = 8088;
|
|
34
37
|
|
|
@@ -136,7 +139,7 @@ const getAuthConfig = (rule, blocklet) => {
|
|
|
136
139
|
// find interface in meta
|
|
137
140
|
const interfaceName = rule.to.realInterfaceName;
|
|
138
141
|
const { interfaces } = _blocklet.meta;
|
|
139
|
-
const _interface = interfaces.find((x) => x.type ===
|
|
142
|
+
const _interface = interfaces.find((x) => x.type === BLOCKLET_INTERFACE_TYPE_WEB && x.name === interfaceName);
|
|
140
143
|
|
|
141
144
|
if (!_interface) {
|
|
142
145
|
return null;
|
|
@@ -154,13 +157,14 @@ const getAuthConfig = (rule, blocklet) => {
|
|
|
154
157
|
return auth.config;
|
|
155
158
|
};
|
|
156
159
|
|
|
157
|
-
const getBlockletBaseUrls = ({ routingEnabled = false, port,
|
|
160
|
+
const getBlockletBaseUrls = ({ routingEnabled = false, port, rules = [], context = {}, blocklet, nodeIp }) => {
|
|
158
161
|
let baseUrls = [];
|
|
159
162
|
|
|
160
|
-
if (routingEnabled && Array.isArray(
|
|
161
|
-
baseUrls =
|
|
162
|
-
.
|
|
163
|
-
|
|
163
|
+
if (routingEnabled && Array.isArray(rules) && rules.length > 0) {
|
|
164
|
+
baseUrls = rules
|
|
165
|
+
.filter((rule) => !(rule.from.domain || '').endsWith(BLOCKLET_SITE_GROUP_SUFFIX))
|
|
166
|
+
.map((rule) => {
|
|
167
|
+
const host = getBlockletHost({ domain: rule.from.domain, context, nodeIp });
|
|
164
168
|
if (host) {
|
|
165
169
|
let protocol = 'http'; // TODO: 这里固定为 http, 因为判断 url 是不是 https 和证书相关,这里实现的话比较复杂
|
|
166
170
|
if (host.includes(DEFAULT_IP_DNS_DOMAIN_SUFFIX)) {
|
|
@@ -168,10 +172,10 @@ const getBlockletBaseUrls = ({ routingEnabled = false, port, sites = [], context
|
|
|
168
172
|
}
|
|
169
173
|
|
|
170
174
|
return {
|
|
171
|
-
ruleId:
|
|
172
|
-
baseUrl: `${protocol}://${host}/${trimSlash(
|
|
173
|
-
interfaceName: get(
|
|
174
|
-
authConfig: getAuthConfig(
|
|
175
|
+
ruleId: rule.id,
|
|
176
|
+
baseUrl: `${protocol}://${host}/${trimSlash(rule.from.pathPrefix)}`,
|
|
177
|
+
interfaceName: get(rule, 'to.interfaceName', ''),
|
|
178
|
+
authConfig: getAuthConfig(rule, blocklet),
|
|
175
179
|
};
|
|
176
180
|
}
|
|
177
181
|
|
|
@@ -187,7 +191,7 @@ const getBlockletBaseUrls = ({ routingEnabled = false, port, sites = [], context
|
|
|
187
191
|
return baseUrls;
|
|
188
192
|
};
|
|
189
193
|
|
|
190
|
-
const getBlockletInterfaces = ({ blocklet, context, nodeInfo,
|
|
194
|
+
const getBlockletInterfaces = ({ blocklet, context, nodeInfo, routingRules, nodeIp }) => {
|
|
191
195
|
const interfaces = [];
|
|
192
196
|
(blocklet.meta.interfaces || []).forEach((x) => {
|
|
193
197
|
if (x.port && x.port.external) {
|
|
@@ -195,7 +199,7 @@ const getBlockletInterfaces = ({ blocklet, context, nodeInfo, sites, nodeIp }) =
|
|
|
195
199
|
const baseUrls = getBlockletBaseUrls({
|
|
196
200
|
routingEnabled: false,
|
|
197
201
|
port: x.port.external,
|
|
198
|
-
|
|
202
|
+
rules: [],
|
|
199
203
|
context,
|
|
200
204
|
});
|
|
201
205
|
baseUrls.forEach(({ baseUrl }) => interfaces.push({ type: x.type, name: x.name, url: baseUrl }));
|
|
@@ -205,9 +209,11 @@ const getBlockletInterfaces = ({ blocklet, context, nodeInfo, sites, nodeIp }) =
|
|
|
205
209
|
const baseUrls = getBlockletBaseUrls({
|
|
206
210
|
routingEnabled: isRoutingEnabled(nodeInfo.routing),
|
|
207
211
|
port,
|
|
208
|
-
|
|
212
|
+
rules: (routingRules || []).filter((r) => {
|
|
209
213
|
return (
|
|
210
214
|
// don't show wellknown interface
|
|
215
|
+
r.to.type !== ROUTING_RULE_TYPES.GENERAL_PROXY &&
|
|
216
|
+
// LEGACY don't show wellknown interface
|
|
211
217
|
r.to.interfaceName !== BLOCKLET_INTERFACE_WELLKNOWN &&
|
|
212
218
|
// don't show child blocklet interface
|
|
213
219
|
(!r.from.groupPathPrefix || r.from.pathPrefix === r.from.groupPathPrefix)
|
|
@@ -328,7 +334,7 @@ const getDataDirs = (dataDir) => ({
|
|
|
328
334
|
services: path.join(dataDir, 'services'),
|
|
329
335
|
});
|
|
330
336
|
|
|
331
|
-
// Ensure data dir for
|
|
337
|
+
// Ensure data dir for Blocklet Server exists
|
|
332
338
|
const ensureDataDirs = (dataDir) => {
|
|
333
339
|
try {
|
|
334
340
|
logger.info('ensure data dir', { dataDir });
|
|
@@ -519,6 +525,14 @@ const getQueueConcurrencyByMem = () => {
|
|
|
519
525
|
return 1;
|
|
520
526
|
};
|
|
521
527
|
|
|
528
|
+
const getSafeEnv = (inputEnv, processEnv = process.env) => {
|
|
529
|
+
const whiteList = ['ABT_NODE', 'ABT_NODE_DID', 'ABT_NODE_PK', 'ABT_NODE_PORT', 'ABT_NODE_SERVICE_PORT'];
|
|
530
|
+
// 此处需要保留 process.env 中的环境变量,只移除和 ABT_NODE 相关的环境变量(否则丢失了 process.env.SHELL 变量可能会造成无法使用 nodejs 的情况)
|
|
531
|
+
const filterProcessEnv = pickBy(processEnv, (value, key) => !key.startsWith('ABT_NODE') || whiteList.includes(key));
|
|
532
|
+
const mergedEnv = { ...filterProcessEnv, ...inputEnv };
|
|
533
|
+
return mergedEnv;
|
|
534
|
+
};
|
|
535
|
+
|
|
522
536
|
const lib = {
|
|
523
537
|
validateOwner,
|
|
524
538
|
getProviderFromNodeInfo,
|
|
@@ -558,6 +572,7 @@ const lib = {
|
|
|
558
572
|
shouldUpdateBlockletStatus,
|
|
559
573
|
transformIPToDomain,
|
|
560
574
|
getQueueConcurrencyByMem,
|
|
575
|
+
getSafeEnv,
|
|
561
576
|
};
|
|
562
577
|
|
|
563
578
|
module.exports = lib;
|
package/lib/util/ready.js
CHANGED
|
@@ -29,19 +29,19 @@ const createStateReadyHandler =
|
|
|
29
29
|
// eslint-disable-next-line no-underscore-dangle
|
|
30
30
|
await states.node.remove({ _id: state._id });
|
|
31
31
|
console.error('\n\x1b[31m======================================================');
|
|
32
|
-
console.error(`Data dir: ${options.dataDir} is used by another
|
|
33
|
-
console.error('Sharing data dir between
|
|
32
|
+
console.error(`Data dir: ${options.dataDir} is used by another Blocklet Server instance, abort!`);
|
|
33
|
+
console.error('Sharing data dir between Blocklet Server instances may break things!');
|
|
34
34
|
console.error('======================================================\x1b[0m');
|
|
35
35
|
console.log('\nIf you intend to use this dir:');
|
|
36
|
-
console.log(` 1. Stop
|
|
36
|
+
console.log(` 1. Stop Blocklet Server by ${chalk.cyan('abtnode stop --force')}`);
|
|
37
37
|
console.log(` 2. Clear data dir by ${chalk.cyan(`rm -r ${options.dataDir}`)}`);
|
|
38
|
-
console.log(
|
|
38
|
+
console.log(' 3. Reinitialize and start Blocklet Server');
|
|
39
39
|
process.exit(1);
|
|
40
40
|
}
|
|
41
41
|
}
|
|
42
42
|
})
|
|
43
43
|
.catch((err) => {
|
|
44
|
-
console.error('Can not ready node state on
|
|
44
|
+
console.error('Can not ready node state on Blocklet Server start:', err.message);
|
|
45
45
|
process.exit(1);
|
|
46
46
|
});
|
|
47
47
|
};
|
package/lib/util/upgrade.js
CHANGED
|
@@ -36,15 +36,15 @@ const checkNewVersion = async (params, context) => {
|
|
|
36
36
|
const latestVersion = versions[0].version;
|
|
37
37
|
if (semver.gt(latestVersion, info.version)) {
|
|
38
38
|
// if (semver.gte(latestVersion, info.version)) {
|
|
39
|
-
logger.info('New version found for
|
|
39
|
+
logger.info('New version found for Blocklet Server', {
|
|
40
40
|
latestVersion,
|
|
41
41
|
currentVersion: info.version,
|
|
42
42
|
nextVersion: info.nextVersion,
|
|
43
43
|
});
|
|
44
44
|
await states.node.updateNodeInfo({ nextVersion: latestVersion });
|
|
45
45
|
await states.notification.create({
|
|
46
|
-
title: '
|
|
47
|
-
description: 'A new and improved version of
|
|
46
|
+
title: 'Blocklet Server upgrade available',
|
|
47
|
+
description: 'A new and improved version of blocklet server is now available',
|
|
48
48
|
entityType: 'node',
|
|
49
49
|
severity: 'info',
|
|
50
50
|
sticky: true,
|
|
@@ -57,7 +57,7 @@ const checkNewVersion = async (params, context) => {
|
|
|
57
57
|
|
|
58
58
|
return '';
|
|
59
59
|
} catch (err) {
|
|
60
|
-
logger.error('Failed to check new version for
|
|
60
|
+
logger.error('Failed to check new version for Blocklet Server', { error: err });
|
|
61
61
|
}
|
|
62
62
|
|
|
63
63
|
return '';
|