@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.
- package/lib/api/node.js +67 -69
- package/lib/api/team.js +386 -55
- package/lib/blocklet/downloader/blocklet-downloader.js +226 -0
- package/lib/blocklet/downloader/bundle-downloader.js +272 -0
- package/lib/blocklet/downloader/constants.js +3 -0
- package/lib/blocklet/downloader/resolve-download.js +199 -0
- package/lib/blocklet/extras.js +83 -26
- package/lib/blocklet/hooks.js +18 -65
- package/lib/blocklet/manager/base.js +10 -16
- package/lib/blocklet/manager/disk.js +1679 -1566
- package/lib/blocklet/manager/helper/install-application-from-backup.js +177 -0
- package/lib/blocklet/manager/helper/install-application-from-dev.js +94 -0
- package/lib/blocklet/manager/helper/install-application-from-general.js +188 -0
- package/lib/blocklet/manager/helper/install-component-from-dev.js +84 -0
- package/lib/blocklet/manager/helper/install-component-from-upload.js +181 -0
- package/lib/blocklet/manager/helper/install-component-from-url.js +173 -0
- package/lib/blocklet/manager/helper/migrate-application-to-struct-v2.js +450 -0
- package/lib/blocklet/manager/helper/rollback-cache.js +41 -0
- package/lib/blocklet/manager/helper/upgrade-components.js +152 -0
- package/lib/blocklet/migration.js +30 -52
- package/lib/blocklet/storage/backup/audit-log.js +27 -0
- package/lib/blocklet/storage/backup/base.js +62 -0
- package/lib/blocklet/storage/backup/blocklet-extras.js +92 -0
- package/lib/blocklet/storage/backup/blocklet.js +70 -0
- package/lib/blocklet/storage/backup/blocklets.js +74 -0
- package/lib/blocklet/storage/backup/data.js +19 -0
- package/lib/blocklet/storage/backup/logs.js +24 -0
- package/lib/blocklet/storage/backup/routing-rule.js +19 -0
- package/lib/blocklet/storage/backup/spaces.js +240 -0
- package/lib/blocklet/storage/restore/base.js +67 -0
- package/lib/blocklet/storage/restore/blocklet-extras.js +86 -0
- package/lib/blocklet/storage/restore/blocklet.js +56 -0
- package/lib/blocklet/storage/restore/blocklets.js +43 -0
- package/lib/blocklet/storage/restore/logs.js +21 -0
- package/lib/blocklet/storage/restore/spaces.js +156 -0
- package/lib/blocklet/storage/utils/hash.js +51 -0
- package/lib/blocklet/storage/utils/zip.js +43 -0
- package/lib/cert.js +206 -0
- package/lib/event.js +237 -64
- package/lib/index.js +191 -83
- package/lib/migrations/1.0.21-update-config.js +1 -1
- package/lib/migrations/1.0.22-max-memory.js +1 -1
- package/lib/migrations/1.0.25.js +1 -1
- package/lib/migrations/1.0.32-update-config.js +1 -1
- package/lib/migrations/1.0.33-blocklets.js +1 -1
- package/lib/migrations/1.5.20-registry.js +15 -0
- package/lib/migrations/1.6.17-blocklet-children.js +48 -0
- package/lib/migrations/1.6.21-rename-ip-echo-domain.js +35 -0
- package/lib/migrations/1.6.4-security.js +59 -0
- package/lib/migrations/1.6.5-security.js +60 -0
- package/lib/migrations/1.6.9-update-node-info-and-certificate.js +38 -0
- package/lib/migrations/1.7.1-blocklet-setup.js +18 -0
- package/lib/migrations/1.7.12-blocklet-meta.js +51 -0
- package/lib/migrations/1.7.15-blocklet-bundle-source.js +42 -0
- package/lib/migrations/1.7.20-blocklet-component.js +41 -0
- package/lib/migrations/1.8.33-blocklet-mem-limit.js +20 -0
- package/lib/migrations/README.md +1 -1
- package/lib/migrations/index.js +6 -2
- package/lib/monitor/blocklet-runtime-monitor.js +200 -0
- package/lib/monitor/get-history-list.js +37 -0
- package/lib/monitor/node-runtime-monitor.js +228 -0
- package/lib/router/helper.js +572 -497
- package/lib/router/index.js +85 -21
- package/lib/router/manager.js +146 -187
- package/lib/states/README.md +36 -1
- package/lib/states/access-key.js +39 -17
- package/lib/states/audit-log.js +462 -0
- package/lib/states/base.js +4 -213
- package/lib/states/blocklet-extras.js +194 -138
- package/lib/states/blocklet.js +361 -104
- package/lib/states/cache.js +8 -6
- package/lib/states/challenge.js +5 -5
- package/lib/states/index.js +19 -36
- package/lib/states/migration.js +4 -4
- package/lib/states/node.js +135 -46
- package/lib/states/notification.js +22 -35
- package/lib/states/session.js +17 -9
- package/lib/states/site.js +50 -25
- package/lib/states/user.js +74 -20
- package/lib/states/webhook.js +10 -6
- package/lib/team/manager.js +124 -7
- package/lib/util/blocklet.js +1223 -246
- package/lib/util/chain.js +1 -1
- package/lib/util/default-node-config.js +5 -23
- package/lib/util/disk-monitor.js +13 -10
- package/lib/util/domain-status.js +84 -15
- package/lib/util/get-accessible-external-node-ip.js +2 -2
- package/lib/util/get-domain-for-blocklet.js +13 -0
- package/lib/util/get-meta-from-url.js +33 -0
- package/lib/util/index.js +207 -272
- package/lib/util/ip.js +6 -0
- package/lib/util/maintain.js +233 -0
- package/lib/util/public-to-store.js +85 -0
- package/lib/util/ready.js +1 -1
- package/lib/util/requirement.js +28 -9
- package/lib/util/reset-node.js +22 -7
- package/lib/util/router.js +13 -0
- package/lib/util/rpc.js +16 -0
- package/lib/util/store.js +179 -0
- package/lib/util/sysinfo.js +44 -0
- package/lib/util/ua.js +54 -0
- package/lib/validators/blocklet-extra.js +24 -0
- package/lib/validators/node.js +25 -12
- package/lib/validators/permission.js +16 -1
- package/lib/validators/role.js +17 -3
- package/lib/validators/router.js +40 -20
- package/lib/validators/trusted-passport.js +1 -0
- package/lib/validators/util.js +22 -5
- package/lib/webhook/index.js +45 -35
- package/lib/webhook/sender/index.js +5 -0
- package/lib/webhook/sender/slack/index.js +1 -1
- package/lib/webhook/sender/wallet/index.js +48 -0
- package/package.json +54 -36
- package/lib/blocklet/registry.js +0 -205
- package/lib/states/https-cert.js +0 -67
- package/lib/util/get-ip-dns-domain-for-blocklet.js +0 -19
- package/lib/util/service.js +0 -66
- package/lib/util/upgrade.js +0 -178
- /package/lib/{queue.js → util/queue.js} +0 -0
package/lib/router/manager.js
CHANGED
|
@@ -8,10 +8,11 @@ const path = require('path');
|
|
|
8
8
|
const os = require('os');
|
|
9
9
|
const fse = require('fs-extra');
|
|
10
10
|
const get = require('lodash/get');
|
|
11
|
-
const
|
|
11
|
+
const toLower = require('lodash/toLower');
|
|
12
12
|
const { EventEmitter } = require('events');
|
|
13
13
|
const uuid = require('uuid');
|
|
14
14
|
const isUrl = require('is-url');
|
|
15
|
+
const joinUrl = require('url-join');
|
|
15
16
|
const cloneDeep = require('lodash/cloneDeep');
|
|
16
17
|
const normalizePathPrefix = require('@abtnode/util/lib/normalize-path-prefix');
|
|
17
18
|
const logger = require('@abtnode/logger')('@abtnode/core:router:manager');
|
|
@@ -27,7 +28,9 @@ const {
|
|
|
27
28
|
BLOCKLET_BUNDLE_FOLDER,
|
|
28
29
|
BLOCKLET_DYNAMIC_PATH_PREFIX,
|
|
29
30
|
BLOCKLET_INTERFACE_TYPE_WEB,
|
|
30
|
-
|
|
31
|
+
BlockletGroup,
|
|
32
|
+
} = require('@blocklet/constant');
|
|
33
|
+
const { forEachChildSync } = require('@blocklet/meta/lib/util');
|
|
31
34
|
|
|
32
35
|
const {
|
|
33
36
|
validateAddSite,
|
|
@@ -37,7 +40,8 @@ const {
|
|
|
37
40
|
validateUpdateSite,
|
|
38
41
|
} = require('../validators/router');
|
|
39
42
|
const { getProviderFromNodeInfo, findInterfaceByName, isCLI, findInterfacePortByName } = require('../util');
|
|
40
|
-
const {
|
|
43
|
+
const { findWebInterface } = require('../util/blocklet');
|
|
44
|
+
const { attachRuntimeDomainAliases, ensureLatestInfo } = require('./helper');
|
|
41
45
|
const Router = require('./index');
|
|
42
46
|
const states = require('../states');
|
|
43
47
|
|
|
@@ -70,8 +74,9 @@ const normalizeRedirectUrl = (url) => {
|
|
|
70
74
|
};
|
|
71
75
|
|
|
72
76
|
class RouterManager extends EventEmitter {
|
|
73
|
-
constructor() {
|
|
77
|
+
constructor({ certManager }) {
|
|
74
78
|
super();
|
|
79
|
+
this.certManager = certManager;
|
|
75
80
|
|
|
76
81
|
// HACK: do not emit any events from CLI
|
|
77
82
|
if (isCLI()) {
|
|
@@ -109,7 +114,7 @@ class RouterManager extends EventEmitter {
|
|
|
109
114
|
for (const rule of newSite.rules) {
|
|
110
115
|
this.fixRootBlockletRule(rule);
|
|
111
116
|
checkPathPrefixInBlackList(rule.from.pathPrefix, dynamicPathBlackList);
|
|
112
|
-
rules.push(...(await this.
|
|
117
|
+
rules.push(...(await this.getRulesForMutation(rule)));
|
|
113
118
|
}
|
|
114
119
|
}
|
|
115
120
|
newSite.rules = rules;
|
|
@@ -121,7 +126,7 @@ class RouterManager extends EventEmitter {
|
|
|
121
126
|
await this.validateRouterConfig('addRoutingSite', { site: newSite });
|
|
122
127
|
|
|
123
128
|
const result = await states.site.add(newSite);
|
|
124
|
-
await
|
|
129
|
+
await attachRuntimeDomainAliases({ sites: result, context, node: states.node });
|
|
125
130
|
|
|
126
131
|
this.emit('router.site.created', result);
|
|
127
132
|
return result;
|
|
@@ -150,20 +155,20 @@ class RouterManager extends EventEmitter {
|
|
|
150
155
|
|
|
151
156
|
// eslint-disable-next-line no-unused-vars
|
|
152
157
|
async updateRoutingSite(params, context = {}) {
|
|
153
|
-
await validateUpdateSite(params, context);
|
|
154
|
-
const
|
|
155
|
-
if (!
|
|
158
|
+
const site = await validateUpdateSite(params, context);
|
|
159
|
+
const existed = await states.site.findOne({ _id: site.id });
|
|
160
|
+
if (!existed) {
|
|
156
161
|
throw new Error('Can not update non-existing site');
|
|
157
162
|
}
|
|
158
163
|
|
|
159
164
|
const updateSet = {};
|
|
160
165
|
|
|
161
|
-
if (
|
|
162
|
-
updateSet.corsAllowedOrigins =
|
|
166
|
+
if (site.corsAllowedOrigins) {
|
|
167
|
+
updateSet.corsAllowedOrigins = site.corsAllowedOrigins.filter((x) => x !== '__none__');
|
|
163
168
|
}
|
|
164
169
|
|
|
165
|
-
if (
|
|
166
|
-
const newDomain =
|
|
170
|
+
if (site.domain) {
|
|
171
|
+
const newDomain = site.domain;
|
|
167
172
|
const ruleCountInDB = await states.site.count({
|
|
168
173
|
$or: [{ domain: newDomain }, { domainAliases: newDomain }, { 'domainAliases.value': newDomain }],
|
|
169
174
|
});
|
|
@@ -175,29 +180,47 @@ class RouterManager extends EventEmitter {
|
|
|
175
180
|
updateSet.domain = newDomain;
|
|
176
181
|
}
|
|
177
182
|
|
|
178
|
-
const updated = await states.site.update({ _id:
|
|
183
|
+
const updated = await states.site.update({ _id: site.id }, { $set: updateSet }, { multi: false, upsert: false });
|
|
179
184
|
|
|
180
|
-
logger.info('router.site.updated', {
|
|
181
|
-
this.emit('router.site.updated',
|
|
185
|
+
logger.info('router.site.updated', { site, updated });
|
|
186
|
+
this.emit('router.site.updated', site.id);
|
|
182
187
|
|
|
183
|
-
const dbSite = await states.site.findOne({ _id:
|
|
184
|
-
await
|
|
188
|
+
const dbSite = await states.site.findOne({ _id: site.id });
|
|
189
|
+
await attachRuntimeDomainAliases({ sites: dbSite, context, node: states.node });
|
|
185
190
|
return dbSite;
|
|
186
191
|
}
|
|
187
192
|
|
|
188
|
-
async addDomainAlias({ id, domainAlias }, context = {}) {
|
|
189
|
-
await validateAddDomainAlias(
|
|
193
|
+
async addDomainAlias({ id, domainAlias: tmpAlias, force }, context = {}) {
|
|
194
|
+
const domainAlias = await validateAddDomainAlias(tmpAlias, context);
|
|
190
195
|
const dbSite = await states.site.findOne({ _id: id });
|
|
191
196
|
if (!dbSite) {
|
|
192
197
|
throw new Error(`site ${id} does not exist`);
|
|
193
198
|
}
|
|
194
199
|
|
|
195
|
-
|
|
196
|
-
|
|
200
|
+
// check domain exists in site domain
|
|
201
|
+
const mainDomainSiteCount = await states.site.count({
|
|
202
|
+
domain: domainAlias,
|
|
197
203
|
});
|
|
198
204
|
|
|
199
|
-
if (
|
|
200
|
-
|
|
205
|
+
if (mainDomainSiteCount > 0) {
|
|
206
|
+
if (!force) {
|
|
207
|
+
throw new Error(`${domainAlias} already exists`);
|
|
208
|
+
} else {
|
|
209
|
+
throw new Error(`${domainAlias} cannot be forced-added`);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// check domain exists in site alias domain
|
|
214
|
+
const aliasDomainSite = await states.site.findOne({
|
|
215
|
+
$or: [{ domainAliases: domainAlias }, { 'domainAliases.value': domainAlias }],
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
if (aliasDomainSite) {
|
|
219
|
+
if (!force) {
|
|
220
|
+
throw new Error(`${domainAlias} already exists`);
|
|
221
|
+
} else {
|
|
222
|
+
await this.deleteDomainAlias({ id: aliasDomainSite.id, domainAlias });
|
|
223
|
+
}
|
|
201
224
|
}
|
|
202
225
|
|
|
203
226
|
const updateResult = await states.site.update(
|
|
@@ -207,13 +230,13 @@ class RouterManager extends EventEmitter {
|
|
|
207
230
|
logger.debug('add domain alias update result', { id, updateResult, domainAlias });
|
|
208
231
|
|
|
209
232
|
const newSite = await states.site.findOne({ _id: id });
|
|
210
|
-
await
|
|
233
|
+
await attachRuntimeDomainAliases({ sites: newSite, context, node: states.node });
|
|
211
234
|
|
|
212
235
|
return newSite;
|
|
213
236
|
}
|
|
214
237
|
|
|
215
|
-
async deleteDomainAlias({ id, domainAlias }, context = {}) {
|
|
216
|
-
await validateAddDomainAlias(
|
|
238
|
+
async deleteDomainAlias({ id, domainAlias: tmpAlias }, context = {}) {
|
|
239
|
+
const domainAlias = await validateAddDomainAlias(tmpAlias, context);
|
|
217
240
|
const dbSite = await states.site.findOne({ _id: id });
|
|
218
241
|
if (!dbSite) {
|
|
219
242
|
throw new Error(`site ${id} does not exist`);
|
|
@@ -221,22 +244,22 @@ class RouterManager extends EventEmitter {
|
|
|
221
244
|
|
|
222
245
|
dbSite.domainAliases = dbSite.domainAliases.filter((x) => {
|
|
223
246
|
if (typeof x === 'string') {
|
|
224
|
-
return x !== domainAlias;
|
|
247
|
+
return toLower(x) !== domainAlias;
|
|
225
248
|
}
|
|
226
249
|
|
|
227
|
-
return x.value !== domainAlias;
|
|
250
|
+
return toLower(x.value) !== domainAlias;
|
|
228
251
|
});
|
|
229
252
|
|
|
230
253
|
const updateResult = await states.site.update({ _id: id }, { $set: { domainAliases: dbSite.domainAliases } });
|
|
231
254
|
logger.debug('remove domain alias update result', { id, updateResult, domainAlias });
|
|
232
255
|
|
|
233
|
-
await
|
|
256
|
+
await attachRuntimeDomainAliases({ sites: dbSite, context, node: states.node });
|
|
234
257
|
|
|
235
258
|
return dbSite;
|
|
236
259
|
}
|
|
237
260
|
|
|
238
|
-
async addRoutingRule({ id, rule, skipCheckDynamicBlacklist = false }, context = {}) {
|
|
239
|
-
await validateAddRule({ id, rule }, context);
|
|
261
|
+
async addRoutingRule({ id, rule: tempRule, skipCheckDynamicBlacklist = false }, context = {}) {
|
|
262
|
+
const { rule } = await validateAddRule({ id, rule: tempRule }, context);
|
|
240
263
|
const dbSite = await states.site.findOne({ _id: id });
|
|
241
264
|
if (!dbSite) {
|
|
242
265
|
throw new Error(`site ${id} does not exist`);
|
|
@@ -260,19 +283,19 @@ class RouterManager extends EventEmitter {
|
|
|
260
283
|
await this.validateRouterConfig('addRoutingRule', { id, rule });
|
|
261
284
|
|
|
262
285
|
// add child blocklet rules
|
|
263
|
-
for (const x of await this.
|
|
286
|
+
for (const x of await this.getRulesForMutation(rule)) {
|
|
264
287
|
await states.site.addRuleToSite(id, x);
|
|
265
288
|
}
|
|
266
289
|
|
|
267
290
|
const newSite = await states.site.findOne({ _id: id });
|
|
268
|
-
await
|
|
291
|
+
await attachRuntimeDomainAliases({ sites: newSite, context, node: states.node });
|
|
269
292
|
|
|
270
293
|
this.emit('router.rule.created', newSite);
|
|
271
294
|
return newSite;
|
|
272
295
|
}
|
|
273
296
|
|
|
274
|
-
async updateRoutingRule({ id, rule, skipProtectedRuleChecking = false }, context = {}) {
|
|
275
|
-
await validateEditRule({ id, rule }, context);
|
|
297
|
+
async updateRoutingRule({ id, rule: tmpRule, skipProtectedRuleChecking = false }, context = {}) {
|
|
298
|
+
const { rule } = await validateEditRule({ id, rule: tmpRule }, context);
|
|
276
299
|
const dbSite = await states.site.findOne({ _id: id, 'rules.id': rule.id });
|
|
277
300
|
if (!dbSite) {
|
|
278
301
|
throw new Error(`site ${id}, rule ${rule.id} does not exist`);
|
|
@@ -300,13 +323,16 @@ class RouterManager extends EventEmitter {
|
|
|
300
323
|
await this.validateRouterConfig('updateRoutingRule', { id, rule });
|
|
301
324
|
|
|
302
325
|
// update rules
|
|
303
|
-
const newRules = [
|
|
326
|
+
const newRules = [
|
|
327
|
+
...dbSite.rules.filter((x) => (x.groupId && x.groupId !== rule.id) || x.id !== rule.id), // 有些路由没有 rule.groupId
|
|
328
|
+
...(await this.getRulesForMutation(rule)),
|
|
329
|
+
];
|
|
304
330
|
|
|
305
331
|
const updateResult = await states.site.update({ _id: id }, { $set: { rules: newRules } });
|
|
306
332
|
logger.info('update result', { updateResult });
|
|
307
333
|
const newSite = await states.site.findOne({ _id: id });
|
|
308
334
|
|
|
309
|
-
await
|
|
335
|
+
await attachRuntimeDomainAliases({ sites: newSite, context, node: states.node });
|
|
310
336
|
|
|
311
337
|
this.emit('router.rule.updated', newSite);
|
|
312
338
|
|
|
@@ -335,64 +361,12 @@ class RouterManager extends EventEmitter {
|
|
|
335
361
|
logger.info('router.rule.removed', { id, ruleId });
|
|
336
362
|
const newSite = await states.site.findOne({ _id: id });
|
|
337
363
|
|
|
338
|
-
await
|
|
364
|
+
await attachRuntimeDomainAliases({ sites: newSite, context, node: states.node });
|
|
339
365
|
|
|
340
366
|
this.emit('router.rule.removed', newSite);
|
|
341
367
|
return newSite;
|
|
342
368
|
}
|
|
343
369
|
|
|
344
|
-
// eslint-disable-next-line no-unused-vars
|
|
345
|
-
async addCertificate(entity, context = {}) {
|
|
346
|
-
entity.type = 'nginx';
|
|
347
|
-
this.fixCertificate(entity);
|
|
348
|
-
|
|
349
|
-
this.validateCertificate(entity);
|
|
350
|
-
|
|
351
|
-
const info = Certificate.fromPEM(entity.certificate);
|
|
352
|
-
const domain = get(info, 'subject.commonName', '');
|
|
353
|
-
|
|
354
|
-
const cert = await states.certificate.find({ domain });
|
|
355
|
-
const hasOne = cert && cert.length;
|
|
356
|
-
|
|
357
|
-
if (hasOne) {
|
|
358
|
-
throw new Error('certificate has exists!');
|
|
359
|
-
}
|
|
360
|
-
|
|
361
|
-
const newCert = await states.certificate.upsert({
|
|
362
|
-
...entity,
|
|
363
|
-
domain,
|
|
364
|
-
});
|
|
365
|
-
logger.info('add certificate result', { domain: newCert.domain });
|
|
366
|
-
this.emit('router.certificate.add', { type: 'nginx', data: newCert });
|
|
367
|
-
}
|
|
368
|
-
|
|
369
|
-
// eslint-disable-next-line no-unused-vars
|
|
370
|
-
async deleteCertificate({ id }, context = {}) {
|
|
371
|
-
const tmpCert = await states.certificate.find({ _id: id });
|
|
372
|
-
if (!tmpCert) {
|
|
373
|
-
throw new Error('certificate does not exist');
|
|
374
|
-
}
|
|
375
|
-
|
|
376
|
-
const removeResult = await states.certificate.remove({ _id: id });
|
|
377
|
-
|
|
378
|
-
logger.info('delete certificate', { removeResult, domain: tmpCert.domain });
|
|
379
|
-
this.emit('router.certificate.remove', { type: 'nginx', data: { domain: tmpCert.domain } });
|
|
380
|
-
return {};
|
|
381
|
-
}
|
|
382
|
-
|
|
383
|
-
// eslint-disable-next-line no-unused-vars
|
|
384
|
-
async updateNginxHttpsCert(entity, context = {}) {
|
|
385
|
-
entity.type = 'nginx';
|
|
386
|
-
this.fixCertificate(entity);
|
|
387
|
-
this.validateCertificate(entity, entity.domain);
|
|
388
|
-
const dbEntity = await states.certificate.upsert(entity);
|
|
389
|
-
this.emit('router.certificate.updated', { type: 'nginx', data: dbEntity });
|
|
390
|
-
}
|
|
391
|
-
|
|
392
|
-
findCertificateByDomain(domain) {
|
|
393
|
-
return states.certificate.findOne({ domain });
|
|
394
|
-
}
|
|
395
|
-
|
|
396
370
|
// ============================================================================================
|
|
397
371
|
// Internal API that are used by public APIs and called from CLI
|
|
398
372
|
// ============================================================================================
|
|
@@ -448,16 +422,16 @@ class RouterManager extends EventEmitter {
|
|
|
448
422
|
}
|
|
449
423
|
|
|
450
424
|
async getMatchedCert(domain) {
|
|
451
|
-
const certs = await
|
|
425
|
+
const certs = await this.certManager.getAllNormal();
|
|
452
426
|
const matchedCert = certs.find((cert) => this.isCertMatchedDomain(cert, domain));
|
|
453
427
|
|
|
454
428
|
if (matchedCert) {
|
|
455
429
|
return {
|
|
456
430
|
id: matchedCert.id,
|
|
457
431
|
domain: matchedCert.domain,
|
|
458
|
-
issuer: matchedCert.issuer,
|
|
459
|
-
validFrom: matchedCert.validFrom,
|
|
460
|
-
validTo: matchedCert.validTo,
|
|
432
|
+
issuer: matchedCert.meta.issuer,
|
|
433
|
+
validFrom: matchedCert.meta.validFrom,
|
|
434
|
+
validTo: matchedCert.meta.validTo,
|
|
461
435
|
};
|
|
462
436
|
}
|
|
463
437
|
|
|
@@ -522,10 +496,7 @@ class RouterManager extends EventEmitter {
|
|
|
522
496
|
const info = await states.node.read();
|
|
523
497
|
const httpsEnabled = get(info, 'routing.https', true);
|
|
524
498
|
|
|
525
|
-
const
|
|
526
|
-
const certificates = httpsEnabled
|
|
527
|
-
? await states.certificate.find({ type: get(info, 'routing.provider', null) }, selection)
|
|
528
|
-
: [];
|
|
499
|
+
const certificates = httpsEnabled ? await this.certManager.getAllNormal() : [];
|
|
529
500
|
|
|
530
501
|
let sites = await states.site.getSites();
|
|
531
502
|
// mutate data by input
|
|
@@ -570,14 +541,14 @@ class RouterManager extends EventEmitter {
|
|
|
570
541
|
// get provider
|
|
571
542
|
const providerName = getProviderFromNodeInfo(info);
|
|
572
543
|
const Provider = getProvider(providerName);
|
|
573
|
-
const
|
|
574
|
-
const provider = new Provider({
|
|
544
|
+
const tmpDir = path.join(os.tmpdir(), `${providerName}-${Date.now()}`);
|
|
545
|
+
const provider = new Provider({ configDir: tmpDir });
|
|
575
546
|
const tempRouter = new Router({
|
|
576
547
|
provider,
|
|
577
548
|
getRoutingParams: async () => ({
|
|
578
549
|
sites: await ensureLatestInfo(sites),
|
|
579
550
|
certificates,
|
|
580
|
-
|
|
551
|
+
commonHeaders: get(info, 'routing.headers', {}),
|
|
581
552
|
services: [], // TODO: do we need to add some item here?
|
|
582
553
|
nodeInfo: info,
|
|
583
554
|
}),
|
|
@@ -586,47 +557,14 @@ class RouterManager extends EventEmitter {
|
|
|
586
557
|
await tempRouter.updateRoutingTable();
|
|
587
558
|
try {
|
|
588
559
|
await tempRouter.validateConfig();
|
|
589
|
-
await fse.remove(
|
|
560
|
+
await fse.remove(tmpDir);
|
|
590
561
|
} catch (error) {
|
|
562
|
+
// 如果出错,保留 Nginx 配置文件,方便定位问题
|
|
591
563
|
logger.error('validate router config failed', { error, action, data });
|
|
592
|
-
await fse.remove(tempPath);
|
|
593
564
|
throw error;
|
|
594
565
|
}
|
|
595
566
|
}
|
|
596
567
|
|
|
597
|
-
validateCertificate(cert, domain) {
|
|
598
|
-
const certificate = Certificate.fromPEM(cert.certificate);
|
|
599
|
-
const privateKey = PrivateKey.fromPEM(cert.privateKey);
|
|
600
|
-
|
|
601
|
-
const data = Buffer.allocUnsafe(100);
|
|
602
|
-
const signature = privateKey.sign(data, 'sha512');
|
|
603
|
-
if (!certificate.publicKey.verify(data, signature, 'sha512')) {
|
|
604
|
-
throw new Error('Invalid certificate: signature verify failed');
|
|
605
|
-
}
|
|
606
|
-
|
|
607
|
-
const certDomain = get(certificate, 'subject.commonName', '');
|
|
608
|
-
if (domain && domain !== certDomain) {
|
|
609
|
-
throw new Error('Invalid certificate: domain does not match');
|
|
610
|
-
}
|
|
611
|
-
|
|
612
|
-
const validFrom = get(certificate, 'validFrom', '');
|
|
613
|
-
if (!validFrom || new Date(validFrom).getTime() > Date.now()) {
|
|
614
|
-
throw new Error('Invalid certificate: not in valid period');
|
|
615
|
-
}
|
|
616
|
-
const validTo = get(certificate, 'validTo', '');
|
|
617
|
-
if (!validTo || new Date(validTo).getTime() < Date.now()) {
|
|
618
|
-
throw new Error('Invalid certificate: not in valid period');
|
|
619
|
-
}
|
|
620
|
-
|
|
621
|
-
return certificate;
|
|
622
|
-
}
|
|
623
|
-
|
|
624
|
-
fixCertificate(entity) {
|
|
625
|
-
// Hack: this logic exists because gql does not allow line breaks in arg values
|
|
626
|
-
entity.privateKey = entity.privateKey.split('|').join('\n');
|
|
627
|
-
entity.certificate = entity.certificate.split('|').join('\n');
|
|
628
|
-
}
|
|
629
|
-
|
|
630
568
|
fixRootBlockletRule(rule) {
|
|
631
569
|
if (!rule.id) {
|
|
632
570
|
rule.id = uuid.v4();
|
|
@@ -634,9 +572,9 @@ class RouterManager extends EventEmitter {
|
|
|
634
572
|
rule.groupId = rule.id;
|
|
635
573
|
rule.from.pathPrefix = normalizePathPrefix(rule.from.pathPrefix);
|
|
636
574
|
if (rule.to.type === ROUTING_RULE_TYPES.BLOCKLET) {
|
|
637
|
-
|
|
638
|
-
rule.
|
|
639
|
-
rule.to.
|
|
575
|
+
// pathPrefix of root blocklet maybe changed to another prefix than '/', so use old groupPathPrefix first
|
|
576
|
+
rule.from.groupPathPrefix = rule.from.groupPathPrefix || rule.from.pathPrefix;
|
|
577
|
+
rule.to.componentId = rule.to.did;
|
|
640
578
|
}
|
|
641
579
|
if (rule.to.url) {
|
|
642
580
|
rule.to.url = normalizeRedirectUrl(rule.to.url);
|
|
@@ -645,64 +583,85 @@ class RouterManager extends EventEmitter {
|
|
|
645
583
|
|
|
646
584
|
/**
|
|
647
585
|
* get all rules to be add or update to site from root rule
|
|
648
|
-
* @param {*}
|
|
586
|
+
* @param {*} rootRule
|
|
649
587
|
*/
|
|
650
|
-
async
|
|
651
|
-
if (
|
|
652
|
-
return [
|
|
588
|
+
async getRulesForMutation(rootRule) {
|
|
589
|
+
if (rootRule.to.type !== ROUTING_RULE_TYPES.BLOCKLET) {
|
|
590
|
+
return [rootRule];
|
|
653
591
|
}
|
|
654
592
|
|
|
655
593
|
const rules = [];
|
|
656
|
-
let occupied = false;
|
|
657
594
|
|
|
658
595
|
// get child rules
|
|
659
|
-
const blocklet = await states.blocklet.getBlocklet(
|
|
660
|
-
for (const childMeta of blocklet.meta.children || []) {
|
|
661
|
-
for (const mountPoint of childMeta.mountPoints) {
|
|
662
|
-
const child = blocklet.children.find((b) => b.meta.name === childMeta.name);
|
|
663
|
-
|
|
664
|
-
if (!child) {
|
|
665
|
-
logger.error(`Child blocklet ${childMeta.name} does not exist`);
|
|
666
|
-
// eslint-disable-next-line no-continue
|
|
667
|
-
continue;
|
|
668
|
-
}
|
|
596
|
+
const blocklet = await states.blocklet.getBlocklet(rootRule.to.did);
|
|
669
597
|
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
if (isRootPath) {
|
|
674
|
-
occupied = true;
|
|
675
|
-
}
|
|
598
|
+
// blocklet may be mounted in relative prefix (for old usage), so blockletPrefix may not be '/'
|
|
599
|
+
// blocklet prefix is the origin pathPrefix in rootRule
|
|
600
|
+
const blockletPrefix = normalizePathPrefix(rootRule.from.pathPrefix);
|
|
676
601
|
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
pathPrefix: normalizePathPrefix(pathPrefix),
|
|
683
|
-
groupPathPrefix: rule.from.pathPrefix,
|
|
684
|
-
},
|
|
685
|
-
to: {
|
|
686
|
-
type: ROUTING_RULE_TYPES.BLOCKLET,
|
|
687
|
-
port: findInterfacePortByName(child, mountPoint.child.interfaceName),
|
|
688
|
-
did: rule.to.did, // root blocklet did
|
|
689
|
-
interfaceName: rule.to.interfaceName, // root blocklet interface
|
|
690
|
-
realDid: child.meta.did, // child blocklet did
|
|
691
|
-
realInterfaceName: mountPoint.child.interfaceName,
|
|
692
|
-
},
|
|
693
|
-
isProtected: isRootPath ? rule.isProtected : true,
|
|
694
|
-
};
|
|
695
|
-
|
|
696
|
-
rules.push(childRule);
|
|
697
|
-
}
|
|
698
|
-
}
|
|
699
|
-
}
|
|
602
|
+
// root component's mountPoint may not be '/'
|
|
603
|
+
const rootComponentPrefix = joinUrl(blockletPrefix, blocklet.mountPoint || '/');
|
|
604
|
+
rootRule.from.pathPrefix = normalizePathPrefix(rootComponentPrefix);
|
|
605
|
+
|
|
606
|
+
const isOccupiable = blocklet.meta.group === BlockletGroup.gateway;
|
|
700
607
|
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
rules.push(rule);
|
|
608
|
+
if (!isOccupiable) {
|
|
609
|
+
rules.push(rootRule);
|
|
704
610
|
}
|
|
705
611
|
|
|
612
|
+
forEachChildSync(blocklet, (component, { id, ancestors }) => {
|
|
613
|
+
if (component.meta.group === BlockletGroup.gateway) {
|
|
614
|
+
return;
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
const { mountPoint } = component;
|
|
618
|
+
if (!mountPoint) {
|
|
619
|
+
logger.error(`mountPoint of child ${component.meta.name} does not exist`);
|
|
620
|
+
// eslint-disable-next-line no-continue
|
|
621
|
+
return;
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
const childWebInterface = findWebInterface(component);
|
|
625
|
+
if (!childWebInterface) {
|
|
626
|
+
logger.error(`web interface of child ${component.meta.name} does not exist`);
|
|
627
|
+
// eslint-disable-next-line no-continue
|
|
628
|
+
return;
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
const pathPrefix = path.join(
|
|
632
|
+
blockletPrefix,
|
|
633
|
+
// level 1 component should be independent with root component
|
|
634
|
+
...ancestors.slice(1).map((x) => x.mountPoint || ''),
|
|
635
|
+
mountPoint
|
|
636
|
+
);
|
|
637
|
+
|
|
638
|
+
const occupied = normalizePathPrefix(pathPrefix) === normalizePathPrefix(rootRule.from.pathPrefix);
|
|
639
|
+
|
|
640
|
+
if (occupied && !isOccupiable) {
|
|
641
|
+
return;
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
// if is root path, child rule become root rule
|
|
645
|
+
const childRule = {
|
|
646
|
+
id: occupied ? rootRule.id : uuid.v4(),
|
|
647
|
+
groupId: rootRule.id,
|
|
648
|
+
from: {
|
|
649
|
+
pathPrefix: normalizePathPrefix(pathPrefix),
|
|
650
|
+
groupPathPrefix: blockletPrefix,
|
|
651
|
+
},
|
|
652
|
+
to: {
|
|
653
|
+
type: ROUTING_RULE_TYPES.BLOCKLET,
|
|
654
|
+
port: findInterfacePortByName(component, childWebInterface.name),
|
|
655
|
+
did: rootRule.to.did, // root component did
|
|
656
|
+
interfaceName: rootRule.to.interfaceName, // root component interface
|
|
657
|
+
componentId: id,
|
|
658
|
+
},
|
|
659
|
+
isProtected: occupied ? rootRule.isProtected : true,
|
|
660
|
+
};
|
|
661
|
+
|
|
662
|
+
rules.push(childRule);
|
|
663
|
+
});
|
|
664
|
+
|
|
706
665
|
return rules;
|
|
707
666
|
}
|
|
708
667
|
}
|
package/lib/states/README.md
CHANGED
|
@@ -1,3 +1,38 @@
|
|
|
1
1
|
# State DB
|
|
2
2
|
|
|
3
|
-
All
|
|
3
|
+
All Blocklet Server states are managed by files in this folder.
|
|
4
|
+
|
|
5
|
+
## blocklet
|
|
6
|
+
|
|
7
|
+
- `meta` meta of bundle _defined in @blocklet/meta/schema_
|
|
8
|
+
- `did`, `name`: component id, default from source meta, can be changed before install
|
|
9
|
+
- `title`, `description`: component info, default from source meta, can be changed before install
|
|
10
|
+
- `bundleDdid`, `bundleName`: bundle id, copy from source meta
|
|
11
|
+
- `version`: component version
|
|
12
|
+
- `price`: used for charging
|
|
13
|
+
- `logo`: component logo
|
|
14
|
+
- `logoUrl`: component logo (from store)
|
|
15
|
+
- `interfaces`: for resolving: runtime port; service config; web base prefix; api base prefix
|
|
16
|
+
- `environments`: component environment
|
|
17
|
+
- `scripts`: for hook of component lifecycle
|
|
18
|
+
- `engine`, `timeout`, `group`, `main`: related to process startup
|
|
19
|
+
- `dist`: bundle source
|
|
20
|
+
- `theme`: app theme
|
|
21
|
+
- `navigation`: app navigation`
|
|
22
|
+
- `source`: type of bundle source
|
|
23
|
+
- `bundleSource`: download infomation of bundle srouce
|
|
24
|
+
- `deployedFrom`: extra information of bundle source
|
|
25
|
+
- `appDid`: app instance id _app only_
|
|
26
|
+
- `status`, `startedAt`, `installedAt`, `stoppedAt` app status
|
|
27
|
+
- `deletedAt` _component only_
|
|
28
|
+
- `mode` app mode
|
|
29
|
+
- `ports` runtime port resolved from meta
|
|
30
|
+
- `<ENV_NAME>`: `<port number>`
|
|
31
|
+
- `environments[]`: env variables generated by server, e.g. BLOCKLET_APP_SK, BLOCKLET_APP_DID
|
|
32
|
+
- `<key>`: `<value>`
|
|
33
|
+
- `children`: component of app, same structure as parent, can be nested
|
|
34
|
+
- `mountPoint` mount path of web site. _component only_
|
|
35
|
+
- `dynamic` is dynamically added. _component only_
|
|
36
|
+
- `controller`
|
|
37
|
+
- `tokens`
|
|
38
|
+
- `downloadTokenList`
|