first commit

This commit is contained in:
stephan
2025-06-29 07:39:33 +02:00
commit 4ed4d69b21
20 changed files with 2754 additions and 0 deletions

975
index.html Normal file
View File

@ -0,0 +1,975 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Wilcon Client Dashboard</title>
<link rel="stylesheet" href="style.css" />
<style>
/* Zusätzliches Styling für Prozesse und Tabellen */
.process-table-container {
max-height: 400px; /* Max Höhe für scrollbare Prozessliste */
overflow-y: auto;
border: 1px solid #ddd;
border-radius: 5px;
margin-top: 15px;
}
.process-table {
width: 100%;
border-collapse: collapse;
}
.process-table th,
.process-table td {
padding: 8px 12px;
border-bottom: 1px solid #eee;
text-align: left;
font-size: 0.9em;
}
.process-table th {
background-color: #f2f2f2;
font-weight: bold;
position: sticky;
top: 0;
z-index: 1; /* Damit der Header beim Scrollen oben bleibt */
}
.process-table tr:hover {
background-color: #f9f9f9;
}
.process-table td.text-right {
text-align: right;
}
.info-card.network-card p,
.info-card.network-card ul,
.info-card.network-card li {
text-align: left;
margin: 5px 0;
}
.info-card.network-card ul {
list-style-type: none;
padding-left: 20px;
}
.info-card.network-card ul ul {
padding-left: 30px;
}
/* Styling für Benutzerliste */
#user-accounts-list li {
padding: 2px 0;
font-size: 0.95em;
}
/* Styling für Filter und sortierbare Spalten */
.table-controls {
margin-bottom: 10px;
}
#process-filter {
width: 100%;
padding: 8px;
box-sizing: border-box;
border: 1px solid #ccc;
border-radius: 4px;
}
.process-table th.sortable {
cursor: pointer;
position: relative;
}
.process-table th.sortable:hover {
background-color: #e0e0e0;
}
.process-table th.sortable span {
font-size: 0.8em;
padding-left: 5px;
}
/* Styling for Network Connections Table */
.connection-table-container {
max-height: 250px; /* Max height for scrollable connection list */
overflow-y: auto;
border: 1px solid #ddd;
border-radius: 5px;
margin-top: 15px;
}
.connection-table {
width: 100%;
border-collapse: collapse;
}
.connection-table th,
.connection-table td {
padding: 8px 12px;
border-bottom: 1px solid #eee;
text-align: left;
font-size: 0.9em;
}
.connection-table th {
background-color: #f2f2f2;
font-weight: bold;
position: sticky;
top: 0;
z-index: 1;
}
.connection-table tr:hover {
background-color: #f9f9f9;
}
.filter-row th {
padding: 4px 8px;
background-color: #f8f8f8;
}
.column-filter {
width: 100%;
padding: 6px;
box-sizing: border-box;
border: 1px solid #ccc;
border-radius: 3px;
font-size: 0.9em;
}
/* Styling for Interface Details */
.interface-details {
margin-bottom: 5px;
border: 1px solid #eee;
border-radius: 4px;
background-color: #fdfdfd;
}
.interface-details summary {
padding: 8px 12px;
cursor: pointer;
font-weight: bold;
background-color: #f5f5f5;
border-radius: 4px;
}
.interface-details summary:hover {
background-color: #e0e0e0;
}
.interface-details ul {
padding: 10px 20px 10px 40px; /* Adjust padding for nested ul */
margin: 0;
}
.interface-details ul li {
margin-bottom: 3px;
}
.subsection-details {
margin-top: 15px;
margin-bottom: 15px;
}
.subsection-details > summary {
font-weight: bold;
font-size: 1.17em; /* Standard h3 size */
cursor: pointer;
padding: 8px 12px;
background-color: #f7f7f7;
border-radius: 4px;
list-style: revert; /* Ensure triangle is visible */
}
.subsection-details > summary:hover {
background-color: #e9e9e9;
}
.subsection-details > div {
padding: 10px 0 0 15px;
}
</style>
</head>
<body>
<div class="container">
<h1>Wilcon Client Dashboard</h1>
<div id="loading" class="message">Lade Systemdaten...</div>
<div id="error" class="message error" style="display: none"></div>
<details class="info-section" open>
<summary><h2>System & Hardware Informationen</h2></summary>
<div id="sysInfoCard" class="info-card">
<p><strong>Hostname:</strong> <span id="sysinfo-hostname"></span></p>
<p>
<strong>Betriebssystem:</strong>
<span id="sysinfo-os-name"></span> (<span
id="sysinfo-os-distribution"
></span>
<span id="sysinfo-os-version"></span>)
</p>
<p>
<strong>Architektur:</strong>
<span id="sysinfo-architecture"></span>
</p>
<p>
<strong>CPU:</strong> <span id="sysinfo-cpu-model"></span> (<span
id="sysinfo-cpu-cores"
></span>
Cores, <span id="sysinfo-cpu-threads"></span> Threads,
<span id="sysinfo-cpu-usage"></span>)
</p>
<p>
<strong>RAM:</strong> <span id="sysinfo-ram-used"></span>GB /
<span id="sysinfo-ram-total"></span>GB (<span
id="sysinfo-ram-percent"
></span
>%)
</p>
<p>
<strong>Disk:</strong> <span id="sysinfo-disk-used"></span>GB /
<span id="sysinfo-disk-total"></span>GB (<span
id="sysinfo-disk-percent"
></span
>%)
</p>
<p><strong>Bootzeit:</strong> <span id="sysinfo-boot-time"></span></p>
<div id="sysinfo-user-accounts-section">
<h3>Benutzerkonten</h3>
<ul
id="user-accounts-list"
style="list-style-type: none; padding-left: 0"
></ul>
</div>
<div id="sysinfo-gpu-info">
<h3>GPU Informationen</h3>
<div id="gpu-list"></div>
</div>
</div>
</details>
<details class="info-section">
<summary><h2>Netzwerk Informationen</h2></summary>
<div id="netInfoCard" class="info-card network-card">
<p><strong>Hostname:</strong> <span id="netinfo-hostname"></span></p>
<p>
<strong>IP-Adresse:</strong> <span id="netinfo-ip-address"></span>
</p>
<p>
<strong>Standard-Gateway:</strong>
<span id="netinfo-gateway"></span>
</p>
<p>
<strong>DNS-Server:</strong> <span id="netinfo-dns-servers"></span>
</p>
<details class="subsection-details">
<summary>Netzwerkschnittstellen</summary>
<div id="netinfo-interfaces-container"></div>
</details>
<h3>Aktive Verbindungen (Top 10):</h3>
<div class="connection-table-container">
<table class="connection-table">
<thead>
<tr>
<th class="sortable" data-sort="type">Typ<span></span></th>
<th class="sortable" data-sort="local_ip">
Lokale IP<span></span>
</th>
<th class="sortable" data-sort="local_port">
Lokaler Port<span></span>
</th>
<th class="sortable" data-sort="remote_ip">
Entfernte IP<span></span>
</th>
<th class="sortable" data-sort="remote_port">
Entfernter Port<span></span>
</th>
<th class="sortable" data-sort="status">
Status<span></span>
</th>
</tr>
<tr class="filter-row">
<th>
<select class="column-filter" data-filter-key="type">
<option value="">Alle</option>
</select>
</th>
<th>
<input
type="text"
class="column-filter"
data-filter-key="local_ip"
placeholder="Filter..."
/>
</th>
<th>
<input
type="text"
class="column-filter"
data-filter-key="local_port"
placeholder="Filter..."
/>
</th>
<th>
<input
type="text"
class="column-filter"
data-filter-key="remote_ip"
placeholder="Filter..."
/>
</th>
<th>
<input
type="text"
class="column-filter"
data-filter-key="remote_port"
placeholder="Filter..."
/>
</th>
<th>
<select class="column-filter" data-filter-key="status">
<option value="">Alle</option>
</select>
</th>
</tr>
</thead>
<tbody id="netinfo-connections-list"></tbody>
</table>
</div>
</div>
</details>
<details class="info-section">
<summary>
<h2>Prozess Informationen (<span id="procinfo-count"></span>)</h2>
</summary>
<div id="procInfoCard" class="info-card">
<div class="process-table-container">
<table class="process-table">
<thead>
<tr>
<th data-sort="pid" class="sortable">PID<span></span></th>
<th data-sort="name" class="sortable">Name<span></span></th>
<th data-sort="user" class="sortable">User<span></span></th>
<th data-sort="cpu" class="text-right sortable">
CPU (%)<span></span>
</th>
<th data-sort="memory" class="text-right sortable">
RAM (%)<span></span>
</th>
<th data-sort="status" class="sortable">
Status<span></span>
</th>
<th data-sort="runtime_seconds" class="sortable">
Laufzeit (s)<span></span>
</th>
</tr>
<tr class="filter-row">
<th>
<input
type="text"
class="column-filter"
data-filter-key="pid"
placeholder="Filter..."
/>
</th>
<th>
<input
type="text"
class="column-filter"
data-filter-key="name"
placeholder="Filter..."
/>
</th>
<th>
<select class="column-filter" data-filter-key="user">
<option value="">Alle Benutzer</option>
</select>
</th>
<th></th>
<th></th>
<th>
<select class="column-filter" data-filter-key="status">
<option value="">Alle Status</option>
</select>
</th>
<th></th>
</tr>
</thead>
<tbody id="procinfo-list"></tbody>
</table>
</div>
</div>
</details>
<button id="refreshButton">Alle Daten aktualisieren</button>
</div>
<script>
const API_BASE_URL = "http://localhost:8080";
let allProcesses = []; // Um die volle Prozessliste zu speichern
let loggedInUsers = []; // Um angemeldete Benutzer zu speichern
let currentSort = { key: "cpu", direction: "desc" }; // Standard-Sortierung
let allConnections = []; // Um die volle Verbindungsliste zu speichern
let currentConnectionSort = { key: "status", direction: "asc" }; // Standard-Sortierung für Verbindungen
const loadingMessage = document.getElementById("loading");
const errorMessage = document.getElementById("error");
const refreshButton = document.getElementById("refreshButton");
function displayMessage(type, message = "") {
loadingMessage.style.display = "none";
errorMessage.style.display = "none";
if (type === "loading") {
loadingMessage.textContent = message || "Lade Systemdaten...";
loadingMessage.style.display = "block";
} else if (type === "error") {
errorMessage.textContent =
message || "Ein unbekannter Fehler ist aufgetreten.";
errorMessage.style.display = "block";
}
}
function hideAllCards() {
// document.getElementById('sysInfoCard').style.display = 'none'; // ENTFERNEN ODER AUSKOMMENTIEREN
// document.getElementById('netInfoCard').style.display = 'none'; // ENTFERNEN ODER AUSKOMMENTIEREN
// document.getElementById('procInfoCard').style.display = 'none'; // ENTFERNEN ODER AUSKOMMENTIEREN
}
async function fetchSysInfo() {
try {
const response = await fetch(`${API_BASE_URL}/sysinfo`);
if (!response.ok)
throw new Error(
`HTTP-Fehler beim Abrufen der Systeminfos! Status: ${response.status}`
);
const data = await response.json();
// SYSTEM & HARDWARE INFORMATIONEN AKTUALISIEREN
document.getElementById("sysinfo-hostname").textContent =
data.network_basic.hostname;
document.getElementById("sysinfo-os-name").textContent = data.os.name;
document.getElementById("sysinfo-os-distribution").textContent =
data.os.distribution;
document.getElementById("sysinfo-os-version").textContent =
data.os.version;
// 'platform.machine()' ist ein Fallback für Architektur, falls es nicht in data.os enthalten ist
document.getElementById("sysinfo-architecture").textContent =
data.os.architecture || "N/A";
document.getElementById("sysinfo-cpu-model").textContent =
data.cpu.model;
document.getElementById("sysinfo-cpu-cores").textContent =
data.cpu.cores;
document.getElementById("sysinfo-cpu-threads").textContent =
data.cpu.threads;
document.getElementById(
"sysinfo-cpu-usage"
).textContent = `${data.cpu.usage}%`;
document.getElementById("sysinfo-ram-total").textContent =
data.ram.total;
document.getElementById("sysinfo-ram-used").textContent =
data.ram.used;
document.getElementById("sysinfo-ram-percent").textContent =
data.ram.percent;
document.getElementById("sysinfo-disk-total").textContent =
data.disk.total;
document.getElementById("sysinfo-disk-used").textContent =
data.disk.used;
document.getElementById("sysinfo-disk-percent").textContent =
data.disk.percent;
document.getElementById("sysinfo-boot-time").textContent =
data.os.boot_time || "N/A";
// BENUTZERKONTEN aktualisieren
const userAccountsList =
document.getElementById("user-accounts-list");
loggedInUsers = data.user_accounts
.filter((u) => u.is_logged_in)
.map((u) => u.name);
userAccountsList.innerHTML = "";
if (data.user_accounts && data.user_accounts.length > 0) {
// Füge diesen Sortier-Code HIER ein:
data.user_accounts.sort((a, b) => {
// Angemeldete Benutzer (true) kommen vor nicht angemeldeten (false)
if (a.is_logged_in === b.is_logged_in) {
// Wenn gleicher Status, alphabetisch nach Name sortieren
return a.name.localeCompare(b.name);
}
return b.is_logged_in - a.is_logged_in; // true - false = 1, false - true = -1
});
data.user_accounts.forEach((user) => {
const li = document.createElement("li");
let statusText = "";
if (user.is_logged_in) {
statusText = `Angemeldet seit: ${
user.login_time || "Unbekannt"
}`;
} else {
statusText = `Letzter Login: ${user.last_login || "Unbekannt"}`;
}
li.textContent = `${user.name} (${statusText})`;
li.style.color = user.is_logged_in ? "#28a745" : "#6c757d";
li.style.fontWeight = user.is_logged_in ? "bold" : "normal";
userAccountsList.appendChild(li);
});
document.getElementById(
"sysinfo-user-accounts-section"
).style.display = "block";
} else {
userAccountsList.innerHTML =
"<li>Keine Benutzerkonten gefunden.</li>";
document.getElementById(
"sysinfo-user-accounts-section"
).style.display = "block";
}
// GPU Informationen aktualisieren
const gpuListDiv = document.getElementById("gpu-list");
gpuListDiv.innerHTML = "";
if (data.gpu && data.gpu.length > 0) {
data.gpu.forEach((gpu) => {
const p = document.createElement("p");
p.innerHTML = `<strong>${gpu.name}:</strong> ${gpu.memory_used_mb}MB / ${gpu.memory_total_mb}MB genutzt (${gpu.load_percent}%)`;
gpuListDiv.appendChild(p);
});
document.getElementById("sysinfo-gpu-info").style.display = "block";
} else {
document.getElementById("sysinfo-gpu-info").style.display = "none";
}
} catch (error) {
console.error("Fehler beim Abrufen der Systeminformationen:", error);
displayMessage(
"error",
`Fehler beim Laden der Systeminfos: ${error.message}.`
);
}
}
async function fetchNetInfo() {
try {
const response = await fetch(`${API_BASE_URL}/netinfo`);
if (!response.ok)
throw new Error(
`HTTP-Fehler beim Abrufen der Netzwerkinfos! Status: ${response.status}`
);
const data = await response.json();
// NETZWERK INFORMATIONEN AKTUALISIEREN
document.getElementById("netinfo-hostname").textContent =
data.hostname;
document.getElementById("netinfo-ip-address").textContent =
data.ip_address;
document.getElementById("netinfo-gateway").textContent =
data.gateway || "N/A";
document.getElementById("netinfo-dns-servers").textContent =
data.dns_servers.join(", ") || "N/A";
// Netzwerkschnittstellen
const interfacesContainer = document.getElementById(
"netinfo-interfaces-container"
);
interfacesContainer.innerHTML = "";
for (const ifaceName in data.interfaces) {
const iface = data.interfaces[ifaceName];
const details = document.createElement("details");
details.classList.add("interface-details"); // Add a class for potential styling
const summary = document.createElement("summary");
summary.textContent = ifaceName;
details.appendChild(summary);
const ul = document.createElement("ul");
ul.style.listStyleType = "none"; // Remove bullet points
ul.style.paddingLeft = "20px"; // Indent
if (iface.ipv4) {
ul.innerHTML += `<li>IPv4: ${iface.ipv4}</li>`;
}
if (iface.ipv6) {
ul.innerHTML += `<li>IPv6: ${iface.ipv6}</li>`;
}
if (iface.mac) {
ul.innerHTML += `<li>MAC: ${iface.mac}</li>`;
}
details.appendChild(ul);
interfacesContainer.appendChild(details);
}
// Verbindungen speichern und Rendering anstoßen
allConnections = data.connections || [];
populateConnectionFilterDropdowns(allConnections);
sortAndRenderConnections();
} catch (error) {
console.error(
"Fehler beim Abrufen der Netzwerkinformationen:",
error
);
displayMessage(
"error",
`Fehler beim Laden der Netzwerkinfos: ${error.message}.`
);
}
}
async function fetchProcInfo() {
try {
const response = await fetch(`${API_BASE_URL}/procinfo`);
if (!response.ok)
throw new Error(
`HTTP-Fehler beim Abrufen der Prozessinfos! Status: ${response.status}`
);
const data = await response.json();
allProcesses = data; // Volle Liste speichern
populateFilterDropdowns(allProcesses);
sortAndRenderProcesses(); // Initial sortieren und rendern, nachdem die Dropdowns gefüllt sind
} catch (error) {
console.error("Fehler beim Abrufen der Prozessinformationen:", error);
displayMessage(
"error",
`Fehler beim Laden der Prozessinfos: ${error.message}.`
);
}
}
function populateConnectionFilterDropdowns(connections) {
const types = [
...new Set(connections.map((c) => c.type).filter(Boolean)),
].sort();
const statuses = [
...new Set(connections.map((c) => c.status).filter(Boolean)),
].sort();
const typeSelect = document.querySelector(
'.connection-table select[data-filter-key="type"]'
);
const statusSelect = document.querySelector(
'.connection-table select[data-filter-key="status"]'
);
// Bewahre den aktuell ausgewählten Wert
const selectedType = typeSelect.value;
const selectedStatus = statusSelect.value;
typeSelect.innerHTML = '<option value="">Alle</option>';
statusSelect.innerHTML = '<option value="">Alle</option>';
types.forEach((type) => {
const option = document.createElement("option");
option.value = type;
option.textContent = type;
typeSelect.appendChild(option);
});
statuses.forEach((status) => {
const option = document.createElement("option");
option.value = status;
option.textContent = status;
statusSelect.appendChild(option);
});
}
function populateFilterDropdowns(processes) {
const users = [
...new Set(processes.map((p) => p.user).filter(Boolean)),
].sort();
const statuses = [
...new Set(processes.map((p) => p.status).filter(Boolean)),
].sort();
const userSelect = document.querySelector(
'select[data-filter-key="user"]'
);
const statusSelect = document.querySelector(
'select[data-filter-key="status"]'
);
// Bewahre den aktuell ausgewählten Wert
const selectedUser = userSelect.value;
const selectedStatus = statusSelect.value;
userSelect.innerHTML = '<option value="">Alle Benutzer</option>';
statusSelect.innerHTML = '<option value="">Alle Status</option>';
users.forEach((user) => {
const option = document.createElement("option");
option.value = user;
option.textContent = user;
userSelect.appendChild(option);
});
statuses.forEach((status) => {
const option = document.createElement("option");
option.value = status;
option.textContent = status;
statusSelect.appendChild(option);
});
}
function renderConnectionTable(connections) {
const connectionsListBody = document.getElementById(
"netinfo-connections-list"
);
connectionsListBody.innerHTML = "";
if (connections.length > 0) {
connections.forEach((conn) => {
const row = document.createElement("tr");
row.innerHTML = `
<td>${conn.type}</td>
<td>${conn.local_ip || "N/A"}</td>
<td>${conn.local_port || "N/A"}</td>
<td>${conn.remote_ip || "N/A"}</td>
<td>${conn.remote_port || "N/A"}</td>
<td>${conn.status}</td>
`;
connectionsListBody.appendChild(row);
});
} else {
const row = document.createElement("tr");
const hasActiveFilter = Array.from(
document.querySelectorAll(".connection-table .column-filter")
).some((el) => el.value);
const message = hasActiveFilter
? "Keine Verbindungen entsprechen dem Filter."
: "Keine aktiven Verbindungen gefunden.";
row.innerHTML = `<td colspan="6">${message}</td>`;
connectionsListBody.appendChild(row);
}
}
function renderProcessTable(processes) {
document.getElementById("procinfo-count").textContent =
processes.length;
const procListBody = document.getElementById("procinfo-list");
procListBody.innerHTML = ""; // Vorherige Einträge löschen
if (processes.length > 0) {
processes.forEach((proc) => {
const row = document.createElement("tr");
row.innerHTML = `
<td>${proc.pid}</td>
<td>${proc.name}</td>
<td>${proc.user || "N/A"}</td>
<td class="text-right">${proc.cpu.toFixed(1)}</td>
<td class="text-right">${proc.memory.toFixed(1)}</td>
<td>${proc.status}</td>
<td>${proc.runtime_seconds}</td>
`;
procListBody.appendChild(row);
});
} else {
const row = document.createElement("tr");
const hasActiveFilter = Array.from(
document.querySelectorAll(".column-filter")
).some((el) => el.value);
const message = hasActiveFilter
? "Keine Prozesse entsprechen dem Filter."
: "Keine Prozessinformationen verfügbar.";
row.innerHTML = `<td colspan="7">${message}</td>`;
procListBody.appendChild(row);
}
}
function sortAndRenderProcesses() {
const { key, direction } = currentSort;
allProcesses.sort((a, b) => {
const valA = a[key];
const valB = b[key];
let comparison = 0;
if (typeof valA === "string") {
comparison = (valA || "").localeCompare(valB || "");
} else {
comparison = (valA || 0) - (valB || 0);
}
return direction === "asc" ? comparison : -comparison;
});
updateSortIndicators(".process-table", currentSort);
applyFiltersAndRender();
}
function sortAndRenderConnections() {
const { key, direction } = currentConnectionSort;
allConnections.sort((a, b) => {
const valA = a[key];
const valB = b[key];
let comparison = 0;
if (typeof valA === "string") {
comparison = (valA || "").localeCompare(valB || "");
} else {
comparison = (valA || 0) - (valB || 0);
}
return direction === "asc" ? comparison : -comparison;
});
updateSortIndicators(".connection-table", currentConnectionSort);
applyConnectionFiltersAndRender();
}
function applyFiltersAndRender() {
const filterValues = {};
document
.querySelectorAll(".process-table .column-filter")
.forEach((input) => {
if (input.value) {
filterValues[input.dataset.filterKey] = input.value.toLowerCase();
}
});
const filteredProcesses = allProcesses.filter((proc) => {
return Object.keys(filterValues).every((key) => {
const procValue = proc[key];
const filterValue = filterValues[key];
return (
procValue != null &&
String(procValue).toLowerCase().includes(filterValue)
);
});
});
renderProcessTable(filteredProcesses);
}
function applyConnectionFiltersAndRender() {
const filterValues = {};
document
.querySelectorAll(".connection-table .column-filter")
.forEach((input) => {
if (input.value) {
filterValues[input.dataset.filterKey] = input.value.toLowerCase();
}
});
const filteredConnections = allConnections.filter((conn) => {
return Object.keys(filterValues).every((key) => {
const connValue = conn[key];
const filterValue = filterValues[key];
return (
connValue != null &&
String(connValue).toLowerCase().includes(filterValue)
);
});
});
renderConnectionTable(filteredConnections);
}
function updateSortIndicators(tableSelector, sortState) {
document
.querySelectorAll(`${tableSelector} th.sortable`)
.forEach((th) => {
const span = th.querySelector("span");
if (th.dataset.sort === sortState.key) {
span.textContent = sortState.direction === "asc" ? "▲" : "▼";
} else {
span.textContent = "";
}
});
}
// Hauptfunktion zum Laden aller Daten
async function loadAllData() {
displayMessage("loading", "Lade alle Systemdaten...");
hideAllCards(); // Alle Karten ausblenden, bevor neue Daten geladen werden
errorMessage.style.display = "none"; // Fehler beim Neuladen verstecken
// Führe alle drei Fetch-Operationen parallel aus
await Promise.all([fetchSysInfo(), fetchNetInfo(), fetchProcInfo()])
.then(() => {
// Setze den Standardfilter, nachdem alle Daten geladen und die Dropdowns gefüllt sind
if (loggedInUsers.length > 0) {
const userFilterSelect = document.querySelector(
'select[data-filter-key="user"]'
);
const userExistsAsOption = Array.from(
userFilterSelect.options
).some((opt) => opt.value === loggedInUsers[0]);
if (userExistsAsOption) {
userFilterSelect.value = loggedInUsers[0];
}
}
// Führe die initiale Sortierung und das Rendern durch, was den Standardfilter berücksichtigt
sortAndRenderProcesses();
displayMessage(); // Lade-Nachricht ausblenden, wenn alles fertig
})
.catch((error) => {
console.error(
"Ein oder mehrere Ladevorgänge sind fehlgeschlagen:",
error
);
displayMessage(
"error",
`Nicht alle Daten konnten geladen werden: ${
error.message
}. Stelle sicher, dass der Agent auf Port ${
API_BASE_URL.split(":")[2]
} läuft.`
);
});
}
// Daten beim Laden der Seite abrufen
document.addEventListener("DOMContentLoaded", () => {
loadAllData();
// Listener für Prozess-Filter
document
.querySelectorAll(".process-table .column-filter")
.forEach((input) => {
input.addEventListener("input", applyFiltersAndRender);
});
// Listener für Verbindungs-Filter
document
.querySelectorAll(".connection-table .column-filter")
.forEach((input) => {
input.addEventListener("input", applyConnectionFiltersAndRender);
});
// Listener für die sortierbaren Spaltenüberschriften
document
.querySelectorAll(".process-table th.sortable")
.forEach((th) => {
th.addEventListener("click", () => {
const sortKey = th.dataset.sort;
if (currentSort.key === sortKey) {
currentSort.direction =
currentSort.direction === "asc" ? "desc" : "asc";
} else {
currentSort.key = sortKey;
currentSort.direction = ["name", "user", "status"].includes(
sortKey
)
? "asc"
: "desc";
}
sortAndRenderProcesses();
});
});
document
.querySelectorAll(".connection-table th.sortable")
.forEach((th) => {
th.addEventListener("click", () => {
const sortKey = th.dataset.sort;
if (currentConnectionSort.key === sortKey) {
currentConnectionSort.direction =
currentConnectionSort.direction === "asc" ? "desc" : "asc";
} else {
currentConnectionSort.key = sortKey;
currentConnectionSort.direction = [
"local_port",
"remote_port",
].includes(sortKey)
? "asc"
: "desc";
}
sortAndRenderConnections();
});
});
});
// Daten beim Klick auf den Button aktualisieren
refreshButton.addEventListener("click", loadAllData);
</script>
</body>
</html>