first commit
This commit is contained in:
975
index.html
Normal file
975
index.html
Normal 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>
|
||||
Reference in New Issue
Block a user