Skip to content

Commit 562bce4

Browse files
author
wlanboy
committed
bugfixes, filter for envoy routes
1 parent a6e7a0e commit 562bce4

File tree

1 file changed

+127
-36
lines changed

1 file changed

+127
-36
lines changed

src/main/resources/public/js/k8s-client.js

Lines changed: 127 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,14 @@ const K8sClient = (() => {
66

77
async function apiFetch(endpoint) {
88
const response = await fetch(endpoint);
9-
if (!response.ok) throw new Error(`HTTP ${response.status}`);
9+
if (!response.ok) {
10+
let errorMsg = `HTTP ${response.status}`;
11+
try {
12+
const body = await response.json();
13+
if (body.error) errorMsg = body.error;
14+
} catch (_) {}
15+
throw new Error(errorMsg);
16+
}
1017
return await response.json();
1118
}
1219

@@ -25,50 +32,125 @@ const K8sClient = (() => {
2532
[configDiv, errorDiv, resourceDiv].forEach(el => { if (el) el.innerHTML = spinner; });
2633

2734
try {
28-
const [report, context] = await Promise.all([
35+
const [report, context, status] = await Promise.all([
2936
apiFetch('/api/k8s/istio/full-report'),
30-
K8sClient.loadContext()
37+
K8sClient.loadContext(),
38+
apiFetch('/api/k8s/status').catch(() => null)
3139
]);
3240

33-
// --- TAB A & B (Config & Errors) ---
34-
// (Deine bestehende Logik für diese Tabs...)
35-
configDiv.innerHTML = `<pre class="console x-small p-3 bg-dark text-success" style="border-radius:4px;">${report.reachability.activeEndpoints}</pre>`;
36-
37-
const errorEntries = Object.entries(report.healthDiagnostics.activeErrorMetrics);
38-
errorDiv.innerHTML = errorEntries.length === 0 ?
39-
`<div class="alert alert-success small">Keine Fehler.</div>` :
40-
`<table class="table table-sm small">${errorEntries.map(([k, v]) => `<tr><td>${k}</td><td>${v}</td></tr>`).join('')}</table>`;
41-
42-
43-
// --- TAB C: POD KONTEXT (Dynamisch) ---
44-
const contextRows = Object.entries(context).map(([key, val]) => {
45-
let displayVal = val;
46-
let badgeClass = "bg-light text-dark border";
47-
48-
// 1. Behandlung von Booleans (true/false)
49-
if (typeof val === 'boolean') {
50-
badgeClass = val ? "bg-success text-white" : "bg-danger text-white";
51-
displayVal = val ? "JA" : "NEIN";
41+
// --- TAB A: Config & Erreichbarkeit ---
42+
if (report.error) {
43+
const warning = `<div class="alert alert-warning m-3 small"><i class="bi bi-exclamation-triangle me-2"></i>${report.error}</div>`;
44+
configDiv.innerHTML = warning;
45+
errorDiv.innerHTML = `<div class="alert alert-warning m-3 small">Keine Daten – Sidecar nicht aktiv.</div>`;
46+
} else {
47+
const r = report.reachability ?? {};
48+
const clusterLines = (r.activeEndpoints ?? '').split('\n').filter(l => l.trim());
49+
configDiv.innerHTML = `
50+
<div class="d-flex align-items-center justify-content-between mb-2 px-1">
51+
<span class="badge bg-secondary"><i class="bi bi-diagram-3 me-1"></i>${r.summary ?? ''}</span>
52+
<button class="btn btn-sm btn-outline-secondary x-small" onclick="this.closest('.d-flex').nextElementSibling.classList.toggle('d-none')">
53+
<i class="bi bi-code-slash me-1"></i>Envoy Config (JSON)
54+
</button>
55+
</div>
56+
<div class="d-none mb-2">
57+
<pre class="x-small p-2" style="background:#1e1e1e;color:#4af626;border-radius:4px;max-height:300px;overflow:auto;">${JSON.stringify(r.envoyConfig, null, 2)}</pre>
58+
</div>
59+
<div class="input-group input-group-sm mb-2">
60+
<span class="input-group-text bg-dark border-secondary text-secondary"><i class="bi bi-search"></i></span>
61+
<input type="text" id="clusterSearch" class="form-control form-control-sm x-small bg-dark text-success border-secondary" placeholder="Cluster filtern...">
62+
<span id="clusterCount" class="input-group-text bg-dark border-secondary text-secondary x-small">${clusterLines.length}</span>
63+
</div>
64+
<pre id="clusterOutput" class="console x-small p-3 bg-dark text-success" style="border-radius:4px;max-height:400px;overflow:auto;">${clusterLines.join('\n')}</pre>`;
65+
66+
document.getElementById('clusterSearch').addEventListener('input', function () {
67+
const term = this.value.toLowerCase();
68+
const filtered = term ? clusterLines.filter(l => l.toLowerCase().includes(term)) : clusterLines;
69+
document.getElementById('clusterOutput').textContent = filtered.join('\n');
70+
document.getElementById('clusterCount').textContent = filtered.length;
71+
});
72+
73+
// --- TAB B: Fehler-Metriken ---
74+
const errorEntries = Object.entries(report.healthDiagnostics?.activeErrorMetrics ?? {});
75+
const errorCount = report.healthDiagnostics?.errorCount ?? errorEntries.length;
76+
errorDiv.innerHTML = errorEntries.length === 0 ?
77+
`<div class="alert alert-success small"><i class="bi bi-check-circle me-2"></i>Keine aktiven Fehler-Metriken.</div>` :
78+
`<div class="mb-2"><span class="badge bg-danger">${errorCount} aktive Fehler</span></div>
79+
<table class="table table-sm x-small">
80+
<thead><tr><th>Metrik</th><th>Wert</th></tr></thead>
81+
<tbody>${errorEntries.map(([k, v]) => `<tr><td class="font-monospace text-truncate" style="max-width:300px;" title="${k}">${k}</td><td class="fw-bold text-danger">${v}</td></tr>`).join('')}</tbody>
82+
</table>`;
83+
}
84+
85+
// --- TAB C: POD KONTEXT ---
86+
const istioDetails = context.istioDetails;
87+
const contextRows = Object.entries(context)
88+
.filter(([key]) => key !== 'istioDetails')
89+
.map(([key, val]) => {
90+
let displayVal = val;
91+
let badgeClass = "bg-light text-dark border";
92+
if (typeof val === 'boolean') {
93+
badgeClass = val ? "bg-success text-white" : "bg-danger text-white";
94+
displayVal = val ? "JA" : "NEIN";
95+
}
96+
return `
97+
<tr>
98+
<td class="bg-light fw-bold small text-muted w-25">${key}</td>
99+
<td><span class="badge ${badgeClass} font-monospace">${displayVal}</span></td>
100+
</tr>`;
101+
}).join('');
102+
103+
const istioDetailsHtml = istioDetails ? (() => {
104+
if (istioDetails.error) {
105+
return `<div class="alert alert-warning x-small mt-3">${istioDetails.error}</div>`;
52106
}
53-
// 2. Behandlung von komplexen Objekten (wie deine istioDetails)
54-
else if (typeof val === 'object' && val !== null) {
55-
// Wir zeigen in der Tabelle nur einen Hinweis, die Details sind im JSON-Button
56-
displayVal = `<i class="bi bi-diagram-3 me-1"></i> Objekt (siehe Details)`;
57-
badgeClass = "bg-info text-dark border";
58-
}
59-
107+
return `
108+
<div class="border-top pt-3 mt-3">
109+
<h6 class="x-small fw-bold text-uppercase text-muted mb-2">Istio Sidecar Details</h6>
110+
<table class="table table-sm x-small border shadow-sm">
111+
<tbody>
112+
<tr>
113+
<td class="bg-light fw-bold text-muted w-25">clusterSummary</td>
114+
<td><span class="badge bg-secondary font-monospace">${istioDetails.clusterSummary ?? '-'}</span></td>
115+
</tr>
116+
</tbody>
117+
</table>
118+
${istioDetails.networkStats && Object.keys(istioDetails.networkStats).length > 0 ? `
119+
<label class="x-small fw-bold text-muted mb-1">Network Stats</label>
120+
<table class="table table-sm x-small border">
121+
<thead><tr><th>Metrik</th><th>Wert</th></tr></thead>
122+
<tbody>${Object.entries(istioDetails.networkStats).map(([k, v]) => `<tr><td class="font-monospace" title="${k}">${k}</td><td>${v}</td></tr>`).join('')}</tbody>
123+
</table>` : ''}
124+
${istioDetails.info ? `
125+
<button class="btn btn-sm btn-outline-secondary x-small mb-2" onclick="this.nextElementSibling.classList.toggle('d-none')">
126+
<i class="bi bi-info-circle me-1"></i>Server Info (JSON)
127+
</button>
128+
<div class="d-none mb-2">
129+
<pre class="x-small p-2" style="background:#1e1e1e;color:#4af626;border-radius:4px;max-height:200px;overflow:auto;">${JSON.stringify(istioDetails.info, null, 2)}</pre>
130+
</div>` : ''}
131+
</div>`;
132+
})() : '';
133+
134+
const statusRows = status ? Object.entries(status).map(([key, val]) => {
135+
let displayVal = Array.isArray(val) ? val.join('<br>') : String(val);
136+
let badgeClass = key === 'initialized'
137+
? (val ? 'bg-success text-white' : 'bg-danger text-white')
138+
: 'bg-light text-dark border';
139+
const cellContent = Array.isArray(val)
140+
? val.map(v => `<span class="badge ${badgeClass} font-monospace me-1 mb-1">${v}</span>`).join('')
141+
: `<span class="badge ${badgeClass} font-monospace">${displayVal}</span>`;
60142
return `
61143
<tr>
62144
<td class="bg-light fw-bold small text-muted w-25">${key}</td>
63-
<td><span class="badge ${badgeClass} font-monospace">${displayVal}</span></td>
145+
<td>${cellContent}</td>
64146
</tr>`;
65-
}).join('');
147+
}).join('') : '';
66148

67149
resourceDiv.innerHTML = `
68150
<div class="p-2">
69151
<div class="d-flex justify-content-between align-items-center border-bottom pb-2 mb-3">
70152
<h6 class="x-small fw-bold text-uppercase text-muted mb-0">Identität & Kontext</h6>
71-
<button class="btn btn-xs btn-outline-primary" onclick="this.parentElement.nextElementSibling.nextElementSibling.classList.toggle('d-none')">
153+
<button class="btn btn-sm btn-outline-primary x-small" onclick="this.closest('.d-flex').nextElementSibling.nextElementSibling.classList.toggle('d-none')">
72154
<i class="bi bi-eye me-1"></i> Details (JSON)
73155
</button>
74156
</div>
@@ -79,10 +161,18 @@ const K8sClient = (() => {
79161
80162
<div class="d-none mt-3">
81163
<label class="x-small fw-bold text-muted mb-1">KOMPLETTER KONTEXT (RAW):</label>
82-
<pre class="x-small p-3 shadow-inner"
83-
style="background-color: #1e1e1e; color: #4af626; border-radius: 6px; border: 1px solid #333; overflow: auto; max-height: 500px; font-family: 'Fira Code', monospace;"
84-
>${JSON.stringify(context, null, 4)}</pre>
164+
<pre class="x-small p-3" style="background-color:#1e1e1e;color:#4af626;border-radius:6px;border:1px solid #333;overflow:auto;max-height:500px;font-family:'Fira Code',monospace;">${JSON.stringify(context, null, 4)}</pre>
85165
</div>
166+
167+
${istioDetailsHtml}
168+
169+
${statusRows ? `
170+
<div class="border-top pt-3 mt-3">
171+
<h6 class="x-small fw-bold text-uppercase text-muted mb-2">K8s Client Status</h6>
172+
<table class="table table-sm border shadow-sm">
173+
<tbody>${statusRows}</tbody>
174+
</table>
175+
</div>` : ''}
86176
</div>`;
87177

88178
} catch (err) {
@@ -104,6 +194,7 @@ document.addEventListener('DOMContentLoaded', async () => {
104194
const ctx = await K8sClient.loadContext();
105195
if (contextEl && !ctx.error) {
106196
contextEl.innerHTML = `
197+
<span class="badge bg-dark border me-2"><i class="bi bi-cpu me-1"></i>${ctx.podName}</span>
107198
<span class="badge bg-dark border me-2"><i class="bi bi-tags me-1"></i>${ctx.namespace}</span>
108199
<span class="badge ${ctx.istioSidecar ? 'bg-success' : 'bg-warning text-dark'}">Istio: ${ctx.istioSidecar ? 'ON' : 'OFF'}</span>`;
109200
}
@@ -122,4 +213,4 @@ document.addEventListener('DOMContentLoaded', async () => {
122213
K8sClient.runFullDiagnostics(currentUrl);
123214
});
124215
}
125-
});
216+
});

0 commit comments

Comments
 (0)