@serve.zone/dcrouter 11.10.3 → 11.10.7
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/dist_serve/bundle.js +5102 -5102
- package/dist_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/cache/classes.cache.cleaner.js +1 -1
- package/dist_ts/cache/classes.cached.document.js +1 -1
- package/dist_ts/cache/classes.cachedb.js +1 -1
- package/dist_ts/cache/documents/classes.cached.email.js +1 -1
- package/dist_ts/cache/documents/classes.cached.ip.reputation.js +1 -1
- package/dist_ts/classes.dcrouter.js +3 -3
- package/dist_ts/config/classes.route-config-manager.d.ts +1 -1
- package/dist_ts/config/validator.js +1 -1
- package/dist_ts/errors/base.errors.js +2 -2
- package/dist_ts/monitoring/classes.metricsmanager.d.ts +6 -1
- package/dist_ts/monitoring/classes.metricsmanager.js +69 -31
- package/dist_ts/opsserver/classes.opsserver.js +2 -2
- package/dist_ts/opsserver/handlers/admin.handler.js +1 -1
- package/dist_ts/opsserver/handlers/certificate.handler.js +1 -1
- package/dist_ts/opsserver/handlers/radius.handler.js +1 -1
- package/dist_ts/opsserver/handlers/stats.handler.js +1 -1
- package/dist_ts/radius/classes.accounting.manager.js +1 -1
- package/dist_ts/radius/classes.radius.server.js +1 -1
- package/dist_ts/radius/classes.vlan.manager.js +1 -1
- package/dist_ts/security/classes.contentscanner.js +3 -3
- package/dist_ts/security/classes.ipreputationchecker.js +4 -4
- package/dist_ts/security/classes.securitylogger.js +5 -3
- package/dist_ts/sms/classes.smsservice.js +2 -2
- package/dist_ts/storage/classes.storagemanager.d.ts +1 -1
- package/dist_ts/storage/classes.storagemanager.js +2 -4
- package/dist_ts_web/00_commitinfo_data.js +1 -1
- package/dist_ts_web/appstate.js +9 -6
- package/dist_ts_web/elements/ops-dashboard.js +3 -2
- package/dist_ts_web/elements/ops-view-certificates.js +1 -1
- package/dist_ts_web/elements/ops-view-config.js +1 -1
- package/dist_ts_web/elements/ops-view-emails.js +1 -1
- package/dist_ts_web/elements/ops-view-network.js +1 -1
- package/dist_ts_web/elements/ops-view-remoteingress.js +1 -1
- package/dist_ts_web/router.js +1 -1
- package/license +21 -0
- package/package.json +15 -15
- package/readme.hints.md +1 -1
- package/readme.md +1 -1
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/cache/classes.cache.cleaner.ts +10 -10
- package/ts/cache/classes.cached.document.ts +1 -1
- package/ts/cache/classes.cachedb.ts +6 -6
- package/ts/cache/documents/classes.cached.email.ts +14 -14
- package/ts/cache/documents/classes.cached.ip.reputation.ts +10 -10
- package/ts/classes.dcrouter.ts +31 -31
- package/ts/config/validator.ts +5 -5
- package/ts/errors/base.errors.ts +1 -1
- package/ts/monitoring/classes.metricsmanager.ts +70 -33
- package/ts/opsserver/classes.opsserver.ts +13 -13
- package/ts/opsserver/handlers/admin.handler.ts +1 -1
- package/ts/opsserver/handlers/certificate.handler.ts +6 -6
- package/ts/opsserver/handlers/radius.handler.ts +4 -4
- package/ts/opsserver/handlers/stats.handler.ts +1 -1
- package/ts/radius/classes.accounting.manager.ts +10 -10
- package/ts/radius/classes.radius.server.ts +2 -2
- package/ts/radius/classes.vlan.manager.ts +5 -5
- package/ts/readme.md +1 -1
- package/ts/security/classes.contentscanner.ts +12 -12
- package/ts/security/classes.ipreputationchecker.ts +26 -26
- package/ts/security/classes.securitylogger.ts +6 -4
- package/ts/sms/classes.smsservice.ts +3 -3
- package/ts/storage/classes.storagemanager.ts +23 -25
- package/ts_apiclient/readme.md +1 -1
- package/ts_web/00_commitinfo_data.ts +1 -1
- package/ts_web/appstate.ts +136 -133
- package/ts_web/elements/ops-dashboard.ts +15 -14
- package/ts_web/elements/ops-view-certificates.ts +10 -10
- package/ts_web/elements/ops-view-config.ts +8 -8
- package/ts_web/elements/ops-view-emails.ts +2 -2
- package/ts_web/elements/ops-view-network.ts +2 -2
- package/ts_web/elements/ops-view-remoteingress.ts +6 -6
- package/ts_web/readme.md +1 -1
- package/ts_web/router.ts +3 -3
- /package/{npmextra.json → .smartconfig.json} +0 -0
|
@@ -35,55 +35,55 @@ export class CachedEmail extends CachedDocument<CachedEmail> {
|
|
|
35
35
|
*/
|
|
36
36
|
@plugins.smartdata.unI()
|
|
37
37
|
@plugins.smartdata.svDb()
|
|
38
|
-
public id
|
|
38
|
+
public id!: string;
|
|
39
39
|
|
|
40
40
|
/**
|
|
41
41
|
* Email message ID (RFC 822 Message-ID header)
|
|
42
42
|
*/
|
|
43
43
|
@plugins.smartdata.svDb()
|
|
44
|
-
public messageId
|
|
44
|
+
public messageId!: string;
|
|
45
45
|
|
|
46
46
|
/**
|
|
47
47
|
* Sender email address (envelope from)
|
|
48
48
|
*/
|
|
49
49
|
@plugins.smartdata.svDb()
|
|
50
|
-
public from
|
|
50
|
+
public from!: string;
|
|
51
51
|
|
|
52
52
|
/**
|
|
53
53
|
* Recipient email addresses
|
|
54
54
|
*/
|
|
55
55
|
@plugins.smartdata.svDb()
|
|
56
|
-
public to
|
|
56
|
+
public to!: string[];
|
|
57
57
|
|
|
58
58
|
/**
|
|
59
59
|
* CC recipients
|
|
60
60
|
*/
|
|
61
61
|
@plugins.smartdata.svDb()
|
|
62
|
-
public cc
|
|
62
|
+
public cc!: string[];
|
|
63
63
|
|
|
64
64
|
/**
|
|
65
65
|
* BCC recipients
|
|
66
66
|
*/
|
|
67
67
|
@plugins.smartdata.svDb()
|
|
68
|
-
public bcc
|
|
68
|
+
public bcc!: string[];
|
|
69
69
|
|
|
70
70
|
/**
|
|
71
71
|
* Email subject
|
|
72
72
|
*/
|
|
73
73
|
@plugins.smartdata.svDb()
|
|
74
|
-
public subject
|
|
74
|
+
public subject!: string;
|
|
75
75
|
|
|
76
76
|
/**
|
|
77
77
|
* Raw RFC822 email content
|
|
78
78
|
*/
|
|
79
79
|
@plugins.smartdata.svDb()
|
|
80
|
-
public rawContent
|
|
80
|
+
public rawContent!: string;
|
|
81
81
|
|
|
82
82
|
/**
|
|
83
83
|
* Current status of the email
|
|
84
84
|
*/
|
|
85
85
|
@plugins.smartdata.svDb()
|
|
86
|
-
public status
|
|
86
|
+
public status!: TCachedEmailStatus;
|
|
87
87
|
|
|
88
88
|
/**
|
|
89
89
|
* Number of delivery attempts
|
|
@@ -101,25 +101,25 @@ export class CachedEmail extends CachedDocument<CachedEmail> {
|
|
|
101
101
|
* Timestamp for next delivery attempt
|
|
102
102
|
*/
|
|
103
103
|
@plugins.smartdata.svDb()
|
|
104
|
-
public nextAttempt
|
|
104
|
+
public nextAttempt!: Date;
|
|
105
105
|
|
|
106
106
|
/**
|
|
107
107
|
* Last error message if delivery failed
|
|
108
108
|
*/
|
|
109
109
|
@plugins.smartdata.svDb()
|
|
110
|
-
public lastError
|
|
110
|
+
public lastError!: string;
|
|
111
111
|
|
|
112
112
|
/**
|
|
113
113
|
* Timestamp when the email was successfully delivered
|
|
114
114
|
*/
|
|
115
115
|
@plugins.smartdata.svDb()
|
|
116
|
-
public deliveredAt
|
|
116
|
+
public deliveredAt!: Date;
|
|
117
117
|
|
|
118
118
|
/**
|
|
119
119
|
* Sender domain (for querying/filtering)
|
|
120
120
|
*/
|
|
121
121
|
@plugins.smartdata.svDb()
|
|
122
|
-
public senderDomain
|
|
122
|
+
public senderDomain!: string;
|
|
123
123
|
|
|
124
124
|
/**
|
|
125
125
|
* Priority level (higher = more important)
|
|
@@ -131,7 +131,7 @@ export class CachedEmail extends CachedDocument<CachedEmail> {
|
|
|
131
131
|
* JSON-serialized route data
|
|
132
132
|
*/
|
|
133
133
|
@plugins.smartdata.svDb()
|
|
134
|
-
public routeData
|
|
134
|
+
public routeData!: string;
|
|
135
135
|
|
|
136
136
|
/**
|
|
137
137
|
* DKIM signature status
|
|
@@ -45,61 +45,61 @@ export class CachedIPReputation extends CachedDocument<CachedIPReputation> {
|
|
|
45
45
|
*/
|
|
46
46
|
@plugins.smartdata.unI()
|
|
47
47
|
@plugins.smartdata.svDb()
|
|
48
|
-
public ipAddress
|
|
48
|
+
public ipAddress!: string;
|
|
49
49
|
|
|
50
50
|
/**
|
|
51
51
|
* Reputation score (0-100, higher = better)
|
|
52
52
|
*/
|
|
53
53
|
@plugins.smartdata.svDb()
|
|
54
|
-
public score
|
|
54
|
+
public score!: number;
|
|
55
55
|
|
|
56
56
|
/**
|
|
57
57
|
* Whether the IP is flagged as spam source
|
|
58
58
|
*/
|
|
59
59
|
@plugins.smartdata.svDb()
|
|
60
|
-
public isSpam
|
|
60
|
+
public isSpam!: boolean;
|
|
61
61
|
|
|
62
62
|
/**
|
|
63
63
|
* Whether the IP is a known proxy
|
|
64
64
|
*/
|
|
65
65
|
@plugins.smartdata.svDb()
|
|
66
|
-
public isProxy
|
|
66
|
+
public isProxy!: boolean;
|
|
67
67
|
|
|
68
68
|
/**
|
|
69
69
|
* Whether the IP is a Tor exit node
|
|
70
70
|
*/
|
|
71
71
|
@plugins.smartdata.svDb()
|
|
72
|
-
public isTor
|
|
72
|
+
public isTor!: boolean;
|
|
73
73
|
|
|
74
74
|
/**
|
|
75
75
|
* Whether the IP is a VPN endpoint
|
|
76
76
|
*/
|
|
77
77
|
@plugins.smartdata.svDb()
|
|
78
|
-
public isVPN
|
|
78
|
+
public isVPN!: boolean;
|
|
79
79
|
|
|
80
80
|
/**
|
|
81
81
|
* Country code (ISO 3166-1 alpha-2)
|
|
82
82
|
*/
|
|
83
83
|
@plugins.smartdata.svDb()
|
|
84
|
-
public country
|
|
84
|
+
public country!: string;
|
|
85
85
|
|
|
86
86
|
/**
|
|
87
87
|
* Autonomous System Number
|
|
88
88
|
*/
|
|
89
89
|
@plugins.smartdata.svDb()
|
|
90
|
-
public asn
|
|
90
|
+
public asn!: string;
|
|
91
91
|
|
|
92
92
|
/**
|
|
93
93
|
* Organization name
|
|
94
94
|
*/
|
|
95
95
|
@plugins.smartdata.svDb()
|
|
96
|
-
public org
|
|
96
|
+
public org!: string;
|
|
97
97
|
|
|
98
98
|
/**
|
|
99
99
|
* List of blacklists the IP appears on
|
|
100
100
|
*/
|
|
101
101
|
@plugins.smartdata.svDb()
|
|
102
|
-
public blacklists
|
|
102
|
+
public blacklists!: string[];
|
|
103
103
|
|
|
104
104
|
/**
|
|
105
105
|
* Number of times this IP has been checked
|
package/ts/classes.dcrouter.ts
CHANGED
|
@@ -215,7 +215,7 @@ export class DcRouter {
|
|
|
215
215
|
public emailServer?: UnifiedEmailServer;
|
|
216
216
|
public radiusServer?: RadiusServer;
|
|
217
217
|
public storageManager: StorageManager;
|
|
218
|
-
public opsServer
|
|
218
|
+
public opsServer!: OpsServer;
|
|
219
219
|
public metricsManager?: MetricsManager;
|
|
220
220
|
|
|
221
221
|
// Cache system (smartdata + LocalTsmDb)
|
|
@@ -448,7 +448,7 @@ export class DcRouter {
|
|
|
448
448
|
}
|
|
449
449
|
|
|
450
450
|
// DNS Server: optional, depends on SmartProxy
|
|
451
|
-
if (this.options.dnsNsDomains
|
|
451
|
+
if (this.options.dnsNsDomains && this.options.dnsNsDomains.length > 0 && this.options.dnsScopes && this.options.dnsScopes.length > 0) {
|
|
452
452
|
this.serviceManager.addService(
|
|
453
453
|
new plugins.taskbuffer.Service('DnsServer')
|
|
454
454
|
.optional()
|
|
@@ -787,7 +787,7 @@ export class DcRouter {
|
|
|
787
787
|
eventComms.log(`Attempting DNS-01 via SmartAcme for ${domain}`);
|
|
788
788
|
eventComms.setSource('smartacme-dns-01');
|
|
789
789
|
const isWildcardDomain = domain.startsWith('*.');
|
|
790
|
-
const cert = await this.smartAcme
|
|
790
|
+
const cert = await this.smartAcme!.getCertificateForDomain(domain, {
|
|
791
791
|
includeWildcard: !isWildcardDomain,
|
|
792
792
|
});
|
|
793
793
|
if (cert.validUntil) {
|
|
@@ -806,10 +806,10 @@ export class DcRouter {
|
|
|
806
806
|
// Success — clear any backoff
|
|
807
807
|
await scheduler.clearBackoff(domain);
|
|
808
808
|
return result;
|
|
809
|
-
} catch (err) {
|
|
809
|
+
} catch (err: unknown) {
|
|
810
810
|
// Record failure for backoff tracking
|
|
811
|
-
await scheduler.recordFailure(domain, err.message);
|
|
812
|
-
eventComms.warn(`SmartAcme DNS-01 failed for ${domain}: ${err.message}, falling back to http-01`);
|
|
811
|
+
await scheduler.recordFailure(domain, (err as Error).message);
|
|
812
|
+
eventComms.warn(`SmartAcme DNS-01 failed for ${domain}: ${(err as Error).message}, falling back to http-01`);
|
|
813
813
|
return 'http01';
|
|
814
814
|
}
|
|
815
815
|
};
|
|
@@ -1248,21 +1248,21 @@ export class DcRouter {
|
|
|
1248
1248
|
// Wire delivery events to MetricsManager and logger
|
|
1249
1249
|
if (this.metricsManager && this.emailServer.deliverySystem) {
|
|
1250
1250
|
this.emailServer.deliverySystem.on('deliveryStart', (item: any) => {
|
|
1251
|
-
this.metricsManager
|
|
1251
|
+
this.metricsManager!.trackEmailReceived(item?.from);
|
|
1252
1252
|
logger.log('info', `Email delivery started: ${item?.from} → ${item?.to}`, { zone: 'email' });
|
|
1253
1253
|
});
|
|
1254
1254
|
this.emailServer.deliverySystem.on('deliverySuccess', (item: any) => {
|
|
1255
|
-
this.metricsManager
|
|
1255
|
+
this.metricsManager!.trackEmailSent(item?.to);
|
|
1256
1256
|
logger.log('info', `Email delivered to ${item?.to}`, { zone: 'email' });
|
|
1257
1257
|
});
|
|
1258
1258
|
this.emailServer.deliverySystem.on('deliveryFailed', (item: any, error: any) => {
|
|
1259
|
-
this.metricsManager
|
|
1259
|
+
this.metricsManager!.trackEmailFailed(item?.to, error?.message);
|
|
1260
1260
|
logger.log('warn', `Email delivery failed to ${item?.to}: ${error?.message}`, { zone: 'email' });
|
|
1261
1261
|
});
|
|
1262
1262
|
}
|
|
1263
1263
|
if (this.metricsManager && this.emailServer) {
|
|
1264
1264
|
this.emailServer.on('bounceProcessed', () => {
|
|
1265
|
-
this.metricsManager
|
|
1265
|
+
this.metricsManager!.trackEmailBounced();
|
|
1266
1266
|
logger.log('warn', 'Email bounce processed', { zone: 'email' });
|
|
1267
1267
|
});
|
|
1268
1268
|
}
|
|
@@ -1305,12 +1305,12 @@ export class DcRouter {
|
|
|
1305
1305
|
}
|
|
1306
1306
|
|
|
1307
1307
|
logger.log('info', 'All unified email components stopped');
|
|
1308
|
-
} catch (error) {
|
|
1309
|
-
logger.log('error', `Error stopping unified email components: ${error.message}`);
|
|
1308
|
+
} catch (error: unknown) {
|
|
1309
|
+
logger.log('error', `Error stopping unified email components: ${(error as Error).message}`);
|
|
1310
1310
|
throw error;
|
|
1311
1311
|
}
|
|
1312
1312
|
}
|
|
1313
|
-
|
|
1313
|
+
|
|
1314
1314
|
/**
|
|
1315
1315
|
* Update domain rules for email routing
|
|
1316
1316
|
* @param rules New domain rules to apply
|
|
@@ -1468,7 +1468,7 @@ export class DcRouter {
|
|
|
1468
1468
|
this.dnsServer.on('query', (event: plugins.smartdns.dnsServerMod.IDnsQueryCompletedEvent) => {
|
|
1469
1469
|
// Metrics tracking
|
|
1470
1470
|
for (const question of event.questions) {
|
|
1471
|
-
this.metricsManager
|
|
1471
|
+
this.metricsManager?.trackDnsQuery(
|
|
1472
1472
|
question.type,
|
|
1473
1473
|
question.name,
|
|
1474
1474
|
false,
|
|
@@ -1553,8 +1553,8 @@ export class DcRouter {
|
|
|
1553
1553
|
// Use the built-in socket handler from smartdns
|
|
1554
1554
|
// This handles HTTP/2, DoH protocol, etc.
|
|
1555
1555
|
await (this.dnsServer as any).handleHttpsSocket(socket);
|
|
1556
|
-
} catch (error) {
|
|
1557
|
-
logger.log('error', `DNS socket handler error: ${error.message}`);
|
|
1556
|
+
} catch (error: unknown) {
|
|
1557
|
+
logger.log('error', `DNS socket handler error: ${(error as Error).message}`);
|
|
1558
1558
|
if (!socket.destroyed) {
|
|
1559
1559
|
socket.destroy();
|
|
1560
1560
|
}
|
|
@@ -1695,14 +1695,14 @@ export class DcRouter {
|
|
|
1695
1695
|
} else {
|
|
1696
1696
|
logger.log('warn', `Invalid DKIM record structure in ${file}`);
|
|
1697
1697
|
}
|
|
1698
|
-
} catch (error) {
|
|
1699
|
-
logger.log('error', `Failed to load DKIM record from ${file}: ${error.message}`);
|
|
1698
|
+
} catch (error: unknown) {
|
|
1699
|
+
logger.log('error', `Failed to load DKIM record from ${file}: ${(error as Error).message}`);
|
|
1700
1700
|
}
|
|
1701
1701
|
}
|
|
1702
|
-
} catch (error) {
|
|
1703
|
-
logger.log('error', `Failed to load DKIM records: ${error.message}`);
|
|
1702
|
+
} catch (error: unknown) {
|
|
1703
|
+
logger.log('error', `Failed to load DKIM records: ${(error as Error).message}`);
|
|
1704
1704
|
}
|
|
1705
|
-
|
|
1705
|
+
|
|
1706
1706
|
return records;
|
|
1707
1707
|
}
|
|
1708
1708
|
|
|
@@ -1734,11 +1734,11 @@ export class DcRouter {
|
|
|
1734
1734
|
// This ensures keys are ready even if DNS mode changes later
|
|
1735
1735
|
await dkimCreator.handleDKIMKeysForDomain(domainConfig.domain);
|
|
1736
1736
|
logger.log('info', `DKIM keys initialized for ${domainConfig.domain}`);
|
|
1737
|
-
} catch (error) {
|
|
1738
|
-
logger.log('error', `Failed to initialize DKIM for ${domainConfig.domain}: ${error.message}`);
|
|
1737
|
+
} catch (error: unknown) {
|
|
1738
|
+
logger.log('error', `Failed to initialize DKIM for ${domainConfig.domain}: ${(error as Error).message}`);
|
|
1739
1739
|
}
|
|
1740
1740
|
}
|
|
1741
|
-
|
|
1741
|
+
|
|
1742
1742
|
logger.log('info', 'DKIM initialization complete');
|
|
1743
1743
|
}
|
|
1744
1744
|
|
|
@@ -1779,10 +1779,10 @@ export class DcRouter {
|
|
|
1779
1779
|
} else {
|
|
1780
1780
|
logger.log('warn', 'Could not auto-discover public IPv4 address');
|
|
1781
1781
|
}
|
|
1782
|
-
} catch (error) {
|
|
1783
|
-
logger.log('error', `Failed to auto-discover public IP: ${error.message}`);
|
|
1782
|
+
} catch (error: unknown) {
|
|
1783
|
+
logger.log('error', `Failed to auto-discover public IP: ${(error as Error).message}`);
|
|
1784
1784
|
}
|
|
1785
|
-
|
|
1785
|
+
|
|
1786
1786
|
if (!publicIp) {
|
|
1787
1787
|
logger.log('warn', 'No public IP available. Nameserver A records require either proxyIps, publicIp, or successful auto-discovery.');
|
|
1788
1788
|
}
|
|
@@ -1876,8 +1876,8 @@ export class DcRouter {
|
|
|
1876
1876
|
}
|
|
1877
1877
|
|
|
1878
1878
|
return null;
|
|
1879
|
-
} catch (error) {
|
|
1880
|
-
logger.log('warn', `Failed to detect public IP: ${error.message}`);
|
|
1879
|
+
} catch (error: unknown) {
|
|
1880
|
+
logger.log('warn', `Failed to detect public IP: ${(error as Error).message}`);
|
|
1881
1881
|
return null;
|
|
1882
1882
|
}
|
|
1883
1883
|
}
|
|
@@ -1911,8 +1911,8 @@ export class DcRouter {
|
|
|
1911
1911
|
const keyPem = plugins.fs.readFileSync(riCfg.tls.keyPath, 'utf8');
|
|
1912
1912
|
tlsConfig = { certPem, keyPem };
|
|
1913
1913
|
logger.log('info', 'Using explicit TLS cert/key for RemoteIngress tunnel');
|
|
1914
|
-
} catch (err) {
|
|
1915
|
-
logger.log('warn', `Failed to read RemoteIngress TLS cert/key files: ${err.message}`);
|
|
1914
|
+
} catch (err: unknown) {
|
|
1915
|
+
logger.log('warn', `Failed to read RemoteIngress TLS cert/key files: ${(err as Error).message}`);
|
|
1916
1916
|
}
|
|
1917
1917
|
}
|
|
1918
1918
|
|
package/ts/config/validator.ts
CHANGED
|
@@ -170,7 +170,7 @@ export class ConfigValidator {
|
|
|
170
170
|
} else if (rules.items.schema && itemType === 'object') {
|
|
171
171
|
const itemResult = this.validate(value[i], rules.items.schema);
|
|
172
172
|
if (!itemResult.valid) {
|
|
173
|
-
errors.push(...itemResult.errors
|
|
173
|
+
errors.push(...itemResult.errors!.map(err => `${key}[${i}].${err}`));
|
|
174
174
|
}
|
|
175
175
|
}
|
|
176
176
|
}
|
|
@@ -181,7 +181,7 @@ export class ConfigValidator {
|
|
|
181
181
|
if (rules.schema) {
|
|
182
182
|
const nestedResult = this.validate(value, rules.schema);
|
|
183
183
|
if (!nestedResult.valid) {
|
|
184
|
-
errors.push(...nestedResult.errors
|
|
184
|
+
errors.push(...nestedResult.errors!.map(err => `${key}.${err}`));
|
|
185
185
|
}
|
|
186
186
|
validatedConfig[key] = nestedResult.config;
|
|
187
187
|
}
|
|
@@ -233,8 +233,8 @@ export class ConfigValidator {
|
|
|
233
233
|
|
|
234
234
|
// Apply defaults to array items
|
|
235
235
|
if (result[key] && rules.type === 'array' && rules.items && rules.items.schema) {
|
|
236
|
-
result[key] = result[key].map(item =>
|
|
237
|
-
typeof item === 'object' ? this.applyDefaults(item, rules.items
|
|
236
|
+
result[key] = result[key].map(item =>
|
|
237
|
+
typeof item === 'object' ? this.applyDefaults(item, rules.items!.schema!) : item
|
|
238
238
|
);
|
|
239
239
|
}
|
|
240
240
|
}
|
|
@@ -255,7 +255,7 @@ export class ConfigValidator {
|
|
|
255
255
|
|
|
256
256
|
if (!result.valid) {
|
|
257
257
|
throw new ValidationError(
|
|
258
|
-
`Configuration validation failed: ${result.errors
|
|
258
|
+
`Configuration validation failed: ${result.errors!.join(', ')}`,
|
|
259
259
|
'CONFIG_VALIDATION_ERROR',
|
|
260
260
|
{ data: { errors: result.errors } }
|
|
261
261
|
);
|
package/ts/errors/base.errors.ts
CHANGED
|
@@ -296,11 +296,11 @@ export class MetricsManager {
|
|
|
296
296
|
const proxyMetrics = this.dcRouter.smartProxy ? this.dcRouter.smartProxy.getMetrics() : null;
|
|
297
297
|
|
|
298
298
|
if (!proxyMetrics) {
|
|
299
|
-
return [];
|
|
299
|
+
return [] as Array<{ type: string; count: number; source: string; lastActivity: Date }>;
|
|
300
300
|
}
|
|
301
|
-
|
|
301
|
+
|
|
302
302
|
const connectionsByRoute = proxyMetrics.connections.byRoute();
|
|
303
|
-
const connectionInfo = [];
|
|
303
|
+
const connectionInfo: Array<{ type: string; count: number; source: string; lastActivity: Date }> = [];
|
|
304
304
|
|
|
305
305
|
for (const [routeName, count] of connectionsByRoute) {
|
|
306
306
|
connectionInfo.push({
|
|
@@ -595,47 +595,84 @@ export class MetricsManager {
|
|
|
595
595
|
const backendMetrics = proxyMetrics.backends.byBackend();
|
|
596
596
|
const protocolCache = proxyMetrics.backends.detectedProtocols();
|
|
597
597
|
|
|
598
|
-
//
|
|
599
|
-
|
|
598
|
+
// Group protocol cache entries by host:port so we can match them to backend metrics.
|
|
599
|
+
// The protocol cache is keyed by (host, port, domain) in Rust, so the same host:port
|
|
600
|
+
// can have multiple entries for different domains.
|
|
601
|
+
const cacheByBackend = new Map<string, (typeof protocolCache)[number][]>();
|
|
600
602
|
for (const entry of protocolCache) {
|
|
601
|
-
|
|
603
|
+
const backendKey = `${entry.host}:${entry.port}`;
|
|
604
|
+
let entries = cacheByBackend.get(backendKey);
|
|
605
|
+
if (!entries) {
|
|
606
|
+
entries = [];
|
|
607
|
+
cacheByBackend.set(backendKey, entries);
|
|
608
|
+
}
|
|
609
|
+
entries.push(entry);
|
|
602
610
|
}
|
|
603
611
|
|
|
604
612
|
const backends: Array<any> = [];
|
|
605
|
-
const
|
|
613
|
+
const seenCacheKeys = new Set<string>();
|
|
606
614
|
|
|
607
615
|
for (const [key, bm] of backendMetrics) {
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
616
|
+
const cacheEntries = cacheByBackend.get(key);
|
|
617
|
+
if (!cacheEntries || cacheEntries.length === 0) {
|
|
618
|
+
// No protocol cache entry — emit one row with backend metrics only
|
|
619
|
+
backends.push({
|
|
620
|
+
backend: key,
|
|
621
|
+
domain: null,
|
|
622
|
+
protocol: bm.protocol,
|
|
623
|
+
activeConnections: bm.activeConnections,
|
|
624
|
+
totalConnections: bm.totalConnections,
|
|
625
|
+
connectErrors: bm.connectErrors,
|
|
626
|
+
handshakeErrors: bm.handshakeErrors,
|
|
627
|
+
requestErrors: bm.requestErrors,
|
|
628
|
+
avgConnectTimeMs: Math.round(bm.avgConnectTimeMs * 10) / 10,
|
|
629
|
+
poolHitRate: Math.round(bm.poolHitRate * 1000) / 1000,
|
|
630
|
+
h2Failures: bm.h2Failures,
|
|
631
|
+
h2Suppressed: false,
|
|
632
|
+
h3Suppressed: false,
|
|
633
|
+
h2CooldownRemainingSecs: null,
|
|
634
|
+
h3CooldownRemainingSecs: null,
|
|
635
|
+
h2ConsecutiveFailures: null,
|
|
636
|
+
h3ConsecutiveFailures: null,
|
|
637
|
+
h3Port: null,
|
|
638
|
+
cacheAgeSecs: null,
|
|
639
|
+
});
|
|
640
|
+
} else {
|
|
641
|
+
// One row per domain, each enriched with the shared backend metrics
|
|
642
|
+
for (const cache of cacheEntries) {
|
|
643
|
+
const compositeKey = `${cache.host}:${cache.port}:${cache.domain ?? ''}`;
|
|
644
|
+
seenCacheKeys.add(compositeKey);
|
|
645
|
+
backends.push({
|
|
646
|
+
backend: key,
|
|
647
|
+
domain: cache.domain ?? null,
|
|
648
|
+
protocol: cache.protocol ?? bm.protocol,
|
|
649
|
+
activeConnections: bm.activeConnections,
|
|
650
|
+
totalConnections: bm.totalConnections,
|
|
651
|
+
connectErrors: bm.connectErrors,
|
|
652
|
+
handshakeErrors: bm.handshakeErrors,
|
|
653
|
+
requestErrors: bm.requestErrors,
|
|
654
|
+
avgConnectTimeMs: Math.round(bm.avgConnectTimeMs * 10) / 10,
|
|
655
|
+
poolHitRate: Math.round(bm.poolHitRate * 1000) / 1000,
|
|
656
|
+
h2Failures: bm.h2Failures,
|
|
657
|
+
h2Suppressed: cache.h2Suppressed,
|
|
658
|
+
h3Suppressed: cache.h3Suppressed,
|
|
659
|
+
h2CooldownRemainingSecs: cache.h2CooldownRemainingSecs,
|
|
660
|
+
h3CooldownRemainingSecs: cache.h3CooldownRemainingSecs,
|
|
661
|
+
h2ConsecutiveFailures: cache.h2ConsecutiveFailures,
|
|
662
|
+
h3ConsecutiveFailures: cache.h3ConsecutiveFailures,
|
|
663
|
+
h3Port: cache.h3Port,
|
|
664
|
+
cacheAgeSecs: cache.ageSecs,
|
|
665
|
+
});
|
|
666
|
+
}
|
|
667
|
+
}
|
|
631
668
|
}
|
|
632
669
|
|
|
633
670
|
// Include protocol cache entries with no matching backend metric
|
|
634
671
|
for (const entry of protocolCache) {
|
|
635
|
-
const
|
|
636
|
-
if (!
|
|
672
|
+
const compositeKey = `${entry.host}:${entry.port}:${entry.domain ?? ''}`;
|
|
673
|
+
if (!seenCacheKeys.has(compositeKey)) {
|
|
637
674
|
backends.push({
|
|
638
|
-
backend:
|
|
675
|
+
backend: `${entry.host}:${entry.port}`,
|
|
639
676
|
domain: entry.domain,
|
|
640
677
|
protocol: entry.protocol,
|
|
641
678
|
activeConnections: 0,
|
|
@@ -7,7 +7,7 @@ import { requireValidIdentity, requireAdminIdentity } from './helpers/guards.js'
|
|
|
7
7
|
|
|
8
8
|
export class OpsServer {
|
|
9
9
|
public dcRouterRef: DcRouter;
|
|
10
|
-
public server
|
|
10
|
+
public server!: plugins.typedserver.utilityservers.UtilityWebsiteServer;
|
|
11
11
|
|
|
12
12
|
// Main TypedRouter — unauthenticated endpoints (login/logout/verify) and own-auth handlers
|
|
13
13
|
public typedrouter = new plugins.typedrequest.TypedRouter();
|
|
@@ -17,17 +17,17 @@ export class OpsServer {
|
|
|
17
17
|
public adminRouter = new plugins.typedrequest.TypedRouter<{ request: { identity: interfaces.data.IIdentity } }>();
|
|
18
18
|
|
|
19
19
|
// Handler instances
|
|
20
|
-
public adminHandler
|
|
21
|
-
private configHandler
|
|
22
|
-
private logsHandler
|
|
23
|
-
private securityHandler
|
|
24
|
-
private statsHandler
|
|
25
|
-
private radiusHandler
|
|
26
|
-
private emailOpsHandler
|
|
27
|
-
private certificateHandler
|
|
28
|
-
private remoteIngressHandler
|
|
29
|
-
private routeManagementHandler
|
|
30
|
-
private apiTokenHandler
|
|
20
|
+
public adminHandler!: handlers.AdminHandler;
|
|
21
|
+
private configHandler!: handlers.ConfigHandler;
|
|
22
|
+
private logsHandler!: handlers.LogsHandler;
|
|
23
|
+
private securityHandler!: handlers.SecurityHandler;
|
|
24
|
+
private statsHandler!: handlers.StatsHandler;
|
|
25
|
+
private radiusHandler!: handlers.RadiusHandler;
|
|
26
|
+
private emailOpsHandler!: handlers.EmailOpsHandler;
|
|
27
|
+
private certificateHandler!: handlers.CertificateHandler;
|
|
28
|
+
private remoteIngressHandler!: handlers.RemoteIngressHandler;
|
|
29
|
+
private routeManagementHandler!: handlers.RouteManagementHandler;
|
|
30
|
+
private apiTokenHandler!: handlers.ApiTokenHandler;
|
|
31
31
|
|
|
32
32
|
constructor(dcRouterRefArg: DcRouter) {
|
|
33
33
|
this.dcRouterRef = dcRouterRefArg;
|
|
@@ -39,7 +39,7 @@ export class OpsServer {
|
|
|
39
39
|
public async start() {
|
|
40
40
|
this.server = new plugins.typedserver.utilityservers.UtilityWebsiteServer({
|
|
41
41
|
domain: 'localhost',
|
|
42
|
-
feedMetadata:
|
|
42
|
+
feedMetadata: undefined,
|
|
43
43
|
serveDir: paths.distServe,
|
|
44
44
|
});
|
|
45
45
|
|
|
@@ -12,7 +12,7 @@ export class AdminHandler {
|
|
|
12
12
|
public typedrouter = new plugins.typedrequest.TypedRouter();
|
|
13
13
|
|
|
14
14
|
// JWT instance
|
|
15
|
-
public smartjwtInstance
|
|
15
|
+
public smartjwtInstance!: plugins.smartjwt.SmartJwt<IJwtData>;
|
|
16
16
|
|
|
17
17
|
// Simple in-memory user storage (in production, use proper database)
|
|
18
18
|
private users = new Map<string, {
|
|
@@ -311,8 +311,8 @@ export class CertificateHandler {
|
|
|
311
311
|
}
|
|
312
312
|
}
|
|
313
313
|
return { success: true, message: `Certificate reprovisioning triggered for route '${routeName}'` };
|
|
314
|
-
} catch (err) {
|
|
315
|
-
return { success: false, message: err.message || 'Failed to reprovision certificate' };
|
|
314
|
+
} catch (err: unknown) {
|
|
315
|
+
return { success: false, message: (err as Error).message || 'Failed to reprovision certificate' };
|
|
316
316
|
}
|
|
317
317
|
}
|
|
318
318
|
|
|
@@ -340,8 +340,8 @@ export class CertificateHandler {
|
|
|
340
340
|
try {
|
|
341
341
|
await dcRouter.smartAcme.getCertificateForDomain(domain);
|
|
342
342
|
return { success: true, message: `Certificate reprovisioning triggered for domain '${domain}'` };
|
|
343
|
-
} catch (err) {
|
|
344
|
-
return { success: false, message: err.message || `Failed to reprovision certificate for ${domain}` };
|
|
343
|
+
} catch (err: unknown) {
|
|
344
|
+
return { success: false, message: (err as Error).message || `Failed to reprovision certificate for ${domain}` };
|
|
345
345
|
}
|
|
346
346
|
}
|
|
347
347
|
|
|
@@ -351,8 +351,8 @@ export class CertificateHandler {
|
|
|
351
351
|
try {
|
|
352
352
|
await smartProxy.provisionCertificate(routeNames[0]);
|
|
353
353
|
return { success: true, message: `Certificate reprovisioning triggered for domain '${domain}' via route '${routeNames[0]}'` };
|
|
354
|
-
} catch (err) {
|
|
355
|
-
return { success: false, message: err.message || `Failed to reprovision certificate for ${domain}` };
|
|
354
|
+
} catch (err: unknown) {
|
|
355
|
+
return { success: false, message: (err as Error).message || `Failed to reprovision certificate for ${domain}` };
|
|
356
356
|
}
|
|
357
357
|
}
|
|
358
358
|
|
|
@@ -52,8 +52,8 @@ export class RadiusHandler {
|
|
|
52
52
|
try {
|
|
53
53
|
await radiusServer.addClient(dataArg.client);
|
|
54
54
|
return { success: true };
|
|
55
|
-
} catch (error) {
|
|
56
|
-
return { success: false, message: error.message };
|
|
55
|
+
} catch (error: unknown) {
|
|
56
|
+
return { success: false, message: (error as Error).message };
|
|
57
57
|
}
|
|
58
58
|
}
|
|
59
59
|
)
|
|
@@ -144,8 +144,8 @@ export class RadiusHandler {
|
|
|
144
144
|
updatedAt: mapping.updatedAt,
|
|
145
145
|
},
|
|
146
146
|
};
|
|
147
|
-
} catch (error) {
|
|
148
|
-
return { success: false, message: error.message };
|
|
147
|
+
} catch (error: unknown) {
|
|
148
|
+
return { success: false, message: (error as Error).message };
|
|
149
149
|
}
|
|
150
150
|
}
|
|
151
151
|
)
|
|
@@ -279,7 +279,7 @@ export class StatsHandler {
|
|
|
279
279
|
if (sections.network && this.opsServerRef.dcRouterRef.metricsManager) {
|
|
280
280
|
promises.push(
|
|
281
281
|
(async () => {
|
|
282
|
-
const stats = await this.opsServerRef.dcRouterRef.metricsManager
|
|
282
|
+
const stats = await this.opsServerRef.dcRouterRef.metricsManager!.getNetworkStats();
|
|
283
283
|
const serverStats = await this.collectServerStats();
|
|
284
284
|
|
|
285
285
|
// Build per-IP bandwidth lookup from throughputByIP
|