@abtnode/core 1.6.21 → 1.6.24

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.
@@ -92,6 +92,7 @@ const {
92
92
  needBlockletDownload,
93
93
  verifyPurchase,
94
94
  } = require('../../util/blocklet');
95
+ const { parseSourceUrl } = require('../../util/registry');
95
96
  const states = require('../../states');
96
97
  const BlockletRegistry = require('../registry');
97
98
  const BaseBlockletManager = require('./base');
@@ -176,6 +177,9 @@ class BlockletManager extends BaseBlockletManager {
176
177
  logger.debug('install blocklet', { params, context });
177
178
 
178
179
  const source = getSourceFromInstallParams(params);
180
+ if (typeof context.startImmediately === 'undefined') {
181
+ context.startImmediately = !!params.startImmediately;
182
+ }
179
183
 
180
184
  if (source === BlockletSource.url) {
181
185
  return this._installFromUrl({ url: params.url, sync: params.sync }, context);
@@ -673,7 +677,7 @@ class BlockletManager extends BaseBlockletManager {
673
677
  /**
674
678
  * upgrade blocklet from registry
675
679
  */
676
- async upgrade({ did, registryUrl }, context) {
680
+ async upgrade({ did, registryUrl, sync }, context) {
677
681
  const blocklet = await states.blocklet.getBlocklet(did);
678
682
 
679
683
  // TODO: 查看了下目前页面中的升级按钮,都是会传 registryUrl 过来的,这个函数里的逻辑感觉需要在以后做一个简化
@@ -740,6 +744,7 @@ class BlockletManager extends BaseBlockletManager {
740
744
  source: BlockletSource.registry,
741
745
  deployedFrom: upgradeFromRegistry,
742
746
  context,
747
+ sync,
743
748
  });
744
749
  }
745
750
 
@@ -920,7 +925,6 @@ class BlockletManager extends BaseBlockletManager {
920
925
  }
921
926
 
922
927
  const { did, version } = meta;
923
- meta.title = `[DEV] ${meta.title || meta.name}`;
924
928
 
925
929
  const exist = await states.blocklet.getBlocklet(did);
926
930
  if (exist) {
@@ -1104,11 +1108,13 @@ class BlockletManager extends BaseBlockletManager {
1104
1108
  blocklet.diskInfo = await getDiskInfo(blocklet, { useFakeDiskInfo: !diskInfo });
1105
1109
 
1106
1110
  try {
1107
- const app = blocklet.meta.group === BlockletGroup.gateway ? blocklet.children[0].env : blocklet.env;
1108
- const { appId } = app;
1109
- blocklet.runtimeInfo = await getRuntimeInfo(appId);
1110
- if (blocklet.runtimeInfo.status && shouldUpdateBlockletStatus(blocklet.status)) {
1111
- blocklet.status = statusMap[blocklet.runtimeInfo.status];
1111
+ const app = blocklet.meta.group === BlockletGroup.gateway ? blocklet.children[0] : blocklet;
1112
+ if (app) {
1113
+ const { appId } = app.env;
1114
+ blocklet.runtimeInfo = await getRuntimeInfo(appId);
1115
+ if (blocklet.runtimeInfo.status && shouldUpdateBlockletStatus(blocklet.status)) {
1116
+ blocklet.status = statusMap[blocklet.runtimeInfo.status];
1117
+ }
1112
1118
  }
1113
1119
  } catch (err) {
1114
1120
  if (err.code !== 'BLOCKLET_PROCESS_404') {
@@ -1379,7 +1385,7 @@ class BlockletManager extends BaseBlockletManager {
1379
1385
  }
1380
1386
  }
1381
1387
 
1382
- async _installFromStore({ did, registry }, context) {
1388
+ async _installFromStore({ did, registry, sync }, context) {
1383
1389
  logger.debug('start install blocklet', { did });
1384
1390
  if (!isValidDid(did)) {
1385
1391
  throw new Error('Blocklet did is invalid');
@@ -1404,6 +1410,7 @@ class BlockletManager extends BaseBlockletManager {
1404
1410
  meta,
1405
1411
  source: BlockletSource.registry,
1406
1412
  deployedFrom: info.cdnUrl || registryUrl,
1413
+ sync,
1407
1414
  context,
1408
1415
  });
1409
1416
  }
@@ -1411,6 +1418,16 @@ class BlockletManager extends BaseBlockletManager {
1411
1418
  async _installFromUrl({ url, sync }, context) {
1412
1419
  logger.debug('start install blocklet', { url });
1413
1420
 
1421
+ const { inStore, registryUrl, blockletDid } = await parseSourceUrl(url);
1422
+ if (inStore) {
1423
+ const exist = await states.blocklet.getBlocklet(blockletDid);
1424
+ if (exist) {
1425
+ return this.upgrade({ did: blockletDid, registryUrl }, context);
1426
+ }
1427
+
1428
+ return this._installFromStore({ did: blockletDid, registry: registryUrl }, context);
1429
+ }
1430
+
1414
1431
  const meta = await getBlockletMetaFromUrl(url);
1415
1432
 
1416
1433
  if (!meta) {
@@ -2335,7 +2352,7 @@ class BlockletManager extends BaseBlockletManager {
2335
2352
  } catch (err) {
2336
2353
  const b = await this._rollback(action, did, oldBlocklet);
2337
2354
  logger.error(`failed to ${action} blocklet`, { did, version, name, error: err });
2338
- this.emit(BlockletEvents.updated, { blocklet: b });
2355
+ this.emit(BlockletEvents.updated, b);
2339
2356
  states.notification.create({
2340
2357
  title: `Blocklet ${capitalize(action)} Failed`,
2341
2358
  description: `Blocklet ${name}@${version} ${action} failed with error: ${err.message}`,
@@ -2622,7 +2639,7 @@ class BlockletManager extends BaseBlockletManager {
2622
2639
  const result = await states.blocklet.updateBlocklet(did, oldBlocklet);
2623
2640
  await this._setConfigs(did);
2624
2641
  logger.info('blocklet rollback successfully', { did });
2625
- this.emit(BlockletEvents.updated, { blocklet: result });
2642
+ this.emit(BlockletEvents.updated, result);
2626
2643
  return result;
2627
2644
  }
2628
2645
 
@@ -1,9 +1,7 @@
1
- const isBase64 = require('is-base64');
2
1
  const { BlockletGroup } = require('@blocklet/meta/lib/constants');
3
2
  const joinURL = require('url-join');
4
3
  const get = require('lodash/get');
5
- const pick = require('lodash/pick');
6
- const { BLOCKLET_STORE_API_PREFIX, BLOCKLET_STORE_META_PATH } = require('@abtnode/constant');
4
+ const { BLOCKLET_STORE_API_PREFIX } = require('@abtnode/constant');
7
5
 
8
6
  const { name } = require('../../package.json');
9
7
 
@@ -15,6 +13,7 @@ const states = require('../states');
15
13
  const isRequirementsSatisfied = require('../util/requirement');
16
14
  const { fixAndVerifyBlockletMeta } = require('../util/blocklet');
17
15
  const { translate } = require('../locales');
16
+ const { validateRegistryURL, getRegistryMeta } = require('../util/registry');
18
17
 
19
18
  const DEFAULT_REFRESH_INTERVAL = 1 * 60 * 1000;
20
19
  const MAX_REFRESH_INTERVAL = 1 * 60 * 1000;
@@ -152,54 +151,8 @@ class BlockletRegistry {
152
151
  }
153
152
  }
154
153
 
155
- BlockletRegistry.validateRegistryURL = async (registry) => {
156
- const url = joinURL(registry, BLOCKLET_STORE_API_PREFIX, `/blocklets.json?__t__=${Date.now()}`);
157
- try {
158
- const res = await request.get(url);
159
- if (Array.isArray(res.data)) {
160
- return res.data;
161
- }
162
-
163
- logger.error('Blocklet list fetch failed ', { url, data: res.data });
164
- throw new Error('blocklet list fetch failed');
165
- } catch (error) {
166
- logger.error('Blocklet registry refresh failed', { url, error });
167
- throw new Error(`Invalid Blocklet Registry URL ${registry}: ${error.message}`);
168
- }
169
- };
170
-
171
- BlockletRegistry.getRegistryMeta = async (registry) => {
172
- try {
173
- const url = joinURL(registry, BLOCKLET_STORE_META_PATH, `?__t__=${Date.now()}`);
174
- const { data } = await request.get(url);
175
-
176
- if (!data) {
177
- return {};
178
- }
154
+ BlockletRegistry.validateRegistryURL = validateRegistryURL;
179
155
 
180
- const requiredFields = ['name', 'description', 'maintainer'];
181
-
182
- const missingFields = requiredFields.filter((x) => !data[x]);
183
- if (missingFields.length > 0) {
184
- throw new Error(`the registry missing required information: ${missingFields.join(', ')}`);
185
- }
186
-
187
- const result = pick(data, ['id', 'name', 'description', 'maintainer', 'cdnUrl', 'chainHost']);
188
- const { logoUrl } = data;
189
- if (logoUrl) {
190
- if (logoUrl.startsWith('http') === true) {
191
- result.logoUrl = logoUrl;
192
- } else if (isBase64(logoUrl, { allowMime: true })) {
193
- result.logoUrl = logoUrl;
194
- } else {
195
- result.logoUrl = joinURL(registry, logoUrl);
196
- }
197
- }
198
-
199
- return result;
200
- } catch (err) {
201
- throw new Error(`Can not get meta info for registry [${registry}]: ${err.message}`);
202
- }
203
- };
156
+ BlockletRegistry.getRegistryMeta = getRegistryMeta;
204
157
 
205
158
  module.exports = BlockletRegistry;
package/lib/event.js CHANGED
@@ -208,6 +208,7 @@ module.exports = ({
208
208
  };
209
209
 
210
210
  events.handleBlockletAdd = handleBlockletAdd;
211
+ events.handleBlockletUpgrade = handleBlockletUpgrade;
211
212
  events.handleBlockletRemove = handleBlockletRemove;
212
213
  events.handleServerEvent = handleServerEvent;
213
214
  events.onEvent = onEvent;
package/lib/index.js CHANGED
@@ -35,6 +35,7 @@ const createQueue = require('./queue');
35
35
  const createEvents = require('./event');
36
36
  const pm2Events = require('./blocklet/manager/pm2-events');
37
37
  const { createStateReadyQueue, createStateReadyHandler } = require('./util/ready');
38
+ const { getSysInfo, SysInfoEmitter } = require('./util/sysinfo');
38
39
  const { toStatus, fromStatus, ensureDataDirs, getQueueConcurrencyByMem } = require('./util');
39
40
 
40
41
  /**
@@ -148,6 +149,7 @@ function ABTNode(options) {
148
149
  getSitesFromSnapshot,
149
150
  getRoutingCrons,
150
151
  ensureWildcardCerts,
152
+ getRouterProvider,
151
153
  } = getRouterHelpers({ dataDirs, routingSnapshot, routerManager, blockletManager, certManager });
152
154
 
153
155
  const teamManager = new TeamManager({ nodeDid: options.nodeDid, dataDirs, states });
@@ -367,6 +369,11 @@ function ABTNode(options) {
367
369
 
368
370
  // Services
369
371
  getServices: (params, context) => getServices({ stringifySchema: true }, context),
372
+
373
+ // Utilities: moved here because some deps require native build
374
+ getSysInfo,
375
+ getSysInfoEmitter: (interval) => new SysInfoEmitter(interval),
376
+ getRouterProvider,
370
377
  };
371
378
 
372
379
  const webhook = WebHook({ events, dataDirs, instance });
@@ -2,13 +2,18 @@ const yaml = require('js-yaml');
2
2
  const fs = require('fs');
3
3
  const set = require('lodash/set');
4
4
  const omit = require('lodash/omit');
5
- const { DEFAULT_WILDCARD_CERT_HOST, DEFAULT_DID_DOMAIN, DEFAULT_DID_REGISTRY } = require('@abtnode/constant');
5
+ const {
6
+ DEFAULT_WILDCARD_CERT_HOST,
7
+ DEFAULT_DID_DOMAIN,
8
+ DEFAULT_DID_REGISTRY,
9
+ DEFAULT_IP_DOMAIN,
10
+ } = require('@abtnode/constant');
6
11
 
7
12
  module.exports = async ({ states, printInfo, configFile }) => {
8
13
  printInfo('Try to rename dashboardDomain to ipWildcardDomain in db...');
9
14
 
10
15
  let info = await states.node.read();
11
- set(info, 'routing.ipWildcardDomain', info.routing.dashboardDomain);
16
+ set(info, 'routing.ipWildcardDomain', info.routing.dashboardDomain || DEFAULT_IP_DOMAIN);
12
17
  set(info, 'routing.wildcardCertHost', DEFAULT_WILDCARD_CERT_HOST);
13
18
  set(info, 'didDomain', DEFAULT_DID_DOMAIN);
14
19
  set(info, 'didRegistry', DEFAULT_DID_REGISTRY);
@@ -16,12 +21,15 @@ module.exports = async ({ states, printInfo, configFile }) => {
16
21
 
17
22
  await states.node.updateNodeInfo(info);
18
23
 
19
- let rawConfig = yaml.safeLoad(fs.readFileSync(configFile).toString());
20
- set(rawConfig, 'node.routing.ipWildcardDomain', info.routing.ipWildcardDomain);
21
- set(rawConfig, 'node.routing.wildcardCertHost', DEFAULT_WILDCARD_CERT_HOST);
22
- set(rawConfig, 'node.didDomain', DEFAULT_DID_DOMAIN);
23
- set(rawConfig, 'node.didRegistry', DEFAULT_DID_REGISTRY);
24
- rawConfig = omit(rawConfig, 'node.routing.dashboardDomain');
25
- fs.writeFileSync(configFile, yaml.dump(rawConfig));
24
+ if (process.env.NODE_ENV !== 'development') {
25
+ let rawConfig = yaml.safeLoad(fs.readFileSync(configFile).toString());
26
+ set(rawConfig, 'node.routing.ipWildcardDomain', info.routing.ipWildcardDomain);
27
+ set(rawConfig, 'node.routing.wildcardCertHost', DEFAULT_WILDCARD_CERT_HOST);
28
+ set(rawConfig, 'node.didDomain', DEFAULT_DID_DOMAIN);
29
+ set(rawConfig, 'node.didRegistry', DEFAULT_DID_REGISTRY);
30
+ rawConfig = omit(rawConfig, 'node.routing.dashboardDomain');
31
+ fs.writeFileSync(configFile, yaml.dump(rawConfig));
32
+ }
33
+
26
34
  printInfo(`> Persist new config to file: ${configFile}`);
27
35
  };
@@ -132,9 +132,13 @@ const runMigrationScripts = async ({
132
132
  for (let i = 0; i < scripts.length; i++) {
133
133
  const { script, version } = scripts[i];
134
134
  try {
135
- const executed = await node.isMigrationExecuted({ script, version });
136
- if (executed === false) {
135
+ if (process.env.NODE_ENV === 'development') {
137
136
  pending.push(scripts[i]);
137
+ } else {
138
+ const executed = await node.isMigrationExecuted({ script, version });
139
+ if (executed === false) {
140
+ pending.push(scripts[i]);
141
+ }
138
142
  }
139
143
  } catch (err) {
140
144
  printError(`Failed to detect migration script execution status: ${script}, error: ${err.message}`);
@@ -9,12 +9,11 @@ const isEqual = require('lodash/isEqual');
9
9
  const joinUrl = require('url-join');
10
10
  const { getProvider } = require('@abtnode/router-provider');
11
11
  const normalizePathPrefix = require('@abtnode/util/lib/normalize-path-prefix');
12
- const getTmpDirectory = require('@abtnode/util/lib/get-tmp-directory');
12
+ const getTmpDir = require('@abtnode/util/lib/get-tmp-directory');
13
13
  const downloadFile = require('@abtnode/util/lib/download-file');
14
14
  const {
15
15
  DOMAIN_FOR_DEFAULT_SITE,
16
16
  DOMAIN_FOR_IP_SITE_REGEXP,
17
- ROUTER_PROVIDER_NONE,
18
17
  DOMAIN_FOR_INTERNAL_SITE,
19
18
  WELLKNOWN_PATH_PREFIX,
20
19
  WELLKNOWN_AUTH_PATH_PREFIX,
@@ -30,6 +29,7 @@ const {
30
29
  BLOCKLET_SITE_GROUP_SUFFIX,
31
30
  WELLKNOWN_ACME_CHALLENGE_PREFIX,
32
31
  WELLKNOWN_DID_RESOLVER_PREFIX,
32
+ WELLKNOWN_PING_PREFIX,
33
33
  } = require('@abtnode/constant');
34
34
  const {
35
35
  BLOCKLET_DYNAMIC_PATH_PREFIX,
@@ -223,6 +223,19 @@ const ensureServiceRule = async (sites) => {
223
223
  return site;
224
224
  });
225
225
  };
226
+ const ensureRootRule = async (sites) => {
227
+ return sites.map((site) => {
228
+ if (!isBasicSite(site.domain) && !site.rules.some((x) => x.from.pathPrefix === '/')) {
229
+ site.rules.push({
230
+ from: { pathPrefix: '/' },
231
+ to: {
232
+ type: ROUTING_RULE_TYPES.NONE,
233
+ },
234
+ });
235
+ }
236
+ return site;
237
+ });
238
+ };
226
239
 
227
240
  const ensureLatestNodeInfo = async (sites = [], { withDefaultCors = true } = {}) => {
228
241
  const info = await states.node.read();
@@ -436,7 +449,7 @@ module.exports = function getRouterHelpers({ dataDirs, routingSnapshot, routerMa
436
449
  const hasRuleByPrefix = (site, value) => site.rules.find((x) => x.isProtected && get(x, 'from.pathPrefix') === value);
437
450
 
438
451
  const downloadCert = async ({ domain, url }) => {
439
- const destFolder = getTmpDirectory(path.join(`certificate-${Date.now()}`));
452
+ const destFolder = getTmpDir(path.join(`certificate-${Date.now()}`));
440
453
  try {
441
454
  const filename = path.join(destFolder, 'certificate.tar.gz');
442
455
  fs.ensureDirSync(destFolder);
@@ -498,11 +511,6 @@ module.exports = function getRouterHelpers({ dataDirs, routingSnapshot, routerMa
498
511
 
499
512
  const updateDashboardCertificates = async () => {
500
513
  const info = await nodeState.read();
501
- const provider = getProviderFromNodeInfo(info);
502
- if (provider === ROUTER_PROVIDER_NONE) {
503
- return;
504
- }
505
-
506
514
  const https = get(info, 'routing.https', true);
507
515
  const ipWildcardDomain = get(info, 'routing.ipWildcardDomain', '');
508
516
  const didDomain = info.didDomain;
@@ -584,6 +592,20 @@ module.exports = function getRouterHelpers({ dataDirs, routingSnapshot, routerMa
584
592
  to: proxyTarget,
585
593
  };
586
594
 
595
+ const pingWellknownRule = {
596
+ isProtected: true,
597
+ from: { pathPrefix: WELLKNOWN_PING_PREFIX },
598
+ to: {
599
+ type: ROUTING_RULE_TYPES.DIRECT_RESPONSE,
600
+ response: {
601
+ status: 200,
602
+ contentType: 'application/javascript',
603
+ body: "console.log('ping: pong')",
604
+ },
605
+ port: info.port,
606
+ },
607
+ };
608
+
587
609
  if (site) {
588
610
  const didResolverRuleUpdateRes = await upsertSiteRule(
589
611
  {
@@ -601,7 +623,15 @@ module.exports = function getRouterHelpers({ dataDirs, routingSnapshot, routerMa
601
623
  context
602
624
  );
603
625
 
604
- return didResolverRuleUpdateRes || acmeRuleUpdateRes;
626
+ const pingRuleRes = await upsertSiteRule(
627
+ {
628
+ site,
629
+ rule: pingWellknownRule,
630
+ },
631
+ context
632
+ );
633
+
634
+ return didResolverRuleUpdateRes || acmeRuleUpdateRes || pingRuleRes;
605
635
  }
606
636
 
607
637
  await routerManager.addRoutingSite(
@@ -610,7 +640,7 @@ module.exports = function getRouterHelpers({ dataDirs, routingSnapshot, routerMa
610
640
  domain: DOMAIN_FOR_INTERNAL_SITE,
611
641
  port: await getWellknownSitePort(),
612
642
  name: NAME_FOR_WELLKNOWN_SITE,
613
- rules: [didResolverWellknownRule, acmeChallengeWellknownRule],
643
+ rules: [didResolverWellknownRule, acmeChallengeWellknownRule, pingWellknownRule],
614
644
  isProtected: true,
615
645
  },
616
646
  skipCheckDynamicBlacklist: true,
@@ -634,12 +664,6 @@ module.exports = function getRouterHelpers({ dataDirs, routingSnapshot, routerMa
634
664
  */
635
665
  const ensureDashboardRouting = async (context = {}) => {
636
666
  const info = await nodeState.read();
637
-
638
- const provider = getProviderFromNodeInfo(info);
639
- if (provider === ROUTER_PROVIDER_NONE) {
640
- return false;
641
- }
642
-
643
667
  const sites = await siteState.getSites();
644
668
  let dashboardSite = (sites || []).find((x) => x.domain === DOMAIN_FOR_IP_SITE);
645
669
  const updatedResult = [];
@@ -880,6 +904,7 @@ module.exports = function getRouterHelpers({ dataDirs, routingSnapshot, routerMa
880
904
  * Add system routing rules for blocklet in dashboard site
881
905
  *
882
906
  * @returns {boolean} if routing changed
907
+ * TODO: do we still need this?
883
908
  */
884
909
  const _ensureBlockletRulesForDashboardSite = async (blocklet, sites, nodeInfo, context = {}) => {
885
910
  // blocklet level duplication detection
@@ -980,12 +1005,6 @@ module.exports = function getRouterHelpers({ dataDirs, routingSnapshot, routerMa
980
1005
  */
981
1006
  const ensureBlockletRouting = async (blocklet, context = {}) => {
982
1007
  const nodeInfo = await nodeState.read();
983
-
984
- const provider = getProviderFromNodeInfo(nodeInfo);
985
- if (provider === ROUTER_PROVIDER_NONE) {
986
- return false;
987
- }
988
-
989
1008
  const hasWebInterface = (blocklet.meta.interfaces || []).some((x) => x.type === BLOCKLET_INTERFACE_TYPE_WEB);
990
1009
  if (!hasWebInterface) {
991
1010
  return false;
@@ -1085,12 +1104,6 @@ module.exports = function getRouterHelpers({ dataDirs, routingSnapshot, routerMa
1085
1104
  * @returns {boolean} if routing changed
1086
1105
  */
1087
1106
  const ensureBlockletRoutingForUpgrade = async (blocklet, context = {}) => {
1088
- const nodeInfo = await nodeState.read();
1089
- const provider = getProviderFromNodeInfo(nodeInfo);
1090
- if (provider === ROUTER_PROVIDER_NONE) {
1091
- return false;
1092
- }
1093
-
1094
1107
  await routerManager.deleteRoutingRulesItemByDid(
1095
1108
  { did: blocklet.meta.did, ruleFilter: (x) => x.isProtected },
1096
1109
  context
@@ -1145,14 +1158,11 @@ module.exports = function getRouterHelpers({ dataDirs, routingSnapshot, routerMa
1145
1158
  return rules.filter((rule) => rule.to.did === did);
1146
1159
  }
1147
1160
 
1148
- const routers = {}; // we need to keep reference for different router instances
1161
+ const providers = {}; // we need to keep reference for different router instances
1149
1162
  const handleRouting = async (nodeInfo) => {
1150
1163
  const providerName = get(nodeInfo, 'routing.provider', null);
1151
1164
  const httpsEnabled = get(nodeInfo, 'routing.https', true);
1152
1165
  logger.debug('handle routing', { providerName, httpsEnabled });
1153
- if (providerName === ROUTER_PROVIDER_NONE) {
1154
- return;
1155
- }
1156
1166
 
1157
1167
  const Provider = getProvider(providerName);
1158
1168
  const checkResult = await Provider.check({ configDir: dataDirs.router });
@@ -1160,12 +1170,12 @@ module.exports = function getRouterHelpers({ dataDirs, routingSnapshot, routerMa
1160
1170
  throw new Error(`${providerName} pre-check failed, ${checkResult.error}`);
1161
1171
  }
1162
1172
 
1163
- if (routers[providerName]) {
1164
- await routers[providerName].restart();
1173
+ if (providers[providerName]) {
1174
+ await providers[providerName].reload();
1165
1175
  } else {
1166
- routers[providerName] = new Router({
1176
+ providers[providerName] = new Router({
1167
1177
  provider: new Provider({
1168
- configDirectory: path.join(dataDirs.router, providerName),
1178
+ configDir: path.join(dataDirs.router, providerName),
1169
1179
  httpPort: nodeInfo.routing.httpPort || DEFAULT_HTTP_PORT,
1170
1180
  httpsPort: nodeInfo.routing.httpsPort || DEFAULT_HTTPS_PORT,
1171
1181
  cacheDisabled: nodeInfo.mode === NODE_MODES.DEBUG,
@@ -1178,6 +1188,7 @@ module.exports = function getRouterHelpers({ dataDirs, routingSnapshot, routerMa
1178
1188
  sites = await ensureLatestInfo(sites);
1179
1189
  sites = await ensureAuthService(sites, blockletManager);
1180
1190
  sites = await ensureServiceRule(sites);
1191
+ sites = await ensureRootRule(sites);
1181
1192
 
1182
1193
  const certificates = httpsEnabled ? await certManager.getAllNormal() : [];
1183
1194
 
@@ -1202,11 +1213,11 @@ module.exports = function getRouterHelpers({ dataDirs, routingSnapshot, routerMa
1202
1213
  },
1203
1214
  });
1204
1215
 
1205
- certManager.on('cert.added', () => routers[providerName].restart());
1206
- certManager.on('cert.removed', () => routers[providerName].restart());
1207
- certManager.on('cert.issued', () => routers[providerName].restart());
1216
+ certManager.on('cert.added', () => providers[providerName].reload());
1217
+ certManager.on('cert.removed', () => providers[providerName].reload());
1218
+ certManager.on('cert.issued', () => providers[providerName].reload());
1208
1219
 
1209
- await routers[providerName].start();
1220
+ await providers[providerName].start();
1210
1221
  }
1211
1222
  };
1212
1223
 
@@ -1214,8 +1225,8 @@ module.exports = function getRouterHelpers({ dataDirs, routingSnapshot, routerMa
1214
1225
  const info = await nodeState.read();
1215
1226
  const providerName = get(info, 'routing.provider', null);
1216
1227
 
1217
- if (providerName && routers[providerName] && typeof routers[providerName].rotateLogs === 'function') {
1218
- await routers[providerName].rotateLogs();
1228
+ if (providerName && providers[providerName] && typeof providers[providerName].rotateLogs === 'function') {
1229
+ await providers[providerName].rotateLogs();
1219
1230
  }
1220
1231
  };
1221
1232
 
@@ -1244,30 +1255,28 @@ module.exports = function getRouterHelpers({ dataDirs, routingSnapshot, routerMa
1244
1255
  await handleRouting(result);
1245
1256
 
1246
1257
  if (newProvider !== info.routing.provider) {
1247
- if (newProvider !== ROUTER_PROVIDER_NONE) {
1248
- // Ensure we have system sites for daemon
1249
- await ensureDashboardRouting(context);
1250
-
1251
- // Ensure we have system rules for blocklets
1252
- const blocklets = await blockletState.getBlocklets();
1253
- const ensureBlocklet = async (x) => {
1254
- const blocklet = await blockletManager.ensureBlocklet(x.meta.did);
1255
- return ensureBlockletRouting(blocklet, context);
1256
- };
1257
- const ensureBlockletResults = await Promise.all(blocklets.map((x) => ensureBlocklet(x)));
1258
+ // Ensure we have system sites for daemon
1259
+ await ensureDashboardRouting(context);
1260
+
1261
+ // Ensure we have system rules for blocklets
1262
+ const blocklets = await blockletState.getBlocklets();
1263
+ const ensureBlocklet = async (x) => {
1264
+ const blocklet = await blockletManager.ensureBlocklet(x.meta.did);
1265
+ return ensureBlockletRouting(blocklet, context);
1266
+ };
1267
+ const ensureBlockletResults = await Promise.all(blocklets.map((x) => ensureBlocklet(x)));
1258
1268
 
1259
- // We need to take snapshot after system rules ensured
1260
- if (ensureBlockletResults.filter(Boolean).length) {
1261
- await takeRoutingSnapshot({ message: `Switch routing engine to ${newProvider}`, dryRun: false }, context);
1262
- logger.info(`take routing snapshot on switch engine: ${newProvider}`, { ensureBlockletResults });
1263
- }
1269
+ // We need to take snapshot after system rules ensured
1270
+ if (ensureBlockletResults.filter(Boolean).length) {
1271
+ await takeRoutingSnapshot({ message: `Switch routing engine to ${newProvider}`, dryRun: false }, context);
1272
+ logger.info(`take routing snapshot on switch engine: ${newProvider}`, { ensureBlockletResults });
1264
1273
  }
1265
1274
 
1266
1275
  const Provider = getProvider(info.routing.provider);
1267
1276
  if (Provider) {
1268
1277
  try {
1269
1278
  const providerInstance = new Provider({
1270
- configDirectory: path.join(dataDirs.router, info.routing.provider),
1279
+ configDir: path.join(dataDirs.router, info.routing.provider),
1271
1280
  httpPort: info.routing.httpPort || DEFAULT_HTTP_PORT,
1272
1281
  httpsPort: info.routing.httpsPort || DEFAULT_HTTPS_PORT,
1273
1282
  cacheDisabled: info.mode === NODE_MODES.DEBUG,
@@ -1410,6 +1419,7 @@ module.exports = function getRouterHelpers({ dataDirs, routingSnapshot, routerMa
1410
1419
  ensureWildcardCerts,
1411
1420
  addWellknownSite,
1412
1421
  upsertSiteRule,
1422
+ getRouterProvider: (name) => providers[name],
1413
1423
 
1414
1424
  getRoutingCrons: () => [
1415
1425
  {
@@ -5,7 +5,7 @@ const {
5
5
  DOMAIN_FOR_DEFAULT_SITE,
6
6
  DOMAIN_FOR_IP_SITE_REGEXP,
7
7
  ROUTING_RULE_TYPES,
8
- DEFAULT_DASHBOARD_DOMAIN,
8
+ DEFAULT_IP_DOMAIN,
9
9
  BLOCKLET_PROXY_PATH_PREFIX,
10
10
  BLOCKLET_SITE_GROUP_SUFFIX,
11
11
  } = require('@abtnode/constant');
@@ -117,12 +117,7 @@ class Router {
117
117
  }
118
118
 
119
119
  async update() {
120
- if (!this.provider) {
121
- throw new Error('use setProvider() to set provider first');
122
- }
123
-
124
120
  logger.info('router: update');
125
-
126
121
  await this.updateRoutingTable();
127
122
  await this.provider.reload();
128
123
  }
@@ -139,6 +134,12 @@ class Router {
139
134
  return this.provider.restart();
140
135
  }
141
136
 
137
+ async reload() {
138
+ logger.info('router: reload');
139
+ await this.updateRoutingTable();
140
+ return this.provider.reload();
141
+ }
142
+
142
143
  stop() {
143
144
  logger.info('router: stop');
144
145
  return this.provider.stop();
@@ -153,6 +154,10 @@ class Router {
153
154
  logger.info('router: rotate logs');
154
155
  await this.provider.rotateLogs();
155
156
  }
157
+
158
+ getLogFilesForToday() {
159
+ return this.provider.getLogFilesForToday();
160
+ }
156
161
  }
157
162
 
158
163
  Router.formatSites = (sites = []) => {
@@ -219,7 +224,7 @@ Router.formatSites = (sites = []) => {
219
224
 
220
225
  Router.flattenSitesToRules = (sites = [], info = {}) => {
221
226
  const result = [];
222
- const ipWildcardDomain = get(info, 'routing.ipWildcardDomain', DEFAULT_DASHBOARD_DOMAIN);
227
+ const ipWildcardDomain = get(info, 'routing.ipWildcardDomain', DEFAULT_IP_DOMAIN);
223
228
  sites.forEach((site) => {
224
229
  const aliases = (site.domainAliases || [])
225
230
  .map((x) => (typeof x === 'string' ? x : x.value))
@@ -594,8 +594,8 @@ class RouterManager extends EventEmitter {
594
594
  // get provider
595
595
  const providerName = getProviderFromNodeInfo(info);
596
596
  const Provider = getProvider(providerName);
597
- const tempPath = path.join(os.tmpdir(), `${providerName}-${Date.now()}`);
598
- const provider = new Provider({ configDirectory: tempPath });
597
+ const tmpDir = path.join(os.tmpdir(), `${providerName}-${Date.now()}`);
598
+ const provider = new Provider({ configDir: tmpDir });
599
599
  const tempRouter = new Router({
600
600
  provider,
601
601
  getRoutingParams: async () => ({
@@ -610,10 +610,10 @@ class RouterManager extends EventEmitter {
610
610
  await tempRouter.updateRoutingTable();
611
611
  try {
612
612
  await tempRouter.validateConfig();
613
- await fse.remove(tempPath);
613
+ await fse.remove(tmpDir);
614
614
  } catch (error) {
615
615
  logger.error('validate router config failed', { error, action, data });
616
- await fse.remove(tempPath);
616
+ await fse.remove(tmpDir);
617
617
  throw error;
618
618
  }
619
619
  }
@@ -9,6 +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
13
  const {
13
14
  BlockletStatus,
14
15
  BlockletSource,
@@ -25,6 +26,7 @@ const { validateBlockletMeta } = require('../util');
25
26
 
26
27
  const lock = new Lock('blocklet-port-assign-lock');
27
28
 
29
+ const isHex = (str) => /^0x[0-9a-f]+$/i.test(str);
28
30
  const getMaxPort = (ports = {}) => Math.max(Object.values(ports).map(Number));
29
31
 
30
32
  const getExternalPortsFromMeta = (meta) =>
@@ -50,10 +52,10 @@ const formatBlocklet = (blocklet, phase, dek) => {
50
52
  if (!env) {
51
53
  return;
52
54
  }
53
- if (phase === 'onUpdate' && env.value.indexOf('0x') === 0) {
55
+ if (phase === 'onUpdate' && isHex(env.value) === true) {
54
56
  env.value = security.encrypt(env.value, b.meta.did, dek);
55
57
  }
56
- if (phase === 'onRead' && env.value.indexOf('0x') === -1) {
58
+ if (phase === 'onRead' && isHex(env.value) === false) {
57
59
  env.value = security.decrypt(env.value, b.meta.did, dek);
58
60
  }
59
61
  });
@@ -243,20 +245,17 @@ class BlockletState extends BaseState {
243
245
  await this.fillChildrenPorts(children, { oldChildren: doc.children, defaultPort: getMaxPort(ports) });
244
246
 
245
247
  // add to db
246
- const newDoc = await this.updateById(doc._id, {
247
- $set: {
248
- meta: omit(sanitized, ['htmlAst']),
249
- source,
250
- deployedFrom,
251
- children,
252
- ports,
253
- },
248
+ const newDoc = await this.updateBlocklet(meta.did, {
249
+ meta: omit(sanitized, ['htmlAst']),
250
+ source,
251
+ deployedFrom,
252
+ children,
253
+ ports,
254
254
  });
255
255
  lock.release();
256
256
 
257
- const formatted = formatBlocklet(newDoc, 'onRead', this.options.dek);
258
- this.emit('upgrade', formatted);
259
- resolve(formatted);
257
+ this.emit('upgrade', newDoc);
258
+ resolve(newDoc);
260
259
  } catch (err) {
261
260
  lock.release();
262
261
  reject(err);
@@ -446,8 +445,7 @@ class BlockletState extends BaseState {
446
445
  return child;
447
446
  });
448
447
 
449
- await this.update(doc._id, { $set: updates });
450
- return formatBlocklet({ ...doc, ...updates }, 'onRead', this.options.dek);
448
+ return this.updateBlocklet(did, updates);
451
449
  }
452
450
 
453
451
  async fillChildrenPorts(children, { defaultPort = 0, oldChildren } = {}) {
@@ -511,7 +509,7 @@ class BlockletState extends BaseState {
511
509
  const { meta, mountPoint, sourceUrl = '', source = '', deployedFrom = '' } = child;
512
510
 
513
511
  if (!mountPoint) {
514
- throw new Error(`mountPoint is required when adding component ${meta.title || meta.name}`);
512
+ throw new Error(`mountPoint is required when adding component ${getDisplayName(child, true)}`);
515
513
  }
516
514
 
517
515
  if (meta.did === parent.meta.did) {
@@ -542,5 +540,6 @@ class BlockletState extends BaseState {
542
540
  }
543
541
 
544
542
  BlockletState.BlockletStatus = BlockletStatus;
543
+ BlockletState.formatBlocklet = formatBlocklet;
545
544
 
546
545
  module.exports = BlockletState;
@@ -6,7 +6,7 @@ const isEmpty = require('lodash/isEmpty');
6
6
  const security = require('@abtnode/util/lib/security');
7
7
  const { isFromPublicKey } = require('@arcblock/did');
8
8
  const logger = require('@abtnode/logger')('@abtnode/core:node');
9
- const { ROUTER_PROVIDER_NONE, NODE_MODES, DISK_ALERT_THRESHOLD_PERCENT } = require('@abtnode/constant');
9
+ const { NODE_MODES, DISK_ALERT_THRESHOLD_PERCENT } = require('@abtnode/constant');
10
10
 
11
11
  const BaseState = require('./base');
12
12
  const { validateOwner } = require('../util');
@@ -87,7 +87,7 @@ class NodeState extends BaseState {
87
87
  nodeOwner,
88
88
  port,
89
89
  version,
90
- routing = { provider: ROUTER_PROVIDER_NONE },
90
+ routing = { provider: 'default' },
91
91
  docker,
92
92
  mode,
93
93
  runtimeConfig,
@@ -1,4 +1,5 @@
1
1
  const logger = require('@abtnode/logger')('@abtnode/core:states:site');
2
+ const { toSlotDomain } = require('@abtnode/router-provider/lib/util');
2
3
 
3
4
  const BaseState = require('./base');
4
5
 
@@ -75,6 +76,14 @@ class SiteState extends BaseState {
75
76
 
76
77
  return count > 0;
77
78
  }
79
+
80
+ async findOneByDomain(domain) {
81
+ // eslint-disable-next-line no-param-reassign
82
+ domain = toSlotDomain(domain);
83
+ return this.asyncDB.findOne({
84
+ $or: [{ domain }, { domainAliases: domain }, { 'domainAliases.value': domain }],
85
+ });
86
+ }
78
87
  }
79
88
 
80
89
  module.exports = SiteState;
@@ -42,7 +42,7 @@ const validateBlockletEntry = require('@blocklet/meta/lib/entry');
42
42
  const getBlockletEngine = require('@blocklet/meta/lib/engine');
43
43
  const getBlockletInfo = require('@blocklet/meta/lib/info');
44
44
  const { validateMeta, fixAndValidateService } = require('@blocklet/meta/lib/validate');
45
- const { forEachBlocklet, isFreeBlocklet } = require('@blocklet/meta/lib/util');
45
+ const { forEachBlocklet, isFreeBlocklet, getDisplayName } = require('@blocklet/meta/lib/util');
46
46
 
47
47
  const { validate: validateEngine, get: getEngine } = require('../blocklet/manager/engine');
48
48
 
@@ -456,7 +456,7 @@ const startBlockletProcess = async (blocklet, { preStart = noop, nodeEnvironment
456
456
 
457
457
  const clusterMode = get(b.meta, 'capabilities.clusterMode', false);
458
458
  if (clusterMode && blocklet.mode !== BLOCKLET_MODES.DEVELOPMENT) {
459
- const clusterSize = Number(blocklet.configObj.BLOCKLET_CLUSTER_SIZE) || Number.POSITIVE_INFINITY;
459
+ const clusterSize = Number(blocklet.configObj.BLOCKLET_CLUSTER_SIZE) || +process.env.ABT_NODE_MAX_CLUSTER_SIZE;
460
460
  options.execMode = 'cluster';
461
461
  options.mergeLogs = true;
462
462
  options.instances = Math.min(os.cpus().length, clusterSize);
@@ -1069,7 +1069,7 @@ const checkDuplicateComponents = (dynamicComponents, staticComponents) => {
1069
1069
  if (duplicates.length) {
1070
1070
  throw new Error(
1071
1071
  `Cannot add duplicate component${duplicates.length > 1 ? 's' : ''}: ${duplicates
1072
- .map((x) => x.meta.title || x.meta.name)
1072
+ .map((x) => getDisplayName(x, true))
1073
1073
  .join(', ')}`
1074
1074
  );
1075
1075
  }
@@ -1,22 +1,26 @@
1
- const systeminformation = require('@abtnode/util/lib/sys-info');
2
1
  const logger = require('@abtnode/logger')('@abtnode/disk-monitor');
2
+ const info = require('./sysinfo');
3
3
 
4
4
  const states = require('../states');
5
5
 
6
6
  /**
7
- *
8
- * @param {number} threshold Threshold for disk alarms, percentage
7
+ * @param {number} threshold Threshold for disk alerts, percentage
9
8
  */
10
9
  const check = async (threshold) => {
11
- const info = await systeminformation.get();
12
- const usageRatio = (info.disk.used / info.disk.total) * 100;
13
- const usageRationText = `${usageRatio.toFixed(2)}%`;
14
- logger.info('monitoring disk usage', { usage: usageRationText, threshold });
10
+ const { disks } = await info.getSysInfo();
11
+ for (const disk of disks) {
12
+ const usageRatio = (disk.used / disk.total) * 100;
13
+ const usageRatioPercent = `${usageRatio.toFixed(2)}%`;
14
+ logger.info('check disk usage', { usage: usageRatioPercent, threshold });
15
15
 
16
- if (usageRatio >= threshold) {
16
+ if (usageRatio < threshold) {
17
+ return;
18
+ }
19
+
20
+ // eslint-disable-next-line no-await-in-loop
17
21
  await states.notification.create({
18
22
  title: 'Disk Usage Alert',
19
- description: `More than ${usageRationText} disk space has been used`,
23
+ description: `More than ${usageRatioPercent} space has been used for disk ${disk.device}, the blocklet server may not function properly.`,
20
24
  entityType: 'node',
21
25
  severity: 'warning',
22
26
  sticky: true,
@@ -30,7 +34,6 @@ const getCron = () => ({
30
34
  fn: async () => {
31
35
  const nodeInfo = await states.node.read();
32
36
  const threshold = nodeInfo.diskAlertThreshold;
33
-
34
37
  await check(threshold);
35
38
  },
36
39
  options: { runOnInit: true },
@@ -1,9 +1,9 @@
1
1
  const joinUrl = require('url-join');
2
2
  const axios = require('@abtnode/util/lib/axios');
3
- const { DEFAULT_DASHBOARD_DOMAIN, DEFAULT_ADMIN_PATH } = require('@abtnode/constant');
3
+ const { DEFAULT_IP_DOMAIN, DEFAULT_ADMIN_PATH } = require('@abtnode/constant');
4
4
  const { get: getIp } = require('./ip');
5
5
 
6
- const getNodeDomain = (ip) => (ip ? DEFAULT_DASHBOARD_DOMAIN.replace(/^\*/, ip.replace(/\./g, '-')) : '');
6
+ const getNodeDomain = (ip) => (ip ? DEFAULT_IP_DOMAIN.replace(/^\*/, ip.replace(/\./g, '-')) : '');
7
7
 
8
8
  let cache = null;
9
9
 
@@ -1,5 +1,6 @@
1
1
  const slugify = require('slugify');
2
2
  const { DEFAULT_IP_DNS_DOMAIN_SUFFIX } = require('@abtnode/constant');
3
+ const md5 = require('@abtnode/util/lib/md5');
3
4
 
4
5
  const SLOT_FOR_IP_DNS_SITE = '888-888-888-888';
5
6
 
@@ -17,7 +18,9 @@ const getIpDnsDomainForBlocklet = (blocklet, blockletInterface) => {
17
18
  };
18
19
 
19
20
  const getDidDomainForBlocklet = ({ name, daemonDid, didDomain }) => {
20
- return `${formatName(name)}-${daemonDid.toLowerCase()}.${didDomain}`;
21
+ const prefix = md5(name).substring(0, 8);
22
+
23
+ return `${prefix}-${daemonDid.toLowerCase()}.${didDomain}`;
21
24
  };
22
25
 
23
26
  module.exports = { getIpDnsDomainForBlocklet, getDidDomainForBlocklet };
package/lib/util/index.js CHANGED
@@ -20,7 +20,6 @@ const { validateMeta, fixAndValidateService } = require('@blocklet/meta/lib/vali
20
20
  const { BlockletStatus, BLOCKLET_INTERFACE_WELLKNOWN } = require('@blocklet/meta/lib/constants');
21
21
  const {
22
22
  StatusCode,
23
- ROUTER_PROVIDER_NONE,
24
23
  DOMAIN_FOR_DEFAULT_SITE,
25
24
  DOMAIN_FOR_IP_SITE,
26
25
  DOMAIN_FOR_IP_SITE_REGEXP,
@@ -242,7 +241,7 @@ const getBlockletInterfaces = ({ blocklet, context, nodeInfo, routingRules, node
242
241
  return uniqBy(interfaces, 'url');
243
242
  };
244
243
 
245
- const isRoutingEnabled = (routing) => !!routing && !!routing.provider && routing.provider !== ROUTER_PROVIDER_NONE;
244
+ const isRoutingEnabled = (routing) => !!routing && !!routing.provider;
246
245
 
247
246
  const isInProgress = (status) =>
248
247
  [
@@ -283,7 +282,7 @@ const asyncExec = (command, options) =>
283
282
  });
284
283
  });
285
284
 
286
- const getProviderFromNodeInfo = (nodeInfo) => get(nodeInfo, 'routing.provider', null) || ROUTER_PROVIDER_NONE;
285
+ const getProviderFromNodeInfo = (info) => get(info, 'routing.provider', 'default');
287
286
 
288
287
  const isCLI = () => !process.env.ABT_NODE_SK;
289
288
 
@@ -370,9 +369,8 @@ const getBaseUrls = async (node, ips) => {
370
369
  return { protocol, port };
371
370
  };
372
371
 
373
- if (info.routing.provider !== ROUTER_PROVIDER_NONE && info.routing.snapshotHash && info.routing.adminPath) {
372
+ if (info.routing.provider && info.routing.snapshotHash && info.routing.adminPath) {
374
373
  const sites = await node.getSitesFromSnapshot();
375
-
376
374
  const { ipWildcardDomain } = info.routing;
377
375
  const adminPath = normalizePathPrefix(info.routing.adminPath);
378
376
  const tmpHttpPort = getPort(httpPort, DEFAULT_HTTP_PORT);
@@ -0,0 +1,90 @@
1
+ const joinURL = require('url-join');
2
+ const pick = require('lodash/pick');
3
+ const isBase64 = require('is-base64');
4
+
5
+ const { BLOCKLET_STORE_API_PREFIX, BLOCKLET_STORE_META_PATH } = require('@abtnode/constant');
6
+
7
+ const { name } = require('../../package.json');
8
+ const logger = require('@abtnode/logger')(`${name}:util:registry`); // eslint-disable-line
9
+
10
+ const request = require('./request');
11
+
12
+ const validateRegistryURL = async (registry) => {
13
+ const url = joinURL(registry, BLOCKLET_STORE_API_PREFIX, `/blocklets.json?__t__=${Date.now()}`);
14
+ try {
15
+ const res = await request.get(url);
16
+ if (Array.isArray(res.data)) {
17
+ return res.data;
18
+ }
19
+
20
+ logger.error('Blocklet list fetch failed ', { url, data: res.data });
21
+ throw new Error('blocklet list fetch failed');
22
+ } catch (error) {
23
+ logger.error('Blocklet registry refresh failed', { url, error });
24
+ throw new Error(`Invalid Blocklet Registry URL ${registry}: ${error.message}`);
25
+ }
26
+ };
27
+
28
+ const getRegistryMeta = async (registry) => {
29
+ try {
30
+ const url = joinURL(registry, BLOCKLET_STORE_META_PATH, `?__t__=${Date.now()}`);
31
+ const { data } = await request.get(url);
32
+
33
+ if (!data) {
34
+ return {};
35
+ }
36
+
37
+ const requiredFields = ['name', 'description', 'maintainer'];
38
+
39
+ const missingFields = requiredFields.filter((x) => !data[x]);
40
+ if (missingFields.length > 0) {
41
+ throw new Error(`the registry missing required information: ${missingFields.join(', ')}`);
42
+ }
43
+
44
+ const result = pick(data, ['id', 'name', 'description', 'maintainer', 'cdnUrl', 'chainHost']);
45
+ const { logoUrl } = data;
46
+ if (logoUrl) {
47
+ if (logoUrl.startsWith('http') === true) {
48
+ result.logoUrl = logoUrl;
49
+ } else if (isBase64(logoUrl, { allowMime: true })) {
50
+ result.logoUrl = logoUrl;
51
+ } else {
52
+ result.logoUrl = joinURL(registry, logoUrl);
53
+ }
54
+ }
55
+
56
+ return result;
57
+ } catch (err) {
58
+ throw new Error(`Can not get meta info for registry [${registry}]: ${err.message}`);
59
+ }
60
+ };
61
+
62
+ const parseSourceUrl = async (url) => {
63
+ const { origin, pathname } = new URL(url);
64
+
65
+ const match = pathname.match(/^\/api\/blocklets\/(\w*)\/blocklet\.json/);
66
+ if (match) {
67
+ try {
68
+ const m = await getRegistryMeta(origin);
69
+ if (m && m.id) {
70
+ return {
71
+ inStore: true,
72
+ registryUrl: origin,
73
+ blockletDid: match[1],
74
+ };
75
+ }
76
+ } catch {
77
+ // meat is not in store, do nothing
78
+ }
79
+ }
80
+
81
+ return {
82
+ inStore: false,
83
+ };
84
+ };
85
+
86
+ module.exports = {
87
+ validateRegistryURL,
88
+ getRegistryMeta,
89
+ parseSourceUrl,
90
+ };
@@ -1,30 +1,15 @@
1
1
  const fs = require('fs-extra');
2
- const { NODE_SERVICES } = require('@abtnode/constant');
3
-
4
- const servicesNames = Object.values(NODE_SERVICES);
5
2
 
6
3
  const getServices = ({ stringifySchema = false } = {}) =>
7
- servicesNames
4
+ ['auth']
8
5
  .map((x) => {
9
- const service = {};
10
-
11
- // Read service meta
12
- try {
13
- const packageFile = require.resolve(`${x}/package.json`);
14
- const { name, description, version } = fs.readJSONSync(packageFile);
15
- service.name = name;
16
- service.description = description || name;
17
- service.version = version;
18
- } catch (err) {
19
- return null;
20
- }
6
+ let service;
21
7
 
22
- // Read service config: optional
23
8
  try {
24
- const schemaFile = require.resolve(`${x}/api/schema.json`);
25
- service.schema = fs.readJSONSync(schemaFile);
9
+ const metaFile = require.resolve(`@abtnode/blocklet-services/services/${x}/meta.json`);
10
+ service = fs.readJSONSync(metaFile);
26
11
  } catch (err) {
27
- service.schema = {};
12
+ service = {};
28
13
  }
29
14
 
30
15
  if (stringifySchema) {
@@ -0,0 +1,79 @@
1
+ const EventEmitter = require('events');
2
+ const driveList = require('drivelist');
3
+ const si = require('systeminformation');
4
+ const checkDiskSpace = require('check-disk-space').default;
5
+
6
+ const getSysInfo = async () => {
7
+ const [info, drives] = await Promise.all([
8
+ si.get({
9
+ cpu: 'physicalCores',
10
+ mem: '*',
11
+ currentLoad: '*',
12
+ osInfo: 'platform',
13
+ }),
14
+ driveList.list(),
15
+ ]);
16
+
17
+ return {
18
+ cpu: {
19
+ ...info.currentLoad,
20
+ ...info.cpu,
21
+ },
22
+ mem: info.mem,
23
+ os: info.osInfo,
24
+ disks: await Promise.all(
25
+ drives
26
+ .filter((x) => x.isSystem && x.isVirtual === false && x.mountpoints.length)
27
+ .map(async (x) => {
28
+ const result = await checkDiskSpace(x.mountpoints[0].path);
29
+ return {
30
+ device: x.device,
31
+ mountPoint: result.diskPath,
32
+ total: result.size,
33
+ used: result.size - result.free,
34
+ free: result.free,
35
+ };
36
+ })
37
+ ),
38
+ };
39
+ };
40
+
41
+ class SysInfoEmitter extends EventEmitter {
42
+ constructor(interval = 3000) {
43
+ super();
44
+ this.timer = null;
45
+ this.polling = false;
46
+ this.interval = interval;
47
+ }
48
+
49
+ startPolling() {
50
+ if (this.polling) {
51
+ return;
52
+ }
53
+
54
+ if (this.timer) {
55
+ clearInterval(this.timer);
56
+ this.timer = null;
57
+ }
58
+
59
+ this.polling = true;
60
+ this.timer = setInterval(() => {
61
+ getSysInfo().then((info) => {
62
+ if (info) {
63
+ this.emit('info', info);
64
+ }
65
+ });
66
+ }, this.interval);
67
+ }
68
+
69
+ stopPolling() {
70
+ if (this.timer) {
71
+ clearInterval(this.timer);
72
+ this.timer = null;
73
+ this.polling = false;
74
+ }
75
+ }
76
+ }
77
+
78
+ module.exports.getSysInfo = getSysInfo;
79
+ module.exports.SysInfoEmitter = SysInfoEmitter;
@@ -39,6 +39,7 @@ const ruleSchema = {
39
39
  ROUTING_RULE_TYPES.BLOCKLET,
40
40
  ROUTING_RULE_TYPES.REDIRECT,
41
41
  ROUTING_RULE_TYPES.GENERAL_PROXY,
42
+ ROUTING_RULE_TYPES.DIRECT_RESPONSE,
42
43
  ROUTING_RULE_TYPES.NONE
43
44
  )
44
45
  .required(),
@@ -54,6 +55,12 @@ const ruleSchema = {
54
55
  .label('interface name')
55
56
  .when('type', { is: ROUTING_RULE_TYPES.BLOCKLET, then: Joi.required() }),
56
57
  realInterfaceName: Joi.string().label('real interface name'), // child blocklet interface
58
+ response: Joi.object({ status: Joi.number().required(), contentType: Joi.string(), body: Joi.string().required() })
59
+ .label('response')
60
+ .when('type', {
61
+ is: ROUTING_RULE_TYPES.DIRECT_RESPONSE,
62
+ then: Joi.required(),
63
+ }),
57
64
  },
58
65
 
59
66
  // List of services that manipulate the request before the upstream blocklet
@@ -8,6 +8,7 @@ const Joi = JOI.extend(didExtension);
8
8
  const trustedPassportsSchema = Joi.array().items(
9
9
  Joi.object({
10
10
  issuerDid: Joi.DID().required(),
11
+ remark: Joi.string().trim(),
11
12
  mappings: Joi.array().items(
12
13
  Joi.object({
13
14
  from: Joi.object({
@@ -1,13 +1,13 @@
1
1
  const logger = require('@abtnode/logger')('@abtnode/core:webhook:index');
2
- const upperFirst = require('lodash/upperFirst');
3
2
 
3
+ const sortPriorityUrl = require('@abtnode/util/lib/sort-priority-url');
4
4
  const WebHookSender = require('./sender');
5
5
  const createQueue = require('../queue');
6
6
  const IP = require('../util/ip');
7
7
  const states = require('../states');
8
8
  const { getBaseUrls } = require('../util');
9
9
 
10
- const getSlackUrlInfo = (actionPath = '/notifications', urls) => {
10
+ const getSlackUrlInfo = async (actionPath = '/notifications', urls) => {
11
11
  const info = [];
12
12
 
13
13
  if (actionPath && actionPath.startsWith('/blocklet/')) {
@@ -28,29 +28,27 @@ const getSlackUrlInfo = (actionPath = '/notifications', urls) => {
28
28
  });
29
29
  }
30
30
 
31
- const httpUrls = urls.filter((x) => x.url.startsWith('http://'));
32
- const httpsUrls = urls.filter((x) => x.url.startsWith('https://'));
33
-
34
- const showUrls = httpsUrls.length ? httpsUrls : httpUrls;
35
- const elements = [];
36
- showUrls.forEach((x) => {
37
- const { protocol } = new URL(x.url);
38
- const normalized = `${x.url}${actionPath}`.replace(`${protocol}//`, '').replace(/\/+/g, '/');
39
- const url = `${protocol}//${normalized}`;
40
- elements.push({
41
- type: 'button',
42
- text: {
43
- type: 'plain_text',
44
- text: `${upperFirst(x.type)} Address`,
45
- },
46
- style: 'primary',
47
- value: `${upperFirst(x.type)} Address`,
48
- url,
49
- });
50
- });
31
+ const prioritys = await sortPriorityUrl(urls);
32
+ const priorityUrl = prioritys[0].url;
33
+
34
+ const { protocol } = new URL(priorityUrl);
35
+ const normalized = `${priorityUrl}${actionPath}`.replace(`${protocol}//`, '').replace(/\/+/g, '/');
36
+ const url = `${protocol}//${normalized}`;
37
+
51
38
  info.push({
52
39
  type: 'actions',
53
- elements,
40
+ elements: [
41
+ {
42
+ type: 'button',
43
+ text: {
44
+ type: 'plain_text',
45
+ text: 'Click Me',
46
+ },
47
+ style: 'primary',
48
+ value: 'Click Me',
49
+ url,
50
+ },
51
+ ],
54
52
  });
55
53
 
56
54
  return info;
@@ -66,7 +64,7 @@ module.exports = ({ events, dataDirs, instance }) => {
66
64
  const webhookList = await webhookState.list();
67
65
  const nodeInfo = await nodeState.read();
68
66
  const { internal, external } = await IP.get();
69
- const baseUrls = await getBaseUrls(instance, [internal, external]);
67
+ const baseUrls = await getBaseUrls(instance, [external, internal]);
70
68
  const senderFns = {};
71
69
 
72
70
  if (webhookList.length) {
@@ -79,7 +77,8 @@ module.exports = ({ events, dataDirs, instance }) => {
79
77
  const { name } = nodeInfo;
80
78
  const options = { ...message, nodeInfo: `*${name}*` };
81
79
  if (item.type === 'slack') {
82
- options.urlInfo = getSlackUrlInfo(message.action, baseUrls);
80
+ // eslint-disable-next-line
81
+ options.urlInfo = await getSlackUrlInfo(message.action, baseUrls);
83
82
  }
84
83
  try {
85
84
  // eslint-disable-next-line
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "1.6.21",
6
+ "version": "1.6.24",
7
7
  "description": "",
8
8
  "main": "lib/index.js",
9
9
  "files": [
@@ -19,34 +19,36 @@
19
19
  "author": "wangshijun <wangshijun2010@gmail.com> (http://github.com/wangshijun)",
20
20
  "license": "MIT",
21
21
  "dependencies": {
22
- "@abtnode/certificate-manager": "1.6.21",
23
- "@abtnode/constant": "1.6.21",
24
- "@abtnode/cron": "1.6.21",
25
- "@abtnode/db": "1.6.21",
26
- "@abtnode/logger": "1.6.21",
27
- "@abtnode/queue": "1.6.21",
28
- "@abtnode/rbac": "1.6.21",
29
- "@abtnode/router-provider": "1.6.21",
30
- "@abtnode/static-server": "1.6.21",
31
- "@abtnode/timemachine": "1.6.21",
32
- "@abtnode/util": "1.6.21",
33
- "@arcblock/did": "^1.14.13",
34
- "@arcblock/event-hub": "1.14.13",
22
+ "@abtnode/certificate-manager": "1.6.24",
23
+ "@abtnode/constant": "1.6.24",
24
+ "@abtnode/cron": "1.6.24",
25
+ "@abtnode/db": "1.6.24",
26
+ "@abtnode/logger": "1.6.24",
27
+ "@abtnode/queue": "1.6.24",
28
+ "@abtnode/rbac": "1.6.24",
29
+ "@abtnode/router-provider": "1.6.24",
30
+ "@abtnode/static-server": "1.6.24",
31
+ "@abtnode/timemachine": "1.6.24",
32
+ "@abtnode/util": "1.6.24",
33
+ "@arcblock/did": "^1.14.19",
34
+ "@arcblock/event-hub": "1.14.19",
35
35
  "@arcblock/pm2-events": "^0.0.5",
36
- "@arcblock/vc": "^1.14.13",
37
- "@blocklet/meta": "1.6.21",
36
+ "@arcblock/vc": "^1.14.19",
37
+ "@blocklet/meta": "1.6.24",
38
38
  "@fidm/x509": "^1.2.1",
39
39
  "@nedb/core": "^1.2.2",
40
40
  "@nedb/multi": "^1.2.2",
41
- "@ocap/mcrypto": "^1.14.13",
42
- "@ocap/util": "^1.14.13",
43
- "@ocap/wallet": "^1.14.13",
41
+ "@ocap/mcrypto": "^1.14.19",
42
+ "@ocap/util": "^1.14.19",
43
+ "@ocap/wallet": "^1.14.19",
44
44
  "@slack/webhook": "^5.0.3",
45
45
  "axios": "^0.25.0",
46
46
  "axon": "^2.0.3",
47
47
  "chalk": "^4.0.0",
48
+ "check-disk-space": "^3.2.0",
48
49
  "deep-diff": "^1.0.2",
49
50
  "detect-port": "^1.3.0",
51
+ "drivelist": "^9.2.4",
50
52
  "flat": "^5.0.2",
51
53
  "fs-extra": "^10.0.0",
52
54
  "get-port": "^5.1.1",
@@ -64,6 +66,7 @@
64
66
  "ssri": "^8.0.0",
65
67
  "stream-throttle": "^0.1.3",
66
68
  "stream-to-promise": "^3.0.0",
69
+ "systeminformation": "^5.11.5",
67
70
  "tar": "^6.1.0",
68
71
  "unzipper": "^0.10.11",
69
72
  "url-join": "^4.0.1",
@@ -75,5 +78,5 @@
75
78
  "express": "^4.17.1",
76
79
  "jest": "^27.4.5"
77
80
  },
78
- "gitHead": "864ae21711c119948475057a1f634fd7d16ae91a"
81
+ "gitHead": "6bd8792a155ebbbefea6ca3d42c5b59b354d9879"
79
82
  }