@abtnode/core 1.15.17 → 1.16.0-beta-b16cb035

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.
Files changed (119) hide show
  1. package/lib/api/node.js +67 -69
  2. package/lib/api/team.js +386 -55
  3. package/lib/blocklet/downloader/blocklet-downloader.js +226 -0
  4. package/lib/blocklet/downloader/bundle-downloader.js +272 -0
  5. package/lib/blocklet/downloader/constants.js +3 -0
  6. package/lib/blocklet/downloader/resolve-download.js +199 -0
  7. package/lib/blocklet/extras.js +83 -26
  8. package/lib/blocklet/hooks.js +18 -65
  9. package/lib/blocklet/manager/base.js +10 -16
  10. package/lib/blocklet/manager/disk.js +1679 -1566
  11. package/lib/blocklet/manager/helper/install-application-from-backup.js +177 -0
  12. package/lib/blocklet/manager/helper/install-application-from-dev.js +94 -0
  13. package/lib/blocklet/manager/helper/install-application-from-general.js +188 -0
  14. package/lib/blocklet/manager/helper/install-component-from-dev.js +84 -0
  15. package/lib/blocklet/manager/helper/install-component-from-upload.js +181 -0
  16. package/lib/blocklet/manager/helper/install-component-from-url.js +173 -0
  17. package/lib/blocklet/manager/helper/migrate-application-to-struct-v2.js +450 -0
  18. package/lib/blocklet/manager/helper/rollback-cache.js +41 -0
  19. package/lib/blocklet/manager/helper/upgrade-components.js +152 -0
  20. package/lib/blocklet/migration.js +30 -52
  21. package/lib/blocklet/storage/backup/audit-log.js +27 -0
  22. package/lib/blocklet/storage/backup/base.js +62 -0
  23. package/lib/blocklet/storage/backup/blocklet-extras.js +92 -0
  24. package/lib/blocklet/storage/backup/blocklet.js +70 -0
  25. package/lib/blocklet/storage/backup/blocklets.js +74 -0
  26. package/lib/blocklet/storage/backup/data.js +19 -0
  27. package/lib/blocklet/storage/backup/logs.js +24 -0
  28. package/lib/blocklet/storage/backup/routing-rule.js +19 -0
  29. package/lib/blocklet/storage/backup/spaces.js +240 -0
  30. package/lib/blocklet/storage/restore/base.js +67 -0
  31. package/lib/blocklet/storage/restore/blocklet-extras.js +86 -0
  32. package/lib/blocklet/storage/restore/blocklet.js +56 -0
  33. package/lib/blocklet/storage/restore/blocklets.js +43 -0
  34. package/lib/blocklet/storage/restore/logs.js +21 -0
  35. package/lib/blocklet/storage/restore/spaces.js +156 -0
  36. package/lib/blocklet/storage/utils/hash.js +51 -0
  37. package/lib/blocklet/storage/utils/zip.js +43 -0
  38. package/lib/cert.js +206 -0
  39. package/lib/event.js +237 -64
  40. package/lib/index.js +191 -83
  41. package/lib/migrations/1.0.21-update-config.js +1 -1
  42. package/lib/migrations/1.0.22-max-memory.js +1 -1
  43. package/lib/migrations/1.0.25.js +1 -1
  44. package/lib/migrations/1.0.32-update-config.js +1 -1
  45. package/lib/migrations/1.0.33-blocklets.js +1 -1
  46. package/lib/migrations/1.5.20-registry.js +15 -0
  47. package/lib/migrations/1.6.17-blocklet-children.js +48 -0
  48. package/lib/migrations/1.6.21-rename-ip-echo-domain.js +35 -0
  49. package/lib/migrations/1.6.4-security.js +59 -0
  50. package/lib/migrations/1.6.5-security.js +60 -0
  51. package/lib/migrations/1.6.9-update-node-info-and-certificate.js +38 -0
  52. package/lib/migrations/1.7.1-blocklet-setup.js +18 -0
  53. package/lib/migrations/1.7.12-blocklet-meta.js +51 -0
  54. package/lib/migrations/1.7.15-blocklet-bundle-source.js +42 -0
  55. package/lib/migrations/1.7.20-blocklet-component.js +41 -0
  56. package/lib/migrations/1.8.33-blocklet-mem-limit.js +20 -0
  57. package/lib/migrations/README.md +1 -1
  58. package/lib/migrations/index.js +6 -2
  59. package/lib/monitor/blocklet-runtime-monitor.js +200 -0
  60. package/lib/monitor/get-history-list.js +37 -0
  61. package/lib/monitor/node-runtime-monitor.js +228 -0
  62. package/lib/router/helper.js +572 -497
  63. package/lib/router/index.js +85 -21
  64. package/lib/router/manager.js +146 -187
  65. package/lib/states/README.md +36 -1
  66. package/lib/states/access-key.js +39 -17
  67. package/lib/states/audit-log.js +462 -0
  68. package/lib/states/base.js +4 -213
  69. package/lib/states/blocklet-extras.js +194 -138
  70. package/lib/states/blocklet.js +361 -104
  71. package/lib/states/cache.js +8 -6
  72. package/lib/states/challenge.js +5 -5
  73. package/lib/states/index.js +19 -36
  74. package/lib/states/migration.js +4 -4
  75. package/lib/states/node.js +135 -46
  76. package/lib/states/notification.js +22 -35
  77. package/lib/states/session.js +17 -9
  78. package/lib/states/site.js +50 -25
  79. package/lib/states/user.js +74 -20
  80. package/lib/states/webhook.js +10 -6
  81. package/lib/team/manager.js +124 -7
  82. package/lib/util/blocklet.js +1223 -246
  83. package/lib/util/chain.js +1 -1
  84. package/lib/util/default-node-config.js +5 -23
  85. package/lib/util/disk-monitor.js +13 -10
  86. package/lib/util/domain-status.js +84 -15
  87. package/lib/util/get-accessible-external-node-ip.js +2 -2
  88. package/lib/util/get-domain-for-blocklet.js +13 -0
  89. package/lib/util/get-meta-from-url.js +33 -0
  90. package/lib/util/index.js +207 -272
  91. package/lib/util/ip.js +6 -0
  92. package/lib/util/maintain.js +233 -0
  93. package/lib/util/public-to-store.js +85 -0
  94. package/lib/util/ready.js +1 -1
  95. package/lib/util/requirement.js +28 -9
  96. package/lib/util/reset-node.js +22 -7
  97. package/lib/util/router.js +13 -0
  98. package/lib/util/rpc.js +16 -0
  99. package/lib/util/store.js +179 -0
  100. package/lib/util/sysinfo.js +44 -0
  101. package/lib/util/ua.js +54 -0
  102. package/lib/validators/blocklet-extra.js +24 -0
  103. package/lib/validators/node.js +25 -12
  104. package/lib/validators/permission.js +16 -1
  105. package/lib/validators/role.js +17 -3
  106. package/lib/validators/router.js +40 -20
  107. package/lib/validators/trusted-passport.js +1 -0
  108. package/lib/validators/util.js +22 -5
  109. package/lib/webhook/index.js +45 -35
  110. package/lib/webhook/sender/index.js +5 -0
  111. package/lib/webhook/sender/slack/index.js +1 -1
  112. package/lib/webhook/sender/wallet/index.js +48 -0
  113. package/package.json +54 -36
  114. package/lib/blocklet/registry.js +0 -205
  115. package/lib/states/https-cert.js +0 -67
  116. package/lib/util/get-ip-dns-domain-for-blocklet.js +0 -19
  117. package/lib/util/service.js +0 -66
  118. package/lib/util/upgrade.js +0 -178
  119. /package/lib/{queue.js → util/queue.js} +0 -0
@@ -3,63 +3,78 @@
3
3
  const fs = require('fs-extra');
4
4
  const path = require('path');
5
5
  const tar = require('tar');
6
+ const isUrl = require('is-url');
6
7
  const get = require('lodash/get');
7
8
  const cloneDeep = require('lodash/cloneDeep');
9
+ const isEqual = require('lodash/isEqual');
8
10
  const joinUrl = require('url-join');
11
+ const { replaceSlotToIp, findComponentById, findWebInterface } = require('@blocklet/meta/lib/util');
9
12
  const { getProvider } = require('@abtnode/router-provider');
10
13
  const normalizePathPrefix = require('@abtnode/util/lib/normalize-path-prefix');
11
- const getTmpDirectory = require('@abtnode/util/lib/get-tmp-directory');
14
+ const getTmpDir = require('@abtnode/util/lib/get-tmp-directory');
12
15
  const downloadFile = require('@abtnode/util/lib/download-file');
16
+ const { updateBlockletDocument } = require('@abtnode/util/lib/did-document');
17
+ const getBlockletInfo = require('@blocklet/meta/lib/info');
18
+ const { forEachBlocklet } = require('@blocklet/meta/lib/util');
13
19
  const {
14
20
  DOMAIN_FOR_DEFAULT_SITE,
15
21
  DOMAIN_FOR_IP_SITE_REGEXP,
16
- ROUTER_PROVIDER_NONE,
17
22
  DOMAIN_FOR_INTERNAL_SITE,
18
23
  WELLKNOWN_PATH_PREFIX,
19
- WELLKNOWN_AUTH_PATH_PREFIX,
24
+ WELLKNOWN_SERVICE_PATH_PREFIX,
25
+ USER_AVATAR_PATH_PREFIX,
20
26
  DOMAIN_FOR_IP_SITE,
21
27
  NAME_FOR_WELLKNOWN_SITE,
22
28
  DEFAULT_HTTP_PORT,
23
29
  DEFAULT_HTTPS_PORT,
24
- DAY_IN_MS,
25
- NODE_MODES,
26
30
  ROUTING_RULE_TYPES,
27
31
  CERTIFICATE_EXPIRES_OFFSET,
28
- CERTIFICATE_EXPIRES_WARNING_OFFSET,
29
- DEFAULT_DAEMON_PORT,
30
32
  DEFAULT_SERVICE_PATH,
31
33
  SLOT_FOR_IP_DNS_SITE,
32
34
  BLOCKLET_SITE_GROUP_SUFFIX,
35
+ WELLKNOWN_ACME_CHALLENGE_PREFIX,
36
+ WELLKNOWN_DID_RESOLVER_PREFIX,
37
+ WELLKNOWN_PING_PREFIX,
38
+ LOG_RETAIN_IN_DAYS,
39
+ EVENTS,
33
40
  } = require('@abtnode/constant');
34
41
  const {
35
42
  BLOCKLET_DYNAMIC_PATH_PREFIX,
36
43
  BLOCKLET_INTERFACE_TYPE_WEB,
44
+ BLOCKLET_INTERFACE_PUBLIC,
37
45
  BLOCKLET_INTERFACE_WELLKNOWN,
38
46
  BLOCKLET_INTERFACE_TYPE_WELLKNOWN,
39
- } = require('@blocklet/meta/lib/constants');
47
+ BLOCKLET_CONFIGURABLE_KEY,
48
+ BlockletEvents,
49
+ BLOCKLET_MODES,
50
+ } = require('@blocklet/constant');
40
51
 
41
52
  // eslint-disable-next-line global-require
42
53
  const logger = require('@abtnode/logger')(`${require('../../package.json').name}:router:helper`);
43
54
  const {
44
55
  getProviderFromNodeInfo,
45
- trimSlash,
46
- getInterfaceUrl,
47
- getBlockletHost,
48
56
  getHttpsCertInfo,
49
57
  findInterfacePortByName,
50
58
  getWellknownSitePort,
59
+ getServerDidDomain,
60
+ isGatewayCacheEnabled,
51
61
  } = require('../util');
52
- const { getServicesFromBlockletInterface } = require('../util/service');
53
- const getIpDnsDomainForBlocklet = require('../util/get-ip-dns-domain-for-blocklet');
62
+ const { getIpDnsDomainForBlocklet, getDidDomainForBlocklet } = require('../util/get-domain-for-blocklet');
54
63
  const { getFromCache: getAccessibleExternalNodeIp } = require('../util/get-accessible-external-node-ip');
55
64
 
56
65
  const Router = require('./index');
57
66
  const states = require('../states');
67
+ const { getBlockletDomainGroupName, getDidFromDomainGroupName } = require('../util/router');
68
+ const { getBlockletKnownAs } = require('../util/blocklet');
58
69
 
59
70
  /**
60
71
  * replace 888-888-888-888 with accessible ip for domain
61
72
  */
62
73
  const attachRuntimeDomainAliases = async ({ sites = [], context = {}, node }) => {
74
+ if (!sites) {
75
+ return [];
76
+ }
77
+
63
78
  let ip;
64
79
  const ipRegex = /\d+[-.]\d+[-.]\d+[-.]\d+/;
65
80
  const match = ipRegex.exec(context.hostname);
@@ -79,7 +94,7 @@ const attachRuntimeDomainAliases = async ({ sites = [], context = {}, node }) =>
79
94
  return domain;
80
95
  }
81
96
  if (domain.value.includes(SLOT_FOR_IP_DNS_SITE) && ip) {
82
- domain.value = domain.value.replace(SLOT_FOR_IP_DNS_SITE, ip.replace(/\./g, '-'));
97
+ domain.value = replaceSlotToIp(domain.value, ip);
83
98
  }
84
99
  return domain;
85
100
  });
@@ -96,89 +111,6 @@ const attachRuntimeDomainAliases = async ({ sites = [], context = {}, node }) =>
96
111
  });
97
112
  };
98
113
 
99
- const attachInterfaceUrls = async ({ sites = [], context, node }) => {
100
- if (!sites) {
101
- return [];
102
- }
103
-
104
- attachRuntimeDomainAliases({ sites, context, node });
105
-
106
- const getUrl = (rule, domain = '') => {
107
- const host = getBlockletHost({ domain, context });
108
- const prefix = trimSlash(rule.from.pathPrefix);
109
- if (prefix) {
110
- return `http://${host}/${prefix}/`;
111
- }
112
-
113
- return `http://${host}/`;
114
- };
115
-
116
- const blocklets = await states.blocklet.getBlocklets();
117
- const getInterfaces = (site) =>
118
- (site.rules || []).map((tmpRule) => {
119
- const rule = { ...tmpRule };
120
-
121
- const ruleId = tmpRule.id;
122
- const baseUrl = getUrl(rule, site.domain);
123
-
124
- const interfaces = [];
125
- if (rule.to.type === ROUTING_RULE_TYPES.BLOCKLET) {
126
- const blocklet = blocklets.find((b) => b.meta.did === rule.to.did);
127
-
128
- if (!blocklet) {
129
- logger.warn(`can not attach interface urls for non-existing blocklet ${rule.to.did}`);
130
- rule.interfaces = interfaces;
131
- return rule;
132
- }
133
-
134
- if (!getBlockletHost({ domain: site.domain, context })) {
135
- logger.warn('blocklet host does not exist');
136
- rule.interfaces = interfaces;
137
- return rule;
138
- }
139
-
140
- (blocklet.meta.interfaces || []).forEach((x) => {
141
- if (x.port && !x.port.external) {
142
- interfaces.push({
143
- ruleId,
144
- type: x.type,
145
- name: x.name,
146
- url: getInterfaceUrl({ baseUrl, url: '/', version: blocklet.meta.version }),
147
- });
148
- }
149
- });
150
- } else if (rule.to.type === ROUTING_RULE_TYPES.REDIRECT || rule.to.type === ROUTING_RULE_TYPES.NONE) {
151
- interfaces.push({
152
- ruleId,
153
- type: 'web',
154
- name: ROUTING_RULE_TYPES.REDIRECT,
155
- url: baseUrl,
156
- });
157
- } else {
158
- interfaces.push({
159
- ruleId,
160
- type: 'web',
161
- name: 'default',
162
- url: getUrl(rule),
163
- });
164
- }
165
-
166
- rule.interfaces = interfaces;
167
- return rule;
168
- });
169
-
170
- if (!Array.isArray(sites)) {
171
- sites.rules = getInterfaces(sites);
172
- return sites;
173
- }
174
-
175
- return sites.map((site) => {
176
- site.rules = getInterfaces(site);
177
-
178
- return site;
179
- });
180
- };
181
-
182
114
  const addCorsToSite = (site, rawUrl) => {
183
115
  if (!site || !rawUrl) {
184
116
  return;
@@ -193,7 +125,7 @@ const addCorsToSite = (site, rawUrl) => {
193
125
  site.corsAllowedOrigins.push(url.hostname);
194
126
  }
195
127
  } catch (err) {
196
- // Do nothing
128
+ console.error('addCorsToSite', err);
197
129
  }
198
130
  };
199
131
 
@@ -222,6 +154,19 @@ const ensureServiceRule = async (sites) => {
222
154
  return site;
223
155
  });
224
156
  };
157
+ const ensureRootRule = async (sites) => {
158
+ return sites.map((site) => {
159
+ if (!isBasicSite(site.domain) && !site.rules.some((x) => x.from.pathPrefix === '/')) {
160
+ site.rules.push({
161
+ from: { pathPrefix: '/' },
162
+ to: {
163
+ type: ROUTING_RULE_TYPES.NONE,
164
+ },
165
+ });
166
+ }
167
+ return site;
168
+ });
169
+ };
225
170
 
226
171
  const ensureLatestNodeInfo = async (sites = [], { withDefaultCors = true } = {}) => {
227
172
  const info = await states.node.read();
@@ -243,6 +188,9 @@ const ensureLatestNodeInfo = async (sites = [], { withDefaultCors = true } = {})
243
188
  site.domain = DOMAIN_FOR_IP_SITE_REGEXP;
244
189
 
245
190
  if (withDefaultCors) {
191
+ // Allow CORS from "Install on ABT Node"
192
+ addCorsToSite(site, info.registerUrl);
193
+
246
194
  // Allow CORS from "Web Wallet"
247
195
  addCorsToSite(site, info.webWalletUrl);
248
196
  }
@@ -263,9 +211,15 @@ const ensureLatestInterfaceInfo = async (sites = []) => {
263
211
  }
264
212
 
265
213
  site.rules = site.rules.map((rule) => {
266
- // If a rule already has a target and target is WELLKNOWN_AUTH_PATH_PREFIX, just return
214
+ if (rule.dynamic) {
215
+ return rule;
216
+ }
217
+ // If a rule already has a target and target is WELLKNOWN_SERVICE_PATH_PREFIX, just return
267
218
  // Indicates that the rule id generated by the system for auth service
268
- if (rule.isProtected && rule.to.target === WELLKNOWN_AUTH_PATH_PREFIX) {
219
+ if (rule.isProtected && rule.to.target === WELLKNOWN_SERVICE_PATH_PREFIX) {
220
+ return rule;
221
+ }
222
+ if (rule.isProtected && rule.to.target === joinUrl(WELLKNOWN_SERVICE_PATH_PREFIX, USER_AVATAR_PATH_PREFIX)) {
269
223
  return rule;
270
224
  }
271
225
 
@@ -294,8 +248,8 @@ const ensureWellknownRule = async (sites) => {
294
248
  for (const site of tempSites) {
295
249
  // 不向 default site & ip site & wellknown site 添加 wellknown rule
296
250
  if (![DOMAIN_FOR_INTERNAL_SITE, DOMAIN_FOR_IP_SITE, DOMAIN_FOR_DEFAULT_SITE].includes(site.domain)) {
251
+ // add /.well-known for blocklet
297
252
  const isExists = site.rules.find((x) => x.from.pathPrefix === WELLKNOWN_PATH_PREFIX);
298
-
299
253
  if (!isExists) {
300
254
  site.rules.push({
301
255
  from: { pathPrefix: WELLKNOWN_PATH_PREFIX },
@@ -308,40 +262,72 @@ const ensureWellknownRule = async (sites) => {
308
262
  });
309
263
  }
310
264
 
311
- // add /.well-known/auth for blocklet
312
- const rules = cloneDeep(site.rules);
313
- rules.forEach((rule) => {
314
- if (
315
- rule.to.type !== ROUTING_RULE_TYPES.BLOCKLET || // is not blocklet
316
- rule.to.did !== rule.to.realDid // is a component endpoint
317
- ) {
318
- return;
319
- }
265
+ // add /.well-known/service for blocklet
266
+ const blockletRules = site.rules
267
+ .filter((x) => x.to.type === ROUTING_RULE_TYPES.BLOCKLET)
268
+ // 可能存在挂载的 blocklet 不是自己, 是其他 blocklet
269
+ // 这里默认 pathPrefix 最短的是自己 ( 通常自己在 /, 其他 blocklet 在 /xxxx )
270
+ // 挂载其他 blocklet 的使用方式不推荐, 将来可能会被废弃
271
+ .sort((a, b) => (a.from.pathPrefix.length > b.from.pathPrefix.length ? 1 : -1));
272
+ if (blockletRules.length) {
273
+ // get pathPrefix for blocklet-service
274
+ const rootBlockletRule = blockletRules.find((x) => x.to.did === x.to.componentId);
275
+
276
+ const servicePathPrefix = joinUrl(
277
+ // rootBlockletRule?.from?.pathPrefix is for backwards compatibility
278
+ // rootBlockletRule?.from?.groupPathPrefix 在旧的场景中可能不为 '/' (blocklet 只能挂载在 relative prefix 下)
279
+ rootBlockletRule?.from?.groupPathPrefix || rootBlockletRule?.from?.pathPrefix || '/',
280
+ WELLKNOWN_SERVICE_PATH_PREFIX
281
+ );
320
282
 
321
- // already exists
322
- if (rule.from.pathPrefix.endsWith(WELLKNOWN_AUTH_PATH_PREFIX)) {
323
- return;
283
+ // requests for /.well-known/service will stay in blocklet-service and never proxy back to blocklet
284
+ // so any rule is ok to be cloned
285
+ if (!site.rules.some((x) => x.from.pathPrefix === servicePathPrefix)) {
286
+ const rule = cloneDeep(rootBlockletRule || blockletRules[0]);
287
+ rule.from.pathPrefix = servicePathPrefix;
288
+ rule.to.target = servicePathPrefix;
289
+ rule.isProtected = true;
290
+ rule.dynamic = true; // mark as dynamic to avoid redundant generated rules
291
+ site.rules.push(rule);
324
292
  }
325
293
 
326
- const pathPrefix = joinUrl(rule.from.pathPrefix, WELLKNOWN_AUTH_PATH_PREFIX);
327
-
328
- // already exists
329
- if (site.rules.some((x) => x.from.pathPrefix === pathPrefix)) {
330
- return;
294
+ // Cache user avatar in nginx
295
+ const avatarPathPrefix = joinUrl(servicePathPrefix, USER_AVATAR_PATH_PREFIX);
296
+ if (!site.rules.some((x) => x.from.pathPrefix === avatarPathPrefix)) {
297
+ const rule = cloneDeep(rootBlockletRule || blockletRules[0]);
298
+ rule.from.pathPrefix = avatarPathPrefix;
299
+ rule.to.cacheGroup = 'blockletProxy';
300
+ rule.to.target = avatarPathPrefix;
301
+ rule.isProtected = true;
302
+ rule.dynamic = true; // mark as dynamic to avoid redundant generated rules
303
+ site.rules.push(rule);
331
304
  }
332
-
333
- rule.from.pathPrefix = pathPrefix;
334
- // let service gateway keep WELLKNOWN_AUTH_PATH_PREFIX prefix
335
- rule.to.target = WELLKNOWN_AUTH_PATH_PREFIX;
336
- rule.isProtected = true;
337
- site.rules.push(rule);
338
- });
305
+ }
339
306
  }
340
307
  }
341
308
 
342
309
  return tempSites;
343
310
  };
344
311
 
312
+ const ensureBlockletDid = async (sites) => {
313
+ const info = await states.node.read();
314
+
315
+ return (sites || []).map((site) => {
316
+ if (site.domain === DOMAIN_FOR_INTERNAL_SITE) {
317
+ return site;
318
+ }
319
+
320
+ if ([DOMAIN_FOR_IP_SITE, DOMAIN_FOR_DEFAULT_SITE, DOMAIN_FOR_IP_SITE_REGEXP].includes(site.domain)) {
321
+ site.blockletDid = info.did;
322
+ return site;
323
+ }
324
+
325
+ site.blockletDid = getDidFromDomainGroupName(site.domain);
326
+
327
+ return site;
328
+ });
329
+ };
330
+
345
331
  const ensureCorsForWebWallet = async (sites) => {
346
332
  const info = await states.node.read();
347
333
  for (const site of sites) {
@@ -353,48 +339,97 @@ const ensureCorsForWebWallet = async (sites) => {
353
339
  return sites;
354
340
  };
355
341
 
356
- const ensureLatestInfo = async (sites = [], { withDefaultCors = true } = {}) => {
357
- let result = await ensureLatestNodeInfo(sites, { withDefaultCors });
358
- result = await ensureWellknownRule(result);
359
- result = await ensureCorsForWebWallet(result);
360
- return ensureLatestInterfaceInfo(result);
342
+ const ensureCorsForDidSpace = async (sites = [], blocklets) => {
343
+ return sites.map((site) => {
344
+ const blocklet = blocklets.find((x) => x.meta.did === site.blockletDid);
345
+ if (blocklet) {
346
+ const endpoint = blocklet.environments.find(
347
+ (x) => x.key === BLOCKLET_CONFIGURABLE_KEY.BLOCKLET_APP_SPACE_ENDPOINT
348
+ );
349
+ if (endpoint && isUrl(endpoint.value)) {
350
+ addCorsToSite(site, endpoint.value);
351
+ }
352
+ }
353
+
354
+ return site;
355
+ });
361
356
  };
362
357
 
363
- const ensureAuthService = async (sites = [], blockletManager) => {
364
- const blocklets = await blockletManager.list({ includeRuntimeInfo: false });
365
- const blockletMap = blocklets.reduce((o, x) => {
366
- o[x.meta.did] = x;
367
- return o;
368
- }, {});
358
+ const filterSitesForRemovedBlocklets = async (sites = [], blocklets) => {
359
+ return sites.filter((site) => {
360
+ if (!site.domain.endsWith(BLOCKLET_SITE_GROUP_SUFFIX)) {
361
+ return true;
362
+ }
369
363
 
370
- sites.forEach((site) => {
371
- site.rules.forEach((rule) => {
372
- if (rule.to.type !== ROUTING_RULE_TYPES.BLOCKLET || !blockletMap[rule.to.did]) {
373
- return;
374
- }
364
+ const blocklet = blocklets.find((x) => x.meta.did === site.blockletDid);
365
+ return !!blocklet;
366
+ });
367
+ };
375
368
 
376
- // find blocklet
377
- let blocklet = blockletMap[rule.to.did];
378
- if (rule.to.realDid && rule.to.did !== rule.to.realDid) {
379
- blocklet = (blocklet.children || []).find((x) => x.meta.did === rule.to.realDid);
380
- }
381
- if (!blocklet) {
382
- return;
369
+ const ensureBlockletCache = async (sites = [], blocklets) => {
370
+ return sites
371
+ .map((site) => {
372
+ if (!site.domain.endsWith(BLOCKLET_SITE_GROUP_SUFFIX)) {
373
+ return site;
383
374
  }
384
375
 
385
- // find interface
386
- // we should only use rule.to.realInterfaceName, rule.to.interfaceName is for backward compatible
387
- const interfaceName = rule.to.realInterfaceName || rule.to.interfaceName;
388
- const { interfaces } = blocklet.meta;
389
- const _interface = interfaces.find((x) => x.type === BLOCKLET_INTERFACE_TYPE_WEB && x.name === interfaceName);
390
- if (_interface) {
391
- rule.services = rule.services || [];
392
- rule.services.unshift(...getServicesFromBlockletInterface(_interface, { logError: logger.error }));
376
+ if (site.cacheableGenerated) {
377
+ return site;
393
378
  }
394
- });
395
- });
396
379
 
397
- return sites;
380
+ // For each rule, get component, check cacheable, clone and push new rule
381
+ const blocklet = blocklets.find((x) => x.meta.did === site.blockletDid);
382
+ const cacheRules = [];
383
+ site.rules
384
+ .filter(
385
+ (x) =>
386
+ x.to.type === ROUTING_RULE_TYPES.BLOCKLET &&
387
+ x.to.interfaceName === BLOCKLET_INTERFACE_PUBLIC &&
388
+ x.from.pathPrefix.startsWith(WELLKNOWN_SERVICE_PATH_PREFIX) === false
389
+ )
390
+ .forEach((rule) => {
391
+ const component = findComponentById(blocklet, rule.to.componentId);
392
+ if (!component) {
393
+ return;
394
+ }
395
+
396
+ if (component.mode !== BLOCKLET_MODES.PRODUCTION) {
397
+ return;
398
+ }
399
+ const cacheable = get(findWebInterface(component), 'cacheable', []);
400
+ cacheable.forEach((cachePrefix) => {
401
+ const clone = cloneDeep(rule);
402
+ clone.from.pathPrefix = joinUrl(rule.from.pathPrefix, cachePrefix);
403
+ clone.to.cacheGroup = 'blockletProxy';
404
+ clone.to.targetPrefix = cachePrefix;
405
+ clone.dynamic = true; // mark as dynamic to avoid redundant generated rules
406
+ cacheRules.push(clone);
407
+ });
408
+ });
409
+
410
+ site.rules = site.rules.concat(cacheRules);
411
+ site.mode = blocklet.mode;
412
+ site.cacheableGenerated = true;
413
+
414
+ return site;
415
+ })
416
+ .filter(Boolean);
417
+ };
418
+
419
+ const ensureLatestInfo = async (sites = [], { withDefaultCors = true } = {}) => {
420
+ const blocklets = await states.blocklet.getBlocklets();
421
+
422
+ // CAUTION: following steps are very important, please do not change the order
423
+ let result = await ensureLatestNodeInfo(sites, { withDefaultCors });
424
+ result = await ensureBlockletDid(result);
425
+ result = await filterSitesForRemovedBlocklets(sites, blocklets);
426
+ result = await ensureBlockletCache(result, blocklets);
427
+ result = await ensureWellknownRule(result);
428
+ result = await ensureCorsForWebWallet(result);
429
+ result = await ensureCorsForDidSpace(result, blocklets);
430
+ result = await ensureLatestInterfaceInfo(result);
431
+
432
+ return result;
398
433
  };
399
434
 
400
435
  const decompressCertificates = async (source, dest) => {
@@ -403,55 +438,29 @@ const decompressCertificates = async (source, dest) => {
403
438
  return dest;
404
439
  };
405
440
 
406
- module.exports = function getRouterHelpers({ dataDirs, routingSnapshot, routerManager, blockletManager }) {
441
+ const joinCertDownUrl = (baseUrl, name) => joinUrl(baseUrl, '/certs', name);
442
+
443
+ const getIpEchoCertDownloadUrl = (baseUrl) => joinCertDownUrl(baseUrl, 'ip-abtnet-io.tar.gz');
444
+ const getDidDomainCertDownloadUrl = (baseUrl) => joinCertDownUrl(baseUrl, 'did-abtnet-io.tar.gz');
445
+ const getDownloadCertBaseUrl = (info) =>
446
+ process.env.ABT_NODE_WILDCARD_CERT_HOST || get(info, 'routing.wildcardCertHost', '');
447
+
448
+ module.exports = function getRouterHelpers({ dataDirs, routingSnapshot, routerManager, blockletManager, certManager }) {
407
449
  const nodeState = states.node;
408
450
  const blockletState = states.blocklet;
409
451
  const siteState = states.site;
410
- const httpsCertState = states.certificate;
411
452
  const notification = states.notification;
412
453
 
413
454
  // site level duplication detection
414
455
  const hasRuleByPrefix = (site, value) => site.rules.find((x) => x.isProtected && get(x, 'from.pathPrefix') === value);
415
456
 
416
- const updateDashboardCertificate = async ({ checkExpire = true }) => {
417
- const info = await nodeState.read();
418
- const provider = getProviderFromNodeInfo(info);
419
- if (provider === ROUTER_PROVIDER_NONE) {
420
- return;
421
- }
422
-
423
- const https = get(info, 'routing.https', true);
424
- const dashboardDomain = get(info, 'routing.dashboardDomain', '');
425
- const certDownloadAddress = get(info, 'routing.dashboardCertDownloadAddress', '');
426
- if (!https || !dashboardDomain || !certDownloadAddress) {
427
- return;
428
- }
429
-
430
- if (checkExpire) {
431
- try {
432
- const cert = await routerManager.findCertificateByDomain(dashboardDomain);
433
- if (!cert) {
434
- return;
435
- }
436
-
437
- const now = Date.now();
438
- const certInfo = getHttpsCertInfo(cert.certificate);
439
- if (certInfo.validTo - now >= CERTIFICATE_EXPIRES_OFFSET) {
440
- logger.info('skip dashboard certificate update before not expired');
441
- return;
442
- }
443
- } catch (err) {
444
- logger.error('failed to check dashboard certificate expiration', { error: err });
445
- return;
446
- }
447
- }
448
-
449
- const destFolder = getTmpDirectory(path.join(`certificate-${Date.now()}`));
457
+ const downloadCert = async ({ domain, url }) => {
458
+ const destFolder = getTmpDir(path.join(`certificate-${Date.now()}`));
450
459
  try {
451
460
  const filename = path.join(destFolder, 'certificate.tar.gz');
452
461
  fs.ensureDirSync(destFolder);
453
462
 
454
- await downloadFile(certDownloadAddress, filename);
463
+ await downloadFile(url, filename);
455
464
  await decompressCertificates(filename, destFolder);
456
465
 
457
466
  const certificateFilePath = path.join(destFolder, 'cert.pem');
@@ -468,58 +477,186 @@ module.exports = function getRouterHelpers({ dataDirs, routingSnapshot, routerMa
468
477
  const certificate = fs.readFileSync(certificateFilePath).toString();
469
478
  const privateKey = fs.readFileSync(privateKeyFilePath).toString();
470
479
 
471
- await routerManager.updateNginxHttpsCert({ domain: dashboardDomain, privateKey, certificate, isProtected: true });
472
- logger.info('dashboard certificate updated');
480
+ await certManager.upsertByDomain({
481
+ domain,
482
+ privateKey,
483
+ certificate,
484
+ isProtected: true,
485
+ });
486
+
487
+ logger.info('dashboard certificate updated', { domain });
473
488
  } catch (error) {
474
- logger.error('Update dashboard certificate failed', { error });
489
+ logger.error('update dashboard certificate failed', { error });
490
+ throw error;
475
491
  } finally {
476
492
  fs.removeSync(destFolder);
477
493
  }
478
494
  };
479
495
 
496
+ const updateCert = async (domain, url) => {
497
+ try {
498
+ const cert = await certManager.getByDomain(domain);
499
+ if (!cert) {
500
+ return;
501
+ }
502
+
503
+ const now = Date.now();
504
+ const certInfo = getHttpsCertInfo(cert.certificate);
505
+
506
+ if (certInfo.validTo - now >= CERTIFICATE_EXPIRES_OFFSET) {
507
+ logger.info('skip dashboard certificate update before not expired', { domain, url });
508
+ return;
509
+ }
510
+
511
+ await downloadCert({
512
+ domain,
513
+ url,
514
+ });
515
+ } catch (err) {
516
+ logger.error('failed to check dashboard certificate expiration', { error: err, domain, url });
517
+ }
518
+ };
519
+
520
+ const updateDashboardCertificates = async () => {
521
+ const info = await nodeState.read();
522
+ const https = get(info, 'routing.https', true);
523
+ const ipWildcardDomain = get(info, 'routing.ipWildcardDomain', '');
524
+ const didDomain = info.didDomain;
525
+ const certDownloadAddress = getDownloadCertBaseUrl(info);
526
+ if (!https || !certDownloadAddress) {
527
+ return;
528
+ }
529
+
530
+ await updateCert(ipWildcardDomain, getIpEchoCertDownloadUrl(certDownloadAddress));
531
+ await updateCert(`*.${didDomain}`, getDidDomainCertDownloadUrl(certDownloadAddress));
532
+ };
533
+
534
+ const ensureWildcardCerts = async () => {
535
+ const info = await nodeState.read();
536
+ const didDomain = info.didDomain;
537
+ const ipWildcardDomain = get(info, 'routing.ipWildcardDomain', '');
538
+
539
+ const ensureDomainCert = async (domain, url) => {
540
+ const cert = await certManager.getByDomain(domain);
541
+ if (!cert || get(cert, 'meta.validTo') <= Date.now()) {
542
+ await downloadCert({
543
+ domain,
544
+ url,
545
+ });
546
+ }
547
+ };
548
+
549
+ const certBaseUrl = getDownloadCertBaseUrl(info);
550
+ await Promise.all([
551
+ ensureDomainCert(ipWildcardDomain, getIpEchoCertDownloadUrl(certBaseUrl)),
552
+ ensureDomainCert(`*.${didDomain}`, getDidDomainCertDownloadUrl(certBaseUrl)),
553
+ ]);
554
+ };
555
+
556
+ const upsertSiteRule = async ({ site, rule }, context) => {
557
+ const findExistingRule = (prefix) => site.rules.find((r) => r.from.pathPrefix === normalizePathPrefix(prefix));
558
+
559
+ const newSiteRule = {
560
+ id: site.id,
561
+ rule,
562
+ };
563
+
564
+ const existingRule = findExistingRule(get(rule, 'from.pathPrefix'));
565
+ if (!existingRule) {
566
+ await routerManager.addRoutingRule(newSiteRule, context);
567
+ return true;
568
+ }
569
+
570
+ if (!isEqual(existingRule.to, rule.to)) {
571
+ newSiteRule.rule.id = existingRule.id;
572
+ newSiteRule.skipProtectedRuleChecking = true;
573
+ await routerManager.updateRoutingRule(newSiteRule, context);
574
+ return true;
575
+ }
576
+
577
+ return false;
578
+ };
579
+
480
580
  const addWellknownSite = async (sites, context) => {
481
581
  const site = (sites || []).find((x) => x.name === NAME_FOR_WELLKNOWN_SITE);
482
582
 
483
583
  try {
484
- const pathPrefix = joinUrl(WELLKNOWN_PATH_PREFIX, '/did.json');
584
+ const info = await nodeState.read();
585
+ const proxyTarget = {
586
+ port: info.port,
587
+ type: ROUTING_RULE_TYPES.GENERAL_PROXY,
588
+ interfaceName: BLOCKLET_INTERFACE_WELLKNOWN,
589
+ };
590
+
485
591
  const didResolverWellknownRule = {
486
- from: { pathPrefix },
592
+ isProtected: true,
593
+ from: { pathPrefix: WELLKNOWN_DID_RESOLVER_PREFIX },
594
+ to: proxyTarget,
595
+ };
596
+
597
+ const acmeChallengeWellknownRule = {
598
+ isProtected: true,
599
+ from: { pathPrefix: WELLKNOWN_ACME_CHALLENGE_PREFIX },
600
+ to: proxyTarget,
601
+ };
602
+
603
+ const pingWellknownRule = {
604
+ isProtected: true,
605
+ from: { pathPrefix: WELLKNOWN_PING_PREFIX },
487
606
  to: {
488
- port: DEFAULT_DAEMON_PORT,
489
- type: ROUTING_RULE_TYPES.GENERAL_PROXY,
490
- interfaceName: BLOCKLET_INTERFACE_WELLKNOWN,
607
+ type: ROUTING_RULE_TYPES.DIRECT_RESPONSE,
608
+ response: {
609
+ status: 200,
610
+ contentType: 'application/javascript',
611
+ body: "'pong'",
612
+ },
613
+ port: info.port,
491
614
  },
492
615
  };
493
616
 
494
617
  if (site) {
495
- if (site.rules.find((r) => r.from.pathPrefix === normalizePathPrefix(pathPrefix))) {
496
- return false;
497
- }
498
-
499
- await routerManager.addRoutingRule(
618
+ const didResolverRuleUpdateRes = await upsertSiteRule(
500
619
  {
501
- id: site.id,
620
+ site,
502
621
  rule: didResolverWellknownRule,
503
622
  },
504
623
  context
505
624
  );
506
- } else {
507
- await routerManager.addRoutingSite(
625
+
626
+ const acmeRuleUpdateRes = await upsertSiteRule(
508
627
  {
509
- site: {
510
- domain: DOMAIN_FOR_INTERNAL_SITE,
511
- port: await getWellknownSitePort(),
512
- name: NAME_FOR_WELLKNOWN_SITE,
513
- rules: [didResolverWellknownRule],
514
- isProtected: true,
515
- },
516
- skipCheckDynamicBlacklist: true,
517
- skipValidation: true,
628
+ site,
629
+ rule: acmeChallengeWellknownRule,
518
630
  },
519
631
  context
520
632
  );
633
+
634
+ const pingRuleRes = await upsertSiteRule(
635
+ {
636
+ site,
637
+ rule: pingWellknownRule,
638
+ },
639
+ context
640
+ );
641
+
642
+ return didResolverRuleUpdateRes || acmeRuleUpdateRes || pingRuleRes;
521
643
  }
522
644
 
645
+ await routerManager.addRoutingSite(
646
+ {
647
+ site: {
648
+ domain: DOMAIN_FOR_INTERNAL_SITE,
649
+ port: await getWellknownSitePort(),
650
+ name: NAME_FOR_WELLKNOWN_SITE,
651
+ rules: [didResolverWellknownRule, acmeChallengeWellknownRule, pingWellknownRule],
652
+ isProtected: true,
653
+ },
654
+ skipCheckDynamicBlacklist: true,
655
+ skipValidation: true,
656
+ },
657
+ context
658
+ );
659
+
523
660
  return true;
524
661
  } catch (err) {
525
662
  console.error('add well-known site failed:', err);
@@ -533,14 +670,8 @@ module.exports = function getRouterHelpers({ dataDirs, routingSnapshot, routerMa
533
670
  *
534
671
  * @returns {boolean} if routing changed
535
672
  */
536
- const ensureDashboardRouting = async (context = {}, output) => {
673
+ const ensureDashboardRouting = async (context = {}) => {
537
674
  const info = await nodeState.read();
538
-
539
- const provider = getProviderFromNodeInfo(info);
540
- if (provider === ROUTER_PROVIDER_NONE) {
541
- return false;
542
- }
543
-
544
675
  const sites = await siteState.getSites();
545
676
  let dashboardSite = (sites || []).find((x) => x.domain === DOMAIN_FOR_IP_SITE);
546
677
  const updatedResult = [];
@@ -569,28 +700,26 @@ module.exports = function getRouterHelpers({ dataDirs, routingSnapshot, routerMa
569
700
  }
570
701
  }
571
702
 
572
- const isExistsInAlias = (domainAlias) =>
573
- (dashboardSite.domainAliases || []).find((item) => {
574
- if (typeof item === 'object') {
575
- return domainAlias === item.value;
576
- }
703
+ const ipWildcardDomain = get(info, 'routing.ipWildcardDomain', '');
577
704
 
578
- return domainAlias === item;
579
- });
705
+ const domainAliases = (dashboardSite.domainAliases || []).filter(
706
+ (item) => !item.value.endsWith(ipWildcardDomain) && !item.value.endsWith(info.didDomain)
707
+ );
580
708
 
581
- const dashboardDomain = get(info, 'routing.dashboardDomain', '');
582
- if (dashboardDomain && !isExistsInAlias(dashboardDomain)) {
583
- try {
584
- const result = await siteState.update(
585
- { _id: dashboardSite.id },
586
- { $push: { domainAliases: { value: dashboardDomain, isProtected: true } } }
587
- );
709
+ const didDomain = getServerDidDomain(info);
710
+ const dashboardAliasDomains = [
711
+ { value: ipWildcardDomain, isProtected: true },
712
+ { value: didDomain, isProtected: true },
713
+ ];
714
+ domainAliases.push(...dashboardAliasDomains);
588
715
 
589
- updatedResult.push(result);
590
- } catch (error) {
591
- logger.error('add dashboard domain rule failed', { error });
592
- console.error('Add dashboard domain rule failed:', error);
593
- }
716
+ try {
717
+ const result = await siteState.update({ _id: dashboardSite.id }, { $set: { domainAliases } });
718
+
719
+ updatedResult.push(result);
720
+ } catch (error) {
721
+ logger.error('add dashboard domain rule failed', { error });
722
+ console.error('Add dashboard domain rule failed:', error);
594
723
  }
595
724
 
596
725
  const defaultRule = sites.find((x) => x.domain === DOMAIN_FOR_DEFAULT_SITE);
@@ -618,18 +747,6 @@ module.exports = function getRouterHelpers({ dataDirs, routingSnapshot, routerMa
618
747
  updatedResult.push(wellknownRes);
619
748
  }
620
749
 
621
- // Download dashboard certificates if not exists
622
- const certDownloadAddress = get(info, 'routing.dashboardCertDownloadAddress', '');
623
- if (dashboardDomain && certDownloadAddress) {
624
- const cert = await routerManager.findCertificateByDomain(dashboardDomain);
625
- if (!cert) {
626
- await updateDashboardCertificate({ checkExpire: false });
627
- if (typeof output === 'function') {
628
- output('Dashboard HTTPS certificate was downloaded successfully!');
629
- }
630
- }
631
- }
632
-
633
750
  if (updatedResult.length) {
634
751
  const hash = await takeRoutingSnapshot({ message: 'ensure dashboard routing rules', dryRun: false }, context);
635
752
  logger.info('take routing snapshot on ensure dashboard routing rules', { updatedResult, hash });
@@ -657,8 +774,8 @@ module.exports = function getRouterHelpers({ dataDirs, routingSnapshot, routerMa
657
774
  return `/${str}`.replace(/^\/+/, '/');
658
775
  };
659
776
 
660
- const domainGroup = `${blocklet.meta.did}${BLOCKLET_SITE_GROUP_SUFFIX}`;
661
- const domain = getIpDnsDomainForBlocklet(blocklet, webInterface);
777
+ const domainGroup = getBlockletDomainGroupName(blocklet.meta.did);
778
+
662
779
  const pathPrefix = getPrefix(webInterface.prefix);
663
780
  const rule = {
664
781
  from: { pathPrefix },
@@ -672,20 +789,45 @@ module.exports = function getRouterHelpers({ dataDirs, routingSnapshot, routerMa
672
789
  };
673
790
 
674
791
  const existSite = await states.site.findOne({ domain: domainGroup });
792
+ const { wallet } = getBlockletInfo(blocklet, nodeInfo.sk);
793
+ updateBlockletDocument({
794
+ wallet,
795
+ appPid: blocklet.appPid,
796
+ alsoKnownAs: getBlockletKnownAs(blocklet),
797
+ daemonDidDomain: getServerDidDomain(nodeInfo),
798
+ didRegistryUrl: nodeInfo.didRegistry,
799
+ domain: nodeInfo.didDomain,
800
+ })
801
+ .then(() => {
802
+ logger.info(`updated blocklet ${blocklet.appDid} dns`);
803
+ })
804
+ .catch((err) => {
805
+ logger.error(`update blocklet ${blocklet.appDid} dns failed`, { error: err });
806
+ });
807
+
675
808
  if (!existSite) {
809
+ const domainAliases = [];
810
+
811
+ const didDomain = getDidDomainForBlocklet({ appPid: blocklet.appPid, didDomain: nodeInfo.didDomain });
812
+ const ipEchoDnsDomain = getIpDnsDomainForBlocklet(blocklet);
813
+
814
+ // let didDomain in front of ipEchoDnsDomain
815
+ domainAliases.push({ value: didDomain, isProtected: true }, { value: ipEchoDnsDomain, isProtected: true });
816
+
676
817
  await routerManager.addRoutingSite(
677
818
  {
678
819
  site: {
679
820
  domain: domainGroup,
680
- domainAliases: [{ value: domain, isProtected: true }],
821
+ domainAliases,
681
822
  isProtected: true,
682
823
  rules: [rule],
683
824
  },
684
825
  skipCheckDynamicBlacklist: true,
826
+ skipValidation: true,
685
827
  },
686
828
  context
687
829
  );
688
- logger.info('add routing site', { site: domain });
830
+ logger.info('add routing site', { site: domainGroup });
689
831
 
690
832
  return true;
691
833
  }
@@ -700,13 +842,13 @@ module.exports = function getRouterHelpers({ dataDirs, routingSnapshot, routerMa
700
842
  },
701
843
  skipProtectedRuleChecking: true,
702
844
  });
703
- logger.info('update routing rule for site', { site: domain });
845
+ logger.info('update routing rule for site', { site: domainGroup });
704
846
  } else {
705
847
  await routerManager.addRoutingRule({
706
848
  id: existSite.id,
707
849
  rule,
708
850
  });
709
- logger.info('add routing rule for site', { site: domain });
851
+ logger.info('add routing rule for site', { site: domainGroup });
710
852
  }
711
853
 
712
854
  return true;
@@ -723,130 +865,79 @@ module.exports = function getRouterHelpers({ dataDirs, routingSnapshot, routerMa
723
865
  return false;
724
866
  }
725
867
 
726
- const tasks = (blocklet.meta.interfaces || [])
727
- .filter((x) => x.type === BLOCKLET_INTERFACE_TYPE_WELLKNOWN)
728
- .map(async (x) => {
729
- const pathPrefix = normalizePathPrefix(x.prefix);
730
- if (!pathPrefix.startsWith(WELLKNOWN_PATH_PREFIX)) {
731
- throw new Error(`Wellknown path prefix must start with: ${WELLKNOWN_PATH_PREFIX}`);
732
- }
868
+ /**
869
+ * 1. component blocklet 不允许有相同多的 wellknown
870
+ * 1. wellknown 可以访问的路由:
871
+ * /.well-known/xxx
872
+ * /{wellknown owner blocklet}/.well-known/xxx
873
+ */
733
874
 
734
- const port = findInterfacePortByName(blocklet, x.name);
735
- const existedRule = hasRuleByPrefix(wellknownSite, pathPrefix);
875
+ const isWellknownInterface = (x) => x.type === BLOCKLET_INTERFACE_TYPE_WELLKNOWN;
736
876
 
737
- const rule = {
738
- from: { pathPrefix },
739
- to: {
740
- did: blocklet.meta.did,
741
- port,
742
- type: ROUTING_RULE_TYPES.GENERAL_PROXY,
743
- interfaceName: x.name,
744
- },
745
- isProtected: true,
746
- };
877
+ const handler = async (tmpBlocklet, tmpInterface, mountPoint) => {
878
+ let pathPrefix = normalizePathPrefix(tmpInterface.prefix);
879
+ if (!pathPrefix.startsWith(WELLKNOWN_PATH_PREFIX)) {
880
+ throw new Error(`Wellknown path prefix must start with: ${WELLKNOWN_PATH_PREFIX}`);
881
+ }
747
882
 
748
- if (!existedRule) {
749
- await routerManager.addRoutingRule({ id: wellknownSite.id, rule, skipCheckDynamicBlacklist: true }, context);
750
- return true;
751
- }
883
+ const tmpMountPoint = mountPoint || tmpBlocklet.mountPoint;
884
+ if (tmpMountPoint) {
885
+ pathPrefix = joinUrl(tmpMountPoint, pathPrefix);
886
+ }
752
887
 
753
- // 兼容代码,旧的 rule 没有 to.did 字段
754
- if (port !== existedRule.to.port || existedRule.to.did !== blocklet.meta.did) {
755
- existedRule.to = rule.to;
888
+ const port = findInterfacePortByName(tmpBlocklet, tmpInterface.name);
889
+ const existedRule = hasRuleByPrefix(wellknownSite, pathPrefix);
756
890
 
757
- await routerManager.updateRoutingRule(
758
- { id: wellknownSite.id, rule: existedRule, skipProtectedRuleChecking: true },
759
- context
760
- );
891
+ const rule = {
892
+ from: { pathPrefix },
893
+ to: {
894
+ did: tmpBlocklet.meta.did,
895
+ port,
896
+ type: ROUTING_RULE_TYPES.GENERAL_PROXY,
897
+ interfaceName: tmpInterface.name,
898
+ },
899
+ isProtected: true,
900
+ };
761
901
 
762
- return true;
763
- }
902
+ if (!existedRule) {
903
+ await routerManager.addRoutingRule({ id: wellknownSite.id, rule, skipCheckDynamicBlacklist: true }, context);
904
+ return true;
905
+ }
764
906
 
765
- return false;
766
- });
907
+ // 兼容代码,旧的 rule 没有 to.did 字段
908
+ if (port !== existedRule.to.port || existedRule.to.did !== tmpBlocklet.meta.did) {
909
+ existedRule.to = rule.to;
767
910
 
768
- const changes = await Promise.all(tasks);
769
- return changes.some(Boolean);
770
- };
911
+ await routerManager.updateRoutingRule(
912
+ { id: wellknownSite.id, rule: existedRule, skipProtectedRuleChecking: true },
913
+ context
914
+ );
771
915
 
772
- /**
773
- * Add system routing rules for blocklet in dashboard site
774
- *
775
- * @returns {boolean} if routing changed
776
- */
777
- const _ensureBlockletRulesForDashboardSite = async (blocklet, sites, nodeInfo, context = {}) => {
778
- // blocklet level duplication detection
779
- const findRulesByInterface = (site, value) =>
780
- site.rules.filter((x) => x.isProtected && x.to.did === blocklet.meta.did && get(x, 'to.interfaceName') === value);
781
-
782
- const ipSite = sites.find((x) => x.domain === DOMAIN_FOR_IP_SITE);
783
- if (!ipSite) {
784
- logger.warn(`Routing rule for dashboard not found, abort on ensure rules for blocklet ${blocklet.meta.did}`);
785
- return false;
786
- }
916
+ return true;
917
+ }
787
918
 
788
- // If we have rule for legacy path prefix, just return
789
- const pathPrefixLegacy = normalizePathPrefix(`/${nodeInfo.routing.adminPath}/${blocklet.meta.name}`);
790
- if (hasRuleByPrefix(ipSite, pathPrefixLegacy)) {
791
919
  return false;
792
- }
793
-
794
- const removedRuleIds = [];
795
- const changes = await Promise.all(
796
- blocklet.meta.interfaces.map(async (x) => {
797
- let changed = false; // if state db was mutated
798
- let pathPrefix = '';
799
- if (x.prefix === BLOCKLET_DYNAMIC_PATH_PREFIX) {
800
- // pathPrefix for dynamic path: `/{adminPath}/{blockletName}/{interfaceName}`
801
- pathPrefix = normalizePathPrefix(`/${pathPrefixLegacy}/${x.name}`);
802
- } else {
803
- // pathPrefix for fixed path: `/{interfacePrefix}`
804
- pathPrefix = normalizePathPrefix(x.prefix);
805
- }
806
-
807
- // Remove if we have rule with same interface
808
- const exists = findRulesByInterface(ipSite, x.name);
809
- if (exists.length) {
810
- // Note: we need to keep a list of removed rules since we might remove multiple rules in the loop
811
- exists.forEach((e) => removedRuleIds.push(e.id));
812
- await states.site.update(
813
- { _id: ipSite.id },
814
- { $set: { rules: ipSite.rules.filter((r) => removedRuleIds.includes(r.id) === false) } }
815
- );
816
- changed = true;
817
- } else if (hasRuleByPrefix(ipSite, pathPrefix)) {
818
- // Skip same pathPrefix rules
819
- return changed;
820
- }
920
+ };
821
921
 
822
- // 不在 dashboard site 中添加 path 和 prefix 都是根路径(/) 的路由
823
- if (x.path === '/' && x.prefix === '/') {
824
- return changed;
825
- }
922
+ const tasks = [];
826
923
 
827
- await routerManager.addRoutingRule(
828
- {
829
- id: ipSite.id,
830
- rule: {
831
- from: { pathPrefix },
832
- to: {
833
- type: ROUTING_RULE_TYPES.BLOCKLET,
834
- port: findInterfacePortByName(blocklet, x.name),
835
- did: blocklet.meta.did, // root blocklet did
836
- interfaceName: x.name, // root blocklet interface
837
- },
838
- isProtected: true,
839
- },
840
- skipCheckDynamicBlacklist: true,
841
- },
842
- context
843
- );
844
- changed = true;
924
+ forEachBlocklet(
925
+ blocklet,
926
+ (b) => {
927
+ (b.meta.interfaces || []).forEach((item) => {
928
+ if (isWellknownInterface(item)) {
929
+ tasks.push(handler(b, item));
845
930
 
846
- return changed;
847
- })
931
+ if (!b.mountPoint || b.mountPoint !== '/') {
932
+ tasks.push(handler(b, item, '/')); // 在站点的根路由下挂载一个
933
+ }
934
+ }
935
+ });
936
+ },
937
+ { sync: true }
848
938
  );
849
939
 
940
+ const changes = await Promise.all(tasks);
850
941
  return changes.some(Boolean);
851
942
  };
852
943
 
@@ -873,12 +964,6 @@ module.exports = function getRouterHelpers({ dataDirs, routingSnapshot, routerMa
873
964
  */
874
965
  const ensureBlockletRouting = async (blocklet, context = {}) => {
875
966
  const nodeInfo = await nodeState.read();
876
-
877
- const provider = getProviderFromNodeInfo(nodeInfo);
878
- if (provider === ROUTER_PROVIDER_NONE) {
879
- return false;
880
- }
881
-
882
967
  const hasWebInterface = (blocklet.meta.interfaces || []).some((x) => x.type === BLOCKLET_INTERFACE_TYPE_WEB);
883
968
  if (!hasWebInterface) {
884
969
  return false;
@@ -891,14 +976,6 @@ module.exports = function getRouterHelpers({ dataDirs, routingSnapshot, routerMa
891
976
  _ensureBlockletSites(blocklet, sites, nodeInfo, context),
892
977
  ];
893
978
 
894
- if (nodeInfo.mode === NODE_MODES.DEBUG || ['e2e', 'test'].includes(process.env.NODE_ENV)) {
895
- logger.info('Add blocklet endpoint in dashboard site', {
896
- nodeMode: nodeInfo.mode,
897
- NODE_ENV: process.env.NODE_ENV,
898
- });
899
- tasks.push(_ensureBlockletRulesForDashboardSite(blocklet, sites, nodeInfo, context));
900
- }
901
-
902
979
  const changes = await Promise.all(tasks);
903
980
 
904
981
  return changes.some(Boolean);
@@ -978,12 +1055,6 @@ module.exports = function getRouterHelpers({ dataDirs, routingSnapshot, routerMa
978
1055
  * @returns {boolean} if routing changed
979
1056
  */
980
1057
  const ensureBlockletRoutingForUpgrade = async (blocklet, context = {}) => {
981
- const nodeInfo = await nodeState.read();
982
- const provider = getProviderFromNodeInfo(nodeInfo);
983
- if (provider === ROUTER_PROVIDER_NONE) {
984
- return false;
985
- }
986
-
987
1058
  await routerManager.deleteRoutingRulesItemByDid(
988
1059
  { did: blocklet.meta.did, ruleFilter: (x) => x.isProtected },
989
1060
  context
@@ -1004,7 +1075,10 @@ module.exports = function getRouterHelpers({ dataDirs, routingSnapshot, routerMa
1004
1075
  const nodeInfo = await nodeState.read();
1005
1076
 
1006
1077
  const ruleChanged = await routerManager.deleteRoutingRulesItemByDid({ did: blocklet.meta.did }, context);
1007
- const siteChanged = await _removeBlockletSites(blocklet, nodeInfo, context);
1078
+ let siteChanged;
1079
+ if (!context.keepRouting) {
1080
+ siteChanged = await _removeBlockletSites(blocklet, nodeInfo, context);
1081
+ }
1008
1082
 
1009
1083
  return ruleChanged || siteChanged;
1010
1084
  };
@@ -1028,21 +1102,21 @@ module.exports = function getRouterHelpers({ dataDirs, routingSnapshot, routerMa
1028
1102
  return result;
1029
1103
  }
1030
1104
 
1031
- async function getRoutingRulesByDid(did) {
1032
- const info = await nodeState.read();
1033
- const { sites } = await readRoutingSites();
1034
- const rules = Router.flattenSitesToRules(sites, info);
1035
- return rules.filter((rule) => rule.to.did === did);
1105
+ async function resetSiteByDid(did, { refreshRouterProvider = true } = {}) {
1106
+ const blocklet = await states.blocklet.getBlocklet(did);
1107
+ await removeBlockletRouting(blocklet);
1108
+ await ensureBlockletRouting(blocklet);
1109
+ if (refreshRouterProvider) {
1110
+ const hash = await takeRoutingSnapshot({ message: `Reset blocklet ${did}`, dryRun: false });
1111
+ logger.info('reset blocklet routing rules', { did, hash });
1112
+ }
1036
1113
  }
1037
1114
 
1038
- const routers = {}; // we need to keep reference for different router instances
1115
+ const providers = {}; // we need to keep reference for different router instances
1039
1116
  const handleRouting = async (nodeInfo) => {
1040
1117
  const providerName = get(nodeInfo, 'routing.provider', null);
1041
1118
  const httpsEnabled = get(nodeInfo, 'routing.https', true);
1042
1119
  logger.debug('handle routing', { providerName, httpsEnabled });
1043
- if (providerName === ROUTER_PROVIDER_NONE) {
1044
- return;
1045
- }
1046
1120
 
1047
1121
  const Provider = getProvider(providerName);
1048
1122
  const checkResult = await Provider.check({ configDir: dataDirs.router });
@@ -1050,27 +1124,25 @@ module.exports = function getRouterHelpers({ dataDirs, routingSnapshot, routerMa
1050
1124
  throw new Error(`${providerName} pre-check failed, ${checkResult.error}`);
1051
1125
  }
1052
1126
 
1053
- if (routers[providerName]) {
1054
- await routers[providerName].restart();
1127
+ if (providers[providerName]) {
1128
+ await providers[providerName].reload();
1055
1129
  } else {
1056
- routers[providerName] = new Router({
1130
+ providers[providerName] = new Router({
1057
1131
  provider: new Provider({
1058
- configDirectory: path.join(dataDirs.router, providerName),
1132
+ configDir: path.join(dataDirs.router, providerName),
1059
1133
  httpPort: nodeInfo.routing.httpPort || DEFAULT_HTTP_PORT,
1060
1134
  httpsPort: nodeInfo.routing.httpsPort || DEFAULT_HTTPS_PORT,
1061
- cacheDisabled: nodeInfo.mode === NODE_MODES.DEBUG,
1135
+ cacheEnabled: isGatewayCacheEnabled(nodeInfo),
1062
1136
  }),
1063
1137
  getRoutingParams: async () => {
1064
1138
  try {
1065
1139
  const info = await nodeState.read();
1066
1140
  let { sites } = await readRoutingSites();
1067
1141
  sites = await ensureLatestInfo(sites);
1068
- sites = await ensureAuthService(sites, blockletManager);
1069
1142
  sites = await ensureServiceRule(sites);
1143
+ sites = await ensureRootRule(sites);
1070
1144
 
1071
- const certificates = httpsEnabled
1072
- ? await httpsCertState.find({ type: providerName }, { domain: 1, certificate: 1, privateKey: 1 })
1073
- : [];
1145
+ const certificates = httpsEnabled ? await certManager.getAllNormal() : [];
1074
1146
 
1075
1147
  return {
1076
1148
  sites,
@@ -1093,11 +1165,17 @@ module.exports = function getRouterHelpers({ dataDirs, routingSnapshot, routerMa
1093
1165
  },
1094
1166
  });
1095
1167
 
1096
- routerManager.on('router.certificate.add', () => routers[providerName].restart());
1097
- routerManager.on('router.certificate.updated', () => routers[providerName].restart());
1098
- routerManager.on('router.certificate.remove', () => routers[providerName].restart());
1168
+ [
1169
+ BlockletEvents.certIssued,
1170
+ EVENTS.CERT_ADDED,
1171
+ EVENTS.CERT_REMOVED,
1172
+ EVENTS.CERT_ISSUED,
1173
+ EVENTS.CERT_UPDATED,
1174
+ ].forEach((event) => {
1175
+ certManager.on(event, () => providers[providerName].reload());
1176
+ });
1099
1177
 
1100
- await routers[providerName].start();
1178
+ await providers[providerName].start();
1101
1179
  }
1102
1180
  };
1103
1181
 
@@ -1105,8 +1183,8 @@ module.exports = function getRouterHelpers({ dataDirs, routingSnapshot, routerMa
1105
1183
  const info = await nodeState.read();
1106
1184
  const providerName = get(info, 'routing.provider', null);
1107
1185
 
1108
- if (providerName && routers[providerName] && typeof routers[providerName].rotateLogs === 'function') {
1109
- await routers[providerName].rotateLogs();
1186
+ if (providerName && providers[providerName] && typeof providers[providerName].rotateLogs === 'function') {
1187
+ await providers[providerName].rotateLogs({ retain: LOG_RETAIN_IN_DAYS });
1110
1188
  }
1111
1189
  };
1112
1190
 
@@ -1135,33 +1213,31 @@ module.exports = function getRouterHelpers({ dataDirs, routingSnapshot, routerMa
1135
1213
  await handleRouting(result);
1136
1214
 
1137
1215
  if (newProvider !== info.routing.provider) {
1138
- if (newProvider !== ROUTER_PROVIDER_NONE) {
1139
- // Ensure we have system sites for daemon
1140
- await ensureDashboardRouting(context);
1141
-
1142
- // Ensure we have system rules for blocklets
1143
- const blocklets = await blockletState.getBlocklets();
1144
- const ensureBlocklet = async (x) => {
1145
- const blocklet = await blockletManager.ensureBlocklet(x.meta.did);
1146
- return ensureBlockletRouting(blocklet, context);
1147
- };
1148
- const ensureBlockletResults = await Promise.all(blocklets.map((x) => ensureBlocklet(x)));
1149
-
1150
- // We need to take snapshot after system rules ensured
1151
- if (ensureBlockletResults.filter(Boolean).length) {
1152
- await takeRoutingSnapshot({ message: `Switch routing engine to ${newProvider}`, dryRun: false }, context);
1153
- logger.info(`take routing snapshot on switch engine: ${newProvider}`, { ensureBlockletResults });
1154
- }
1216
+ // Ensure we have system sites for daemon
1217
+ await ensureDashboardRouting(context);
1218
+
1219
+ // Ensure we have system rules for blocklets
1220
+ const blocklets = await blockletState.getBlocklets();
1221
+ const ensureBlocklet = async (x) => {
1222
+ const blocklet = await blockletManager.getBlocklet(x.meta.did);
1223
+ return ensureBlockletRouting(blocklet, context);
1224
+ };
1225
+ const ensureBlockletResults = await Promise.all(blocklets.map((x) => ensureBlocklet(x)));
1226
+
1227
+ // We need to take snapshot after system rules ensured
1228
+ if (ensureBlockletResults.filter(Boolean).length) {
1229
+ await takeRoutingSnapshot({ message: `Switch routing engine to ${newProvider}`, dryRun: false }, context);
1230
+ logger.info(`take routing snapshot on switch engine: ${newProvider}`, { ensureBlockletResults });
1155
1231
  }
1156
1232
 
1157
1233
  const Provider = getProvider(info.routing.provider);
1158
1234
  if (Provider) {
1159
1235
  try {
1160
1236
  const providerInstance = new Provider({
1161
- configDirectory: path.join(dataDirs.router, info.routing.provider),
1237
+ configDir: path.join(dataDirs.router, info.routing.provider),
1162
1238
  httpPort: info.routing.httpPort || DEFAULT_HTTP_PORT,
1163
1239
  httpsPort: info.routing.httpsPort || DEFAULT_HTTPS_PORT,
1164
- cacheDisabled: info.mode === NODE_MODES.DEBUG,
1240
+ cacheEnabled: isGatewayCacheEnabled(info),
1165
1241
  });
1166
1242
  await providerInstance.stop();
1167
1243
  logger.info('original router stopped:', { provider: info.routing.provider });
@@ -1240,7 +1316,7 @@ module.exports = function getRouterHelpers({ dataDirs, routingSnapshot, routerMa
1240
1316
  return sites;
1241
1317
  }
1242
1318
 
1243
- return attachInterfaceUrls({
1319
+ return attachRuntimeDomainAliases({
1244
1320
  sites: await ensureLatestInfo(sites, { withDefaultCors }),
1245
1321
  context,
1246
1322
  node: nodeState,
@@ -1249,7 +1325,7 @@ module.exports = function getRouterHelpers({ dataDirs, routingSnapshot, routerMa
1249
1325
 
1250
1326
  const getSnapshotSites = async ({ hash }, context = {}, { withDefaultCors = true } = {}) => {
1251
1327
  const sites = await routingSnapshot.readSnapshotSites(hash);
1252
- return attachInterfaceUrls({
1328
+ return attachRuntimeDomainAliases({
1253
1329
  sites: await ensureLatestInfo(sites, { withDefaultCors }),
1254
1330
  context,
1255
1331
  node: nodeState,
@@ -1257,65 +1333,56 @@ module.exports = function getRouterHelpers({ dataDirs, routingSnapshot, routerMa
1257
1333
  };
1258
1334
 
1259
1335
  const getCertificates = async () => {
1260
- const certificates = await httpsCertState.find();
1336
+ const certificates = await certManager.getAll();
1261
1337
  const sites = await getSitesFromSnapshot();
1338
+
1339
+ const isMatch = (cert, domain) =>
1340
+ domain !== DOMAIN_FOR_DEFAULT_SITE && domain && routerManager.isCertMatchedDomain(cert, domain);
1341
+
1262
1342
  return certificates.map((cert) => {
1263
1343
  cert.matchedSites = [];
1264
1344
  sites.forEach((site) => {
1265
- if (
1266
- site.domain !== DOMAIN_FOR_DEFAULT_SITE &&
1267
- site.domain &&
1268
- routerManager.isCertMatchedDomain(cert, site.domain)
1269
- ) {
1270
- cert.matchedSites.push({ id: site.id, domain: site.domain });
1271
- }
1345
+ const domains = [site.domain, ...(site.domainAliases || []).map((x) => x.value)];
1346
+ domains.forEach((domain) => {
1347
+ if (isMatch(cert, domain)) {
1348
+ cert.matchedSites.push({ id: site.id, domain });
1349
+ }
1350
+ });
1272
1351
  });
1273
1352
 
1274
1353
  return cert;
1275
1354
  });
1276
1355
  };
1277
1356
 
1278
- const checkDomain = async (domain) => {
1279
- const matchedCert = await routerManager.getMatchedCert(domain);
1357
+ /**
1358
+ * proxy to routerManager and do takeRoutingSnapshot
1359
+ */
1360
+ const _proxyToRouterManager =
1361
+ (fnName) =>
1362
+ async (...args) => {
1363
+ const res = await routerManager[fnName](...args);
1280
1364
 
1281
- return { domain, isHttps: !!matchedCert, matchedCert };
1282
- };
1365
+ takeRoutingSnapshot({ message: fnName, dryRun: false }).catch((error) => {
1366
+ logger.error('failed to takeRoutingSnapshot', { error });
1367
+ });
1283
1368
 
1284
- const checkCertificatesExpiration = async () => {
1285
- const now = Date.now();
1286
-
1287
- const certificates = await getCertificates();
1288
- for (let i = 0; i < certificates.length; i++) {
1289
- const cert = certificates[i];
1290
- const alreadyExpired = now >= cert.validTo;
1291
- const aboutToExpire = cert.validTo - now > 0 && cert.validTo - now < CERTIFICATE_EXPIRES_WARNING_OFFSET;
1292
-
1293
- if (alreadyExpired) {
1294
- logger.info('send certificate expire notification', { domain: cert.domain });
1295
- notification.create({
1296
- title: 'SSL Certificate Expired',
1297
- description: `Your SSL certificate for domain ${cert.domain} has expired, please update it in Blocklet Server`,
1298
- severity: 'error',
1299
- entityType: 'certificate',
1300
- entityId: cert._id, // eslint-disable-line no-underscore-dangle
1301
- });
1302
- } else if (aboutToExpire) {
1303
- logger.info('send certificate about-expire notification', { domain: cert.domain });
1304
- const expireInDays = Math.ceil((cert.validTo - now) / DAY_IN_MS);
1305
- notification.create({
1306
- title: 'SSL Certificate Expire Warning',
1307
- description: `Your SSL certificate for domain ${
1308
- cert.domain
1309
- } will expire in ${expireInDays} days (on ${new Date(
1310
- cert.validTo
1311
- ).toLocaleString()}), please remember to update it in Blocklet Server`,
1312
- severity: 'warning',
1313
- entityType: 'certificate',
1314
- entityId: cert._id, // eslint-disable-line no-underscore-dangle
1315
- });
1369
+ const { teamDid: did } = args[0] || {};
1370
+ if (did) {
1371
+ routerManager.emit(BlockletEvents.updated, { meta: { did } });
1316
1372
  }
1317
- }
1318
- };
1373
+
1374
+ return res;
1375
+ };
1376
+
1377
+ const addRoutingSite = _proxyToRouterManager('addRoutingSite');
1378
+ const updateRoutingSite = _proxyToRouterManager('updateRoutingSite');
1379
+ const deleteRoutingSite = _proxyToRouterManager('deleteRoutingSite');
1380
+ const addRoutingRule = _proxyToRouterManager('addRoutingRule');
1381
+ const updateRoutingRule = _proxyToRouterManager('updateRoutingRule');
1382
+ const deleteRoutingRule = _proxyToRouterManager('deleteRoutingRule');
1383
+ // FIXME: should verify domain owner before added
1384
+ const addDomainAlias = _proxyToRouterManager('addDomainAlias');
1385
+ const deleteDomainAlias = _proxyToRouterManager('deleteDomainAlias');
1319
1386
 
1320
1387
  return {
1321
1388
  ensureDashboardRouting,
@@ -1323,39 +1390,47 @@ module.exports = function getRouterHelpers({ dataDirs, routingSnapshot, routerMa
1323
1390
  ensureBlockletRoutingForUpgrade,
1324
1391
  removeBlockletRouting,
1325
1392
  handleRouting,
1326
- getRoutingRulesByDid,
1393
+ resetSiteByDid,
1327
1394
  updateNodeRouting,
1328
1395
  takeRoutingSnapshot,
1329
1396
  getRoutingSites,
1330
1397
  getSnapshotSites,
1331
- getCertificates,
1332
1398
  getSitesFromSnapshot,
1333
- checkDomain,
1399
+ getCertificates,
1400
+ ensureWildcardCerts,
1401
+ addWellknownSite,
1402
+ upsertSiteRule,
1403
+ getRouterProvider: (name) => providers[name],
1334
1404
 
1335
1405
  getRoutingCrons: () => [
1336
1406
  {
1337
1407
  name: 'update-dashboard-certificate',
1338
1408
  time: '0 1 */6 * * *', // refetch on 0:00, 6:00, etc.
1339
- fn: () => updateDashboardCertificate({ checkExpire: true }),
1409
+ fn: () => updateDashboardCertificates(),
1340
1410
  options: { runOnInit: true },
1341
1411
  },
1342
- {
1343
- name: 'check-certificate-expiration',
1344
- time: '0 0 9 * * *', // check on 09:00 every day
1345
- fn: checkCertificatesExpiration,
1346
- options: { runOnInit: false },
1347
- },
1348
1412
  {
1349
1413
  name: 'rotate-log-files',
1350
- time: '5 0 0 * * *', // rotate at 05:00 every day
1414
+ time: '1 0 0 * * *', // rotate at 00:00:01 every day
1351
1415
  fn: rotateRouterLog,
1352
1416
  options: { runOnInit: false },
1353
1417
  },
1354
1418
  ],
1419
+
1420
+ addRoutingSite,
1421
+ deleteRoutingSite,
1422
+ updateRoutingSite,
1423
+ addRoutingRule,
1424
+ updateRoutingRule,
1425
+ deleteRoutingRule,
1426
+ addDomainAlias,
1427
+ deleteDomainAlias,
1428
+
1429
+ isGatewayCacheEnabled,
1355
1430
  };
1356
1431
  };
1357
1432
 
1358
- module.exports.attachInterfaceUrls = attachInterfaceUrls;
1433
+ module.exports.attachRuntimeDomainAliases = attachRuntimeDomainAliases;
1359
1434
  module.exports.ensureLatestNodeInfo = ensureLatestNodeInfo;
1360
1435
  module.exports.ensureLatestInterfaceInfo = ensureLatestInterfaceInfo;
1361
1436
  module.exports.ensureLatestInfo = ensureLatestInfo;