"feat: Added debug server and related components for improved development experience"

This commit is contained in:
2025-05-23 07:24:51 +02:00
parent d457a8d86b
commit 9f6219832c
189 changed files with 35730 additions and 133 deletions

View File

@@ -0,0 +1,417 @@
/* Variablen */
:root {
--primary-color: #3f51b5;
--secondary-color: #283593;
--accent-color: #ff4081;
--background-color: #f5f5f5;
--card-color: #ffffff;
--text-color: #333333;
--text-light: #757575;
--border-color: #dddddd;
--success-color: #4caf50;
--warning-color: #ff9800;
--danger-color: #f44336;
--shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
--card-shadow: 0 3px 6px rgba(0, 0, 0, 0.16);
}
/* Grundlegende Stile */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
line-height: 1.6;
color: var(--text-color);
background-color: var(--background-color);
display: flex;
flex-direction: column;
min-height: 100vh;
}
/* Header */
header {
background-color: var(--primary-color);
color: white;
padding: 1rem 2rem;
box-shadow: var(--shadow);
position: sticky;
top: 0;
z-index: 100;
}
.header-content {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
align-items: center;
max-width: 1400px;
margin: 0 auto;
}
h1 {
font-size: 1.8rem;
margin-bottom: 0.5rem;
}
.server-info {
display: flex;
flex-wrap: wrap;
gap: 1rem;
font-size: 0.9rem;
}
/* Navigation */
nav {
background-color: var(--secondary-color);
display: flex;
justify-content: center;
padding: 0.5rem 1rem;
overflow-x: auto;
white-space: nowrap;
position: sticky;
top: 0;
z-index: 99;
box-shadow: var(--shadow);
}
.nav-button {
background: none;
color: rgba(255, 255, 255, 0.8);
border: none;
padding: 0.75rem 1.25rem;
margin: 0 0.25rem;
cursor: pointer;
font-size: 1rem;
border-radius: 4px;
transition: all 0.3s;
}
.nav-button:hover {
color: white;
background-color: rgba(255, 255, 255, 0.1);
}
.nav-button.active {
color: white;
background-color: var(--primary-color);
font-weight: bold;
}
/* Main Content */
main {
flex: 1;
padding: 2rem;
max-width: 1400px;
margin: 0 auto;
width: 100%;
}
.panel {
display: none;
}
.panel.active {
display: block;
animation: fadeIn 0.3s;
}
@keyframes fadeIn {
from { opacity: 0; }
to { opacity: 1; }
}
h2 {
margin-bottom: 1.5rem;
color: var(--secondary-color);
border-bottom: 2px solid var(--primary-color);
padding-bottom: 0.5rem;
}
.card-container {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 1.5rem;
}
.card {
background-color: var(--card-color);
border-radius: 8px;
padding: 1.5rem;
box-shadow: var(--card-shadow);
transition: transform 0.3s, box-shadow 0.3s;
}
.card:hover {
transform: translateY(-2px);
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1);
}
.card.full-width {
grid-column: 1 / -1;
}
h3 {
margin-bottom: 1rem;
color: var(--primary-color);
font-size: 1.2rem;
}
/* Loader */
.loader {
text-align: center;
margin: 2rem 0;
color: var(--text-light);
font-style: italic;
}
/* Fortschrittsbalken */
.progress-container {
margin-top: 1rem;
background-color: #e0e0e0;
border-radius: 4px;
height: 8px;
overflow: hidden;
}
.progress-bar {
height: 100%;
background-color: var(--primary-color);
width: 0%;
transition: width 0.5s ease-in-out;
}
/* Tool-Karten */
.tool-card {
display: flex;
flex-direction: column;
height: 100%;
}
.tool-input {
display: flex;
margin-bottom: 1rem;
gap: 0.5rem;
}
.tool-input input {
flex: 1;
padding: 0.5rem;
border: 1px solid var(--border-color);
border-radius: 4px;
font-size: 1rem;
}
.tool-input button {
background-color: var(--primary-color);
color: white;
border: none;
padding: 0.5rem 1rem;
cursor: pointer;
border-radius: 4px;
font-size: 1rem;
transition: background-color 0.3s;
}
.tool-input button:hover {
background-color: var(--secondary-color);
}
.result-box {
background-color: #f8f9fa;
border: 1px solid var(--border-color);
border-radius: 4px;
padding: 1rem;
overflow: auto;
min-height: 150px;
max-height: 300px;
font-family: 'Consolas', 'Courier New', monospace;
font-size: 0.9rem;
flex: 1;
}
/* Tabellen */
table {
width: 100%;
border-collapse: collapse;
margin-top: 1rem;
}
th, td {
padding: 0.75rem;
text-align: left;
border-bottom: 1px solid var(--border-color);
}
th {
background-color: rgba(0, 0, 0, 0.05);
font-weight: bold;
color: var(--text-color);
}
tr:hover {
background-color: rgba(0, 0, 0, 0.02);
}
/* Status-Anzeigen */
.status {
display: inline-block;
padding: 0.25rem 0.5rem;
border-radius: 4px;
font-size: 0.9rem;
font-weight: bold;
}
.status-online {
background-color: var(--success-color);
color: white;
}
.status-offline {
background-color: var(--danger-color);
color: white;
}
.status-warning {
background-color: var(--warning-color);
color: white;
}
/* Echtzeit-Monitor */
.gauge-container {
display: flex;
justify-content: center;
margin: 1rem 0;
}
.gauge {
position: relative;
width: 150px;
height: 75px;
margin: 0 auto;
}
.gauge-body {
position: relative;
width: 100%;
height: 100%;
border-top-left-radius: 100px;
border-top-right-radius: 100px;
background-color: #e0e0e0;
overflow: hidden;
}
.gauge-fill {
position: absolute;
bottom: 0;
width: 100%;
height: 0%;
background-color: var(--primary-color);
transition: height 0.5s;
}
.gauge-cover {
position: absolute;
bottom: 0;
width: 90%;
height: 90%;
margin-left: 5%;
margin-bottom: 5%;
background-color: var(--card-color);
border-top-left-radius: 80px;
border-top-right-radius: 80px;
}
.gauge-value {
position: absolute;
bottom: -25px;
width: 100%;
text-align: center;
font-size: 1.5rem;
font-weight: bold;
color: var(--primary-color);
}
/* CPU-Cores */
.cpu-core {
margin: 0.5rem 0;
}
.cpu-core-label {
display: flex;
justify-content: space-between;
margin-bottom: 0.25rem;
}
.cpu-core-bar {
height: 6px;
background-color: #e0e0e0;
border-radius: 3px;
}
.cpu-core-fill {
height: 100%;
background-color: var(--primary-color);
border-radius: 3px;
transition: width 0.5s;
}
/* Network Chart */
canvas {
margin-top: 1rem;
width: 100%;
height: 200px;
}
/* Footer */
footer {
background-color: var(--secondary-color);
color: white;
text-align: center;
padding: 1rem;
margin-top: auto;
font-size: 0.9rem;
}
footer p {
margin: 0.25rem 0;
}
/* Responsive Design */
@media (max-width: 768px) {
header {
padding: 1rem;
}
.header-content {
flex-direction: column;
align-items: flex-start;
}
.server-info {
margin-top: 1rem;
flex-direction: column;
gap: 0.5rem;
}
main {
padding: 1rem;
}
.card-container {
grid-template-columns: 1fr;
}
.nav-button {
padding: 0.5rem 0.75rem;
font-size: 0.9rem;
}
.gauge {
width: 120px;
height: 60px;
}
}

View File

@@ -0,0 +1,505 @@
// DOM-Element-Referenzen
const navButtons = document.querySelectorAll('.nav-button');
const panels = document.querySelectorAll('.panel');
// Panel-Navigation
navButtons.forEach(button => {
button.addEventListener('click', () => {
const panelId = button.getAttribute('data-panel');
// Aktiven Button und Panel wechseln
navButtons.forEach(btn => btn.classList.remove('active'));
panels.forEach(panel => panel.classList.remove('active'));
button.classList.add('active');
document.getElementById(panelId).classList.add('active');
// Lade Panel-Daten, wenn sie noch nicht geladen wurden
if (panelId === 'system' && document.getElementById('system-container').style.display === 'none') {
loadSystemInfo();
} else if (panelId === 'network' && document.getElementById('network-container').style.display === 'none') {
loadNetworkInfo();
} else if (panelId === 'services' && document.getElementById('services-container').style.display === 'none') {
loadServicesInfo();
}
});
});
// API-Anfragen
async function fetchData(endpoint) {
try {
const response = await fetch(endpoint);
if (!response.ok) {
throw new Error(`HTTP-Fehler: ${response.status}`);
}
return await response.json();
} catch (error) {
console.error(`Fehler beim Abrufen von ${endpoint}:`, error);
return null;
}
}
// System-Informationen laden
async function loadSystemInfo() {
const loader = document.getElementById('system-loader');
const container = document.getElementById('system-container');
loader.style.display = 'block';
container.style.display = 'none';
const data = await fetchData('/api/system');
if (!data) {
loader.textContent = 'Fehler beim Laden der Systemdaten';
return;
}
// Betriebssystem-Info
document.getElementById('os-info').innerHTML = `
<p><strong>Plattform:</strong> ${data.os.platform}</p>
<p><strong>Distribution:</strong> ${data.os.distro}</p>
<p><strong>Version:</strong> ${data.os.release}</p>
<p><strong>Architektur:</strong> ${data.os.arch}</p>
<p><strong>Betriebszeit:</strong> ${data.os.uptime}</p>
`;
// CPU-Info
document.getElementById('cpu-info').innerHTML = `
<p><strong>Hersteller:</strong> ${data.cpu.manufacturer}</p>
<p><strong>Modell:</strong> ${data.cpu.brand}</p>
<p><strong>Geschwindigkeit:</strong> ${data.cpu.speed} GHz</p>
<p><strong>Kerne:</strong> ${data.cpu.cores} (${data.cpu.physicalCores} physisch)</p>
`;
// Speicher-Info
document.getElementById('memory-info').innerHTML = `
<p><strong>Gesamt:</strong> ${data.memory.total}</p>
<p><strong>Verwendet:</strong> ${data.memory.used} (${data.memory.usedPercent}%)</p>
<p><strong>Frei:</strong> ${data.memory.free}</p>
`;
document.getElementById('memory-bar').style.width = `${data.memory.usedPercent}%`;
document.getElementById('memory-bar').style.backgroundColor = getColorByPercentage(data.memory.usedPercent);
// Festplatten-Info
let diskHTML = '<table><tr><th>Laufwerk</th><th>Typ</th><th>Größe</th><th>Verwendet</th><th>Verfügbar</th><th>Nutzung</th></tr>';
data.filesystem.forEach(fs => {
diskHTML += `
<tr>
<td>${fs.mount}</td>
<td>${fs.type}</td>
<td>${fs.size}</td>
<td>${fs.used}</td>
<td>${fs.available}</td>
<td>
<div class="progress-container" style="margin: 0; width: 100%">
<div class="progress-bar" style="width: ${fs.usePercent}%; background-color: ${getColorByPercentage(fs.usePercent)}"></div>
</div>
${fs.usePercent}%
</td>
</tr>
`;
});
diskHTML += '</table>';
document.getElementById('disk-info').innerHTML = diskHTML;
// UI anzeigen
loader.style.display = 'none';
container.style.display = 'grid';
}
// Netzwerk-Informationen laden
async function loadNetworkInfo() {
const loader = document.getElementById('network-loader');
const container = document.getElementById('network-container');
loader.style.display = 'block';
container.style.display = 'none';
const data = await fetchData('/api/network');
if (!data) {
loader.textContent = 'Fehler beim Laden der Netzwerkdaten';
return;
}
// Netzwerkschnittstellen
let interfacesHTML = '<table><tr><th>Name</th><th>Status</th><th>IPv4</th><th>IPv6</th><th>MAC</th><th>Typ</th><th>DHCP</th></tr>';
data.interfaces.forEach(iface => {
interfacesHTML += `
<tr>
<td>${iface.iface}</td>
<td><span class="status ${iface.operstate === 'up' ? 'status-online' : 'status-offline'}">${iface.operstate}</span></td>
<td>${iface.ip4 || '-'}</td>
<td>${iface.ip6 || '-'}</td>
<td>${iface.mac || '-'}</td>
<td>${iface.type || '-'}</td>
<td>${iface.dhcp ? 'Ja' : 'Nein'}</td>
</tr>
`;
});
interfacesHTML += '</table>';
document.getElementById('network-interfaces').innerHTML = interfacesHTML;
// DNS-Server
let dnsHTML = '<ul>';
data.dns.forEach(server => {
dnsHTML += `<li>${server}</li>`;
});
dnsHTML += '</ul>';
document.getElementById('dns-servers').innerHTML = dnsHTML;
// Gateway
document.getElementById('default-gateway').innerHTML = `<p>${data.gateway || 'Nicht verfügbar'}</p>`;
// Netzwerkstatistiken
let statsHTML = '<table><tr><th>Schnittstelle</th><th>Empfangen</th><th>Gesendet</th><th>Empfangen/s</th><th>Gesendet/s</th></tr>';
data.stats.forEach(stat => {
statsHTML += `
<tr>
<td>${stat.iface}</td>
<td>${stat.rx_bytes}</td>
<td>${stat.tx_bytes}</td>
<td>${stat.rx_sec}</td>
<td>${stat.tx_sec}</td>
</tr>
`;
});
statsHTML += '</table>';
document.getElementById('network-stats').innerHTML = statsHTML;
// UI anzeigen
loader.style.display = 'none';
container.style.display = 'grid';
}
// Dienst-Informationen laden
async function loadServicesInfo() {
const loader = document.getElementById('services-loader');
const container = document.getElementById('services-container');
loader.style.display = 'block';
container.style.display = 'none';
const data = await fetchData('/api/services');
if (!data) {
loader.textContent = 'Fehler beim Laden der Dienstdaten';
return;
}
// Frontend-Status
document.getElementById('frontend-status').innerHTML = `
<p>Status: <span class="status ${data.frontend.status === 'online' ? 'status-online' : 'status-offline'}">${data.frontend.status}</span></p>
<p><strong>Name:</strong> ${data.frontend.name}</p>
<p><strong>Host:</strong> ${data.frontend.host}</p>
<p><strong>Port:</strong> ${data.frontend.port}</p>
`;
// Backend-Status
document.getElementById('backend-status').innerHTML = `
<p>Status: <span class="status ${data.backend.status === 'online' ? 'status-online' : 'status-offline'}">${data.backend.status}</span></p>
<p><strong>Name:</strong> ${data.backend.name}</p>
<p><strong>Host:</strong> ${data.backend.host}</p>
<p><strong>Port:</strong> ${data.backend.port}</p>
`;
// Docker-Container
if (data.docker.containers && data.docker.containers.length > 0) {
let containersHTML = '<table><tr><th>ID</th><th>Name</th><th>Image</th><th>Status</th><th>Ports</th></tr>';
data.docker.containers.forEach(container => {
containersHTML += `
<tr>
<td>${container.id}</td>
<td>${container.name}</td>
<td>${container.image}</td>
<td>${container.status}</td>
<td>${container.ports}</td>
</tr>
`;
});
containersHTML += '</table>';
document.getElementById('docker-container-list').innerHTML = containersHTML;
} else {
document.getElementById('docker-container-list').innerHTML = '<p>Keine Docker-Container gefunden</p>';
}
// UI anzeigen
loader.style.display = 'none';
container.style.display = 'grid';
}
// Netzwerk-Tools
document.getElementById('ping-button').addEventListener('click', async () => {
const hostInput = document.getElementById('ping-host');
const resultBox = document.getElementById('ping-result');
const host = hostInput.value.trim();
if (!host) {
resultBox.textContent = 'Bitte geben Sie einen Hostnamen oder eine IP-Adresse ein';
return;
}
resultBox.textContent = 'Ping wird ausgeführt...';
const data = await fetchData(`/api/ping/${encodeURIComponent(host)}`);
if (!data) {
resultBox.textContent = 'Fehler beim Ausführen des Ping-Befehls';
return;
}
resultBox.textContent = data.output || data.error || 'Keine Ausgabe';
});
document.getElementById('traceroute-button').addEventListener('click', async () => {
const hostInput = document.getElementById('traceroute-host');
const resultBox = document.getElementById('traceroute-result');
const host = hostInput.value.trim();
if (!host) {
resultBox.textContent = 'Bitte geben Sie einen Hostnamen oder eine IP-Adresse ein';
return;
}
resultBox.textContent = 'Traceroute wird ausgeführt...';
const data = await fetchData(`/api/traceroute/${encodeURIComponent(host)}`);
if (!data) {
resultBox.textContent = 'Fehler beim Ausführen des Traceroute-Befehls';
return;
}
resultBox.textContent = data.output || data.error || 'Keine Ausgabe';
});
document.getElementById('nslookup-button').addEventListener('click', async () => {
const hostInput = document.getElementById('nslookup-host');
const resultBox = document.getElementById('nslookup-result');
const host = hostInput.value.trim();
if (!host) {
resultBox.textContent = 'Bitte geben Sie einen Hostnamen oder eine IP-Adresse ein';
return;
}
resultBox.textContent = 'DNS-Abfrage wird ausgeführt...';
const data = await fetchData(`/api/nslookup/${encodeURIComponent(host)}`);
if (!data) {
resultBox.textContent = 'Fehler beim Ausführen des NSLookup-Befehls';
return;
}
resultBox.textContent = data.output || data.error || 'Keine Ausgabe';
});
// Echtzeit-Monitoring mit WebSockets
const socket = io();
// CPU- und RAM-Statistiken
socket.on('system-stats', data => {
// CPU-Anzeige aktualisieren
const cpuPercentage = data.cpu.load;
document.getElementById('cpu-percentage').textContent = `${cpuPercentage}%`;
document.getElementById('cpu-gauge').style.height = `${cpuPercentage}%`;
document.getElementById('cpu-gauge').style.backgroundColor = getColorByPercentage(cpuPercentage);
// CPU-Kerne anzeigen
const cpuCoresContainer = document.getElementById('cpu-cores-container');
if (cpuCoresContainer.childElementCount === 0) {
// Erstelle Kern-Anzeigen, falls sie noch nicht existieren
data.cpu.cores.forEach((load, index) => {
const coreElement = document.createElement('div');
coreElement.className = 'cpu-core';
coreElement.innerHTML = `
<div class="cpu-core-label">
<span>Kern ${index}</span>
<span id="cpu-core-${index}-value">${load}%</span>
</div>
<div class="cpu-core-bar">
<div id="cpu-core-${index}-fill" class="cpu-core-fill" style="width: ${load}%; background-color: ${getColorByPercentage(load)}"></div>
</div>
`;
cpuCoresContainer.appendChild(coreElement);
});
} else {
// Aktualisiere bestehende Kern-Anzeigen
data.cpu.cores.forEach((load, index) => {
const valueElement = document.getElementById(`cpu-core-${index}-value`);
const fillElement = document.getElementById(`cpu-core-${index}-fill`);
if (valueElement && fillElement) {
valueElement.textContent = `${load}%`;
fillElement.style.width = `${load}%`;
fillElement.style.backgroundColor = getColorByPercentage(load);
}
});
}
// RAM-Anzeige aktualisieren
const memPercentage = data.memory.usedPercent;
document.getElementById('memory-percentage').textContent = `${memPercentage}%`;
document.getElementById('memory-gauge').style.height = `${memPercentage}%`;
document.getElementById('memory-gauge').style.backgroundColor = getColorByPercentage(memPercentage);
document.getElementById('memory-details').innerHTML = `
<p><strong>Gesamt:</strong> ${formatBytes(data.memory.total)}</p>
<p><strong>Verwendet:</strong> ${formatBytes(data.memory.used)}</p>
<p><strong>Frei:</strong> ${formatBytes(data.memory.free)}</p>
`;
});
// Netzwerkstatistiken
let networkChart;
socket.on('network-stats', data => {
const networkThroughput = document.getElementById('network-throughput');
let throughputHTML = '';
data.stats.forEach(stat => {
throughputHTML += `
<div class="network-stat">
<strong>${stat.iface}:</strong> ↓ ${formatBytes(stat.rx_sec)}/s | ↑ ${formatBytes(stat.tx_sec)}/s
</div>
`;
});
networkThroughput.innerHTML = throughputHTML;
// Aktualisiere oder erstelle Netzwerk-Chart
updateNetworkChart(data.stats);
});
// Netzwerk-Chart initialisieren oder aktualisieren
function updateNetworkChart(stats) {
const ctx = document.getElementById('network-chart').getContext('2d');
if (!networkChart) {
// Chart initialisieren, wenn er noch nicht existiert
const labels = [];
const rxData = [];
const txData = [];
// Fülle anfängliche Daten
for (let i = 0; i < 20; i++) {
labels.push('');
rxData.push(0);
txData.push(0);
}
networkChart = new Chart(ctx, {
type: 'line',
data: {
labels: labels,
datasets: [
{
label: 'Empfangen (Bytes/s)',
data: rxData,
borderColor: '#3f51b5',
backgroundColor: 'rgba(63, 81, 181, 0.1)',
borderWidth: 2,
tension: 0.2,
fill: true
},
{
label: 'Gesendet (Bytes/s)',
data: txData,
borderColor: '#f44336',
backgroundColor: 'rgba(244, 67, 54, 0.1)',
borderWidth: 2,
tension: 0.2,
fill: true
}
]
},
options: {
responsive: true,
maintainAspectRatio: false,
scales: {
y: {
beginAtZero: true,
ticks: {
callback: function(value) {
return formatBytes(value, 0);
}
}
}
},
animation: {
duration: 300
}
}
});
} else {
// Chart aktualisieren
let rxTotal = 0;
let txTotal = 0;
stats.forEach(stat => {
rxTotal += stat.rx_sec || 0;
txTotal += stat.tx_sec || 0;
});
// Füge neue Daten hinzu und entferne alte
networkChart.data.labels.push('');
networkChart.data.labels.shift();
networkChart.data.datasets[0].data.push(rxTotal);
networkChart.data.datasets[0].data.shift();
networkChart.data.datasets[1].data.push(txTotal);
networkChart.data.datasets[1].data.shift();
networkChart.update();
}
}
// Hilfsfunktionen
function getColorByPercentage(percent) {
// Farbverlauf von Grün über Gelb nach Rot
if (percent < 70) {
return 'var(--success-color)';
} else if (percent < 90) {
return 'var(--warning-color)';
} else {
return 'var(--danger-color)';
}
}
function formatBytes(bytes, decimals = 2) {
if (bytes === 0) return '0 B';
const k = 1024;
const sizes = ['B', 'KB', 'MB', 'GB', 'TB', 'PB'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(decimals)) + ' ' + sizes[i];
}
// Initialisierung
document.addEventListener('DOMContentLoaded', () => {
// Zeit aktualisieren
setInterval(() => {
document.getElementById('current-time').innerHTML = `<strong>Zeitstempel:</strong> ${new Date().toLocaleString('de-DE')}`;
}, 1000);
// Lade Systemdaten, wenn das System-Panel aktiv ist
if (document.getElementById('system').classList.contains('active')) {
loadSystemInfo();
}
// Lade Script für Netzwerk-Chart, falls noch nicht geladen
if (typeof Chart === 'undefined') {
const script = document.createElement('script');
script.src = 'https://cdn.jsdelivr.net/npm/chart.js';
script.onload = () => {
console.log('Chart.js geladen');
};
document.head.appendChild(script);
}
});

View File

@@ -0,0 +1,227 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title><%= title %></title>
<style>
body {
font-family: Arial, sans-serif;
max-width: 800px;
margin: 0 auto;
padding: 20px;
background-color: #f5f5f5;
}
h1, h2, h3 {
color: #2c3e50;
}
.card {
background-color: white;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
padding: 15px;
margin-bottom: 20px;
}
.form-group {
margin-bottom: 15px;
}
label {
display: block;
margin-bottom: 5px;
font-weight: bold;
color: #34495e;
}
input[type="text"] {
width: 100%;
padding: 8px;
border: 1px solid #ddd;
border-radius: 4px;
box-sizing: border-box;
}
.btn {
background-color: #3498db;
color: white;
border: none;
padding: 10px 15px;
border-radius: 4px;
cursor: pointer;
margin-right: 5px;
}
.btn:hover {
background-color: #2980b9;
}
.btn-primary {
background-color: #2ecc71;
}
.btn-primary:hover {
background-color: #27ae60;
}
.status {
padding: 8px;
border-radius: 4px;
margin-top: 5px;
}
.status-good {
background-color: #d4edda;
color: #155724;
}
.status-warning {
background-color: #fff3cd;
color: #856404;
}
.status-error {
background-color: #f8d7da;
color: #721c24;
}
.message {
display: none;
padding: 10px;
margin: 10px 0;
border-radius: 4px;
}
.message-success {
background-color: #d4edda;
color: #155724;
}
.message-error {
background-color: #f8d7da;
color: #721c24;
}
.interface-list {
list-style-type: none;
padding: 0;
}
.interface-item {
background-color: #f8f9fa;
padding: 10px;
margin-bottom: 10px;
border-radius: 4px;
border-left: 4px solid #3498db;
}
</style>
</head>
<body>
<h1><%= title %></h1>
<p>Letzte Aktualisierung: <%= lastCheck %></p>
<div id="message" class="message"></div>
<div class="card">
<h2>Backend-Konfiguration</h2>
<div class="form-group">
<label for="backendUrl">Backend-URL:</label>
<input type="text" id="backendUrl" value="<%= backendUrl %>">
</div>
<button type="button" class="btn" onclick="testBackend()">Verbindung testen</button>
<button type="button" class="btn btn-primary" onclick="updateBackend()">URL aktualisieren</button>
</div>
<div class="card">
<h2>Backend-Status</h2>
<div class="status <%= backendStatus === 'Verbunden' ? 'status-good' : 'status-error' %>">
<strong>Status:</strong> <%= backendStatus %>
</div>
<div class="status <%= pingStatus ? 'status-good' : 'status-error' %>">
<strong>Ping:</strong> <%= pingStatus ? 'Erfolgreich' : 'Fehlgeschlagen' %>
</div>
<div class="status">
<strong>Host:</strong> <%= backendHost %>
</div>
<div class="status">
<strong>Port:</strong> <%= backendPort %>
</div>
</div>
<div class="card">
<h2>Netzwerkschnittstellen</h2>
<ul class="interface-list">
<% if (interfaces && interfaces.length > 0) { %>
<% interfaces.forEach(function(iface) { %>
<li class="interface-item">
<strong><%= iface.name %>:</strong> <%= iface.address %>
</li>
<% }); %>
<% } else { %>
<li class="interface-item">Keine Netzwerkschnittstellen gefunden</li>
<% } %>
</ul>
</div>
<script>
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 testBackend() {
const backendUrl = document.getElementById('backendUrl').value;
if (!backendUrl) {
showMessage('Bitte geben Sie eine Backend-URL ein', true);
return;
}
fetch('/test-backend', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ backendUrl })
})
.then(response => response.json())
.then(data => {
if (data.success) {
const pingStatus = data.ping ? 'Erfolgreich' : 'Fehlgeschlagen';
const connectionStatus = data.connection ? 'Verbunden' : 'Keine Verbindung';
showMessage(`Ping: ${pingStatus}, API-Verbindung: ${connectionStatus}`, !(data.ping && data.connection));
} else {
showMessage(data.message, true);
}
})
.catch(error => {
showMessage(`Fehler: ${error}`, true);
});
}
function updateBackend() {
const backendUrl = document.getElementById('backendUrl').value;
if (!backendUrl) {
showMessage('Bitte geben Sie eine Backend-URL ein', true);
return;
}
fetch('/update-backend', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ backendUrl })
})
.then(response => response.json())
.then(data => {
showMessage(data.message, !data.success);
if (data.success) {
// Lade die Seite neu nach erfolgreicher Aktualisierung
setTimeout(() => {
location.reload();
}, 1500);
}
})
.catch(error => {
showMessage(`Fehler: ${error}`, true);
});
}
</script>
</body>
</html>