@serve.zone/dcrouter 13.17.5 → 13.17.9
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 +2 -2
- package/dist_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/classes.dcrouter.d.ts +1 -1
- package/dist_ts/classes.dcrouter.js +12 -11
- package/dist_ts/config/classes.route-config-manager.d.ts +7 -2
- package/dist_ts/config/classes.route-config-manager.js +64 -38
- package/dist_ts/dns/manager.dns.js +3 -3
- package/dist_ts/monitoring/classes.metricsmanager.d.ts +1 -1
- package/dist_ts/monitoring/classes.metricsmanager.js +20 -19
- package/dist_ts/opsserver/handlers/certificate.handler.js +6 -9
- package/dist_ts_web/00_commitinfo_data.js +1 -1
- package/dist_ts_web/elements/network/ops-view-network-activity.js +2 -2
- package/package.json +6 -6
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/classes.dcrouter.ts +9 -9
- package/ts/config/classes.route-config-manager.ts +80 -36
- package/ts/dns/manager.dns.ts +2 -2
- package/ts/monitoring/classes.metricsmanager.ts +20 -19
- package/ts/opsserver/handlers/certificate.handler.ts +4 -5
- package/ts_web/00_commitinfo_data.ts +1 -1
- package/ts_web/elements/network/ops-view-network-activity.ts +1 -1
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@serve.zone/dcrouter",
|
|
3
3
|
"private": false,
|
|
4
|
-
"version": "13.17.
|
|
4
|
+
"version": "13.17.9",
|
|
5
5
|
"description": "A multifaceted routing service handling mail and SMS delivery functions.",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"exports": {
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
"author": "Task Venture Capital GmbH",
|
|
13
13
|
"license": "MIT",
|
|
14
14
|
"scripts": {
|
|
15
|
-
"test": "(tstest test/ --logfile --timeout 60)",
|
|
15
|
+
"test": "(tstest test/ --verbose --logfile --timeout 60)",
|
|
16
16
|
"start": "(node ./cli.js)",
|
|
17
17
|
"startTs": "(node cli.ts.js)",
|
|
18
18
|
"build": "(tsbuild tsfolders --allowimplicitany && npm run bundle)",
|
|
@@ -51,10 +51,10 @@
|
|
|
51
51
|
"@push.rocks/smartmetrics": "^3.0.3",
|
|
52
52
|
"@push.rocks/smartmigration": "1.2.0",
|
|
53
53
|
"@push.rocks/smartmta": "^5.3.1",
|
|
54
|
-
"@push.rocks/smartnetwork": "^4.
|
|
54
|
+
"@push.rocks/smartnetwork": "^4.6.0",
|
|
55
55
|
"@push.rocks/smartpath": "^6.0.0",
|
|
56
56
|
"@push.rocks/smartpromise": "^4.2.3",
|
|
57
|
-
"@push.rocks/smartproxy": "^27.
|
|
57
|
+
"@push.rocks/smartproxy": "^27.7.4",
|
|
58
58
|
"@push.rocks/smartradius": "^1.1.1",
|
|
59
59
|
"@push.rocks/smartrequest": "^5.0.1",
|
|
60
60
|
"@push.rocks/smartrx": "^3.0.10",
|
|
@@ -62,12 +62,12 @@
|
|
|
62
62
|
"@push.rocks/smartunique": "^3.0.9",
|
|
63
63
|
"@push.rocks/smartvpn": "1.19.2",
|
|
64
64
|
"@push.rocks/taskbuffer": "^8.0.2",
|
|
65
|
-
"@serve.zone/catalog": "^2.12.
|
|
65
|
+
"@serve.zone/catalog": "^2.12.4",
|
|
66
66
|
"@serve.zone/interfaces": "^5.3.0",
|
|
67
67
|
"@serve.zone/remoteingress": "^4.15.3",
|
|
68
68
|
"@tsclass/tsclass": "^9.5.0",
|
|
69
69
|
"@types/qrcode": "^1.5.6",
|
|
70
|
-
"lru-cache": "^11.3.
|
|
70
|
+
"lru-cache": "^11.3.5",
|
|
71
71
|
"qrcode": "^1.5.4",
|
|
72
72
|
"uuid": "^13.0.0"
|
|
73
73
|
},
|
package/ts/00_commitinfo_data.ts
CHANGED
package/ts/classes.dcrouter.ts
CHANGED
|
@@ -315,7 +315,8 @@ export class DcRouter {
|
|
|
315
315
|
// Seed routes assembled during setupSmartProxy, passed to RouteConfigManager for DB seeding
|
|
316
316
|
private seedConfigRoutes: plugins.smartproxy.IRouteConfig[] = [];
|
|
317
317
|
private seedEmailRoutes: plugins.smartproxy.IRouteConfig[] = [];
|
|
318
|
-
|
|
318
|
+
// Runtime-only DoH routes. These carry live socket handlers and must never be persisted.
|
|
319
|
+
private runtimeDnsRoutes: plugins.smartproxy.IRouteConfig[] = [];
|
|
319
320
|
|
|
320
321
|
// Environment access
|
|
321
322
|
private qenv = new plugins.qenv.Qenv('./', '.nogit/');
|
|
@@ -580,13 +581,13 @@ export class DcRouter {
|
|
|
580
581
|
this.tunnelManager.syncAllowedEdges();
|
|
581
582
|
}
|
|
582
583
|
},
|
|
584
|
+
() => this.runtimeDnsRoutes,
|
|
583
585
|
);
|
|
584
586
|
this.apiTokenManager = new ApiTokenManager();
|
|
585
587
|
await this.apiTokenManager.initialize();
|
|
586
588
|
await this.routeConfigManager.initialize(
|
|
587
589
|
this.seedConfigRoutes as import('../ts_interfaces/data/remoteingress.js').IDcRouterRouteConfig[],
|
|
588
590
|
this.seedEmailRoutes as import('../ts_interfaces/data/remoteingress.js').IDcRouterRouteConfig[],
|
|
589
|
-
this.seedDnsRoutes as import('../ts_interfaces/data/remoteingress.js').IDcRouterRouteConfig[],
|
|
590
591
|
);
|
|
591
592
|
await this.targetProfileManager.normalizeAllRouteRefs();
|
|
592
593
|
|
|
@@ -892,7 +893,7 @@ export class DcRouter {
|
|
|
892
893
|
this.smartProxy = undefined;
|
|
893
894
|
}
|
|
894
895
|
|
|
895
|
-
// Assemble seed routes from constructor config — these will be seeded into DB
|
|
896
|
+
// Assemble serializable seed routes from constructor config — these will be seeded into DB
|
|
896
897
|
// by RouteConfigManager.initialize() when the ConfigManagers service starts.
|
|
897
898
|
this.seedConfigRoutes = (this.options.smartProxyConfig?.routes || []) as plugins.smartproxy.IRouteConfig[];
|
|
898
899
|
logger.log('info', `Found ${this.seedConfigRoutes.length} routes in config`);
|
|
@@ -903,17 +904,17 @@ export class DcRouter {
|
|
|
903
904
|
logger.log('debug', 'Email routes generated', { routes: JSON.stringify(this.seedEmailRoutes) });
|
|
904
905
|
}
|
|
905
906
|
|
|
906
|
-
this.
|
|
907
|
+
this.runtimeDnsRoutes = [];
|
|
907
908
|
if (this.options.dnsNsDomains && this.options.dnsNsDomains.length > 0) {
|
|
908
|
-
this.
|
|
909
|
-
logger.log('debug', `DNS routes for nameservers ${this.options.dnsNsDomains.join(', ')}`, { routes: JSON.stringify(this.
|
|
909
|
+
this.runtimeDnsRoutes = this.generateDnsRoutes();
|
|
910
|
+
logger.log('debug', `DNS routes for nameservers ${this.options.dnsNsDomains.join(', ')}`, { routes: JSON.stringify(this.runtimeDnsRoutes) });
|
|
910
911
|
}
|
|
911
912
|
|
|
912
913
|
// Combined routes for SmartProxy bootstrap (before DB routes are loaded)
|
|
913
914
|
let routes: plugins.smartproxy.IRouteConfig[] = [
|
|
914
915
|
...this.seedConfigRoutes,
|
|
915
916
|
...this.seedEmailRoutes,
|
|
916
|
-
...this.
|
|
917
|
+
...this.runtimeDnsRoutes,
|
|
917
918
|
];
|
|
918
919
|
|
|
919
920
|
// Build the ACME options for SmartProxy from the DB-backed AcmeConfigManager.
|
|
@@ -1463,7 +1464,6 @@ export class DcRouter {
|
|
|
1463
1464
|
await this.routeConfigManager.initialize(
|
|
1464
1465
|
this.seedConfigRoutes as import('../ts_interfaces/data/remoteingress.js').IDcRouterRouteConfig[],
|
|
1465
1466
|
this.seedEmailRoutes as import('../ts_interfaces/data/remoteingress.js').IDcRouterRouteConfig[],
|
|
1466
|
-
this.seedDnsRoutes as import('../ts_interfaces/data/remoteingress.js').IDcRouterRouteConfig[],
|
|
1467
1467
|
);
|
|
1468
1468
|
}
|
|
1469
1469
|
|
|
@@ -2185,7 +2185,7 @@ export class DcRouter {
|
|
|
2185
2185
|
// Pass current bootstrap routes so the manager can derive edge ports initially.
|
|
2186
2186
|
// Once RouteConfigManager applies the full DB set, the onRoutesApplied callback
|
|
2187
2187
|
// will push the complete merged routes here.
|
|
2188
|
-
const bootstrapRoutes = [...this.seedConfigRoutes, ...this.seedEmailRoutes, ...this.
|
|
2188
|
+
const bootstrapRoutes = [...this.seedConfigRoutes, ...this.seedEmailRoutes, ...this.runtimeDnsRoutes];
|
|
2189
2189
|
this.remoteIngressManager.setRoutes(bootstrapRoutes as any[]);
|
|
2190
2190
|
|
|
2191
2191
|
// If ConfigManagers finished before us, re-apply routes
|
|
@@ -55,6 +55,7 @@ export class RouteConfigManager {
|
|
|
55
55
|
private getVpnClientIpsForRoute?: (route: IDcRouterRouteConfig, routeId?: string) => TIpAllowEntry[],
|
|
56
56
|
private referenceResolver?: ReferenceResolver,
|
|
57
57
|
private onRoutesApplied?: (routes: plugins.smartproxy.IRouteConfig[]) => void,
|
|
58
|
+
private getRuntimeRoutes?: () => plugins.smartproxy.IRouteConfig[],
|
|
58
59
|
) {}
|
|
59
60
|
|
|
60
61
|
/** Expose routes map for reference resolution lookups. */
|
|
@@ -63,7 +64,8 @@ export class RouteConfigManager {
|
|
|
63
64
|
}
|
|
64
65
|
|
|
65
66
|
/**
|
|
66
|
-
* Load persisted routes, seed config/email/dns routes,
|
|
67
|
+
* Load persisted routes, seed serializable config/email/dns routes,
|
|
68
|
+
* compute warnings, and apply the combined DB-backed + runtime route set to SmartProxy.
|
|
67
69
|
*/
|
|
68
70
|
public async initialize(
|
|
69
71
|
configRoutes: IDcRouterRouteConfig[] = [],
|
|
@@ -284,23 +286,40 @@ export class RouteConfigManager {
|
|
|
284
286
|
|
|
285
287
|
private async loadRoutes(): Promise<void> {
|
|
286
288
|
const docs = await RouteDoc.findAll();
|
|
289
|
+
let prunedRuntimeRoutes = 0;
|
|
290
|
+
|
|
287
291
|
for (const doc of docs) {
|
|
288
|
-
if (doc.id)
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
292
|
+
if (!doc.id) continue;
|
|
293
|
+
|
|
294
|
+
const storedRoute: IRoute = {
|
|
295
|
+
id: doc.id,
|
|
296
|
+
route: doc.route,
|
|
297
|
+
enabled: doc.enabled,
|
|
298
|
+
createdAt: doc.createdAt,
|
|
299
|
+
updatedAt: doc.updatedAt,
|
|
300
|
+
createdBy: doc.createdBy,
|
|
301
|
+
origin: doc.origin || 'api',
|
|
302
|
+
metadata: doc.metadata,
|
|
303
|
+
};
|
|
304
|
+
|
|
305
|
+
if (this.isPersistedRuntimeRoute(storedRoute)) {
|
|
306
|
+
await doc.delete();
|
|
307
|
+
prunedRuntimeRoutes++;
|
|
308
|
+
logger.log(
|
|
309
|
+
'warn',
|
|
310
|
+
`Removed persisted runtime-only route '${storedRoute.route.name || storedRoute.id}' (${storedRoute.id}) from RouteDoc`,
|
|
311
|
+
);
|
|
312
|
+
continue;
|
|
299
313
|
}
|
|
314
|
+
|
|
315
|
+
this.routes.set(doc.id, storedRoute);
|
|
300
316
|
}
|
|
301
317
|
if (this.routes.size > 0) {
|
|
302
318
|
logger.log('info', `Loaded ${this.routes.size} route(s) from database`);
|
|
303
319
|
}
|
|
320
|
+
if (prunedRuntimeRoutes > 0) {
|
|
321
|
+
logger.log('info', `Pruned ${prunedRuntimeRoutes} persisted runtime-only route(s) from RouteDoc`);
|
|
322
|
+
}
|
|
304
323
|
}
|
|
305
324
|
|
|
306
325
|
private async persistRoute(stored: IRoute): Promise<void> {
|
|
@@ -389,36 +408,18 @@ export class RouteConfigManager {
|
|
|
389
408
|
|
|
390
409
|
const enabledRoutes: plugins.smartproxy.IRouteConfig[] = [];
|
|
391
410
|
|
|
392
|
-
const http3Config = this.getHttp3Config?.();
|
|
393
|
-
const vpnCallback = this.getVpnClientIpsForRoute;
|
|
394
|
-
|
|
395
|
-
// Helper: inject VPN security into a vpnOnly route
|
|
396
|
-
const injectVpn = (route: plugins.smartproxy.IRouteConfig, routeId?: string): plugins.smartproxy.IRouteConfig => {
|
|
397
|
-
if (!vpnCallback) return route;
|
|
398
|
-
const dcRoute = route as IDcRouterRouteConfig;
|
|
399
|
-
if (!dcRoute.vpnOnly) return route;
|
|
400
|
-
const vpnEntries = vpnCallback(dcRoute, routeId);
|
|
401
|
-
const existingEntries = route.security?.ipAllowList || [];
|
|
402
|
-
return {
|
|
403
|
-
...route,
|
|
404
|
-
security: {
|
|
405
|
-
...route.security,
|
|
406
|
-
ipAllowList: [...existingEntries, ...vpnEntries],
|
|
407
|
-
},
|
|
408
|
-
};
|
|
409
|
-
};
|
|
410
|
-
|
|
411
411
|
// Add all enabled routes with HTTP/3 and VPN augmentation
|
|
412
412
|
for (const route of this.routes.values()) {
|
|
413
413
|
if (route.enabled) {
|
|
414
|
-
|
|
415
|
-
if (http3Config?.enabled !== false) {
|
|
416
|
-
r = augmentRouteWithHttp3(r, { enabled: true, ...http3Config });
|
|
417
|
-
}
|
|
418
|
-
enabledRoutes.push(injectVpn(r, route.id));
|
|
414
|
+
enabledRoutes.push(this.prepareRouteForApply(route.route, route.id));
|
|
419
415
|
}
|
|
420
416
|
}
|
|
421
417
|
|
|
418
|
+
const runtimeRoutes = this.getRuntimeRoutes?.() || [];
|
|
419
|
+
for (const route of runtimeRoutes) {
|
|
420
|
+
enabledRoutes.push(this.prepareRouteForApply(route));
|
|
421
|
+
}
|
|
422
|
+
|
|
422
423
|
await smartProxy.updateRoutes(enabledRoutes);
|
|
423
424
|
|
|
424
425
|
// Notify listeners (e.g. RemoteIngressManager) of the route set
|
|
@@ -429,4 +430,47 @@ export class RouteConfigManager {
|
|
|
429
430
|
logger.log('info', `Applied ${enabledRoutes.length} routes to SmartProxy (${this.routes.size} total)`);
|
|
430
431
|
});
|
|
431
432
|
}
|
|
433
|
+
|
|
434
|
+
private prepareRouteForApply(
|
|
435
|
+
route: plugins.smartproxy.IRouteConfig,
|
|
436
|
+
routeId?: string,
|
|
437
|
+
): plugins.smartproxy.IRouteConfig {
|
|
438
|
+
let preparedRoute = route;
|
|
439
|
+
const http3Config = this.getHttp3Config?.();
|
|
440
|
+
|
|
441
|
+
if (http3Config?.enabled !== false) {
|
|
442
|
+
preparedRoute = augmentRouteWithHttp3(preparedRoute, { enabled: true, ...http3Config });
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
return this.injectVpnSecurity(preparedRoute, routeId);
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
private injectVpnSecurity(
|
|
449
|
+
route: plugins.smartproxy.IRouteConfig,
|
|
450
|
+
routeId?: string,
|
|
451
|
+
): plugins.smartproxy.IRouteConfig {
|
|
452
|
+
const vpnCallback = this.getVpnClientIpsForRoute;
|
|
453
|
+
if (!vpnCallback) return route;
|
|
454
|
+
|
|
455
|
+
const dcRoute = route as IDcRouterRouteConfig;
|
|
456
|
+
if (!dcRoute.vpnOnly) return route;
|
|
457
|
+
|
|
458
|
+
const vpnEntries = vpnCallback(dcRoute, routeId);
|
|
459
|
+
const existingEntries = route.security?.ipAllowList || [];
|
|
460
|
+
return {
|
|
461
|
+
...route,
|
|
462
|
+
security: {
|
|
463
|
+
...route.security,
|
|
464
|
+
ipAllowList: [...existingEntries, ...vpnEntries],
|
|
465
|
+
},
|
|
466
|
+
};
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
private isPersistedRuntimeRoute(storedRoute: IRoute): boolean {
|
|
470
|
+
const routeName = storedRoute.route.name || '';
|
|
471
|
+
const actionType = storedRoute.route.action?.type;
|
|
472
|
+
|
|
473
|
+
return (routeName.startsWith('dns-over-https-') && actionType === 'socket-handler')
|
|
474
|
+
|| (storedRoute.origin === 'dns' && actionType === 'socket-handler');
|
|
475
|
+
}
|
|
432
476
|
}
|
package/ts/dns/manager.dns.ts
CHANGED
|
@@ -97,8 +97,8 @@ export class DnsManager {
|
|
|
97
97
|
if (hasLegacyConfig) {
|
|
98
98
|
logger.log(
|
|
99
99
|
'warn',
|
|
100
|
-
'DnsManager: DB has DomainDoc entries — ignoring legacy dnsScopes/dnsRecords
|
|
101
|
-
'
|
|
100
|
+
'DnsManager: DB has DomainDoc entries — ignoring legacy dnsScopes/dnsRecords constructor config. ' +
|
|
101
|
+
'dnsNsDomains is still required for nameserver and DoH bootstrap unless that moves into DB-backed config.',
|
|
102
102
|
);
|
|
103
103
|
}
|
|
104
104
|
return;
|
|
@@ -733,16 +733,17 @@ export class MetricsManager {
|
|
|
733
733
|
}
|
|
734
734
|
}
|
|
735
735
|
|
|
736
|
-
// Map route
|
|
736
|
+
// Map canonical route key → domains from route config
|
|
737
737
|
const routeDomains = new Map<string, string[]>();
|
|
738
738
|
if (this.dcRouter.smartProxy) {
|
|
739
739
|
for (const route of this.dcRouter.smartProxy.routeManager.getRoutes()) {
|
|
740
|
-
|
|
740
|
+
const routeKey = route.name || route.id;
|
|
741
|
+
if (!routeKey || !route.match.domains) continue;
|
|
741
742
|
const domains = Array.isArray(route.match.domains)
|
|
742
743
|
? route.match.domains
|
|
743
744
|
: [route.match.domains];
|
|
744
745
|
if (domains.length > 0) {
|
|
745
|
-
routeDomains.set(
|
|
746
|
+
routeDomains.set(routeKey, domains);
|
|
746
747
|
}
|
|
747
748
|
}
|
|
748
749
|
}
|
|
@@ -753,23 +754,23 @@ export class MetricsManager {
|
|
|
753
754
|
if (entry.domain) allKnownDomains.add(entry.domain);
|
|
754
755
|
}
|
|
755
756
|
|
|
756
|
-
// Build reverse map: concrete domain → route
|
|
757
|
+
// Build reverse map: concrete domain → canonical route key(s)
|
|
757
758
|
const domainToRoutes = new Map<string, string[]>();
|
|
758
|
-
for (const [
|
|
759
|
+
for (const [routeKey, domains] of routeDomains) {
|
|
759
760
|
for (const pattern of domains) {
|
|
760
761
|
if (pattern.includes('*')) {
|
|
761
762
|
const regex = new RegExp('^' + pattern.replace(/\./g, '\\.').replace(/\*/g, '[^.]+') + '$');
|
|
762
763
|
for (const knownDomain of allKnownDomains) {
|
|
763
764
|
if (regex.test(knownDomain)) {
|
|
764
765
|
const existing = domainToRoutes.get(knownDomain);
|
|
765
|
-
if (existing) { existing.push(
|
|
766
|
-
else { domainToRoutes.set(knownDomain, [
|
|
766
|
+
if (existing) { existing.push(routeKey); }
|
|
767
|
+
else { domainToRoutes.set(knownDomain, [routeKey]); }
|
|
767
768
|
}
|
|
768
769
|
}
|
|
769
770
|
} else {
|
|
770
771
|
const existing = domainToRoutes.get(pattern);
|
|
771
|
-
if (existing) { existing.push(
|
|
772
|
-
else { domainToRoutes.set(pattern, [
|
|
772
|
+
if (existing) { existing.push(routeKey); }
|
|
773
|
+
else { domainToRoutes.set(pattern, [routeKey]); }
|
|
773
774
|
}
|
|
774
775
|
}
|
|
775
776
|
}
|
|
@@ -777,10 +778,10 @@ export class MetricsManager {
|
|
|
777
778
|
// For each route, compute the total request count across all its resolved domains
|
|
778
779
|
// so we can distribute throughput/connections proportionally
|
|
779
780
|
const routeTotalRequests = new Map<string, number>();
|
|
780
|
-
for (const [domain,
|
|
781
|
+
for (const [domain, routeKeys] of domainToRoutes) {
|
|
781
782
|
const reqs = domainRequestTotals.get(domain) || 0;
|
|
782
|
-
for (const
|
|
783
|
-
routeTotalRequests.set(
|
|
783
|
+
for (const routeKey of routeKeys) {
|
|
784
|
+
routeTotalRequests.set(routeKey, (routeTotalRequests.get(routeKey) || 0) + reqs);
|
|
784
785
|
}
|
|
785
786
|
}
|
|
786
787
|
|
|
@@ -793,16 +794,16 @@ export class MetricsManager {
|
|
|
793
794
|
requestCount: number;
|
|
794
795
|
}>();
|
|
795
796
|
|
|
796
|
-
for (const [domain,
|
|
797
|
+
for (const [domain, routeKeys] of domainToRoutes) {
|
|
797
798
|
const domainReqs = domainRequestTotals.get(domain) || 0;
|
|
798
799
|
let totalConns = 0;
|
|
799
800
|
let totalIn = 0;
|
|
800
801
|
let totalOut = 0;
|
|
801
802
|
|
|
802
|
-
for (const
|
|
803
|
-
const conns = connectionsByRoute.get(
|
|
804
|
-
const tp = throughputByRoute.get(
|
|
805
|
-
const routeTotal = routeTotalRequests.get(
|
|
803
|
+
for (const routeKey of routeKeys) {
|
|
804
|
+
const conns = connectionsByRoute.get(routeKey) || 0;
|
|
805
|
+
const tp = throughputByRoute.get(routeKey) || { in: 0, out: 0 };
|
|
806
|
+
const routeTotal = routeTotalRequests.get(routeKey) || 0;
|
|
806
807
|
|
|
807
808
|
const share = routeTotal > 0 ? domainReqs / routeTotal : 0;
|
|
808
809
|
totalConns += conns * share;
|
|
@@ -814,7 +815,7 @@ export class MetricsManager {
|
|
|
814
815
|
activeConnections: Math.round(totalConns),
|
|
815
816
|
bytesInPerSec: totalIn,
|
|
816
817
|
bytesOutPerSec: totalOut,
|
|
817
|
-
routeCount:
|
|
818
|
+
routeCount: routeKeys.length,
|
|
818
819
|
requestCount: domainReqs,
|
|
819
820
|
});
|
|
820
821
|
}
|
|
@@ -990,4 +991,4 @@ export class MetricsManager {
|
|
|
990
991
|
|
|
991
992
|
return { queries };
|
|
992
993
|
}
|
|
993
|
-
}
|
|
994
|
+
}
|
|
@@ -198,12 +198,11 @@ export class CertificateHandler {
|
|
|
198
198
|
try {
|
|
199
199
|
const rustStatus = await smartProxy.getCertificateStatus(info.routeNames[0]);
|
|
200
200
|
if (rustStatus) {
|
|
201
|
-
if (rustStatus.
|
|
202
|
-
|
|
203
|
-
if (rustStatus.issuedAt) issuedAt = rustStatus.issuedAt;
|
|
204
|
-
if (rustStatus.status === 'valid' || rustStatus.status === 'expired') {
|
|
205
|
-
status = rustStatus.status;
|
|
201
|
+
if (rustStatus.expiresAt > 0) {
|
|
202
|
+
expiryDate = new Date(rustStatus.expiresAt).toISOString();
|
|
206
203
|
}
|
|
204
|
+
if (rustStatus.source) issuer = rustStatus.source;
|
|
205
|
+
status = rustStatus.isValid ? 'valid' : 'expired';
|
|
207
206
|
}
|
|
208
207
|
} catch {
|
|
209
208
|
// Rust bridge may not support this command yet — ignore
|
|
@@ -374,7 +374,7 @@ export class OpsViewNetworkActivity extends DeesElement {
|
|
|
374
374
|
type: 'number',
|
|
375
375
|
icon: 'lucide:Plug',
|
|
376
376
|
color: activeConnections > 100 ? '#f59e0b' : '#22c55e',
|
|
377
|
-
description: `Total: ${this.
|
|
377
|
+
description: `Total: ${this.formatNumber(this.statsState.serverStats?.totalConnections || 0)} connections`,
|
|
378
378
|
actions: [
|
|
379
379
|
{
|
|
380
380
|
name: 'View Details',
|