1234 lines
45 KiB
JavaScript

// Debug-Dashboard JavaScript
// Globale Variablen für Charts
let cpuChart = null;
let memoryChart = null;
let diskChart = null;
let containerStatusChart = null;
// Speicher für historische Daten
const cpuData = {
labels: Array(20).fill(''),
datasets: [{
label: 'CPU-Auslastung (%)',
data: Array(20).fill(0),
borderColor: '#3498db',
backgroundColor: 'rgba(52, 152, 219, 0.2)',
tension: 0.4,
fill: true
}]
};
const memoryData = {
labels: ['Verwendet', 'Frei'],
datasets: [{
data: [0, 100],
backgroundColor: ['#e74c3c', '#2ecc71'],
hoverBackgroundColor: ['#c0392b', '#27ae60']
}]
};
// Gemeinsame Chart-Optionen
const lineChartOptions = {
responsive: true,
maintainAspectRatio: false,
animation: {
duration: 500
},
scales: {
y: {
beginAtZero: true,
max: 100,
ticks: {
callback: value => `${value}%`
}
}
},
plugins: {
legend: {
display: true,
position: 'top'
}
}
};
const pieChartOptions = {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: {
position: 'right'
}
}
};
// Helper-Funktionen
function showMessage(message, isError = false) {
const messageEl = document.getElementById('message');
messageEl.textContent = message;
messageEl.className = isError ? 'message message-error' : 'message message-success';
messageEl.style.display = 'block';
// Verstecke Nachricht nach 5 Sekunden
setTimeout(() => {
messageEl.style.display = 'none';
}, 5000);
}
function formatBytes(bytes, decimals = 2) {
if (bytes === 0) return '0 Bytes';
const k = 1024;
const dm = decimals < 0 ? 0 : decimals;
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i];
}
function formatUptime(seconds) {
const days = Math.floor(seconds / 86400);
const hours = Math.floor((seconds % 86400) / 3600);
const minutes = Math.floor((seconds % 3600) / 60);
return `${days}d ${hours}h ${minutes}m`;
}
// Dashboard-Initialisierung
document.addEventListener('DOMContentLoaded', function() {
// Tab-Wechsel einrichten
setupTabs();
// Charts initialisieren
initCharts();
// Daten laden
loadSystemMetrics();
loadDockerStatus();
refreshNetworkInterfaces();
refreshActiveConnections();
loadRouteTable();
// Regelmäßige Aktualisierungen
setInterval(loadSystemMetrics, 5000);
setInterval(loadDockerStatus, 10000);
console.log('Debug-Dashboard initialisiert');
});
// Tab-Funktionalität
function setupTabs() {
document.querySelectorAll('.tab').forEach(tab => {
tab.addEventListener('click', function() {
// Aktiven Tab-Status wechseln
document.querySelectorAll('.tab').forEach(t => t.classList.remove('active'));
this.classList.add('active');
// Tab-Inhalte wechseln
const tabId = this.getAttribute('data-tab');
document.querySelectorAll('.tab-content').forEach(content => {
content.classList.remove('active');
});
document.getElementById(tabId + '-tab').classList.add('active');
});
});
}
// Chart-Initialisierung
function initCharts() {
// CPU-Chart
const cpuCtx = document.getElementById('cpu-usage-chart').getContext('2d');
cpuChart = new Chart(cpuCtx, {
type: 'line',
data: cpuData,
options: lineChartOptions
});
// Memory-Chart
const memoryCtx = document.getElementById('memory-usage-chart').getContext('2d');
memoryChart = new Chart(memoryCtx, {
type: 'doughnut',
data: memoryData,
options: pieChartOptions
});
// Disk-Chart (Wird später initialisiert, wenn Daten verfügbar sind)
// Container-Status-Chart (Wird später initialisiert, wenn Daten verfügbar sind)
}
// Daten-Lade-Funktionen
function loadSystemMetrics() {
fetch('/api/system/metrics')
.then(response => response.json())
.then(data => {
if (data.success) {
updateSystemMetrics(data);
}
})
.catch(error => {
console.error('Fehler beim Laden der Systemmetriken:', error);
});
}
function updateSystemMetrics(data) {
// CPU-Auslastung aktualisieren
document.getElementById('cpu-percent').textContent = `${data.cpu_percent.toFixed(1)}%`;
// CPU-Chart aktualisieren
cpuData.datasets[0].data.shift();
cpuData.datasets[0].data.push(data.cpu_percent);
cpuChart.update();
// RAM-Auslastung aktualisieren
document.getElementById('memory-percent').textContent = `${data.memory.percent.toFixed(1)}%`;
document.getElementById('memory-used').textContent = formatBytes(data.memory.used);
document.getElementById('memory-available').textContent = formatBytes(data.memory.available);
document.getElementById('memory-total').textContent = formatBytes(data.memory.total);
// Memory-Chart aktualisieren
memoryData.datasets[0].data = [data.memory.percent, 100 - data.memory.percent];
memoryChart.update();
// Disk-Chart aktualisieren oder initialisieren, wenn noch nicht vorhanden
if (data.disk_usage && data.disk_usage.length > 0) {
updateDiskChart(data.disk_usage);
}
}
function updateDiskChart(diskData) {
if (!diskChart) {
// Chart initialisieren, wenn noch nicht vorhanden
const labels = diskData.map(disk => disk.mountpoint);
const usedData = diskData.map(disk => disk.used);
const freeData = diskData.map(disk => disk.free);
const data = {
labels: labels,
datasets: [
{
label: 'Verwendet',
data: usedData,
backgroundColor: 'rgba(231, 76, 60, 0.7)'
},
{
label: 'Frei',
data: freeData,
backgroundColor: 'rgba(46, 204, 113, 0.7)'
}
]
};
const options = {
responsive: true,
maintainAspectRatio: false,
scales: {
x: {
stacked: true
},
y: {
stacked: true,
ticks: {
callback: value => formatBytes(value)
}
}
}
};
const diskCtx = document.getElementById('disk-usage-chart').getContext('2d');
diskChart = new Chart(diskCtx, {
type: 'bar',
data: data,
options: options
});
} else {
// Chart aktualisieren
diskChart.data.labels = diskData.map(disk => disk.mountpoint);
diskChart.data.datasets[0].data = diskData.map(disk => disk.used);
diskChart.data.datasets[1].data = diskData.map(disk => disk.free);
diskChart.update();
}
}
// Docker-Informationen laden
function loadDockerStatus() {
fetch('/api/docker/status')
.then(response => response.json())
.then(data => {
if (data.success) {
updateDockerStatus(data);
}
})
.catch(error => {
console.error('Fehler beim Laden des Docker-Status:', error);
});
}
function updateDockerStatus(data) {
// Docker-Version und Info aktualisieren
document.getElementById('docker-version').textContent = data.info.version || 'Nicht verfügbar';
document.getElementById('docker-api-version').textContent = data.info.api_version || 'Nicht verfügbar';
document.getElementById('docker-os').textContent = data.info.os || 'Nicht verfügbar';
document.getElementById('docker-status').textContent = data.info.status || 'Nicht verfügbar';
// Anzahl aktiver Container aktualisieren
const activeContainers = data.containers.filter(c => c.state === 'running').length;
document.getElementById('active-containers').textContent = activeContainers;
// Container-Status-Chart aktualisieren oder initialisieren
updateContainerStatusChart(data.containers);
// Container-Tabelle aktualisieren
updateContainerTable(data.containers);
// Container-Select für Logs aktualisieren
updateContainerSelect(data.containers);
}
function updateContainerStatusChart(containers) {
// Zähle Container nach Status
const statusCounts = {
running: 0,
exited: 0,
created: 0,
other: 0
};
containers.forEach(container => {
if (container.state === 'running') {
statusCounts.running++;
} else if (container.state === 'exited') {
statusCounts.exited++;
} else if (container.state === 'created') {
statusCounts.created++;
} else {
statusCounts.other++;
}
});
if (!containerStatusChart) {
// Chart initialisieren
const data = {
labels: ['Laufend', 'Beendet', 'Erstellt', 'Andere'],
datasets: [{
data: [
statusCounts.running,
statusCounts.exited,
statusCounts.created,
statusCounts.other
],
backgroundColor: [
'#2ecc71', // Grün für laufende Container
'#e74c3c', // Rot für beendete Container
'#3498db', // Blau für erstellte Container
'#95a5a6' // Grau für andere Status
]
}]
};
const ctx = document.getElementById('container-status-chart').getContext('2d');
containerStatusChart = new Chart(ctx, {
type: 'doughnut',
data: data,
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: {
position: 'right'
}
}
}
});
} else {
// Chart aktualisieren
containerStatusChart.data.datasets[0].data = [
statusCounts.running,
statusCounts.exited,
statusCounts.created,
statusCounts.other
];
containerStatusChart.update();
}
}
function updateContainerTable(containers) {
const tableBody = document.querySelector('#container-table tbody');
if (containers.length === 0) {
tableBody.innerHTML = '<tr><td colspan="6">Keine Container gefunden</td></tr>';
return;
}
tableBody.innerHTML = '';
containers.forEach(container => {
const row = document.createElement('tr');
row.className = `container-row ${container.state}`;
row.setAttribute('data-id', container.id);
// Container-Status-Klasse
let statusClass = '';
if (container.state === 'running') {
statusClass = 'status-good';
} else if (container.state === 'exited') {
statusClass = 'status-error';
} else {
statusClass = 'status-warning';
}
// Container-Name und Info
row.innerHTML = `
<td>
<div class="container-name">${container.name}</div>
<div class="container-image">${container.image}</div>
</td>
<td><span class="status ${statusClass}">${container.state}</span></td>
<td>${container.cpu || 'N/A'}</td>
<td>${container.memory ? formatBytes(container.memory) : 'N/A'}</td>
<td>${container.ports || 'N/A'}</td>
<td class="actions">
<button class="btn btn-sm" onclick="inspectContainer('${container.id}')"><i class="fas fa-info-circle"></i></button>
<button class="btn btn-sm" onclick="restartContainer('${container.id}')"><i class="fas fa-sync-alt"></i></button>
<button class="btn btn-sm" onclick="viewContainerLogs('${container.id}')"><i class="fas fa-file-alt"></i></button>
</td>
`;
tableBody.appendChild(row);
});
}
function updateContainerSelect(containers) {
const select = document.getElementById('log-container-select');
// Alle Einträge außer dem ersten leeren löschen
while (select.options.length > 1) {
select.remove(1);
}
// Container sortieren (laufende zuerst)
const sortedContainers = [...containers].sort((a, b) => {
if (a.state === 'running' && b.state !== 'running') return -1;
if (a.state !== 'running' && b.state === 'running') return 1;
return a.name.localeCompare(b.name);
});
// Container zum Select hinzufügen
sortedContainers.forEach(container => {
const option = document.createElement('option');
option.value = container.id;
option.textContent = `${container.name} (${container.state})`;
// Laufende Container hervorheben
if (container.state === 'running') {
option.className = 'container-running';
}
select.appendChild(option);
});
}
// Container-Aktionen
function inspectContainer(containerId) {
fetch(`/api/docker/inspect/${containerId}`)
.then(response => response.json())
.then(data => {
if (data.success) {
showContainerInspect(data.data);
} else {
showMessage(`Fehler: ${data.error}`, true);
}
})
.catch(error => {
showMessage(`Fehler bei der Container-Inspektion: ${error}`, true);
});
}
function showContainerInspect(inspectData) {
// Hier können wir ein Modal oder einen anderen Bereich für die Anzeige der Inspektionsdaten implementieren
const detailsHTML = `
<div class="inspect-header">
<h3>${inspectData.name}</h3>
<div class="inspect-id">${inspectData.id.substring(0, 12)}</div>
</div>
<div class="inspect-section">
<h4>Allgemein</h4>
<table>
<tr><th>Image</th><td>${inspectData.image}</td></tr>
<tr><th>Erstellt</th><td>${new Date(inspectData.created).toLocaleString()}</td></tr>
<tr><th>Status</th><td>${inspectData.state.Status}</td></tr>
<tr><th>Gestartet</th><td>${inspectData.state.StartedAt ? new Date(inspectData.state.StartedAt).toLocaleString() : 'Nicht gestartet'}</td></tr>
</table>
</div>
<div class="inspect-section">
<h4>Netzwerk</h4>
<div class="inspect-networks">
${Object.keys(inspectData.networks).map(net => `
<div class="network-item">
<div class="network-name">${net}</div>
<div>IP: ${inspectData.networks[net].IPAddress}</div>
<div>Gateway: ${inspectData.networks[net].Gateway}</div>
</div>
`).join('')}
</div>
</div>
<div class="inspect-section">
<h4>Ports</h4>
<div class="inspect-ports">
${inspectData.ports ? Object.keys(inspectData.ports).map(port => `
<div class="port-item">
${port}${inspectData.ports[port] ? inspectData.ports[port].map(p => `${p.HostIp}:${p.HostPort}`).join(', ') : 'Nicht gemappt'}
</div>
`).join('') : 'Keine Ports'}
</div>
</div>
`;
// Hier könnten wir ein Modal anzeigen oder die Daten in einen bestimmten Bereich einfügen
// Für dieses Beispiel verwenden wir ein einfaches Alert, aber in Produktion sollte ein ordentliches Modal verwendet werden
alert(`Container-Details:\n${inspectData.name}\n${inspectData.image}\nStatus: ${inspectData.state.Status}`);
}
function restartContainer(containerId) {
if (confirm('Möchten Sie diesen Container wirklich neu starten?')) {
fetch(`/api/docker/restart/${containerId}`, {
method: 'POST'
})
.then(response => response.json())
.then(data => {
if (data.success) {
showMessage(`Container erfolgreich neu gestartet`, false);
setTimeout(() => loadDockerStatus(), 2000);
} else {
showMessage(`Fehler: ${data.error}`, true);
}
})
.catch(error => {
showMessage(`Fehler beim Neustart des Containers: ${error}`, true);
});
}
}
function viewContainerLogs(containerId) {
// Container im Log-Select auswählen
document.getElementById('log-container-select').value = containerId;
// Logs laden
loadContainerLogs();
}
function loadContainerLogs() {
const containerId = document.getElementById('log-container-select').value;
const filter = document.getElementById('log-filter').value;
if (!containerId) {
showMessage('Bitte wählen Sie einen Container aus', true);
return;
}
const logsContainer = document.getElementById('container-logs');
logsContainer.innerHTML = '<div class="loading">Lade Logs...</div>';
fetch(`/api/docker/logs/${containerId}${filter ? `?filter=${filter}` : ''}`)
.then(response => response.json())
.then(data => {
if (data.success) {
if (data.logs.length === 0) {
logsContainer.innerHTML = '<div class="log-placeholder">Keine Logs gefunden</div>';
} else {
const logLines = data.logs.split('\n');
// Logs mit Syntax-Highlighting und Fehlerhervorhebung anzeigen
logsContainer.innerHTML = `
<div class="log-header">
<div>Logs für Container: <strong>${data.container_name}</strong></div>
<div>Zeilen: ${logLines.length}</div>
</div>
<pre class="logs">${formatLogs(logLines)}</pre>
`;
}
} else {
logsContainer.innerHTML = `<div class="error">Fehler: ${data.error}</div>`;
}
})
.catch(error => {
logsContainer.innerHTML = `<div class="error">Fehler beim Laden der Logs: ${error}</div>`;
});
}
function formatLogs(logLines) {
return logLines.map(line => {
// Zeitstempel hervorheben
line = line.replace(/(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.\d+Z)/g, '<span class="log-timestamp">$1</span>');
// Fehler hervorheben
if (/error|exception|fail|critical/i.test(line)) {
return `<div class="log-line log-error">${line}</div>`;
}
// Warnungen hervorheben
if (/warning|warn/i.test(line)) {
return `<div class="log-line log-warning">${line}</div>`;
}
// Info-Meldungen hervorheben
if (/info|information/i.test(line)) {
return `<div class="log-line log-info">${line}</div>`;
}
return `<div class="log-line">${line}</div>`;
}).join('');
}
function downloadContainerLogs() {
const containerId = document.getElementById('log-container-select').value;
if (!containerId) {
showMessage('Bitte wählen Sie einen Container aus', true);
return;
}
// Direkter Download über einen Link
window.location.href = `/api/docker/logs/download/${containerId}`;
}
// Netzwerk-Funktionen
function refreshNetworkInterfaces() {
const container = document.getElementById('network-interfaces');
container.innerHTML = '<div class="loading">Lade Netzwerkschnittstellen...</div>';
fetch('/api/network/interfaces')
.then(response => response.json())
.then(data => {
if (data.success) {
renderNetworkInterfaces(data.interfaces);
} else {
container.innerHTML = `<div class="error">Fehler: ${data.error}</div>`;
}
})
.catch(error => {
container.innerHTML = `<div class="error">Fehler beim Laden der Netzwerkschnittstellen: ${error}</div>`;
});
}
function renderNetworkInterfaces(interfaces) {
const container = document.getElementById('network-interfaces');
if (interfaces.length === 0) {
container.innerHTML = '<div class="empty">Keine Netzwerkschnittstellen gefunden</div>';
return;
}
let html = '';
interfaces.forEach(iface => {
html += `
<div class="interface-item">
<div class="interface-header">
<strong>${iface.name}</strong>
<span class="interface-mac">${iface.mac}</span>
</div>
<div class="interface-ips">
<h4>IPv4-Adressen</h4>
${iface.ipv4.length > 0 ? iface.ipv4.map(ip => `
<div class="ip-item">
<div><strong>IP:</strong> ${ip.addr}</div>
<div><strong>Netzmaske:</strong> ${ip.netmask}</div>
<div><strong>Broadcast:</strong> ${ip.broadcast || 'N/A'}</div>
</div>
`).join('') : '<div>Keine IPv4-Adressen</div>'}
</div>
${iface.stats !== 'Nicht verfügbar' ? `
<div class="interface-stats">
<h4>Statistiken</h4>
<div class="stats-row">
<div><strong>Gesendet:</strong> ${formatBytes(iface.stats.bytes_sent)}</div>
<div><strong>Empfangen:</strong> ${formatBytes(iface.stats.bytes_recv)}</div>
</div>
<div class="stats-row">
<div><strong>Pakete gesendet:</strong> ${iface.stats.packets_sent}</div>
<div><strong>Pakete empfangen:</strong> ${iface.stats.packets_recv}</div>
</div>
<div class="stats-row">
<div><strong>Fehler (Ein):</strong> ${iface.stats.errin}</div>
<div><strong>Fehler (Aus):</strong> ${iface.stats.errout}</div>
</div>
</div>
` : ''}
</div>
`;
});
container.innerHTML = html;
}
function refreshActiveConnections() {
const tableBody = document.querySelector('#connections-table tbody');
tableBody.innerHTML = '<tr><td colspan="4">Lade aktive Verbindungen...</td></tr>';
fetch('/api/network/active-connections')
.then(response => response.json())
.then(data => {
if (data.success) {
renderActiveConnections(data.connections);
} else {
tableBody.innerHTML = `<tr><td colspan="4">Fehler: ${data.error}</td></tr>`;
}
})
.catch(error => {
tableBody.innerHTML = `<tr><td colspan="4">Fehler beim Laden der aktiven Verbindungen: ${error}</td></tr>`;
});
}
function renderActiveConnections(connections) {
const tableBody = document.querySelector('#connections-table tbody');
if (connections.length === 0) {
tableBody.innerHTML = '<tr><td colspan="4">Keine aktiven Verbindungen gefunden</td></tr>';
return;
}
tableBody.innerHTML = '';
connections.forEach(conn => {
const row = document.createElement('tr');
row.innerHTML = `
<td>${conn.local_address}</td>
<td>${conn.remote_address}</td>
<td>${conn.status}</td>
<td>${conn.process ? `${conn.process.name} (PID: ${conn.pid})` : `PID: ${conn.pid || 'N/A'}`}</td>
`;
tableBody.appendChild(row);
});
}
function loadRouteTable() {
const container = document.getElementById('route-table');
container.textContent = 'Lade Routing-Tabelle...';
fetch('/api/network/route-table')
.then(response => response.json())
.then(data => {
if (data.success) {
container.textContent = data.route_table;
} else {
container.textContent = `Fehler: ${data.error}`;
}
})
.catch(error => {
container.textContent = `Fehler beim Laden der Routing-Tabelle: ${error}`;
});
}
// Diagnose-Funktionen
function pingHost() {
const host = document.getElementById('ping-host').value;
if (!host) {
showMessage('Bitte geben Sie einen Host ein', true);
return;
}
document.getElementById('ping-result').innerHTML = '<div class="loading">Ping wird durchgeführt...</div>';
document.getElementById('ping-result').className = 'status';
fetch(`/ping/${host}`)
.then(response => response.json())
.then(data => {
let html = `<h4>Ping-Ergebnisse für ${host}</h4>`;
html += `<pre class="ping-output">${data.output}</pre>`;
document.getElementById('ping-result').innerHTML = html;
document.getElementById('ping-result').className = data.success ? 'status status-good' : 'status status-error';
})
.catch(error => {
document.getElementById('ping-result').innerHTML = `<div class="error">Fehler beim Durchführen des Ping-Tests: ${error}</div>`;
document.getElementById('ping-result').className = 'status status-error';
});
}
function tracerouteHost() {
const host = document.getElementById('traceroute-host').value;
if (!host) {
showMessage('Bitte geben Sie einen Host ein', true);
return;
}
document.getElementById('traceroute-result').innerHTML = '<div class="loading">Traceroute wird durchgeführt...</div>';
document.getElementById('traceroute-result').className = 'status';
fetch(`/traceroute/${host}`)
.then(response => response.json())
.then(data => {
let html = `<h4>Traceroute-Ergebnisse für ${host}</h4>`;
html += `<pre class="traceroute-output">${data.output}</pre>`;
if (data.error) {
html += `<div class="error">Fehler: ${data.error}</div>`;
}
document.getElementById('traceroute-result').innerHTML = html;
document.getElementById('traceroute-result').className = 'status';
})
.catch(error => {
document.getElementById('traceroute-result').innerHTML = `<div class="error">Fehler beim Durchführen des Traceroute: ${error}</div>`;
document.getElementById('traceroute-result').className = 'status status-error';
});
}
function dnsLookup() {
const host = document.getElementById('dns-host').value;
if (!host) {
showMessage('Bitte geben Sie einen Hostnamen ein', true);
return;
}
document.getElementById('dns-result').innerHTML = '<div class="loading">DNS-Abfrage wird durchgeführt...</div>';
document.getElementById('dns-result').className = 'status';
fetch(`/nslookup/${host}`)
.then(response => response.json())
.then(data => {
let html = `<h4>DNS-Abfrageergebnisse für ${host}</h4>`;
html += `<pre class="dns-output">${data.output}</pre>`;
if (data.error) {
html += `<div class="error">Fehler: ${data.error}</div>`;
}
document.getElementById('dns-result').innerHTML = html;
document.getElementById('dns-result').className = 'status';
})
.catch(error => {
document.getElementById('dns-result').innerHTML = `<div class="error">Fehler bei der DNS-Abfrage: ${error}</div>`;
document.getElementById('dns-result').className = 'status status-error';
});
}
function startPortScan() {
const host = document.getElementById('port-scan-host').value;
const portRange = document.getElementById('port-scan-range').value;
if (!host) {
showMessage('Bitte geben Sie einen Host ein', true);
return;
}
document.getElementById('port-scan-status').innerHTML = '<div class="loading">Port-Scan wird gestartet...</div>';
document.getElementById('port-scan-status').className = 'status';
// Tabelle leeren
document.querySelector('#port-scan-table tbody').innerHTML = '';
fetch('/api/network/scan-ports', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
host: host,
port_range: portRange
})
})
.then(response => response.json())
.then(data => {
if (data.success) {
document.getElementById('port-scan-status').innerHTML = data.message;
document.getElementById('port-scan-status').className = 'status status-good';
// Status-Polling starten
pollPortScanStatus();
} else {
document.getElementById('port-scan-status').innerHTML = `<div class="error">Fehler: ${data.error}</div>`;
document.getElementById('port-scan-status').className = 'status status-error';
}
})
.catch(error => {
document.getElementById('port-scan-status').innerHTML = `<div class="error">Fehler beim Starten des Port-Scans: ${error}</div>`;
document.getElementById('port-scan-status').className = 'status status-error';
});
}
function checkPortScanStatus() {
fetch('/api/network/scan-status')
.then(response => response.json())
.then(data => {
if (data.success) {
if (data.status === 'running') {
document.getElementById('port-scan-status').innerHTML = '<div class="loading">Port-Scan läuft...</div>';
document.getElementById('port-scan-status').className = 'status status-warning';
} else if (data.status === 'completed' && data.results) {
renderPortScanResults(data.results);
} else {
document.getElementById('port-scan-status').innerHTML = data.message;
document.getElementById('port-scan-status').className = 'status';
}
} else {
document.getElementById('port-scan-status').innerHTML = `<div class="error">Fehler: ${data.error}</div>`;
document.getElementById('port-scan-status').className = 'status status-error';
}
})
.catch(error => {
document.getElementById('port-scan-status').innerHTML = `<div class="error">Fehler beim Abrufen des Port-Scan-Status: ${error}</div>`;
document.getElementById('port-scan-status').className = 'status status-error';
});
}
function pollPortScanStatus() {
const intervalId = setInterval(() => {
fetch('/api/network/scan-status')
.then(response => response.json())
.then(data => {
if (data.success) {
if (data.status === 'running') {
document.getElementById('port-scan-status').innerHTML = '<div class="loading">Port-Scan läuft...</div>';
document.getElementById('port-scan-status').className = 'status status-warning';
} else {
// Scan ist abgeschlossen oder anderweitig beendet
clearInterval(intervalId);
if (data.status === 'completed' && data.results) {
renderPortScanResults(data.results);
} else {
document.getElementById('port-scan-status').innerHTML = data.message;
document.getElementById('port-scan-status').className = 'status';
}
}
} else {
clearInterval(intervalId);
document.getElementById('port-scan-status').innerHTML = `<div class="error">Fehler: ${data.error}</div>`;
document.getElementById('port-scan-status').className = 'status status-error';
}
})
.catch(error => {
clearInterval(intervalId);
document.getElementById('port-scan-status').innerHTML = `<div class="error">Fehler beim Abrufen des Port-Scan-Status: ${error}</div>`;
document.getElementById('port-scan-status').className = 'status status-error';
});
}, 1000);
}
function renderPortScanResults(results) {
const tableBody = document.querySelector('#port-scan-table tbody');
tableBody.innerHTML = '';
document.getElementById('port-scan-status').innerHTML = `
<div>Scan für ${results.host} abgeschlossen</div>
<div>Ports: ${results.start_port}-${results.end_port}</div>
<div>Zeit: ${new Date(results.timestamp).toLocaleString()}</div>
<div>Offene Ports: ${results.open_ports.length}</div>
`;
document.getElementById('port-scan-status').className = 'status status-good';
if (results.open_ports.length === 0) {
tableBody.innerHTML = '<tr><td colspan="3">Keine offenen Ports gefunden</td></tr>';
return;
}
results.open_ports.forEach(port => {
const row = document.createElement('tr');
row.innerHTML = `
<td>${port.port}</td>
<td>${port.status}</td>
<td>${port.service}</td>
`;
tableBody.appendChild(row);
});
}
function loadLogs() {
const logType = document.getElementById('log-type').value;
const lines = document.getElementById('log-lines').value;
const container = document.getElementById('log-content');
container.innerHTML = '<div class="loading">Lade Logs...</div>';
fetch(`/api/logs/tail/${logType}?lines=${lines}`)
.then(response => response.json())
.then(data => {
if (data.success) {
if (data.entries.length === 0) {
container.innerHTML = '<div class="log-placeholder">Keine Logs gefunden</div>';
} else {
container.innerHTML = `
<div class="log-header">
<div>Logs für: <strong>${logType}</strong></div>
<div>Zeilen: ${data.count}</div>
</div>
<pre class="logs">${formatLogEntries(data.entries)}</pre>
`;
}
} else {
container.innerHTML = `<div class="error">Fehler: ${data.error}</div>`;
}
})
.catch(error => {
container.innerHTML = `<div class="error">Fehler beim Laden der Logs: ${error}</div>`;
});
}
function formatLogEntries(entries) {
return entries.map(line => {
// Zeitstempel hervorheben
line = line.replace(/(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}(,\d+)?)/g, '<span class="log-timestamp">$1</span>');
// Fehler hervorheben
if (/error|exception|fail|critical/i.test(line)) {
return `<div class="log-line log-error">${line}</div>`;
}
// Warnungen hervorheben
if (/warning|warn/i.test(line)) {
return `<div class="log-line log-warning">${line}</div>`;
}
// Info-Meldungen hervorheben
if (/info|information/i.test(line)) {
return `<div class="log-line log-info">${line}</div>`;
}
// Debug-Meldungen hervorheben
if (/debug/i.test(line)) {
return `<div class="log-line log-debug">${line}</div>`;
}
return `<div class="log-line">${line}</div>`;
}).join('');
}
function analyzeLogs() {
const logType = document.getElementById('log-type').value;
const container = document.getElementById('log-content');
container.innerHTML = '<div class="loading">Analysiere Logs...</div>';
fetch(`/api/logs/analyze?type=${logType}`)
.then(response => response.json())
.then(data => {
if (data.success) {
if (data.analysis.error_count === 0 && data.analysis.warning_count === 0) {
container.innerHTML = '<div class="log-placeholder">Keine Fehler oder Warnungen in den Logs gefunden</div>';
} else {
let html = `
<div class="log-header">
<div>Log-Analyse für: <strong>${logType}</strong></div>
<div>Fehler: ${data.analysis.error_count}, Warnungen: ${data.analysis.warning_count}</div>
</div>
`;
if (data.analysis.errors.length > 0) {
html += '<h4>Fehler</h4>';
html += '<div class="error-list">';
data.analysis.errors.forEach(error => {
html += `
<div class="error-item">
<div class="error-time">${error.timestamp}</div>
<div class="error-message">${error.message}</div>
</div>
`;
});
html += '</div>';
}
if (data.analysis.warnings.length > 0) {
html += '<h4>Warnungen</h4>';
html += '<div class="warning-list">';
data.analysis.warnings.forEach(warning => {
html += `
<div class="warning-item">
<div class="warning-time">${warning.timestamp}</div>
<div class="warning-message">${warning.message}</div>
</div>
`;
});
html += '</div>';
}
container.innerHTML = html;
}
} else {
container.innerHTML = `<div class="error">Fehler: ${data.error}</div>`;
}
})
.catch(error => {
container.innerHTML = `<div class="error">Fehler bei der Log-Analyse: ${error}</div>`;
});
}
function checkHealth() {
const banner = document.getElementById('systemHealthBanner');
banner.style.display = 'flex';
banner.className = 'system-health-banner checking';
banner.innerHTML = `
<div class="health-icon"><i class="fas fa-spinner fa-spin"></i></div>
<div class="health-status">Systemstatus wird geprüft...</div>
`;
fetch('/healthcheck')
.then(response => response.json())
.then(data => {
let statusClass = '';
let icon = '';
if (data.status === 'healthy') {
statusClass = 'healthy';
icon = '<i class="fas fa-check-circle"></i>';
} else if (data.status === 'warning') {
statusClass = 'warning';
icon = '<i class="fas fa-exclamation-triangle"></i>';
} else {
statusClass = 'critical';
icon = '<i class="fas fa-times-circle"></i>';
}
let statusHTML = `
<div class="health-icon">${icon}</div>
<div class="health-status">
<div class="health-status-title">Systemstatus: ${data.status.toUpperCase()}</div>
<div class="health-details">
`;
// Einzelne Komponenten hinzufügen
Object.keys(data.checks).forEach(component => {
const check = data.checks[component];
let componentIcon = '';
if (check.overall === 'healthy') {
componentIcon = '<i class="fas fa-check-circle health-good"></i>';
} else if (check.overall === 'warning') {
componentIcon = '<i class="fas fa-exclamation-triangle health-warning"></i>';
} else {
componentIcon = '<i class="fas fa-times-circle health-critical"></i>';
}
statusHTML += `<div>${componentIcon} ${component}: ${check.status}</div>`;
});
statusHTML += `
</div>
</div>
<button class="btn btn-sm" onclick="document.getElementById('systemHealthBanner').style.display = 'none'">
<i class="fas fa-times"></i>
</button>
`;
banner.className = `system-health-banner ${statusClass}`;
banner.innerHTML = statusHTML;
// Banner nach 10 Sekunden ausblenden, wenn es nicht kritisch ist
if (data.status !== 'critical') {
setTimeout(() => {
if (banner.className.includes(statusClass)) {
banner.style.display = 'none';
}
}, 10000);
}
})
.catch(error => {
banner.className = 'system-health-banner critical';
banner.innerHTML = `
<div class="health-icon"><i class="fas fa-times-circle"></i></div>
<div class="health-status">Fehler bei der Systemstatus-Prüfung: ${error}</div>
<button class="btn btn-sm" onclick="document.getElementById('systemHealthBanner').style.display = 'none'">
<i class="fas fa-times"></i>
</button>
`;
});
}
function refreshPage() {
window.location.reload();
}
// Konfiguration
function saveConfig() {
const config = {
backend_hostname: document.getElementById('backend_hostname').value,
backend_port: parseInt(document.getElementById('backend_port').value, 10),
frontend_hostname: document.getElementById('frontend_hostname').value,
frontend_port: parseInt(document.getElementById('frontend_port').value, 10)
};
fetch('/save-config', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(config)
})
.then(response => response.json())
.then(data => {
if (data.success) {
showMessage('Konfiguration erfolgreich gespeichert', false);
// Nach kurzer Verzögerung die Seite neu laden
setTimeout(() => {
window.location.reload();
}, 1500);
} else {
showMessage(`Fehler: ${data.message}`, true);
}
})
.catch(error => {
showMessage(`Fehler: ${error}`, true);
});
}
function testConnection() {
const config = {
backend_hostname: document.getElementById('backend_hostname').value,
backend_port: parseInt(document.getElementById('backend_port').value, 10),
frontend_hostname: document.getElementById('frontend_hostname').value,
frontend_port: parseInt(document.getElementById('frontend_port').value, 10)
};
fetch('/test-connection', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(config)
})
.then(response => response.json())
.then(data => {
if (data.success) {
let message = 'Backend: ';
message += data.results.backend.ping ? 'Ping erfolgreich' : 'Ping fehlgeschlagen';
message += ', ';
message += data.results.backend.connection ? 'Verbindung erfolgreich' : 'Verbindung fehlgeschlagen';
message += ' | Frontend: ';
message += data.results.frontend.ping ? 'Ping erfolgreich' : 'Ping fehlgeschlagen';
message += ', ';
message += data.results.frontend.connection ? 'Verbindung erfolgreich' : 'Verbindung fehlgeschlagen';
showMessage(message, false);
} else {
showMessage(`Fehler: ${data.message}`, true);
}
})
.catch(error => {
showMessage(`Fehler: ${error}`, true);
});
}
function syncFrontend() {
fetch('/sync-frontend', {
method: 'POST'
})
.then(response => response.json())
.then(data => {
if (data.success) {
showMessage('Frontend erfolgreich synchronisiert', false);
} else {
showMessage(`Fehler: ${data.message}`, true);
}
})
.catch(error => {
showMessage(`Fehler: ${error}`, true);
});
}