@abtnode/core 1.4.13 → 1.4.14

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.
@@ -34,6 +34,7 @@ const {
34
34
  fromBlockletSource,
35
35
  } = require('@blocklet/meta/lib/constants');
36
36
  const util = require('../../util');
37
+ const getAccessibleExternalNodeIp = require('../../util/get-accessible-external-node-ip');
37
38
  const {
38
39
  forEachBlocklet,
39
40
  getBlockletDirs,
@@ -900,7 +901,8 @@ class BlockletManager extends BaseBlockletManager {
900
901
  // Put shorter url first
901
902
  return a.from.pathPrefix.length < b.from.pathPrefix ? 1 : -1;
902
903
  });
903
- blocklet.interfaces = getBlockletInterfaces({ blocklet, context, nodeInfo, sites: routingRules });
904
+ const nodeIp = await getAccessibleExternalNodeIp(nodeInfo);
905
+ blocklet.interfaces = getBlockletInterfaces({ blocklet, context, nodeInfo, sites: routingRules, nodeIp });
904
906
 
905
907
  try {
906
908
  const { appId } = blocklet.meta.group === BlockletGroup.gateway ? blocklet.children[0].env : blocklet.env;
package/lib/index.js CHANGED
@@ -129,7 +129,6 @@ function ABTNode(options) {
129
129
  getRoutingSites,
130
130
  getSnapshotSites,
131
131
  ensureDashboardRouting,
132
- ensureIpDnsRouting,
133
132
  ensureBlockletRouting,
134
133
  ensureBlockletRoutingForUpgrade,
135
134
  removeBlockletRouting,
@@ -305,7 +304,6 @@ function ABTNode(options) {
305
304
  takeRoutingSnapshot,
306
305
  getSitesFromSnapshot,
307
306
  ensureDashboardRouting,
308
- ensureIpDnsRouting,
309
307
 
310
308
  addDomainAlias: routerManager.addDomainAlias.bind(routerManager),
311
309
  deleteDomainAlias: routerManager.deleteDomainAlias.bind(routerManager),
@@ -24,7 +24,6 @@ const {
24
24
  ROUTING_RULE_TYPES,
25
25
  CERTIFICATE_EXPIRES_OFFSET,
26
26
  CERTIFICATE_EXPIRES_WARNING_OFFSET,
27
- DEFAULT_DASHBOARD_DOMAIN,
28
27
  DEFAULT_DAEMON_PORT,
29
28
  } = require('@abtnode/constant');
30
29
  const {
@@ -45,7 +44,6 @@ const {
45
44
  getWellknownSitePort,
46
45
  } = require('../util');
47
46
  const { getServicesFromBlockletInterface } = require('../util/service');
48
- const { getAccessibleIp, getAccessibleIpDnsDomainForNode } = require('../util/get-accessible-ip');
49
47
  const getIpDnsDomainForBlocklet = require('../util/get-ip-dns-domain-for-blocklet');
50
48
 
51
49
  const Router = require('./index');
@@ -321,27 +319,6 @@ module.exports = function getRouterHelpers({ dataDirs, routingSnapshot, routerMa
321
319
  const httpsCertState = states.certificate;
322
320
  const notification = states.notification;
323
321
 
324
- let _nodeDomainRequest = null; // singleton of node-domain-request
325
- const _getIpDnsDomainForNode = ({ nodeInfo = {} } = {}) => {
326
- const timeout = process.env.NODE_ENV === 'test' ? 500 : 5000;
327
- if (_nodeDomainRequest) {
328
- return _nodeDomainRequest;
329
- }
330
-
331
- const req = getAccessibleIpDnsDomainForNode({ nodeInfo, timeout });
332
- _nodeDomainRequest = req;
333
-
334
- req
335
- .then(() => {
336
- _nodeDomainRequest = null;
337
- })
338
- .catch(() => {
339
- _nodeDomainRequest = null;
340
- });
341
-
342
- return req;
343
- };
344
-
345
322
  // site level duplication detection
346
323
  const hasRuleByPrefix = (site, value) => site.rules.find((x) => x.isProtected && get(x, 'from.pathPrefix') === value);
347
324
 
@@ -580,13 +557,6 @@ module.exports = function getRouterHelpers({ dataDirs, routingSnapshot, routerMa
580
557
  const _ensureBlockletSites = async (blocklet, sites, nodeInfo, context = {}) => {
581
558
  const interfaces = (blocklet.meta.interfaces || []).filter((x) => x.type === BLOCKLET_INTERFACE_TYPE_WEB);
582
559
 
583
- const nodeDomain = await _getIpDnsDomainForNode({ nodeInfo });
584
-
585
- if (!nodeDomain) {
586
- logger.error('IpDnsDomain of abtnode does not found');
587
- return false;
588
- }
589
-
590
560
  const getPrefix = (str) => {
591
561
  if (!str || str === '*') {
592
562
  return '/';
@@ -596,34 +566,57 @@ module.exports = function getRouterHelpers({ dataDirs, routingSnapshot, routerMa
596
566
 
597
567
  const changes = await Promise.all(
598
568
  interfaces.map(async (x) => {
599
- let changed = false; // if state db changed
569
+ const domain = getIpDnsDomainForBlocklet(blocklet, x);
570
+ const pathPrefix = getPrefix(x.prefix);
571
+ const rule = {
572
+ from: { pathPrefix },
573
+ to: {
574
+ port: findInterfacePortByName(blocklet, x.name),
575
+ did: blocklet.meta.did,
576
+ type: ROUTING_RULE_TYPES.BLOCKLET,
577
+ interfaceName: x.name, // root blocklet interface
578
+ },
579
+ isProtected: true,
580
+ };
600
581
 
601
- const domain = getIpDnsDomainForBlocklet(blocklet, x, nodeDomain);
582
+ let changed = false; // if state db changed
602
583
 
603
- if (!(await states.site.domainExists(domain))) {
584
+ const exist = await states.site.findOne({ domain });
585
+ if (!exist) {
604
586
  await routerManager.addRoutingSite(
605
587
  {
606
588
  site: {
607
589
  domain,
608
590
  isProtected: true,
609
- rules: [
610
- {
611
- from: { pathPrefix: getPrefix(x.prefix) },
612
- to: {
613
- port: findInterfacePortByName(blocklet, x.name),
614
- did: blocklet.meta.did,
615
- type: ROUTING_RULE_TYPES.BLOCKLET,
616
- interfaceName: x.name, // root blocklet interface
617
- },
618
- isProtected: true,
619
- },
620
- ],
591
+ rules: [rule],
621
592
  },
622
593
  skipCheckDynamicBlacklist: true,
623
594
  },
624
595
  context
625
596
  );
597
+ logger.info('add routing site', { site: domain });
626
598
 
599
+ changed = true;
600
+ } else {
601
+ const existRule = (exist.rules || []).find((y) => get(y, 'from.pathPrefix') === pathPrefix);
602
+ if (existRule) {
603
+ await routerManager.updateRoutingRule({
604
+ id: exist.id,
605
+ rule: {
606
+ ...rule,
607
+ id: exist.id,
608
+ },
609
+ skipProtectedRuleChecking: true,
610
+ });
611
+ logger.info('update routing rule for site', { site: domain });
612
+ } else {
613
+ await routerManager.addRoutingRule({
614
+ id: exist.id,
615
+ rule,
616
+ skipProtectedRuleChecking: true,
617
+ });
618
+ logger.info('add routing rule for site', { site: domain });
619
+ }
627
620
  changed = true;
628
621
  }
629
622
 
@@ -773,14 +766,13 @@ module.exports = function getRouterHelpers({ dataDirs, routingSnapshot, routerMa
773
766
  };
774
767
 
775
768
  const _removeBlockletSites = async (blocklet, nodeInfo, context = {}) => {
776
- const nodeDomain = await _getIpDnsDomainForNode({ nodeInfo });
777
769
  const interfaces = (blocklet.meta.interfaces || []).filter((x) => x.type === BLOCKLET_INTERFACE_TYPE_WEB);
778
770
 
779
771
  const changes = await Promise.all(
780
772
  interfaces.map(async (x) => {
781
773
  let changed = false;
782
774
 
783
- const domain = getIpDnsDomainForBlocklet(blocklet, x, nodeDomain);
775
+ const domain = getIpDnsDomainForBlocklet(blocklet, x);
784
776
 
785
777
  const site = await states.site.findOne({ domain });
786
778
  if (
@@ -824,11 +816,20 @@ module.exports = function getRouterHelpers({ dataDirs, routingSnapshot, routerMa
824
816
 
825
817
  const sites = await siteState.getSites();
826
818
 
827
- const changes = await Promise.all([
828
- _ensureBlockletRulesForDashboardSite(blocklet, sites, nodeInfo, context),
819
+ const tasks = [
829
820
  _ensureBlockletRulesForWellknownSite(blocklet, sites, context),
830
821
  _ensureBlockletSites(blocklet, sites, nodeInfo, context),
831
- ]);
822
+ ];
823
+
824
+ if (nodeInfo.mode === NODE_MODES.DEBUG || ['e2e', 'test'].includes(process.env.NODE_ENV)) {
825
+ logger.info('Add blocklet endpoint in dashboard site', {
826
+ nodeMode: nodeInfo.mode,
827
+ NODE_ENV: process.env.NODE_ENV,
828
+ });
829
+ tasks.push(_ensureBlockletRulesForDashboardSite(blocklet, sites, nodeInfo, context));
830
+ }
831
+
832
+ const changes = await Promise.all(tasks);
832
833
 
833
834
  return changes.some(Boolean);
834
835
  };
@@ -846,72 +847,14 @@ module.exports = function getRouterHelpers({ dataDirs, routingSnapshot, routerMa
846
847
  return false;
847
848
  }
848
849
 
849
- const interfaces = (blocklet.meta.interfaces || []).filter((x) => x.type === BLOCKLET_INTERFACE_TYPE_WEB);
850
- const hasInterface = (name) => interfaces.some((x) => x.name === name);
851
-
852
- // If no interfaces found, all routing rules should be removed, this rarely happens, but this logic is needed
853
- if (interfaces.length === 0) {
854
- return routerManager.deleteRoutingRulesItemByDid({ did: blocklet.meta.did }, context);
855
- }
856
-
857
- // First, we need ensure system rules for blocklet on dashboard site
858
- let changed = await ensureBlockletRouting(blocklet, context);
859
-
860
- // Then, we need to remove rules whose corresponding interface is not there any more
861
- const sites = await siteState.getRulesByDid(blocklet.meta.did);
862
- for (const site of sites) {
863
- const rulesToRemove = [];
864
- const rulesToUpdate = [];
865
- for (const rule of site.rules) {
866
- if (
867
- rule.to.type !== ROUTING_RULE_TYPES.BLOCKLET ||
868
- !rule.to.interfaceName ||
869
- rule.to.did !== blocklet.meta.did
870
- ) {
871
- // eslint-disable-next-line no-continue
872
- continue;
873
- }
874
-
875
- if (hasInterface(rule.to.interfaceName) === false) {
876
- rulesToRemove.push(rule.id);
877
- }
878
-
879
- if (rule.to.type === ROUTING_RULE_TYPES.BLOCKLET && rule.id === rule.groupId && !rule.isProtected) {
880
- rulesToUpdate.push(rule);
881
- }
882
- }
883
-
884
- for (const rule of rulesToUpdate) {
885
- // eslint-disable-next-line no-await-in-loop
886
- await routerManager.updateRoutingRule({ id: site.id, rule });
887
- changed = true;
888
- }
889
-
890
- if (rulesToRemove.length === 0) {
891
- // eslint-disable-next-line no-continue
892
- continue;
893
- }
894
-
895
- try {
896
- // eslint-disable-next-line no-await-in-loop
897
- await states.site.update(
898
- { _id: site.id },
899
- { $set: { rules: site.rules.filter((x) => rulesToRemove.includes(x.id) === false) } }
900
- );
901
- changed = true;
902
- logger.info('remove routing rule since interface does not exist', { rulesToRemove, did: blocklet.meta.did });
903
- } catch (err) {
904
- logger.error('failed to remove routing rule since interface does not exist', {
905
- rulesToRemove,
906
- did: blocklet.meta.did,
907
- error: err,
908
- });
909
- }
910
- }
911
-
912
- await _removeBlockletSites(blocklet, nodeInfo, context);
850
+ await routerManager.deleteRoutingRulesItemByDid(
851
+ { did: blocklet.meta.did, ruleFilter: (x) => x.isProtected },
852
+ context
853
+ );
854
+ await ensureBlockletRouting(blocklet, context);
913
855
 
914
- return changed;
856
+ // Always return true to trigger update of provider
857
+ return true;
915
858
  };
916
859
 
917
860
  /**
@@ -928,51 +871,6 @@ module.exports = function getRouterHelpers({ dataDirs, routingSnapshot, routerMa
928
871
  return ruleChanged || siteChanged;
929
872
  };
930
873
 
931
- /**
932
- * Refresh ip of in ip-dns-domain if ip changed
933
- */
934
- const ensureIpDnsRouting = async (context = {}, output) => {
935
- const nodeInfo = await nodeState.read();
936
- const sites = await siteState.getSites();
937
-
938
- const ip = await getAccessibleIp(nodeInfo);
939
- if (!ip) {
940
- logger.error('Cannot get get accessible ip for this node');
941
- return false;
942
- }
943
-
944
- const formattedIp = ip.replace(/\./g, '-');
945
- const suffix = DEFAULT_DASHBOARD_DOMAIN.replace(/^\*/, '');
946
- const domainSuffixRegex = new RegExp(`(\\d+-\\d+-\\d+-\\d+)${suffix.replace(/\./g, '\\.')}$`); // xxx-xxx-xxx-xxx.ip.abtnet.io
947
- const domainSuffix = `${formattedIp}${suffix}`;
948
-
949
- const changes = [];
950
-
951
- sites.forEach((x) => {
952
- const match = domainSuffixRegex.exec(x.domain);
953
- if (match && match[1] !== formattedIp) {
954
- const domain = x.domain.replace(domainSuffixRegex, domainSuffix);
955
- changes.push({ id: x.id, domain });
956
- }
957
- });
958
-
959
- if (changes.length) {
960
- for (const change of changes) {
961
- // eslint-disable-next-line no-await-in-loop
962
- await siteState.update({ _id: change.id }, { $set: { domain: change.domain } });
963
- if (typeof output === 'function') {
964
- output(`Blocklet endpoint was updated: ${change.domain}`);
965
- }
966
- }
967
-
968
- const hash = await takeRoutingSnapshot({ message: 'ensure ip dns routing rules', dryRun: false }, context);
969
- logger.info('take routing snapshot on ensure dashboard routing rules', { changes, hash });
970
- return true;
971
- }
972
-
973
- return false;
974
- };
975
-
976
874
  async function readRoutingSites() {
977
875
  const info = await nodeState.read();
978
876
  let snapshotHash = get(info, 'routing.snapshotHash', null);
@@ -1282,7 +1180,6 @@ module.exports = function getRouterHelpers({ dataDirs, routingSnapshot, routerMa
1282
1180
  getCertificates,
1283
1181
  getSitesFromSnapshot,
1284
1182
  checkDomain,
1285
- ensureIpDnsRouting,
1286
1183
 
1287
1184
  getRoutingCrons: () => [
1288
1185
  {
@@ -48,6 +48,23 @@ const mergeAllowedOrigins = (domain, allowedOrigins) => {
48
48
  return origins;
49
49
  };
50
50
 
51
+ const getRoutingTable = ({ sites, nodeInfo }) => {
52
+ let routingTable = Router.formatSites(sites);
53
+ routingTable = expandSites(routingTable);
54
+
55
+ // put dashboardDomain to last, to let blockletDomain match first
56
+ // e.g.
57
+ // dashboardDomain: 192-168-3-2.ip.abtnet.io
58
+ // blockletDomain: static-demo-xxx-192-168-3-2.ip.abtnet.io
59
+ const dashboardDomain = get(nodeInfo, 'routing.dashboardDomain', '');
60
+ const index = routingTable.findIndex((x) => x.domain === dashboardDomain);
61
+ if (index > -1) {
62
+ routingTable.push(...routingTable.splice(index, 1));
63
+ }
64
+
65
+ return routingTable;
66
+ };
67
+
51
68
  class Router {
52
69
  /**
53
70
  * Router
@@ -77,12 +94,13 @@ class Router {
77
94
 
78
95
  logger.info('updateRoutingTable sites:', { sites });
79
96
 
80
- this.routingTable = Router.formatSites(sites);
97
+ this.routingTable = getRoutingTable({ sites, nodeInfo });
98
+
81
99
  logger.info('updateRoutingTable routingTable:', { routingTable: this.routingTable });
82
100
  logger.info('updateRoutingTable certificates:', { certificates: certificates.map((item) => item.domain) });
83
101
 
84
102
  await this.provider.update({
85
- routingTable: expandSites(this.routingTable),
103
+ routingTable: this.routingTable,
86
104
  certificates,
87
105
  globalHeaders: headers,
88
106
  services,
@@ -218,5 +236,6 @@ Router.flattenSitesToRules = (sites = [], info = {}) => {
218
236
  };
219
237
 
220
238
  Router._expandSites = expandSites; // eslint-disable-line no-underscore-dangle
239
+ Router._getRoutingTable = getRoutingTable; // eslint-disable-line no-underscore-dangle
221
240
 
222
241
  module.exports = Router;
@@ -1,6 +1,7 @@
1
1
  /* eslint-disable no-await-in-loop */
2
2
  /* eslint-disable function-paren-newline */
3
3
  /* eslint-disable no-underscore-dangle */
4
+ const omit = require('lodash/omit');
4
5
  const uniq = require('lodash/uniq');
5
6
  const detectPort = require('detect-port');
6
7
  const Lock = require('@abtnode/util/lib/lock');
@@ -128,8 +129,6 @@ class BlockletState extends BaseState {
128
129
  // get ports
129
130
  await lock.acquire();
130
131
 
131
- delete meta.htmlAst;
132
-
133
132
  const ports = await this.getBlockletPorts({ interfaces: sanitized.interfaces || [] });
134
133
 
135
134
  const children = await this.fillChildrenPorts(this.getChildrenFromMetas(childrenMeta), {
@@ -140,7 +139,7 @@ class BlockletState extends BaseState {
140
139
  this.db.insert(
141
140
  {
142
141
  mode,
143
- meta: sanitized,
142
+ meta: omit(sanitized, ['htmlAst']),
144
143
  status,
145
144
  source,
146
145
  deployedFrom,
@@ -177,13 +176,15 @@ class BlockletState extends BaseState {
177
176
  }
178
177
 
179
178
  try {
179
+ fixPerson(meta);
180
+ fixInterfaces(meta);
181
+ const sanitized = validateBlockletMeta(meta);
182
+
180
183
  // get ports
181
184
  await lock.acquire();
182
185
 
183
- delete meta.htmlAst;
184
-
185
186
  const ports = await this.getBlockletPorts({
186
- interfaces: meta.interfaces || [],
187
+ interfaces: sanitized.interfaces || [],
187
188
  skipOccupiedCheckPorts: getExternalPortsFromMeta(doc.meta),
188
189
  });
189
190
  Object.keys(ports).forEach((p) => {
@@ -197,7 +198,7 @@ class BlockletState extends BaseState {
197
198
  // add to db
198
199
  const newDoc = await this.updateById(doc._id, {
199
200
  $set: {
200
- meta,
201
+ meta: omit(sanitized, ['htmlAst']),
201
202
  source,
202
203
  deployedFrom,
203
204
  children,
@@ -223,25 +223,36 @@ const ensureBlockletExpanded = async (meta, appDir) => {
223
223
  const getRootSystemEnvironments = (blocklet, nodeInfo) => {
224
224
  const { did, name, title, description } = blocklet.meta;
225
225
  let wallet;
226
- let blockletAppSK;
227
- let blockletAppId;
226
+ let appSk;
227
+ let appId;
228
+ let appName = title || name;
229
+ let appDescription = description || '';
228
230
 
229
231
  if (process.env.NODE_ENV !== 'test') {
230
- const result = getBlockletInfo(blocklet, nodeInfo.sk);
232
+ const keys = Object.keys(BLOCKLET_CONFIGURABLE_KEY);
233
+ const result = getBlockletInfo(
234
+ {
235
+ meta: blocklet.meta,
236
+ environments: keys.map((key) => ({ key, value: blocklet.configObj[key] })).filter((x) => x.value),
237
+ },
238
+ nodeInfo.sk
239
+ );
231
240
  wallet = result.wallet;
232
- blockletAppSK = toHex(wallet.secretKey);
233
- blockletAppId = wallet.toAddress();
241
+ appSk = toHex(wallet.secretKey);
242
+ appId = wallet.toAddress();
243
+ appName = appName || result.name;
244
+ appDescription = appDescription || result.description;
234
245
  } else {
235
- blockletAppSK = 'AppSK:FIXME:WalletWorksFailedInJest';
236
- blockletAppId = 'AppID:FIXME:WalletWorksFailedInJest';
246
+ appSk = 'AppSK:FIXME:WalletWorksFailedInJest';
247
+ appId = 'AppID:FIXME:WalletWorksFailedInJest';
237
248
  }
238
249
 
239
250
  return {
240
251
  BLOCKLET_DID: did,
241
- BLOCKLET_APP_SK: blockletAppSK,
242
- BLOCKLET_APP_ID: blockletAppId,
243
- BLOCKLET_APP_NAME: title || name,
244
- BLOCKLET_APP_DESCRIPTION: description,
252
+ BLOCKLET_APP_SK: appSk,
253
+ BLOCKLET_APP_ID: appId,
254
+ BLOCKLET_APP_NAME: appName,
255
+ BLOCKLET_APP_DESCRIPTION: appDescription,
245
256
  };
246
257
  };
247
258
 
@@ -269,10 +280,7 @@ const getOverwrittenEnvironments = (blocklet, nodeInfo) => {
269
280
  const { wallet } = getBlockletInfo(
270
281
  {
271
282
  meta: blocklet.meta,
272
- environments: keys.map((key) => ({
273
- key,
274
- value: blocklet.configObj[key] || blocklet.environmentObj[key],
275
- })),
283
+ environments: keys.map((key) => ({ key, value: blocklet.configObj[key] })).filter((x) => x.value),
276
284
  },
277
285
  nodeInfo.sk
278
286
  );
@@ -0,0 +1,41 @@
1
+ const joinUrl = require('url-join');
2
+ const axios = require('@abtnode/util/lib/axios');
3
+ const { DEFAULT_DASHBOARD_DOMAIN, DEFAULT_ADMIN_PATH } = require('@abtnode/constant');
4
+ const { get: getIp } = require('./ip');
5
+
6
+ const getNodeDomain = (ip) => (ip ? DEFAULT_DASHBOARD_DOMAIN.replace(/^\*/, ip.replace(/\./g, '-')) : '');
7
+
8
+ let accessibleIp = null; // cache
9
+
10
+ const timeout = process.env.NODE_ENV === 'test' ? 500 : 5000;
11
+
12
+ // check if node dashboard https endpoint return 2xx
13
+ // FIXME: check connected by wellknown endpoint
14
+ const checkConnected = async ({ ip, nodeInfo }) => {
15
+ const { adminPath = DEFAULT_ADMIN_PATH } = nodeInfo.routing || {};
16
+ const origin = `https://${getNodeDomain(ip)}`;
17
+ const endpoint = joinUrl(origin, adminPath);
18
+ await axios.get(endpoint, { timeout });
19
+ };
20
+
21
+ /**
22
+ * Get accessible external ip of abtnode
23
+ */
24
+ const getAccessibleExternalNodeIp = async (nodeInfo) => {
25
+ const { external } = await getIp();
26
+
27
+ if (accessibleIp === external) {
28
+ return accessibleIp;
29
+ }
30
+
31
+ try {
32
+ await checkConnected({ ip: external, nodeInfo });
33
+ accessibleIp = external;
34
+ } catch {
35
+ accessibleIp = null;
36
+ }
37
+
38
+ return accessibleIp;
39
+ };
40
+
41
+ module.exports = getAccessibleExternalNodeIp;
@@ -1,14 +1,17 @@
1
1
  const slugify = require('slugify');
2
+ const { DEFAULT_IP_DNS_DOMAIN_SUFFIX, SLOT_FOR_IP_DNS_SITE } = require('@abtnode/constant');
2
3
 
3
4
  const formatName = (name) => slugify(name.replace(/^[@./-]/, '').replace(/[@./]/g, '-'));
4
5
 
5
6
  const hiddenInterfaceNames = ['public', 'publicUrl'];
6
7
 
7
- const getIpDnsDomainForBlocklet = (blocklet, blockletInterface, nodeIpDnsDomain) => {
8
+ const getIpDnsDomainForBlocklet = (blocklet, blockletInterface) => {
8
9
  const nameSuffix = blocklet.meta.did.slice(-3).toLowerCase();
9
10
  const iName = hiddenInterfaceNames.includes(blockletInterface.name) ? '' : blockletInterface.name.toLowerCase();
10
11
 
11
- return `${formatName(blocklet.meta.name)}-${nameSuffix}${iName ? '-' : ''}${iName}-${nodeIpDnsDomain}`;
12
+ return `${formatName(blocklet.meta.name)}-${nameSuffix}${
13
+ iName ? '-' : ''
14
+ }${iName}-${SLOT_FOR_IP_DNS_SITE}.${DEFAULT_IP_DNS_DOMAIN_SUFFIX}`;
12
15
  };
13
16
 
14
17
  module.exports = getIpDnsDomainForBlocklet;
package/lib/util/index.js CHANGED
@@ -27,6 +27,8 @@ const {
27
27
  DEFAULT_HTTP_PORT,
28
28
  DEFAULT_HTTPS_PORT,
29
29
  ROUTING_RULE_TYPES,
30
+ SLOT_FOR_IP_DNS_SITE,
31
+ DEFAULT_IP_DNS_DOMAIN_SUFFIX,
30
32
  } = require('@abtnode/constant');
31
33
 
32
34
  const DEFAULT_WELLKNOWN_PORT = 8088;
@@ -71,34 +73,42 @@ const getInterfaceUrl = ({ baseUrl, url, version }) => {
71
73
 
72
74
  const trimSlash = (str = '') => str.replace(/^\/+/, '').replace(/\/+$/, '');
73
75
 
74
- const getBlockletHost = ({ domain, context }) => {
76
+ const getBlockletHost = ({ domain, context, nodeIp }) => {
77
+ const { protocol, port } = context || {};
78
+
75
79
  if (domain === DOMAIN_FOR_DEFAULT_SITE) {
76
80
  return '';
77
81
  }
78
82
 
79
83
  let tmpDomain = domain;
80
84
  if ([DOMAIN_FOR_IP_SITE, DOMAIN_FOR_IP_SITE_REGEXP].includes(domain) || !domain) {
81
- tmpDomain = context.hostname;
85
+ tmpDomain = context.hostname || '';
82
86
  }
83
87
 
84
- if (!context.port) {
85
- return tmpDomain;
88
+ if (tmpDomain.includes(SLOT_FOR_IP_DNS_SITE)) {
89
+ const ipRegex = /\d+[-.]\d+[-.]\d+[-.]\d+/;
90
+ const match = ipRegex.exec(context.hostname);
91
+ if (match) {
92
+ const ip = match[0].replace(/\./g, '-');
93
+ tmpDomain = tmpDomain.replace(SLOT_FOR_IP_DNS_SITE, ip);
94
+ } else if (nodeIp) {
95
+ tmpDomain = tmpDomain.replace(SLOT_FOR_IP_DNS_SITE, nodeIp.replace(/\./g, '-'));
96
+ }
86
97
  }
87
98
 
88
- const tmpPort = Number(context.port);
89
- if (context.protocol === 'https') {
90
- if (tmpPort === 443) {
91
- return tmpDomain; // 不展示 https 中的 443 端口
92
- }
99
+ if (!port) {
100
+ return tmpDomain;
101
+ }
93
102
 
94
- return '';
103
+ if (protocol === 'https' && Number(port) === 443) {
104
+ return tmpDomain; // 不展示 https 中的 443 端口
95
105
  }
96
106
 
97
- if (tmpPort === 80) {
107
+ if (Number(port) === 80) {
98
108
  return tmpDomain; // 不展示 http 中的 80 端
99
109
  }
100
110
 
101
- return `${tmpDomain}:${context.port}`;
111
+ return `${tmpDomain}:${port}`;
102
112
  };
103
113
 
104
114
  /**
@@ -145,15 +155,19 @@ const getAuthConfig = (rule, blocklet) => {
145
155
  return auth.config;
146
156
  };
147
157
 
148
- const getBlockletBaseUrls = ({ routingEnabled = false, port, sites = [], context = {}, blocklet }) => {
149
- const protocol = 'http'; // TODO: 这里固定为 http, 因为判断 url 是不是 https 和证书相关,这里实现的话比较复杂
158
+ const getBlockletBaseUrls = ({ routingEnabled = false, port, sites = [], context = {}, blocklet, nodeIp }) => {
150
159
  let baseUrls = [];
151
160
 
152
161
  if (routingEnabled && Array.isArray(sites) && sites.length > 0) {
153
162
  baseUrls = sites
154
163
  .map((site) => {
155
- const host = getBlockletHost({ domain: site.from.domain, context });
164
+ const host = getBlockletHost({ domain: site.from.domain, context, nodeIp });
156
165
  if (host) {
166
+ let protocol = 'http'; // TODO: 这里固定为 http, 因为判断 url 是不是 https 和证书相关,这里实现的话比较复杂
167
+ if (host.includes(DEFAULT_IP_DNS_DOMAIN_SUFFIX)) {
168
+ protocol = 'https';
169
+ }
170
+
157
171
  return {
158
172
  ruleId: site.id,
159
173
  baseUrl: `${protocol}://${host}/${trimSlash(site.from.pathPrefix)}`,
@@ -167,14 +181,14 @@ const getBlockletBaseUrls = ({ routingEnabled = false, port, sites = [], context
167
181
  .filter(Boolean);
168
182
  }
169
183
 
170
- if (!baseUrls.length && isIp(context.hostname) && protocol !== 'https') {
171
- baseUrls = [{ baseUrl: `${protocol}://${context.hostname}:${port}` }];
184
+ if (!baseUrls.length && isIp(context.hostname)) {
185
+ baseUrls = [{ baseUrl: `http://${context.hostname}:${port}` }];
172
186
  }
173
187
 
174
188
  return baseUrls;
175
189
  };
176
190
 
177
- const getBlockletInterfaces = ({ blocklet, context, nodeInfo, sites }) => {
191
+ const getBlockletInterfaces = ({ blocklet, context, nodeInfo, sites, nodeIp }) => {
178
192
  const interfaces = [];
179
193
  (blocklet.meta.interfaces || []).forEach((x) => {
180
194
  if (x.port && x.port.external) {
@@ -202,6 +216,7 @@ const getBlockletInterfaces = ({ blocklet, context, nodeInfo, sites }) => {
202
216
  }),
203
217
  context,
204
218
  blocklet,
219
+ nodeIp,
205
220
  });
206
221
 
207
222
  baseUrls.forEach(({ baseUrl, ruleId, interfaceName, authConfig }) => {
@@ -140,7 +140,11 @@ const doUpgrade = async (session) => {
140
140
  await goNextState(NODE_UPGRADE_PROGRESS.COMPLETE);
141
141
  await sleep(5000);
142
142
  await states.node.updateNodeInfo({ nextVersion: '', version: to, upgradeSessionId: '' });
143
- await states.node.exitMode(NODE_MODES.MAINTENANCE);
143
+ try {
144
+ await states.node.exitMode(NODE_MODES.MAINTENANCE);
145
+ } catch (error) {
146
+ logger.error('Failed to exit maintenance mode', { error });
147
+ }
144
148
  }
145
149
  await lock.release();
146
150
  };
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "1.4.13",
6
+ "version": "1.4.14",
7
7
  "description": "",
8
8
  "main": "lib/index.js",
9
9
  "files": [
@@ -19,26 +19,26 @@
19
19
  "author": "wangshijun <wangshijun2010@gmail.com> (http://github.com/wangshijun)",
20
20
  "license": "MIT",
21
21
  "dependencies": {
22
- "@abtnode/constant": "1.4.13",
23
- "@abtnode/cron": "1.4.13",
24
- "@abtnode/event-hub": "1.4.13",
25
- "@abtnode/logger": "1.4.13",
26
- "@abtnode/queue": "1.4.13",
27
- "@abtnode/rbac": "1.4.13",
28
- "@abtnode/router-provider": "1.4.13",
29
- "@abtnode/static-server": "1.4.13",
30
- "@abtnode/timemachine": "1.4.13",
31
- "@abtnode/util": "1.4.13",
32
- "@arcblock/did": "^1.13.8",
22
+ "@abtnode/constant": "1.4.14",
23
+ "@abtnode/cron": "1.4.14",
24
+ "@abtnode/event-hub": "1.4.14",
25
+ "@abtnode/logger": "1.4.14",
26
+ "@abtnode/queue": "1.4.14",
27
+ "@abtnode/rbac": "1.4.14",
28
+ "@abtnode/router-provider": "1.4.14",
29
+ "@abtnode/static-server": "1.4.14",
30
+ "@abtnode/timemachine": "1.4.14",
31
+ "@abtnode/util": "1.4.14",
32
+ "@arcblock/did": "^1.13.15",
33
33
  "@arcblock/pm2-events": "^0.0.5",
34
- "@arcblock/vc": "^1.13.8",
35
- "@blocklet/meta": "1.4.13",
34
+ "@arcblock/vc": "^1.13.15",
35
+ "@blocklet/meta": "1.4.14",
36
36
  "@fidm/x509": "^1.2.1",
37
37
  "@nedb/core": "^1.1.0",
38
38
  "@nedb/multi": "^1.1.0",
39
- "@ocap/mcrypto": "^1.13.8",
40
- "@ocap/util": "^1.13.8",
41
- "@ocap/wallet": "^1.13.8",
39
+ "@ocap/mcrypto": "^1.13.15",
40
+ "@ocap/util": "^1.13.15",
41
+ "@ocap/wallet": "^1.13.15",
42
42
  "@slack/webhook": "^5.0.3",
43
43
  "axios": "^0.21.4",
44
44
  "axon": "^2.0.3",
@@ -72,5 +72,5 @@
72
72
  "express": "^4.17.1",
73
73
  "jest": "^26.4.2"
74
74
  },
75
- "gitHead": "1ad4c9d0e45a551d997b7f65ea4971463f3708a3"
75
+ "gitHead": "9176b462ff92c787e18a8b218f06a451aad4cf1d"
76
76
  }
@@ -1,42 +0,0 @@
1
- const joinUrl = require('url-join');
2
- const axios = require('@abtnode/util/lib/axios');
3
- const getIp = require('@abtnode/util/lib/get-ip');
4
- const { DEFAULT_DASHBOARD_DOMAIN, DEFAULT_ADMIN_PATH } = require('@abtnode/constant');
5
-
6
- const getNodeDomain = (ip) => (ip ? DEFAULT_DASHBOARD_DOMAIN.replace(/^\*/, ip.replace(/\./g, '-')) : '');
7
-
8
- const getAccessibleIp = async ({ nodeInfo = {}, timeout = 5000 } = {}) => {
9
- const { adminPath = DEFAULT_ADMIN_PATH } = nodeInfo.routing || {};
10
-
11
- // check if node dashboard https endpoint return 2xx
12
- // FIXME: check connected by wellknown endpoint
13
- const checkConnected = async ({ ip }) => {
14
- const origin = `https://${getNodeDomain(ip)}`;
15
- const endpoint = joinUrl(origin, adminPath);
16
- await axios.get(endpoint, { timeout });
17
- };
18
-
19
- const { internal, external } = await getIp({ timeout });
20
-
21
- if (!external) {
22
- return internal;
23
- }
24
-
25
- try {
26
- await checkConnected({ ip: external });
27
- return external;
28
- } catch {
29
- return internal;
30
- }
31
- };
32
-
33
- const getAccessibleIpDnsDomainForNode = async ({ nodeInfo = {}, timeout = 5000 } = {}) => {
34
- const accessibleIp = await getAccessibleIp({ nodeInfo, timeout });
35
-
36
- return getNodeDomain(accessibleIp);
37
- };
38
-
39
- module.exports = {
40
- getAccessibleIp,
41
- getAccessibleIpDnsDomainForNode,
42
- };