"feat: Implement new debug server components for reservation platform"
This commit is contained in:
parent
83ad25a97f
commit
653f06fa88
@ -1 +1,307 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from flask import Flask, render_template, jsonify
|
||||||
|
import socket
|
||||||
|
import platform
|
||||||
|
import os
|
||||||
|
import subprocess
|
||||||
|
import json
|
||||||
|
import datetime
|
||||||
|
import psutil
|
||||||
|
import netifaces
|
||||||
|
|
||||||
|
app = Flask(__name__)
|
||||||
|
DEBUG_PORT = 5555 # Port für den Debug-Server
|
||||||
|
|
||||||
|
@app.route('/')
|
||||||
|
def index():
|
||||||
|
"""Rendert die Hauptseite des Debug-Servers."""
|
||||||
|
return render_template('index.html',
|
||||||
|
hostname=socket.gethostname(),
|
||||||
|
ip_address=get_ip_address(),
|
||||||
|
timestamp=datetime.datetime.now().strftime("%d.%m.%Y %H:%M:%S"))
|
||||||
|
|
||||||
|
@app.route('/systeminfo')
|
||||||
|
def systeminfo():
|
||||||
|
"""Gibt Systeminformationen zurück."""
|
||||||
|
info = {
|
||||||
|
"hostname": socket.gethostname(),
|
||||||
|
"platform": platform.platform(),
|
||||||
|
"architecture": platform.machine(),
|
||||||
|
"processor": platform.processor(),
|
||||||
|
"python_version": platform.python_version(),
|
||||||
|
"uptime": get_uptime(),
|
||||||
|
"memory": get_memory_info(),
|
||||||
|
"disk": get_disk_info(),
|
||||||
|
}
|
||||||
|
return jsonify(info)
|
||||||
|
|
||||||
|
@app.route('/network')
|
||||||
|
def network():
|
||||||
|
"""Gibt Netzwerkinformationen zurück."""
|
||||||
|
info = {
|
||||||
|
"interfaces": get_network_interfaces(),
|
||||||
|
"connections": get_active_connections(),
|
||||||
|
"dns_servers": get_dns_servers(),
|
||||||
|
"gateway": get_default_gateway(),
|
||||||
|
}
|
||||||
|
return jsonify(info)
|
||||||
|
|
||||||
|
@app.route('/docker')
|
||||||
|
def docker():
|
||||||
|
"""Gibt Docker-Informationen zurück."""
|
||||||
|
return jsonify(get_docker_info())
|
||||||
|
|
||||||
|
@app.route('/ping/<host>')
|
||||||
|
def ping_host(host):
|
||||||
|
"""Pingt einen Zielhost an und gibt das Ergebnis zurück."""
|
||||||
|
if is_valid_hostname(host):
|
||||||
|
try:
|
||||||
|
result = subprocess.run(['ping', '-n', '4', host],
|
||||||
|
capture_output=True, text=True, timeout=10)
|
||||||
|
return jsonify({
|
||||||
|
"success": result.returncode == 0,
|
||||||
|
"output": result.stdout,
|
||||||
|
"error": result.stderr,
|
||||||
|
"return_code": result.returncode
|
||||||
|
})
|
||||||
|
except subprocess.TimeoutExpired:
|
||||||
|
return jsonify({"success": False, "error": "Zeitüberschreitung beim Ping-Befehl"})
|
||||||
|
except Exception as e:
|
||||||
|
return jsonify({"success": False, "error": str(e)})
|
||||||
|
else:
|
||||||
|
return jsonify({"success": False, "error": "Ungültiger Hostname oder IP-Adresse"})
|
||||||
|
|
||||||
|
@app.route('/traceroute/<host>')
|
||||||
|
def traceroute_host(host):
|
||||||
|
"""Führt einen Traceroute zum Zielhost durch und gibt das Ergebnis zurück."""
|
||||||
|
if is_valid_hostname(host):
|
||||||
|
try:
|
||||||
|
result = subprocess.run(['tracert', host],
|
||||||
|
capture_output=True, text=True, timeout=30)
|
||||||
|
return jsonify({
|
||||||
|
"success": True,
|
||||||
|
"output": result.stdout,
|
||||||
|
"error": result.stderr,
|
||||||
|
"return_code": result.returncode
|
||||||
|
})
|
||||||
|
except subprocess.TimeoutExpired:
|
||||||
|
return jsonify({"success": False, "error": "Zeitüberschreitung beim Traceroute-Befehl"})
|
||||||
|
except Exception as e:
|
||||||
|
return jsonify({"success": False, "error": str(e)})
|
||||||
|
else:
|
||||||
|
return jsonify({"success": False, "error": "Ungültiger Hostname oder IP-Adresse"})
|
||||||
|
|
||||||
|
@app.route('/nslookup/<host>')
|
||||||
|
def nslookup_host(host):
|
||||||
|
"""Führt eine DNS-Abfrage für den angegebenen Host durch."""
|
||||||
|
if is_valid_hostname(host):
|
||||||
|
try:
|
||||||
|
result = subprocess.run(['nslookup', host],
|
||||||
|
capture_output=True, text=True, timeout=10)
|
||||||
|
return jsonify({
|
||||||
|
"success": result.returncode == 0,
|
||||||
|
"output": result.stdout,
|
||||||
|
"error": result.stderr,
|
||||||
|
"return_code": result.returncode
|
||||||
|
})
|
||||||
|
except subprocess.TimeoutExpired:
|
||||||
|
return jsonify({"success": False, "error": "Zeitüberschreitung beim NSLookup-Befehl"})
|
||||||
|
except Exception as e:
|
||||||
|
return jsonify({"success": False, "error": str(e)})
|
||||||
|
else:
|
||||||
|
return jsonify({"success": False, "error": "Ungültiger Hostname oder IP-Adresse"})
|
||||||
|
|
||||||
|
@app.route('/backend-status')
|
||||||
|
def backend_status():
|
||||||
|
"""Überprüft den Status des Haupt-Backend-Servers."""
|
||||||
|
try:
|
||||||
|
# Benutze den Localhost und den Port des Haupt-Backends (Standard: 5000)
|
||||||
|
backend_port = 5000
|
||||||
|
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||||
|
s.settimeout(2)
|
||||||
|
result = s.connect_ex(('localhost', backend_port))
|
||||||
|
s.close()
|
||||||
|
|
||||||
|
return jsonify({
|
||||||
|
"status": "online" if result == 0 else "offline",
|
||||||
|
"port": backend_port,
|
||||||
|
"error_code": result
|
||||||
|
})
|
||||||
|
except Exception as e:
|
||||||
|
return jsonify({
|
||||||
|
"status": "error",
|
||||||
|
"message": str(e)
|
||||||
|
})
|
||||||
|
|
||||||
|
# Hilfsfunktionen
|
||||||
|
|
||||||
|
def get_ip_address():
|
||||||
|
"""Ermittelt die primäre IP-Adresse des Servers."""
|
||||||
|
try:
|
||||||
|
# Für die lokale Netzwerk-IP
|
||||||
|
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||||
|
s.connect(("8.8.8.8", 80))
|
||||||
|
return s.getsockname()[0]
|
||||||
|
except:
|
||||||
|
return "127.0.0.1" # Fallback auf localhost
|
||||||
|
finally:
|
||||||
|
s.close()
|
||||||
|
|
||||||
|
def get_uptime():
|
||||||
|
"""Ermittelt die Systemlaufzeit."""
|
||||||
|
return str(datetime.timedelta(seconds=int(psutil.boot_time())))
|
||||||
|
|
||||||
|
def get_memory_info():
|
||||||
|
"""Gibt Informationen über den Speicherverbrauch zurück."""
|
||||||
|
mem = psutil.virtual_memory()
|
||||||
|
return {
|
||||||
|
"total": format_bytes(mem.total),
|
||||||
|
"used": format_bytes(mem.used),
|
||||||
|
"free": format_bytes(mem.available),
|
||||||
|
"percent": mem.percent
|
||||||
|
}
|
||||||
|
|
||||||
|
def get_disk_info():
|
||||||
|
"""Gibt Informationen über den Festplattenspeicher zurück."""
|
||||||
|
disk = psutil.disk_usage('/')
|
||||||
|
return {
|
||||||
|
"total": format_bytes(disk.total),
|
||||||
|
"used": format_bytes(disk.used),
|
||||||
|
"free": format_bytes(disk.free),
|
||||||
|
"percent": disk.percent
|
||||||
|
}
|
||||||
|
|
||||||
|
def get_network_interfaces():
|
||||||
|
"""Gibt Informationen über die Netzwerkschnittstellen zurück."""
|
||||||
|
interfaces = {}
|
||||||
|
for iface in netifaces.interfaces():
|
||||||
|
addrs = netifaces.ifaddresses(iface)
|
||||||
|
if netifaces.AF_INET in addrs:
|
||||||
|
ipv4 = addrs[netifaces.AF_INET][0]
|
||||||
|
interfaces[iface] = {
|
||||||
|
"ip": ipv4.get('addr', ''),
|
||||||
|
"netmask": ipv4.get('netmask', ''),
|
||||||
|
"broadcast": ipv4.get('broadcast', '')
|
||||||
|
}
|
||||||
|
else:
|
||||||
|
interfaces[iface] = {"ip": "Keine IPv4-Adresse", "netmask": "", "broadcast": ""}
|
||||||
|
|
||||||
|
return interfaces
|
||||||
|
|
||||||
|
def get_active_connections():
|
||||||
|
"""Gibt Informationen über aktive Netzwerkverbindungen zurück."""
|
||||||
|
connections = []
|
||||||
|
for conn in psutil.net_connections(kind='inet'):
|
||||||
|
if conn.status == 'ESTABLISHED':
|
||||||
|
connections.append({
|
||||||
|
"local_address": f"{conn.laddr.ip}:{conn.laddr.port}",
|
||||||
|
"remote_address": f"{conn.raddr.ip}:{conn.raddr.port}",
|
||||||
|
"status": conn.status,
|
||||||
|
"pid": conn.pid
|
||||||
|
})
|
||||||
|
|
||||||
|
return connections[:20] # Begrenze auf 20 Verbindungen
|
||||||
|
|
||||||
|
def get_dns_servers():
|
||||||
|
"""Ermittelt die DNS-Server-Konfiguration."""
|
||||||
|
dns_servers = []
|
||||||
|
try:
|
||||||
|
if os.name == 'nt': # Windows
|
||||||
|
output = subprocess.check_output(
|
||||||
|
['powershell', '-Command',
|
||||||
|
"Get-DnsClientServerAddress -AddressFamily IPv4 | Select-Object -ExpandProperty ServerAddresses"],
|
||||||
|
text=True)
|
||||||
|
for line in output.strip().split('\n'):
|
||||||
|
line = line.strip()
|
||||||
|
if line:
|
||||||
|
dns_servers.append(line)
|
||||||
|
else: # Linux/Unix
|
||||||
|
with open('/etc/resolv.conf', 'r') as f:
|
||||||
|
for line in f:
|
||||||
|
if line.startswith('nameserver'):
|
||||||
|
dns_servers.append(line.split()[1])
|
||||||
|
except Exception as e:
|
||||||
|
dns_servers.append(f"Fehler: {str(e)}")
|
||||||
|
|
||||||
|
return dns_servers
|
||||||
|
|
||||||
|
def get_default_gateway():
|
||||||
|
"""Ermittelt das Standard-Gateway."""
|
||||||
|
gateways = netifaces.gateways()
|
||||||
|
if 'default' in gateways and netifaces.AF_INET in gateways['default']:
|
||||||
|
return gateways['default'][netifaces.AF_INET][0]
|
||||||
|
return "Kein Standard-Gateway gefunden"
|
||||||
|
|
||||||
|
def get_docker_info():
|
||||||
|
"""Gibt Informationen über Docker und laufende Container zurück."""
|
||||||
|
docker_info = {"installed": False, "version": "", "containers": []}
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Prüfe, ob Docker installiert ist
|
||||||
|
version_result = subprocess.run(['docker', '--version'],
|
||||||
|
capture_output=True, text=True, timeout=5)
|
||||||
|
if version_result.returncode == 0:
|
||||||
|
docker_info["installed"] = True
|
||||||
|
docker_info["version"] = version_result.stdout.strip()
|
||||||
|
|
||||||
|
# Hole Informationen über laufende Container
|
||||||
|
container_result = subprocess.run(
|
||||||
|
['docker', 'ps', '--format', '{{.ID}},{{.Image}},{{.Status}},{{.Ports}},{{.Names}}'],
|
||||||
|
capture_output=True, text=True, timeout=5)
|
||||||
|
|
||||||
|
if container_result.returncode == 0:
|
||||||
|
for line in container_result.stdout.strip().split('\n'):
|
||||||
|
if line:
|
||||||
|
parts = line.split(',')
|
||||||
|
if len(parts) >= 5:
|
||||||
|
docker_info["containers"].append({
|
||||||
|
"id": parts[0],
|
||||||
|
"image": parts[1],
|
||||||
|
"status": parts[2],
|
||||||
|
"ports": parts[3],
|
||||||
|
"name": parts[4]
|
||||||
|
})
|
||||||
|
except (subprocess.SubprocessError, FileNotFoundError):
|
||||||
|
pass # Docker ist nicht installiert oder nicht zugänglich
|
||||||
|
|
||||||
|
return docker_info
|
||||||
|
|
||||||
|
def is_valid_hostname(hostname):
|
||||||
|
"""Überprüft, ob ein Hostname oder eine IP-Adresse gültig ist."""
|
||||||
|
if len(hostname) > 255:
|
||||||
|
return False
|
||||||
|
if hostname == "localhost" or hostname == "127.0.0.1":
|
||||||
|
return True
|
||||||
|
|
||||||
|
# Prüfe auf IP-Format
|
||||||
|
try:
|
||||||
|
socket.inet_pton(socket.AF_INET, hostname)
|
||||||
|
return True
|
||||||
|
except socket.error:
|
||||||
|
pass
|
||||||
|
|
||||||
|
try:
|
||||||
|
socket.inet_pton(socket.AF_INET6, hostname)
|
||||||
|
return True
|
||||||
|
except socket.error:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# Prüfe auf Hostname-Format
|
||||||
|
allowed = set("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-.")
|
||||||
|
return all(c in allowed for c in hostname) and not hostname.startswith(".")
|
||||||
|
|
||||||
|
def format_bytes(size):
|
||||||
|
"""Formatiert Bytes in eine lesbare Größe."""
|
||||||
|
power = 2**10 # 1024
|
||||||
|
n = 0
|
||||||
|
power_labels = {0: 'B', 1: 'KB', 2: 'MB', 3: 'GB', 4: 'TB'}
|
||||||
|
while size > power and n < 4:
|
||||||
|
size /= power
|
||||||
|
n += 1
|
||||||
|
return f"{size:.2f} {power_labels[n]}"
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
print(f"MYP Backend Debug-Server wird gestartet auf Port {DEBUG_PORT}...")
|
||||||
|
app.run(host='0.0.0.0', port=DEBUG_PORT, debug=True)
|
3
backend/debug-server/requirements.txt
Normal file
3
backend/debug-server/requirements.txt
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
Flask==2.0.1
|
||||||
|
psutil==5.9.0
|
||||||
|
netifaces==0.11.0
|
291
backend/debug-server/static/css/style.css
Normal file
291
backend/debug-server/static/css/style.css
Normal file
@ -0,0 +1,291 @@
|
|||||||
|
/* Allgemeine Stile */
|
||||||
|
:root {
|
||||||
|
--primary-color: #3498db;
|
||||||
|
--secondary-color: #2c3e50;
|
||||||
|
--background-color: #f5f7fa;
|
||||||
|
--card-color: #ffffff;
|
||||||
|
--text-color: #333333;
|
||||||
|
--border-color: #e0e0e0;
|
||||||
|
--success-color: #2ecc71;
|
||||||
|
--warning-color: #f39c12;
|
||||||
|
--danger-color: #e74c3c;
|
||||||
|
--shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
* {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
.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;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-button {
|
||||||
|
background: none;
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
padding: 0.75rem 1.25rem;
|
||||||
|
margin: 0 0.25rem;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 1rem;
|
||||||
|
border-radius: 4px;
|
||||||
|
transition: background-color 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-button:hover {
|
||||||
|
background-color: rgba(255, 255, 255, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-button.active {
|
||||||
|
background-color: var(--primary-color);
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Hauptbereich */
|
||||||
|
main {
|
||||||
|
flex: 1;
|
||||||
|
padding: 2rem;
|
||||||
|
max-width: 1400px;
|
||||||
|
margin: 0 auto;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.panel {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.panel.active {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
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(--shadow);
|
||||||
|
}
|
||||||
|
|
||||||
|
.card.full-width {
|
||||||
|
grid-column: 1 / -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
color: var(--secondary-color);
|
||||||
|
font-size: 1.2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Fortschrittsbalken */
|
||||||
|
.progress-container {
|
||||||
|
margin-top: 1rem;
|
||||||
|
background-color: #f0f0f0;
|
||||||
|
border-radius: 4px;
|
||||||
|
height: 10px;
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
.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: #2980b9;
|
||||||
|
}
|
||||||
|
|
||||||
|
.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: #f0f0f0;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
tr:hover {
|
||||||
|
background-color: #f8f9fa;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Status-Anzeigen */
|
||||||
|
.status {
|
||||||
|
display: inline-block;
|
||||||
|
padding: 0.25rem 0.5rem;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
margin-left: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 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;
|
||||||
|
}
|
||||||
|
}
|
306
backend/debug-server/static/js/script.js
Normal file
306
backend/debug-server/static/js/script.js
Normal file
@ -0,0 +1,306 @@
|
|||||||
|
// DOM-Element-Referenzen
|
||||||
|
const navButtons = document.querySelectorAll('.nav-button');
|
||||||
|
const panels = document.querySelectorAll('.panel');
|
||||||
|
|
||||||
|
// Panel-Navigation
|
||||||
|
navButtons.forEach(button => {
|
||||||
|
button.addEventListener('click', () => {
|
||||||
|
const targetPanel = button.dataset.target;
|
||||||
|
|
||||||
|
// 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(targetPanel).classList.add('active');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Automatische Aktualisierung der Daten
|
||||||
|
const updateInterval = 10000; // 10 Sekunden
|
||||||
|
|
||||||
|
// Initialisierung und erste Datenladung
|
||||||
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
|
// Systemdaten laden
|
||||||
|
loadSystemInfo();
|
||||||
|
|
||||||
|
// Netzwerkdaten laden
|
||||||
|
loadNetworkInfo();
|
||||||
|
|
||||||
|
// Docker-Daten laden
|
||||||
|
loadDockerInfo();
|
||||||
|
|
||||||
|
// Backend-Status laden
|
||||||
|
loadBackendStatus();
|
||||||
|
|
||||||
|
// Event-Listener für die Netzwerk-Tools
|
||||||
|
document.getElementById('ping-button').addEventListener('click', performPing);
|
||||||
|
document.getElementById('traceroute-button').addEventListener('click', performTraceroute);
|
||||||
|
document.getElementById('nslookup-button').addEventListener('click', performNSLookup);
|
||||||
|
|
||||||
|
// Automatische Aktualisierung einrichten
|
||||||
|
setInterval(() => {
|
||||||
|
if (document.getElementById('system-panel').classList.contains('active')) {
|
||||||
|
loadSystemInfo();
|
||||||
|
} else if (document.getElementById('network-panel').classList.contains('active')) {
|
||||||
|
loadNetworkInfo();
|
||||||
|
} else if (document.getElementById('docker-panel').classList.contains('active')) {
|
||||||
|
loadDockerInfo();
|
||||||
|
} else if (document.getElementById('backend-panel').classList.contains('active')) {
|
||||||
|
loadBackendStatus();
|
||||||
|
}
|
||||||
|
}, updateInterval);
|
||||||
|
});
|
||||||
|
|
||||||
|
// API-Anfragen
|
||||||
|
async function fetchData(endpoint) {
|
||||||
|
try {
|
||||||
|
const response = await fetch(endpoint);
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`HTTP-Fehler! Status: ${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 data = await fetchData('/systeminfo');
|
||||||
|
if (!data) return;
|
||||||
|
|
||||||
|
// Betriebssystem-Info aktualisieren
|
||||||
|
document.getElementById('platform-info').innerHTML = `
|
||||||
|
<p><strong>Plattform:</strong> ${data.platform}</p>
|
||||||
|
<p><strong>Python Version:</strong> ${data.python_version}</p>
|
||||||
|
<p><strong>Betriebszeit:</strong> ${data.uptime}</p>
|
||||||
|
`;
|
||||||
|
|
||||||
|
// Hardware-Info aktualisieren
|
||||||
|
document.getElementById('hardware-info').innerHTML = `
|
||||||
|
<p><strong>Prozessor:</strong> ${data.processor}</p>
|
||||||
|
<p><strong>Architektur:</strong> ${data.architecture}</p>
|
||||||
|
`;
|
||||||
|
|
||||||
|
// Speicher-Info aktualisieren
|
||||||
|
document.getElementById('memory-info').innerHTML = `
|
||||||
|
<p><strong>Gesamt:</strong> ${data.memory.total}</p>
|
||||||
|
<p><strong>Verwendet:</strong> ${data.memory.used} (${data.memory.percent}%)</p>
|
||||||
|
<p><strong>Frei:</strong> ${data.memory.free}</p>
|
||||||
|
`;
|
||||||
|
document.getElementById('memory-bar').style.width = `${data.memory.percent}%`;
|
||||||
|
document.getElementById('memory-bar').style.backgroundColor = getColorByPercentage(data.memory.percent);
|
||||||
|
|
||||||
|
// Festplatten-Info aktualisieren
|
||||||
|
document.getElementById('disk-info').innerHTML = `
|
||||||
|
<p><strong>Gesamt:</strong> ${data.disk.total}</p>
|
||||||
|
<p><strong>Verwendet:</strong> ${data.disk.used} (${data.disk.percent}%)</p>
|
||||||
|
<p><strong>Frei:</strong> ${data.disk.free}</p>
|
||||||
|
`;
|
||||||
|
document.getElementById('disk-bar').style.width = `${data.disk.percent}%`;
|
||||||
|
document.getElementById('disk-bar').style.backgroundColor = getColorByPercentage(data.disk.percent);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Netzwerk-Informationen laden
|
||||||
|
async function loadNetworkInfo() {
|
||||||
|
const data = await fetchData('/network');
|
||||||
|
if (!data) return;
|
||||||
|
|
||||||
|
// Netzwerkschnittstellen aktualisieren
|
||||||
|
let interfacesHTML = '<table><tr><th>Schnittstelle</th><th>IP-Adresse</th><th>Netzmaske</th><th>Broadcast</th></tr>';
|
||||||
|
for (const [name, info] of Object.entries(data.interfaces)) {
|
||||||
|
interfacesHTML += `
|
||||||
|
<tr>
|
||||||
|
<td>${name}</td>
|
||||||
|
<td>${info.ip}</td>
|
||||||
|
<td>${info.netmask}</td>
|
||||||
|
<td>${info.broadcast}</td>
|
||||||
|
</tr>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
interfacesHTML += '</table>';
|
||||||
|
document.getElementById('network-interfaces').innerHTML = interfacesHTML;
|
||||||
|
|
||||||
|
// DNS-Server aktualisieren
|
||||||
|
let dnsHTML = '<ul>';
|
||||||
|
for (const server of data.dns_servers) {
|
||||||
|
dnsHTML += `<li>${server}</li>`;
|
||||||
|
}
|
||||||
|
dnsHTML += '</ul>';
|
||||||
|
document.getElementById('dns-servers').innerHTML = dnsHTML;
|
||||||
|
|
||||||
|
// Gateway aktualisieren
|
||||||
|
document.getElementById('default-gateway').innerHTML = `<p>${data.gateway}</p>`;
|
||||||
|
|
||||||
|
// Aktive Verbindungen aktualisieren
|
||||||
|
if (data.connections && data.connections.length > 0) {
|
||||||
|
let connectionsHTML = '<table><tr><th>Lokale Adresse</th><th>Remote-Adresse</th><th>Status</th><th>PID</th></tr>';
|
||||||
|
for (const conn of data.connections) {
|
||||||
|
connectionsHTML += `
|
||||||
|
<tr>
|
||||||
|
<td>${conn.local_address}</td>
|
||||||
|
<td>${conn.remote_address}</td>
|
||||||
|
<td>${conn.status}</td>
|
||||||
|
<td>${conn.pid}</td>
|
||||||
|
</tr>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
connectionsHTML += '</table>';
|
||||||
|
document.getElementById('active-connections').innerHTML = connectionsHTML;
|
||||||
|
} else {
|
||||||
|
document.getElementById('active-connections').innerHTML = '<p>Keine aktiven Verbindungen gefunden.</p>';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Docker-Informationen laden
|
||||||
|
async function loadDockerInfo() {
|
||||||
|
const data = await fetchData('/docker');
|
||||||
|
if (!data) return;
|
||||||
|
|
||||||
|
// Docker-Status aktualisieren
|
||||||
|
if (data.installed) {
|
||||||
|
document.getElementById('docker-status').innerHTML = `
|
||||||
|
<p><span class="status status-online">Installiert</span></p>
|
||||||
|
<p><strong>Version:</strong> ${data.version}</p>
|
||||||
|
`;
|
||||||
|
|
||||||
|
// Container aktualisieren
|
||||||
|
if (data.containers && data.containers.length > 0) {
|
||||||
|
let containersHTML = '<table><tr><th>ID</th><th>Name</th><th>Image</th><th>Status</th><th>Ports</th></tr>';
|
||||||
|
for (const container of data.containers) {
|
||||||
|
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-containers').innerHTML = containersHTML;
|
||||||
|
} else {
|
||||||
|
document.getElementById('docker-containers').innerHTML = '<p>Keine laufenden Container gefunden.</p>';
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
document.getElementById('docker-status').innerHTML = '<p><span class="status status-offline">Nicht installiert</span></p>';
|
||||||
|
document.getElementById('docker-containers').innerHTML = '<p>Docker ist nicht installiert oder nicht zugänglich.</p>';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Backend-Status laden
|
||||||
|
async function loadBackendStatus() {
|
||||||
|
const data = await fetchData('/backend-status');
|
||||||
|
if (!data) return;
|
||||||
|
|
||||||
|
let statusHTML = '';
|
||||||
|
if (data.status === 'online') {
|
||||||
|
statusHTML = `
|
||||||
|
<p>Status: <span class="status status-online">Online</span></p>
|
||||||
|
<p><strong>Port:</strong> ${data.port}</p>
|
||||||
|
`;
|
||||||
|
} else if (data.status === 'offline') {
|
||||||
|
statusHTML = `
|
||||||
|
<p>Status: <span class="status status-offline">Offline</span></p>
|
||||||
|
<p><strong>Port:</strong> ${data.port}</p>
|
||||||
|
<p><strong>Fehlercode:</strong> ${data.error_code}</p>
|
||||||
|
`;
|
||||||
|
} else {
|
||||||
|
statusHTML = `
|
||||||
|
<p>Status: <span class="status status-warning">Fehler</span></p>
|
||||||
|
<p><strong>Nachricht:</strong> ${data.message}</p>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
document.getElementById('main-backend-status').innerHTML = statusHTML;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Netzwerk-Tools
|
||||||
|
async function performPing() {
|
||||||
|
const hostInput = document.getElementById('ping-host');
|
||||||
|
const resultElement = document.getElementById('ping-result');
|
||||||
|
const host = hostInput.value.trim();
|
||||||
|
|
||||||
|
if (!host) {
|
||||||
|
resultElement.textContent = 'Bitte geben Sie einen Hostnamen oder eine IP-Adresse ein.';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
resultElement.textContent = 'Ping wird ausgeführt...';
|
||||||
|
|
||||||
|
const data = await fetchData(`/ping/${encodeURIComponent(host)}`);
|
||||||
|
if (!data) {
|
||||||
|
resultElement.textContent = 'Fehler beim Ausführen des Ping-Befehls.';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.success) {
|
||||||
|
resultElement.textContent = data.output;
|
||||||
|
} else {
|
||||||
|
resultElement.textContent = `Fehler: ${data.error || 'Unbekannter Fehler'}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function performTraceroute() {
|
||||||
|
const hostInput = document.getElementById('traceroute-host');
|
||||||
|
const resultElement = document.getElementById('traceroute-result');
|
||||||
|
const host = hostInput.value.trim();
|
||||||
|
|
||||||
|
if (!host) {
|
||||||
|
resultElement.textContent = 'Bitte geben Sie einen Hostnamen oder eine IP-Adresse ein.';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
resultElement.textContent = 'Traceroute wird ausgeführt...';
|
||||||
|
|
||||||
|
const data = await fetchData(`/traceroute/${encodeURIComponent(host)}`);
|
||||||
|
if (!data) {
|
||||||
|
resultElement.textContent = 'Fehler beim Ausführen des Traceroute-Befehls.';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.success) {
|
||||||
|
resultElement.textContent = data.output;
|
||||||
|
} else {
|
||||||
|
resultElement.textContent = `Fehler: ${data.error || 'Unbekannter Fehler'}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function performNSLookup() {
|
||||||
|
const hostInput = document.getElementById('nslookup-host');
|
||||||
|
const resultElement = document.getElementById('nslookup-result');
|
||||||
|
const host = hostInput.value.trim();
|
||||||
|
|
||||||
|
if (!host) {
|
||||||
|
resultElement.textContent = 'Bitte geben Sie einen Hostnamen oder eine IP-Adresse ein.';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
resultElement.textContent = 'DNS-Abfrage wird ausgeführt...';
|
||||||
|
|
||||||
|
const data = await fetchData(`/nslookup/${encodeURIComponent(host)}`);
|
||||||
|
if (!data) {
|
||||||
|
resultElement.textContent = 'Fehler beim Ausführen des NSLookup-Befehls.';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.success) {
|
||||||
|
resultElement.textContent = data.output;
|
||||||
|
} else {
|
||||||
|
resultElement.textContent = `Fehler: ${data.error || 'Unbekannter Fehler'}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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)';
|
||||||
|
}
|
||||||
|
}
|
150
backend/debug-server/templates/index.html
Normal file
150
backend/debug-server/templates/index.html
Normal file
@ -0,0 +1,150 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="de">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>MYP Backend Debug-Server</title>
|
||||||
|
<link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}">
|
||||||
|
<link rel="icon" href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 100 100%22><text y=%22.9em%22 font-size=%2290%22>🔍</text></svg>">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<header>
|
||||||
|
<div class="header-content">
|
||||||
|
<h1>MYP Backend Debug-Server</h1>
|
||||||
|
<div class="server-info">
|
||||||
|
<span id="hostname"><strong>Hostname:</strong> {{ hostname }}</span>
|
||||||
|
<span id="ip-address"><strong>IP-Adresse:</strong> {{ ip_address }}</span>
|
||||||
|
<span id="timestamp"><strong>Timestamp:</strong> {{ timestamp }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<nav>
|
||||||
|
<button class="nav-button active" data-target="system-panel">System</button>
|
||||||
|
<button class="nav-button" data-target="network-panel">Netzwerk</button>
|
||||||
|
<button class="nav-button" data-target="docker-panel">Docker</button>
|
||||||
|
<button class="nav-button" data-target="tools-panel">Tools</button>
|
||||||
|
<button class="nav-button" data-target="backend-panel">Backend-Status</button>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<main>
|
||||||
|
<!-- System-Informationen -->
|
||||||
|
<section id="system-panel" class="panel active">
|
||||||
|
<h2>Systeminformationen</h2>
|
||||||
|
<div class="card-container">
|
||||||
|
<div class="card">
|
||||||
|
<h3>Betriebssystem</h3>
|
||||||
|
<div id="platform-info">Wird geladen...</div>
|
||||||
|
</div>
|
||||||
|
<div class="card">
|
||||||
|
<h3>Hardware</h3>
|
||||||
|
<div id="hardware-info">Wird geladen...</div>
|
||||||
|
</div>
|
||||||
|
<div class="card">
|
||||||
|
<h3>Speicher</h3>
|
||||||
|
<div id="memory-info">Wird geladen...</div>
|
||||||
|
<div class="progress-container">
|
||||||
|
<div id="memory-bar" class="progress-bar"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="card">
|
||||||
|
<h3>Festplatte</h3>
|
||||||
|
<div id="disk-info">Wird geladen...</div>
|
||||||
|
<div class="progress-container">
|
||||||
|
<div id="disk-bar" class="progress-bar"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- Netzwerk-Informationen -->
|
||||||
|
<section id="network-panel" class="panel">
|
||||||
|
<h2>Netzwerkinformationen</h2>
|
||||||
|
<div class="card-container">
|
||||||
|
<div class="card full-width">
|
||||||
|
<h3>Netzwerkschnittstellen</h3>
|
||||||
|
<div id="network-interfaces">Wird geladen...</div>
|
||||||
|
</div>
|
||||||
|
<div class="card">
|
||||||
|
<h3>DNS Server</h3>
|
||||||
|
<div id="dns-servers">Wird geladen...</div>
|
||||||
|
</div>
|
||||||
|
<div class="card">
|
||||||
|
<h3>Standard-Gateway</h3>
|
||||||
|
<div id="default-gateway">Wird geladen...</div>
|
||||||
|
</div>
|
||||||
|
<div class="card full-width">
|
||||||
|
<h3>Aktive Verbindungen</h3>
|
||||||
|
<div id="active-connections">Wird geladen...</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- Docker-Informationen -->
|
||||||
|
<section id="docker-panel" class="panel">
|
||||||
|
<h2>Docker-Informationen</h2>
|
||||||
|
<div class="card-container">
|
||||||
|
<div class="card">
|
||||||
|
<h3>Docker-Status</h3>
|
||||||
|
<div id="docker-status">Wird geladen...</div>
|
||||||
|
</div>
|
||||||
|
<div class="card full-width">
|
||||||
|
<h3>Container</h3>
|
||||||
|
<div id="docker-containers">Wird geladen...</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- Netzwerk-Tools -->
|
||||||
|
<section id="tools-panel" class="panel">
|
||||||
|
<h2>Netzwerk-Tools</h2>
|
||||||
|
<div class="card-container">
|
||||||
|
<div class="card tool-card">
|
||||||
|
<h3>Ping</h3>
|
||||||
|
<div class="tool-input">
|
||||||
|
<input type="text" id="ping-host" placeholder="Hostname oder IP-Adresse">
|
||||||
|
<button id="ping-button">Ping</button>
|
||||||
|
</div>
|
||||||
|
<pre id="ping-result" class="result-box">Geben Sie einen Hostnamen oder eine IP-Adresse ein...</pre>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card tool-card">
|
||||||
|
<h3>Traceroute</h3>
|
||||||
|
<div class="tool-input">
|
||||||
|
<input type="text" id="traceroute-host" placeholder="Hostname oder IP-Adresse">
|
||||||
|
<button id="traceroute-button">Traceroute</button>
|
||||||
|
</div>
|
||||||
|
<pre id="traceroute-result" class="result-box">Geben Sie einen Hostnamen oder eine IP-Adresse ein...</pre>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card tool-card">
|
||||||
|
<h3>DNS-Lookup</h3>
|
||||||
|
<div class="tool-input">
|
||||||
|
<input type="text" id="nslookup-host" placeholder="Hostname oder IP-Adresse">
|
||||||
|
<button id="nslookup-button">NSLookup</button>
|
||||||
|
</div>
|
||||||
|
<pre id="nslookup-result" class="result-box">Geben Sie einen Hostnamen oder eine IP-Adresse ein...</pre>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- Backend-Status -->
|
||||||
|
<section id="backend-panel" class="panel">
|
||||||
|
<h2>Backend-Status</h2>
|
||||||
|
<div class="card-container">
|
||||||
|
<div class="card">
|
||||||
|
<h3>Haupt-Backend</h3>
|
||||||
|
<div id="main-backend-status">Wird geladen...</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<footer>
|
||||||
|
<p>© 2025 MYP (Manage your Printer) | Debug-Server v1.0.0</p>
|
||||||
|
<p>Netzwerk- und Systemdiagnose-Tool</p>
|
||||||
|
</footer>
|
||||||
|
|
||||||
|
<script src="{{ url_for('static', filename='js/script.js') }}"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
1
packages/reservation-platform/debug-server/package.json
Normal file
1
packages/reservation-platform/debug-server/package.json
Normal file
@ -0,0 +1 @@
|
|||||||
|
|
Loading…
x
Reference in New Issue
Block a user