@serve.zone/dcrouter 11.9.1 → 11.10.1
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 +627 -569
- package/dist_ts/00_commitinfo_data.js +2 -2
- package/dist_ts/monitoring/classes.metricsmanager.d.ts +1 -0
- package/dist_ts/monitoring/classes.metricsmanager.js +65 -1
- package/dist_ts/opsserver/handlers/security.handler.js +3 -1
- package/dist_ts/opsserver/handlers/stats.handler.js +2 -1
- package/dist_ts_interfaces/data/stats.d.ts +22 -0
- package/dist_ts_interfaces/requests/stats.d.ts +1 -0
- package/dist_ts_web/00_commitinfo_data.js +2 -2
- package/dist_ts_web/appstate.d.ts +1 -0
- package/dist_ts_web/appstate.js +3 -1
- package/dist_ts_web/elements/ops-view-network.d.ts +2 -0
- package/dist_ts_web/elements/ops-view-network.js +132 -1
- package/package.json +3 -3
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/monitoring/classes.metricsmanager.ts +69 -0
- package/ts/opsserver/handlers/security.handler.ts +2 -0
- package/ts/opsserver/handlers/stats.handler.ts +1 -0
- package/ts_web/00_commitinfo_data.ts +1 -1
- package/ts_web/appstate.ts +3 -0
- package/ts_web/elements/ops-view-network.ts +136 -0
|
@@ -558,6 +558,7 @@ export class MetricsManager {
|
|
|
558
558
|
throughputByIP: new Map<string, { in: number; out: number }>(),
|
|
559
559
|
requestsPerSecond: 0,
|
|
560
560
|
requestsTotal: 0,
|
|
561
|
+
backends: [] as Array<any>,
|
|
561
562
|
};
|
|
562
563
|
}
|
|
563
564
|
|
|
@@ -590,6 +591,73 @@ export class MetricsManager {
|
|
|
590
591
|
const requestsPerSecond = proxyMetrics.requests.perSecond();
|
|
591
592
|
const requestsTotal = proxyMetrics.requests.total();
|
|
592
593
|
|
|
594
|
+
// Collect backend protocol data
|
|
595
|
+
const backendMetrics = proxyMetrics.backends.byBackend();
|
|
596
|
+
const protocolCache = proxyMetrics.backends.detectedProtocols();
|
|
597
|
+
|
|
598
|
+
// Index protocol cache by "host:port"
|
|
599
|
+
const cacheByKey = new Map<string, (typeof protocolCache)[number]>();
|
|
600
|
+
for (const entry of protocolCache) {
|
|
601
|
+
cacheByKey.set(`${entry.host}:${entry.port}`, entry);
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
const backends: Array<any> = [];
|
|
605
|
+
const seen = new Set<string>();
|
|
606
|
+
|
|
607
|
+
for (const [key, bm] of backendMetrics) {
|
|
608
|
+
seen.add(key);
|
|
609
|
+
const cache = cacheByKey.get(key);
|
|
610
|
+
backends.push({
|
|
611
|
+
backend: key,
|
|
612
|
+
domain: cache?.domain ?? null,
|
|
613
|
+
protocol: bm.protocol,
|
|
614
|
+
activeConnections: bm.activeConnections,
|
|
615
|
+
totalConnections: bm.totalConnections,
|
|
616
|
+
connectErrors: bm.connectErrors,
|
|
617
|
+
handshakeErrors: bm.handshakeErrors,
|
|
618
|
+
requestErrors: bm.requestErrors,
|
|
619
|
+
avgConnectTimeMs: Math.round(bm.avgConnectTimeMs * 10) / 10,
|
|
620
|
+
poolHitRate: Math.round(bm.poolHitRate * 1000) / 1000,
|
|
621
|
+
h2Failures: bm.h2Failures,
|
|
622
|
+
h2Suppressed: cache?.h2Suppressed ?? false,
|
|
623
|
+
h3Suppressed: cache?.h3Suppressed ?? false,
|
|
624
|
+
h2CooldownRemainingSecs: cache?.h2CooldownRemainingSecs ?? null,
|
|
625
|
+
h3CooldownRemainingSecs: cache?.h3CooldownRemainingSecs ?? null,
|
|
626
|
+
h2ConsecutiveFailures: cache?.h2ConsecutiveFailures ?? null,
|
|
627
|
+
h3ConsecutiveFailures: cache?.h3ConsecutiveFailures ?? null,
|
|
628
|
+
h3Port: cache?.h3Port ?? null,
|
|
629
|
+
cacheAgeSecs: cache?.ageSecs ?? null,
|
|
630
|
+
});
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
// Include protocol cache entries with no matching backend metric
|
|
634
|
+
for (const entry of protocolCache) {
|
|
635
|
+
const key = `${entry.host}:${entry.port}`;
|
|
636
|
+
if (!seen.has(key)) {
|
|
637
|
+
backends.push({
|
|
638
|
+
backend: key,
|
|
639
|
+
domain: entry.domain,
|
|
640
|
+
protocol: entry.protocol,
|
|
641
|
+
activeConnections: 0,
|
|
642
|
+
totalConnections: 0,
|
|
643
|
+
connectErrors: 0,
|
|
644
|
+
handshakeErrors: 0,
|
|
645
|
+
requestErrors: 0,
|
|
646
|
+
avgConnectTimeMs: 0,
|
|
647
|
+
poolHitRate: 0,
|
|
648
|
+
h2Failures: 0,
|
|
649
|
+
h2Suppressed: entry.h2Suppressed,
|
|
650
|
+
h3Suppressed: entry.h3Suppressed,
|
|
651
|
+
h2CooldownRemainingSecs: entry.h2CooldownRemainingSecs,
|
|
652
|
+
h3CooldownRemainingSecs: entry.h3CooldownRemainingSecs,
|
|
653
|
+
h2ConsecutiveFailures: entry.h2ConsecutiveFailures,
|
|
654
|
+
h3ConsecutiveFailures: entry.h3ConsecutiveFailures,
|
|
655
|
+
h3Port: entry.h3Port,
|
|
656
|
+
cacheAgeSecs: entry.ageSecs,
|
|
657
|
+
});
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
|
|
593
661
|
return {
|
|
594
662
|
connectionsByIP,
|
|
595
663
|
throughputRate,
|
|
@@ -599,6 +667,7 @@ export class MetricsManager {
|
|
|
599
667
|
throughputByIP,
|
|
600
668
|
requestsPerSecond,
|
|
601
669
|
requestsTotal,
|
|
670
|
+
backends,
|
|
602
671
|
};
|
|
603
672
|
}, 1000); // 1s cache — matches typical dashboard poll interval
|
|
604
673
|
}
|
|
@@ -101,6 +101,7 @@ export class SecurityHandler {
|
|
|
101
101
|
throughputByIP,
|
|
102
102
|
requestsPerSecond: networkStats.requestsPerSecond || 0,
|
|
103
103
|
requestsTotal: networkStats.requestsTotal || 0,
|
|
104
|
+
backends: networkStats.backends || [],
|
|
104
105
|
};
|
|
105
106
|
}
|
|
106
107
|
|
|
@@ -114,6 +115,7 @@ export class SecurityHandler {
|
|
|
114
115
|
throughputByIP: [],
|
|
115
116
|
requestsPerSecond: 0,
|
|
116
117
|
requestsTotal: 0,
|
|
118
|
+
backends: [],
|
|
117
119
|
};
|
|
118
120
|
}
|
|
119
121
|
)
|
package/ts_web/appstate.ts
CHANGED
|
@@ -53,6 +53,7 @@ export interface INetworkState {
|
|
|
53
53
|
throughputHistory: Array<{ timestamp: number; in: number; out: number }>;
|
|
54
54
|
requestsPerSecond: number;
|
|
55
55
|
requestsTotal: number;
|
|
56
|
+
backends: interfaces.data.IBackendInfo[];
|
|
56
57
|
lastUpdated: number;
|
|
57
58
|
isLoading: boolean;
|
|
58
59
|
error: string | null;
|
|
@@ -148,6 +149,7 @@ export const networkStatePart = await appState.getStatePart<INetworkState>(
|
|
|
148
149
|
throughputHistory: [],
|
|
149
150
|
requestsPerSecond: 0,
|
|
150
151
|
requestsTotal: 0,
|
|
152
|
+
backends: [],
|
|
151
153
|
lastUpdated: 0,
|
|
152
154
|
isLoading: false,
|
|
153
155
|
error: null,
|
|
@@ -503,6 +505,7 @@ export const fetchNetworkStatsAction = networkStatePart.createAction(async (stat
|
|
|
503
505
|
throughputHistory: networkStatsResponse.throughputHistory || [],
|
|
504
506
|
requestsPerSecond: networkStatsResponse.requestsPerSecond || 0,
|
|
505
507
|
requestsTotal: networkStatsResponse.requestsTotal || 0,
|
|
508
|
+
backends: networkStatsResponse.backends || [],
|
|
506
509
|
lastUpdated: Date.now(),
|
|
507
510
|
isLoading: false,
|
|
508
511
|
error: null,
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { DeesElement, property, html, customElement, type TemplateResult, css, state, cssManager } from '@design.estate/dees-element';
|
|
2
2
|
import * as appstate from '../appstate.js';
|
|
3
|
+
import * as interfaces from '../../dist_ts_interfaces/index.js';
|
|
3
4
|
import { viewHostCss } from './shared/css.js';
|
|
4
5
|
import { type IStatsTile } from '@design.estate/dees-catalog';
|
|
5
6
|
|
|
@@ -198,6 +199,38 @@ export class OpsViewNetwork extends DeesElement {
|
|
|
198
199
|
color: ${cssManager.bdTheme('#00796b', '#4db6ac')};
|
|
199
200
|
}
|
|
200
201
|
|
|
202
|
+
.protocolBadge.h1 {
|
|
203
|
+
background: ${cssManager.bdTheme('#e3f2fd', '#1a2c3a')};
|
|
204
|
+
color: ${cssManager.bdTheme('#1976d2', '#5a9fd4')};
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
.protocolBadge.h2 {
|
|
208
|
+
background: ${cssManager.bdTheme('#e8f5e9', '#1a3a1a')};
|
|
209
|
+
color: ${cssManager.bdTheme('#388e3c', '#66bb6a')};
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
.protocolBadge.h3 {
|
|
213
|
+
background: ${cssManager.bdTheme('#f3e5f5', '#2a1a3a')};
|
|
214
|
+
color: ${cssManager.bdTheme('#7b1fa2', '#ba68c8')};
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
.protocolBadge.unknown {
|
|
218
|
+
background: ${cssManager.bdTheme('#f5f5f5', '#2a2a2a')};
|
|
219
|
+
color: ${cssManager.bdTheme('#757575', '#999999')};
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
.suppressionBadge {
|
|
223
|
+
display: inline-flex;
|
|
224
|
+
align-items: center;
|
|
225
|
+
padding: 2px 6px;
|
|
226
|
+
border-radius: 3px;
|
|
227
|
+
font-size: 11px;
|
|
228
|
+
font-weight: 500;
|
|
229
|
+
background: ${cssManager.bdTheme('#fff3e0', '#3a2a1a')};
|
|
230
|
+
color: ${cssManager.bdTheme('#f57c00', '#ff9933')};
|
|
231
|
+
margin-left: 4px;
|
|
232
|
+
}
|
|
233
|
+
|
|
201
234
|
.statusBadge {
|
|
202
235
|
display: inline-flex;
|
|
203
236
|
align-items: center;
|
|
@@ -265,6 +298,9 @@ export class OpsViewNetwork extends DeesElement {
|
|
|
265
298
|
<!-- Top IPs Section -->
|
|
266
299
|
${this.renderTopIPs()}
|
|
267
300
|
|
|
301
|
+
<!-- Backend Protocols Section -->
|
|
302
|
+
${this.renderBackendProtocols()}
|
|
303
|
+
|
|
268
304
|
<!-- Requests Table -->
|
|
269
305
|
<dees-table
|
|
270
306
|
.data=${this.networkRequests}
|
|
@@ -519,6 +555,106 @@ export class OpsViewNetwork extends DeesElement {
|
|
|
519
555
|
`;
|
|
520
556
|
}
|
|
521
557
|
|
|
558
|
+
private renderBackendProtocols(): TemplateResult {
|
|
559
|
+
const backends = this.networkState.backends;
|
|
560
|
+
if (!backends || backends.length === 0) {
|
|
561
|
+
return html``;
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
return html`
|
|
565
|
+
<dees-table
|
|
566
|
+
.data=${backends}
|
|
567
|
+
.displayFunction=${(item: interfaces.data.IBackendInfo) => {
|
|
568
|
+
const totalErrors = item.connectErrors + item.handshakeErrors + item.requestErrors;
|
|
569
|
+
const protocolClass = item.protocol.toLowerCase().replace(/[^a-z0-9]/g, '');
|
|
570
|
+
|
|
571
|
+
return {
|
|
572
|
+
'Backend': item.backend,
|
|
573
|
+
'Domain': item.domain || '-',
|
|
574
|
+
'Protocol': html`
|
|
575
|
+
<span class="protocolBadge ${protocolClass}">${item.protocol.toUpperCase()}</span>
|
|
576
|
+
${item.h2Suppressed ? html`<span class="suppressionBadge" title="H2 suppressed: ${item.h2ConsecutiveFailures ?? 0} failures, cooldown ${item.h2CooldownRemainingSecs ?? 0}s">H2 suppressed</span>` : ''}
|
|
577
|
+
${item.h3Suppressed ? html`<span class="suppressionBadge" title="H3 suppressed: ${item.h3ConsecutiveFailures ?? 0} failures, cooldown ${item.h3CooldownRemainingSecs ?? 0}s">H3 suppressed</span>` : ''}
|
|
578
|
+
`,
|
|
579
|
+
'Active': item.activeConnections,
|
|
580
|
+
'Total': this.formatNumber(item.totalConnections),
|
|
581
|
+
'Avg Connect': item.avgConnectTimeMs > 0 ? `${item.avgConnectTimeMs.toFixed(1)}ms` : '-',
|
|
582
|
+
'Pool Hit Rate': item.poolHitRate > 0 ? `${(item.poolHitRate * 100).toFixed(1)}%` : '-',
|
|
583
|
+
'Errors': totalErrors > 0
|
|
584
|
+
? html`<span class="statusBadge error">${totalErrors}</span>`
|
|
585
|
+
: html`<span class="statusBadge success">0</span>`,
|
|
586
|
+
'Cache Age': item.cacheAgeSecs != null ? `${Math.round(item.cacheAgeSecs)}s` : '-',
|
|
587
|
+
};
|
|
588
|
+
}}
|
|
589
|
+
.dataActions=${[
|
|
590
|
+
{
|
|
591
|
+
name: 'View Details',
|
|
592
|
+
iconName: 'lucide:info',
|
|
593
|
+
type: ['inRow', 'doubleClick', 'contextmenu'] as any,
|
|
594
|
+
actionFunc: async (actionData: any) => {
|
|
595
|
+
await this.showBackendDetails(actionData.item);
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
]}
|
|
599
|
+
heading1="Backend Protocols"
|
|
600
|
+
heading2="Auto-detected backend protocols and connection pool health"
|
|
601
|
+
searchable
|
|
602
|
+
.pagination=${false}
|
|
603
|
+
dataName="backend"
|
|
604
|
+
></dees-table>
|
|
605
|
+
`;
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
private async showBackendDetails(backend: interfaces.data.IBackendInfo) {
|
|
609
|
+
const { DeesModal } = await import('@design.estate/dees-catalog');
|
|
610
|
+
|
|
611
|
+
await DeesModal.createAndShow({
|
|
612
|
+
heading: `Backend: ${backend.backend}`,
|
|
613
|
+
content: html`
|
|
614
|
+
<div style="padding: 20px;">
|
|
615
|
+
<dees-dataview-codebox
|
|
616
|
+
.heading=${'Backend Details'}
|
|
617
|
+
progLang="json"
|
|
618
|
+
.codeToDisplay=${JSON.stringify({
|
|
619
|
+
backend: backend.backend,
|
|
620
|
+
domain: backend.domain,
|
|
621
|
+
protocol: backend.protocol,
|
|
622
|
+
activeConnections: backend.activeConnections,
|
|
623
|
+
totalConnections: backend.totalConnections,
|
|
624
|
+
avgConnectTimeMs: backend.avgConnectTimeMs,
|
|
625
|
+
poolHitRate: backend.poolHitRate,
|
|
626
|
+
errors: {
|
|
627
|
+
connect: backend.connectErrors,
|
|
628
|
+
handshake: backend.handshakeErrors,
|
|
629
|
+
request: backend.requestErrors,
|
|
630
|
+
h2Failures: backend.h2Failures,
|
|
631
|
+
},
|
|
632
|
+
suppression: {
|
|
633
|
+
h2Suppressed: backend.h2Suppressed,
|
|
634
|
+
h3Suppressed: backend.h3Suppressed,
|
|
635
|
+
h2CooldownRemainingSecs: backend.h2CooldownRemainingSecs,
|
|
636
|
+
h3CooldownRemainingSecs: backend.h3CooldownRemainingSecs,
|
|
637
|
+
h2ConsecutiveFailures: backend.h2ConsecutiveFailures,
|
|
638
|
+
h3ConsecutiveFailures: backend.h3ConsecutiveFailures,
|
|
639
|
+
},
|
|
640
|
+
h3Port: backend.h3Port,
|
|
641
|
+
cacheAgeSecs: backend.cacheAgeSecs,
|
|
642
|
+
}, null, 2)}
|
|
643
|
+
></dees-dataview-codebox>
|
|
644
|
+
</div>
|
|
645
|
+
`,
|
|
646
|
+
menuOptions: [
|
|
647
|
+
{
|
|
648
|
+
name: 'Copy Backend Key',
|
|
649
|
+
iconName: 'lucide:Copy',
|
|
650
|
+
action: async () => {
|
|
651
|
+
await navigator.clipboard.writeText(backend.backend);
|
|
652
|
+
}
|
|
653
|
+
}
|
|
654
|
+
]
|
|
655
|
+
});
|
|
656
|
+
}
|
|
657
|
+
|
|
522
658
|
private async updateNetworkData() {
|
|
523
659
|
// Track requests/sec history for the trend sparkline (moved out of render)
|
|
524
660
|
const reqPerSec = this.networkState.requestsPerSecond || 0;
|