@abtnode/core 1.6.6 → 1.6.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/blocklet/manager/disk.js +56 -15
- package/lib/cert.js +124 -0
- package/lib/event.js +9 -2
- package/lib/index.js +30 -10
- package/lib/migrations/1.6.7-certificate.js +30 -0
- package/lib/migrations/1.6.9-update-node-info-and-certificate.js +38 -0
- package/lib/router/helper.js +132 -115
- package/lib/router/manager.js +9 -8
- package/lib/states/base.js +3 -220
- package/lib/states/index.js +4 -21
- package/lib/states/node.js +10 -1
- package/lib/util/blocklet.js +29 -4
- package/lib/util/{get-ip-dns-domain-for-blocklet.js → get-domain-for-blocklet.js} +5 -1
- package/lib/util/index.js +33 -19
- package/lib/util/ready.js +1 -1
- package/lib/webhook/index.js +3 -3
- package/package.json +22 -20
package/lib/router/helper.js
CHANGED
|
@@ -21,15 +21,13 @@ const {
|
|
|
21
21
|
NAME_FOR_WELLKNOWN_SITE,
|
|
22
22
|
DEFAULT_HTTP_PORT,
|
|
23
23
|
DEFAULT_HTTPS_PORT,
|
|
24
|
-
DAY_IN_MS,
|
|
25
24
|
NODE_MODES,
|
|
26
25
|
ROUTING_RULE_TYPES,
|
|
27
26
|
CERTIFICATE_EXPIRES_OFFSET,
|
|
28
|
-
CERTIFICATE_EXPIRES_WARNING_OFFSET,
|
|
29
|
-
DEFAULT_DAEMON_PORT,
|
|
30
27
|
DEFAULT_SERVICE_PATH,
|
|
31
28
|
SLOT_FOR_IP_DNS_SITE,
|
|
32
29
|
BLOCKLET_SITE_GROUP_SUFFIX,
|
|
30
|
+
WELLKNOWN_ACME_CHALLENGE_PREFIX,
|
|
33
31
|
} = require('@abtnode/constant');
|
|
34
32
|
const {
|
|
35
33
|
BLOCKLET_DYNAMIC_PATH_PREFIX,
|
|
@@ -50,7 +48,7 @@ const {
|
|
|
50
48
|
getWellknownSitePort,
|
|
51
49
|
} = require('../util');
|
|
52
50
|
const { getServicesFromBlockletInterface } = require('../util/service');
|
|
53
|
-
const getIpDnsDomainForBlocklet = require('../util/get-
|
|
51
|
+
const { getIpDnsDomainForBlocklet, getDidDomainForBlocklet } = require('../util/get-domain-for-blocklet');
|
|
54
52
|
const { getFromCache: getAccessibleExternalNodeIp } = require('../util/get-accessible-external-node-ip');
|
|
55
53
|
|
|
56
54
|
const Router = require('./index');
|
|
@@ -403,11 +401,10 @@ const decompressCertificates = async (source, dest) => {
|
|
|
403
401
|
return dest;
|
|
404
402
|
};
|
|
405
403
|
|
|
406
|
-
module.exports = function getRouterHelpers({ dataDirs, routingSnapshot, routerManager, blockletManager }) {
|
|
404
|
+
module.exports = function getRouterHelpers({ dataDirs, routingSnapshot, routerManager, blockletManager, certManager }) {
|
|
407
405
|
const nodeState = states.node;
|
|
408
406
|
const blockletState = states.blocklet;
|
|
409
407
|
const siteState = states.site;
|
|
410
|
-
const httpsCertState = states.certificate;
|
|
411
408
|
const notification = states.notification;
|
|
412
409
|
|
|
413
410
|
// site level duplication detection
|
|
@@ -468,58 +465,105 @@ module.exports = function getRouterHelpers({ dataDirs, routingSnapshot, routerMa
|
|
|
468
465
|
const certificate = fs.readFileSync(certificateFilePath).toString();
|
|
469
466
|
const privateKey = fs.readFileSync(privateKeyFilePath).toString();
|
|
470
467
|
|
|
471
|
-
await
|
|
468
|
+
await certManager.upsertByDomain({
|
|
469
|
+
domain: dashboardDomain,
|
|
470
|
+
privateKey,
|
|
471
|
+
certificate,
|
|
472
|
+
isProtected: true,
|
|
473
|
+
});
|
|
472
474
|
logger.info('dashboard certificate updated');
|
|
473
475
|
} catch (error) {
|
|
474
|
-
logger.error('
|
|
476
|
+
logger.error('update dashboard certificate failed', { error });
|
|
477
|
+
throw error;
|
|
475
478
|
} finally {
|
|
476
479
|
fs.removeSync(destFolder);
|
|
477
480
|
}
|
|
478
481
|
};
|
|
479
482
|
|
|
483
|
+
const ensureDashboardCertificate = async () => {
|
|
484
|
+
const info = await nodeState.read();
|
|
485
|
+
const downloadUrl = get(info, 'routing.dashboardCertDownloadAddress', '');
|
|
486
|
+
const dashboardDomain = get(info, 'routing.dashboardDomain', '');
|
|
487
|
+
if (!dashboardDomain || !downloadUrl) {
|
|
488
|
+
throw new Error('dashboardCertDownloadAddress and dashboardDomain are not found in the routing configs');
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
const cert = await certManager.getByDomain(dashboardDomain);
|
|
492
|
+
if (cert) {
|
|
493
|
+
return { status: 'existed' };
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
logger.debug('downloading certificate', { url: downloadUrl, dashboardDomain });
|
|
497
|
+
await updateDashboardCertificate({ checkExpire: false });
|
|
498
|
+
logger.debug('downloading certificate', { url: downloadUrl, dashboardDomain });
|
|
499
|
+
return { status: 'downloaded' };
|
|
500
|
+
};
|
|
501
|
+
|
|
480
502
|
const addWellknownSite = async (sites, context) => {
|
|
481
503
|
const site = (sites || []).find((x) => x.name === NAME_FOR_WELLKNOWN_SITE);
|
|
482
504
|
|
|
483
505
|
try {
|
|
484
|
-
const
|
|
506
|
+
const info = await nodeState.read();
|
|
507
|
+
const proxyTarget = {
|
|
508
|
+
port: info.port,
|
|
509
|
+
type: ROUTING_RULE_TYPES.GENERAL_PROXY,
|
|
510
|
+
interfaceName: BLOCKLET_INTERFACE_WELLKNOWN,
|
|
511
|
+
};
|
|
512
|
+
|
|
485
513
|
const didResolverWellknownRule = {
|
|
486
|
-
from: { pathPrefix },
|
|
487
|
-
to:
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
},
|
|
514
|
+
from: { pathPrefix: joinUrl(WELLKNOWN_PATH_PREFIX, '/did.json') },
|
|
515
|
+
to: proxyTarget,
|
|
516
|
+
};
|
|
517
|
+
|
|
518
|
+
const acmeChallengeWellknownRule = {
|
|
519
|
+
from: { pathPrefix: WELLKNOWN_ACME_CHALLENGE_PREFIX },
|
|
520
|
+
to: proxyTarget,
|
|
492
521
|
};
|
|
493
522
|
|
|
494
523
|
if (site) {
|
|
495
|
-
|
|
496
|
-
|
|
524
|
+
let changed = false;
|
|
525
|
+
const exists = (prefix) => !!site.rules.find((r) => r.from.pathPrefix === normalizePathPrefix(prefix));
|
|
526
|
+
|
|
527
|
+
if (!exists(didResolverWellknownRule.from.pathPrefix)) {
|
|
528
|
+
await routerManager.addRoutingRule(
|
|
529
|
+
{
|
|
530
|
+
id: site.id,
|
|
531
|
+
rule: didResolverWellknownRule,
|
|
532
|
+
},
|
|
533
|
+
context
|
|
534
|
+
);
|
|
535
|
+
changed = true;
|
|
497
536
|
}
|
|
498
537
|
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
context
|
|
505
|
-
);
|
|
506
|
-
} else {
|
|
507
|
-
await routerManager.addRoutingSite(
|
|
508
|
-
{
|
|
509
|
-
site: {
|
|
510
|
-
domain: DOMAIN_FOR_INTERNAL_SITE,
|
|
511
|
-
port: await getWellknownSitePort(),
|
|
512
|
-
name: NAME_FOR_WELLKNOWN_SITE,
|
|
513
|
-
rules: [didResolverWellknownRule],
|
|
514
|
-
isProtected: true,
|
|
538
|
+
if (!exists(acmeChallengeWellknownRule.from.pathPrefix)) {
|
|
539
|
+
await routerManager.addRoutingRule(
|
|
540
|
+
{
|
|
541
|
+
id: site.id,
|
|
542
|
+
rule: acmeChallengeWellknownRule,
|
|
515
543
|
},
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
544
|
+
context
|
|
545
|
+
);
|
|
546
|
+
changed = true;
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
return changed;
|
|
521
550
|
}
|
|
522
551
|
|
|
552
|
+
await routerManager.addRoutingSite(
|
|
553
|
+
{
|
|
554
|
+
site: {
|
|
555
|
+
domain: DOMAIN_FOR_INTERNAL_SITE,
|
|
556
|
+
port: await getWellknownSitePort(),
|
|
557
|
+
name: NAME_FOR_WELLKNOWN_SITE,
|
|
558
|
+
rules: [didResolverWellknownRule, acmeChallengeWellknownRule],
|
|
559
|
+
isProtected: true,
|
|
560
|
+
},
|
|
561
|
+
skipCheckDynamicBlacklist: true,
|
|
562
|
+
skipValidation: true,
|
|
563
|
+
},
|
|
564
|
+
context
|
|
565
|
+
);
|
|
566
|
+
|
|
523
567
|
return true;
|
|
524
568
|
} catch (err) {
|
|
525
569
|
console.error('add well-known site failed:', err);
|
|
@@ -533,7 +577,7 @@ module.exports = function getRouterHelpers({ dataDirs, routingSnapshot, routerMa
|
|
|
533
577
|
*
|
|
534
578
|
* @returns {boolean} if routing changed
|
|
535
579
|
*/
|
|
536
|
-
const ensureDashboardRouting = async (context = {}
|
|
580
|
+
const ensureDashboardRouting = async (context = {}) => {
|
|
537
581
|
const info = await nodeState.read();
|
|
538
582
|
|
|
539
583
|
const provider = getProviderFromNodeInfo(info);
|
|
@@ -579,11 +623,16 @@ module.exports = function getRouterHelpers({ dataDirs, routingSnapshot, routerMa
|
|
|
579
623
|
});
|
|
580
624
|
|
|
581
625
|
const dashboardDomain = get(info, 'routing.dashboardDomain', '');
|
|
582
|
-
|
|
626
|
+
const didDomain = `${info.did.toLowerCase()}.${info.didDomain}`;
|
|
627
|
+
const dashboardAliasDomains = [dashboardDomain, didDomain]
|
|
628
|
+
.filter((item) => item && !isExistsInAlias(item))
|
|
629
|
+
.map((item) => ({ value: item, isProtected: true }));
|
|
630
|
+
|
|
631
|
+
if (dashboardAliasDomains.length > 0) {
|
|
583
632
|
try {
|
|
584
633
|
const result = await siteState.update(
|
|
585
634
|
{ _id: dashboardSite.id },
|
|
586
|
-
{ $push: { domainAliases: {
|
|
635
|
+
{ $push: { domainAliases: { $each: dashboardAliasDomains } } }
|
|
587
636
|
);
|
|
588
637
|
|
|
589
638
|
updatedResult.push(result);
|
|
@@ -618,18 +667,6 @@ module.exports = function getRouterHelpers({ dataDirs, routingSnapshot, routerMa
|
|
|
618
667
|
updatedResult.push(wellknownRes);
|
|
619
668
|
}
|
|
620
669
|
|
|
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
670
|
if (updatedResult.length) {
|
|
634
671
|
const hash = await takeRoutingSnapshot({ message: 'ensure dashboard routing rules', dryRun: false }, context);
|
|
635
672
|
logger.info('take routing snapshot on ensure dashboard routing rules', { updatedResult, hash });
|
|
@@ -658,7 +695,7 @@ module.exports = function getRouterHelpers({ dataDirs, routingSnapshot, routerMa
|
|
|
658
695
|
};
|
|
659
696
|
|
|
660
697
|
const domainGroup = `${blocklet.meta.did}${BLOCKLET_SITE_GROUP_SUFFIX}`;
|
|
661
|
-
|
|
698
|
+
|
|
662
699
|
const pathPrefix = getPrefix(webInterface.prefix);
|
|
663
700
|
const rule = {
|
|
664
701
|
from: { pathPrefix },
|
|
@@ -673,11 +710,24 @@ module.exports = function getRouterHelpers({ dataDirs, routingSnapshot, routerMa
|
|
|
673
710
|
|
|
674
711
|
const existSite = await states.site.findOne({ domain: domainGroup });
|
|
675
712
|
if (!existSite) {
|
|
713
|
+
const ipEchoDnsDomain = getIpDnsDomainForBlocklet(blocklet, webInterface, nodeInfo.did);
|
|
714
|
+
const appIdEnv = blocklet.environments.find((e) => e.key === 'BLOCKLET_APP_ID');
|
|
715
|
+
const domainAliases = [{ value: ipEchoDnsDomain, isProtected: true }];
|
|
716
|
+
|
|
717
|
+
const didDomain = getDidDomainForBlocklet({
|
|
718
|
+
appId: appIdEnv.value,
|
|
719
|
+
didDomain: nodeInfo.didDomain,
|
|
720
|
+
});
|
|
721
|
+
|
|
722
|
+
if (blocklet.mode !== 'development') {
|
|
723
|
+
domainAliases.push({ value: didDomain, isProtected: true });
|
|
724
|
+
}
|
|
725
|
+
|
|
676
726
|
await routerManager.addRoutingSite(
|
|
677
727
|
{
|
|
678
728
|
site: {
|
|
679
729
|
domain: domainGroup,
|
|
680
|
-
domainAliases
|
|
730
|
+
domainAliases,
|
|
681
731
|
isProtected: true,
|
|
682
732
|
rules: [rule],
|
|
683
733
|
},
|
|
@@ -685,7 +735,14 @@ module.exports = function getRouterHelpers({ dataDirs, routingSnapshot, routerMa
|
|
|
685
735
|
},
|
|
686
736
|
context
|
|
687
737
|
);
|
|
688
|
-
logger.info('add routing site', { site:
|
|
738
|
+
logger.info('add routing site', { site: domainGroup });
|
|
739
|
+
if (
|
|
740
|
+
process.env.NODE_ENV !== 'development' &&
|
|
741
|
+
process.env.NODE_ENV !== 'test' &&
|
|
742
|
+
blocklet.mode !== 'development'
|
|
743
|
+
) {
|
|
744
|
+
await certManager.issue({ domain: didDomain });
|
|
745
|
+
}
|
|
689
746
|
|
|
690
747
|
return true;
|
|
691
748
|
}
|
|
@@ -700,13 +757,13 @@ module.exports = function getRouterHelpers({ dataDirs, routingSnapshot, routerMa
|
|
|
700
757
|
},
|
|
701
758
|
skipProtectedRuleChecking: true,
|
|
702
759
|
});
|
|
703
|
-
logger.info('update routing rule for site', { site:
|
|
760
|
+
logger.info('update routing rule for site', { site: domainGroup });
|
|
704
761
|
} else {
|
|
705
762
|
await routerManager.addRoutingRule({
|
|
706
763
|
id: existSite.id,
|
|
707
764
|
rule,
|
|
708
765
|
});
|
|
709
|
-
logger.info('add routing rule for site', { site:
|
|
766
|
+
logger.info('add routing rule for site', { site: domainGroup });
|
|
710
767
|
}
|
|
711
768
|
|
|
712
769
|
return true;
|
|
@@ -1068,9 +1125,7 @@ module.exports = function getRouterHelpers({ dataDirs, routingSnapshot, routerMa
|
|
|
1068
1125
|
sites = await ensureAuthService(sites, blockletManager);
|
|
1069
1126
|
sites = await ensureServiceRule(sites);
|
|
1070
1127
|
|
|
1071
|
-
const certificates = httpsEnabled
|
|
1072
|
-
? await httpsCertState.find({ type: providerName }, { domain: 1, certificate: 1, privateKey: 1 })
|
|
1073
|
-
: [];
|
|
1128
|
+
const certificates = httpsEnabled ? await certManager.getAllNormal() : [];
|
|
1074
1129
|
|
|
1075
1130
|
return {
|
|
1076
1131
|
sites,
|
|
@@ -1093,9 +1148,9 @@ module.exports = function getRouterHelpers({ dataDirs, routingSnapshot, routerMa
|
|
|
1093
1148
|
},
|
|
1094
1149
|
});
|
|
1095
1150
|
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1151
|
+
certManager.on('cert.added', () => routers[providerName].restart());
|
|
1152
|
+
certManager.on('cert.removed', () => routers[providerName].restart());
|
|
1153
|
+
certManager.on('cert.issued', () => routers[providerName].restart());
|
|
1099
1154
|
|
|
1100
1155
|
await routers[providerName].start();
|
|
1101
1156
|
}
|
|
@@ -1257,18 +1312,21 @@ module.exports = function getRouterHelpers({ dataDirs, routingSnapshot, routerMa
|
|
|
1257
1312
|
};
|
|
1258
1313
|
|
|
1259
1314
|
const getCertificates = async () => {
|
|
1260
|
-
const certificates = await
|
|
1315
|
+
const certificates = await certManager.getAll();
|
|
1261
1316
|
const sites = await getSitesFromSnapshot();
|
|
1317
|
+
|
|
1318
|
+
const isMatch = (cert, domain) =>
|
|
1319
|
+
domain !== DOMAIN_FOR_DEFAULT_SITE && domain && routerManager.isCertMatchedDomain(cert, domain);
|
|
1320
|
+
|
|
1262
1321
|
return certificates.map((cert) => {
|
|
1263
1322
|
cert.matchedSites = [];
|
|
1264
1323
|
sites.forEach((site) => {
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
}
|
|
1324
|
+
const domains = [site.domain, ...(site.domainAliases || []).map((x) => x.value)];
|
|
1325
|
+
domains.forEach((domain) => {
|
|
1326
|
+
if (isMatch(cert, domain)) {
|
|
1327
|
+
cert.matchedSites.push({ id: site.id, domain });
|
|
1328
|
+
}
|
|
1329
|
+
});
|
|
1272
1330
|
});
|
|
1273
1331
|
|
|
1274
1332
|
return cert;
|
|
@@ -1281,42 +1339,6 @@ module.exports = function getRouterHelpers({ dataDirs, routingSnapshot, routerMa
|
|
|
1281
1339
|
return { domain, isHttps: !!matchedCert, matchedCert };
|
|
1282
1340
|
};
|
|
1283
1341
|
|
|
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
|
-
});
|
|
1316
|
-
}
|
|
1317
|
-
}
|
|
1318
|
-
};
|
|
1319
|
-
|
|
1320
1342
|
return {
|
|
1321
1343
|
ensureDashboardRouting,
|
|
1322
1344
|
ensureBlockletRouting,
|
|
@@ -1328,9 +1350,10 @@ module.exports = function getRouterHelpers({ dataDirs, routingSnapshot, routerMa
|
|
|
1328
1350
|
takeRoutingSnapshot,
|
|
1329
1351
|
getRoutingSites,
|
|
1330
1352
|
getSnapshotSites,
|
|
1331
|
-
getCertificates,
|
|
1332
1353
|
getSitesFromSnapshot,
|
|
1354
|
+
getCertificates,
|
|
1333
1355
|
checkDomain,
|
|
1356
|
+
ensureDashboardCertificate,
|
|
1334
1357
|
|
|
1335
1358
|
getRoutingCrons: () => [
|
|
1336
1359
|
{
|
|
@@ -1339,12 +1362,6 @@ module.exports = function getRouterHelpers({ dataDirs, routingSnapshot, routerMa
|
|
|
1339
1362
|
fn: () => updateDashboardCertificate({ checkExpire: true }),
|
|
1340
1363
|
options: { runOnInit: true },
|
|
1341
1364
|
},
|
|
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
1365
|
{
|
|
1349
1366
|
name: 'rotate-log-files',
|
|
1350
1367
|
time: '5 0 0 * * *', // rotate at 05:00 every day
|
package/lib/router/manager.js
CHANGED
|
@@ -70,8 +70,9 @@ const normalizeRedirectUrl = (url) => {
|
|
|
70
70
|
};
|
|
71
71
|
|
|
72
72
|
class RouterManager extends EventEmitter {
|
|
73
|
-
constructor() {
|
|
73
|
+
constructor({ certManager }) {
|
|
74
74
|
super();
|
|
75
|
+
this.certManager = certManager;
|
|
75
76
|
|
|
76
77
|
// HACK: do not emit any events from CLI
|
|
77
78
|
if (isCLI()) {
|
|
@@ -366,7 +367,7 @@ class RouterManager extends EventEmitter {
|
|
|
366
367
|
domain,
|
|
367
368
|
});
|
|
368
369
|
logger.info('add certificate result', { domain: newCert.domain });
|
|
369
|
-
this.emit('
|
|
370
|
+
this.emit('cert.added', { type: 'nginx', data: newCert });
|
|
370
371
|
}
|
|
371
372
|
|
|
372
373
|
// eslint-disable-next-line no-unused-vars
|
|
@@ -379,7 +380,7 @@ class RouterManager extends EventEmitter {
|
|
|
379
380
|
const removeResult = await states.certificate.remove({ _id: id });
|
|
380
381
|
|
|
381
382
|
logger.info('delete certificate', { removeResult, domain: tmpCert.domain });
|
|
382
|
-
this.emit('
|
|
383
|
+
this.emit('cert.removed', { type: 'nginx', data: { domain: tmpCert.domain } });
|
|
383
384
|
return {};
|
|
384
385
|
}
|
|
385
386
|
|
|
@@ -389,7 +390,7 @@ class RouterManager extends EventEmitter {
|
|
|
389
390
|
this.fixCertificate(entity);
|
|
390
391
|
this.validateCertificate(entity, entity.domain);
|
|
391
392
|
const dbEntity = await states.certificate.upsert(entity);
|
|
392
|
-
this.emit('
|
|
393
|
+
this.emit('cert.issued', { type: 'nginx', data: dbEntity });
|
|
393
394
|
}
|
|
394
395
|
|
|
395
396
|
findCertificateByDomain(domain) {
|
|
@@ -451,16 +452,16 @@ class RouterManager extends EventEmitter {
|
|
|
451
452
|
}
|
|
452
453
|
|
|
453
454
|
async getMatchedCert(domain) {
|
|
454
|
-
const certs = await
|
|
455
|
+
const certs = await this.certManager.getAll();
|
|
455
456
|
const matchedCert = certs.find((cert) => this.isCertMatchedDomain(cert, domain));
|
|
456
457
|
|
|
457
458
|
if (matchedCert) {
|
|
458
459
|
return {
|
|
459
460
|
id: matchedCert.id,
|
|
460
461
|
domain: matchedCert.domain,
|
|
461
|
-
issuer: matchedCert.issuer,
|
|
462
|
-
validFrom: matchedCert.validFrom,
|
|
463
|
-
validTo: matchedCert.validTo,
|
|
462
|
+
issuer: matchedCert.meta.issuer,
|
|
463
|
+
validFrom: matchedCert.meta.validFrom,
|
|
464
|
+
validTo: matchedCert.meta.validTo,
|
|
464
465
|
};
|
|
465
466
|
}
|
|
466
467
|
|
package/lib/states/base.js
CHANGED
|
@@ -1,234 +1,17 @@
|
|
|
1
|
-
const
|
|
2
|
-
const path = require('path');
|
|
3
|
-
const util = require('util');
|
|
4
|
-
const { EventEmitter } = require('events');
|
|
5
|
-
const cloneDeep = require('lodash/cloneDeep');
|
|
6
|
-
const DataStore =
|
|
7
|
-
process.env.NODE_ENV === 'test' ? require('@nedb/core') : require('@nedb/multi')(Number(process.env.NEDB_MULTI_PORT));
|
|
1
|
+
const DB = require('@abtnode/db');
|
|
8
2
|
const logger = require('@abtnode/logger')('@abtnode/core:states');
|
|
9
3
|
|
|
10
4
|
const { isCLI } = require('../util');
|
|
11
5
|
|
|
12
|
-
class BaseState extends
|
|
6
|
+
class BaseState extends DB {
|
|
13
7
|
constructor(baseDir, options) {
|
|
14
|
-
super();
|
|
8
|
+
super(baseDir, options);
|
|
15
9
|
|
|
16
10
|
// HACK: do not emit any events from CLI
|
|
17
11
|
if (isCLI() && process.env.NODE_ENV !== 'test') {
|
|
18
12
|
this.emit = (name) => logger.debug('stopped state db event in CLI', name);
|
|
19
13
|
}
|
|
20
|
-
|
|
21
|
-
const dbOptions = options.db || {};
|
|
22
|
-
this.filename = path.join(baseDir, options.filename);
|
|
23
|
-
this.options = Object.freeze(cloneDeep(options));
|
|
24
|
-
this.db = new DataStore({
|
|
25
|
-
filename: this.filename,
|
|
26
|
-
timestampData: true,
|
|
27
|
-
...dbOptions,
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
logger.info('initialized', { filename: this.filename });
|
|
31
|
-
|
|
32
|
-
this.ready = false;
|
|
33
|
-
this.readyCallbacks = [];
|
|
34
|
-
this.db.loadDatabase((err) => {
|
|
35
|
-
if (err) {
|
|
36
|
-
logger.error(`failed to load disk database ${this.filename}`, { error: err });
|
|
37
|
-
console.error(err);
|
|
38
|
-
} else {
|
|
39
|
-
this.ready = true;
|
|
40
|
-
if (this.readyCallbacks.length) {
|
|
41
|
-
this.readyCallbacks.forEach((x) => x());
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
this.asyncDB = new Proxy(this.db, {
|
|
47
|
-
get(target, property) {
|
|
48
|
-
if (typeof target[property] === 'function') {
|
|
49
|
-
return util
|
|
50
|
-
.promisify((...args) => {
|
|
51
|
-
const cb = args[args.length - 1];
|
|
52
|
-
const rest = args.slice(0, args.length - 1);
|
|
53
|
-
|
|
54
|
-
target[property](...rest, (err, ...result) => {
|
|
55
|
-
if (err) {
|
|
56
|
-
return cb(err);
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
if (result.length === 1) {
|
|
60
|
-
return cb(null, result[0]);
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
return cb(null, result);
|
|
64
|
-
});
|
|
65
|
-
})
|
|
66
|
-
.bind(target);
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
return target[property];
|
|
70
|
-
},
|
|
71
|
-
});
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
onReady(cb) {
|
|
75
|
-
if (this.ready) {
|
|
76
|
-
cb();
|
|
77
|
-
} else {
|
|
78
|
-
this.readyCallbacks.push(cb);
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
createNotification(payload) {
|
|
83
|
-
if (this.notification && typeof this.notification.create === 'function') {
|
|
84
|
-
this.notification.create(payload);
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
paginate(conditions, sort, paging) {
|
|
89
|
-
const { pageSize: size = 20, page = 1 } = paging || {};
|
|
90
|
-
const pageSize = Math.min(100, size);
|
|
91
|
-
|
|
92
|
-
return new Promise((resolve, reject) => {
|
|
93
|
-
this.db
|
|
94
|
-
.find(conditions)
|
|
95
|
-
.sort(sort)
|
|
96
|
-
.exec((err, docs) => {
|
|
97
|
-
if (err) {
|
|
98
|
-
return reject(err);
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
const pageCount = Math.ceil(docs.length / pageSize);
|
|
102
|
-
const total = docs.length;
|
|
103
|
-
const skip = (page - 1) * pageSize;
|
|
104
|
-
const list = docs.slice(skip, skip + pageSize);
|
|
105
|
-
|
|
106
|
-
list.forEach((doc) => {
|
|
107
|
-
// eslint-disable-next-line no-underscore-dangle
|
|
108
|
-
doc.id = doc._id;
|
|
109
|
-
});
|
|
110
|
-
|
|
111
|
-
return resolve({
|
|
112
|
-
list,
|
|
113
|
-
paging: {
|
|
114
|
-
total,
|
|
115
|
-
pageSize,
|
|
116
|
-
pageCount,
|
|
117
|
-
page,
|
|
118
|
-
},
|
|
119
|
-
});
|
|
120
|
-
});
|
|
121
|
-
});
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
async updateById(id, updates, options = {}) {
|
|
125
|
-
const [, doc] = await this.asyncDB.update({ _id: id }, updates, {
|
|
126
|
-
multi: false,
|
|
127
|
-
upsert: false,
|
|
128
|
-
returnUpdatedDocs: true,
|
|
129
|
-
...options,
|
|
130
|
-
});
|
|
131
|
-
|
|
132
|
-
return doc;
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
update(...args) {
|
|
136
|
-
if (args.length === 0) {
|
|
137
|
-
throw new Error('param is required by update method');
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
if (typeof args[0] === 'string') {
|
|
141
|
-
return this.updateById(...args);
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
return this.asyncDB.update(args[0], args[1], { returnUpdatedDocs: true, ...(args[2] || {}) });
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
count(conditions = {}) {
|
|
148
|
-
return new Promise((resolve, reject) => {
|
|
149
|
-
this.db.count(conditions, (err, num) => {
|
|
150
|
-
if (err) {
|
|
151
|
-
return reject(err);
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
return resolve(num);
|
|
155
|
-
});
|
|
156
|
-
});
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
remove(conditions = {}, options = { multi: false }) {
|
|
160
|
-
return new Promise((resolve, reject) => {
|
|
161
|
-
this.db.remove(conditions, options, (err, num) => {
|
|
162
|
-
if (err) {
|
|
163
|
-
return reject(err);
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
return resolve(num);
|
|
167
|
-
});
|
|
168
|
-
});
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
reset() {
|
|
172
|
-
fs.unlinkSync(this.filename);
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
find(...args) {
|
|
176
|
-
if (args.length === 0) {
|
|
177
|
-
return this.asyncDB.find({});
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
return this.asyncDB.find(...args);
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
findOne(...args) {
|
|
184
|
-
if (args.length === 0) {
|
|
185
|
-
return this.asyncDB.findOne({});
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
return this.asyncDB.findOne(...args);
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
insert(...args) {
|
|
192
|
-
return this.asyncDB.insert(...args);
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
compactDatafile() {
|
|
196
|
-
this.db.persistence.compactDatafile((err) => {
|
|
197
|
-
if (err) {
|
|
198
|
-
console.error(`failed to compact datafile: ${this.filename}`, err);
|
|
199
|
-
}
|
|
200
|
-
});
|
|
201
14
|
}
|
|
202
15
|
}
|
|
203
16
|
|
|
204
|
-
/**
|
|
205
|
-
* Rename _id field name to id, this method has side effects
|
|
206
|
-
* @param {object} entities
|
|
207
|
-
*/
|
|
208
|
-
const renameIdFiledName = (entities, from = '_id', to = 'id') => {
|
|
209
|
-
/* eslint-disable no-underscore-dangle, no-param-reassign */
|
|
210
|
-
|
|
211
|
-
if (!entities) {
|
|
212
|
-
return entities;
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
const mapEntity = (entity) => {
|
|
216
|
-
if (entity[from]) {
|
|
217
|
-
entity[to] = entity[from];
|
|
218
|
-
delete entity[from];
|
|
219
|
-
}
|
|
220
|
-
};
|
|
221
|
-
|
|
222
|
-
if (!Array.isArray(entities)) {
|
|
223
|
-
mapEntity(entities);
|
|
224
|
-
return entities;
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
entities.forEach(mapEntity);
|
|
228
|
-
|
|
229
|
-
return entities;
|
|
230
|
-
};
|
|
231
|
-
|
|
232
|
-
BaseState.renameIdFiledName = renameIdFiledName;
|
|
233
|
-
|
|
234
17
|
module.exports = BaseState;
|