diff --git a/html/index.php b/html/index.php index 3ccce5f..4e74ac0 100755 --- a/html/index.php +++ b/html/index.php @@ -1,4 +1,6 @@ + $interface, - 'method' => $_POST['method'] ?? '', - 'ip' => $_POST['ip'] ?? '', - 'netmask' => $_POST['netmask'] ?? '', - 'gateway' => $_POST['gateway'] ?? '', - 'dns' => $_POST['dns'] ?? '', - 'multicast' => $_POST['multicast'] ?? 'off' - ]; - - $network_config[$interface] = $config; - file_put_contents($config_file, json_encode($network_config, JSON_PRETTY_PRINT)); - } elseif ($action === 'activate') { - // Activate interface - exec("sudo ip link set $interface up", $output, $return_code); - } elseif ($action === 'deactivate') { - // Deactivate interface - exec("sudo ip link set $interface down", $output, $return_code); - } - } -} - -// Get network interfaces excluding specific ones -$interfaces = []; -$output = []; -exec('ip addr show', $output); - -$current_interface = null; -$interface_data = []; - -foreach ($output as $line) { - // Match interface name - if (preg_match('/^\d+:\s+([a-zA-Z0-9]+):/', $line, $matches)) { - $current_interface = $matches[1]; - - // Skip interfaces we want to exclude - if (strpos($current_interface, 'enx') === 0) { - $current_interface = null; - continue; - } - - if ($current_interface === 'lo') { - $current_interface = null; - continue; - } - - // Check if interface is a bridge or docker interface - if ( - strpos($current_interface, 'docker') === 0 || - strpos($current_interface, 'br-') === 0 || - strpos($current_interface, 'veth') === 0 - ) { - $current_interface = null; - continue; - } - - $interface_data[$current_interface] = [ - 'name' => $current_interface, - 'ip' => '', - 'mac' => '', - 'status' => 'down', - 'config' => $network_config[$current_interface] ?? null - ]; - } - - // Extract IP address - if ($current_interface && preg_match('/inet\s+(\d+\.\d+\.\d+\.\d+)/', $line, $matches)) { - $interface_data[$current_interface]['ip'] = $matches[1]; - } - - // Extract MAC address - if ($current_interface && preg_match('/link\/ether\s+([a-f0-9:]+)/', $line, $matches)) { - $interface_data[$current_interface]['mac'] = $matches[1]; - } - - // Check if interface is up - if ($current_interface && strpos($line, 'state UP') !== false) { - $interface_data[$current_interface]['status'] = 'up'; - } -} - -// Get selected interface from GET parameter or first interface -$selected_interface = $_GET['interface'] ?? array_keys($interface_data)[0] ?? null; -?> +include 'header.php'; ?>
+
+

CPU (%)

+
+
+
+

RAM (%)

+
+
-

Network Configuration

- - -
-

Select Interface:

-
- - - - - -
-
- - -
- -
-
-
Interface Settings
- - - -
-
-

IP Address:

-

MAC Address:

-
- -
- -
- No network interfaces found or selected. -
- -
+

Network (KB/s)

+
+
+
+

Disk I/O (KB/s) & Disk %

+
+ +
+
Last update:
+
CPU: % · RAM: % · In: KB/s · + Out: KB/s
+
+
+
+
+
- - \ No newline at end of file + const ramChart = new Chart(document.getElementById('ramChart').getContext('2d'), { + type: 'line', + data: { + labels: [], + datasets: [{ + label: 'RAM %', + data: [], + fill: false, + borderColor: '#00b8ff', + backgroundColor: 'rgba(0, 184, 255, 0.1)', + tension: 0.4, + pointRadius: 0, + pointHoverRadius: 6 + }] + }, + options: { + responsive: true, + maintainAspectRatio: false, + scales: { + y: { + min: 0, + max: 100, + grid: { + color: 'rgba(92, 158, 255, 0.1)' + }, + ticks: { + color: '#a9c7ff' + } + }, + x: { + grid: { + color: 'rgba(92, 158, 255, 0.1)' + }, + ticks: { + color: '#a9c7ff' + } + } + }, + plugins: { + legend: { + labels: { + color: '#a9c7ff' + } + } + } + } + }); + + const netChart = new Chart(document.getElementById('netChart').getContext('2d'), { + type: 'line', + data: { + labels: [], + datasets: [{ + label: 'Net In (KB/s)', + data: [], + fill: false, + borderColor: '#00a8ff', + backgroundColor: 'rgba(0, 168, 255, 0.1)', + tension: 0.4, + pointRadius: 0, + pointHoverRadius: 6 + }, + { + label: 'Net Out (KB/s)', + data: [], + fill: false, + borderColor: '#00b8ff', + backgroundColor: 'rgba(0, 184, 255, 0.1)', + tension: 0.4, + pointRadius: 0, + pointHoverRadius: 6 + } + ] + }, + options: { + responsive: true, + maintainAspectRatio: false, + scales: { + y: { + beginAtZero: true, + grid: { + color: 'rgba(92, 158, 255, 0.1)' + }, + ticks: { + color: '#a9c7ff' + } + }, + x: { + grid: { + color: 'rgba(92, 158, 255, 0.1)' + }, + ticks: { + color: '#a9c7ff' + } + } + }, + plugins: { + legend: { + labels: { + color: '#a9c7ff' + } + } + } + } + }); + + const diskChart = new Chart(document.getElementById('diskChart').getContext('2d'), { + type: 'line', + data: { + labels: [], + datasets: [{ + label: 'Disk Read (KB/s)', + data: [], + fill: false, + borderColor: '#00a8ff', + backgroundColor: 'rgba(0, 168, 255, 0.1)', + tension: 0.4, + pointRadius: 0, + pointHoverRadius: 6 + }, + { + label: 'Disk Write (KB/s)', + data: [], + fill: false, + borderColor: '#00b8ff', + backgroundColor: 'rgba(0, 184, 255, 0.1)', + tension: 0.4, + pointRadius: 0, + pointHoverRadius: 6 + }, + { + label: 'Disk %', + data: [], + yAxisID: 'percent', + fill: false, + borderColor: '#00e0ff', + backgroundColor: 'rgba(0, 224, 255, 0.1)', + tension: 0.4, + pointRadius: 0, + pointHoverRadius: 6 + } + ] + }, + options: { + responsive: true, + maintainAspectRatio: false, + scales: { + y: { + position: 'left', + beginAtZero: true, + grid: { + color: 'rgba(92, 158, 255, 0.1)' + }, + ticks: { + color: '#a9c7ff' + } + }, + percent: { + position: 'right', + min: 0, + max: 100, + grid: { + display: false + }, + ticks: { + color: '#a9c7ff', + callback: v => v + '%' + } + }, + x: { + grid: { + color: 'rgba(92, 158, 255, 0.1)' + }, + ticks: { + color: '#a9c7ff' + } + } + }, + plugins: { + legend: { + labels: { + color: '#a9c7ff' + } + } + } + } + }); + + async function update() { + try { + const res = await fetch(JSON_URL + "?_=" + Date.now(), { + cache: 'no-store' + }); + if (!res.ok) throw new Error('fetch fail ' + res.status); + const j = await res.json(); + + const labels = j.timestamps.map(t => new Date(t).toLocaleTimeString()); + cpuChart.data.labels = labels; + cpuChart.data.datasets[0].data = j.cpu_percent; + + ramChart.data.labels = labels; + ramChart.data.datasets[0].data = j.ram_percent; + + netChart.data.labels = labels; + netChart.data.datasets[0].data = j.net_in_Bps.map(toKB); + netChart.data.datasets[1].data = j.net_out_Bps.map(toKB); + + diskChart.data.labels = labels; + diskChart.data.datasets[0].data = j.disk_read_Bps.map(toKB); + diskChart.data.datasets[1].data = j.disk_write_Bps.map(toKB); + diskChart.data.datasets[2].data = j.disk_percent; + + cpuChart.update(); + ramChart.update(); + netChart.update(); + diskChart.update(); + + const last = labels.length - 1; + if (last >= 0) { + document.getElementById('lastUpdate').textContent = labels[last]; + document.getElementById('lastCpu').textContent = j.cpu_percent[last]; + document.getElementById('lastRam').textContent = j.ram_percent[last]; + document.getElementById('lastIn').textContent = toKB(j.net_in_Bps[last]); + document.getElementById('lastOut').textContent = toKB(j.net_out_Bps[last]); + } + } catch (e) { + console.error('update failed', e); + } + } + + setInterval(update, POLL_MS); + update(); + + \ No newline at end of file diff --git a/html/network.php b/html/network.php index badce53..e3d05bd 100755 --- a/html/network.php +++ b/html/network.php @@ -116,15 +116,15 @@ $selected_interface = $_GET['interface'] ?? array_keys($interface_data)[0] ?? nu

Network Configuration

- -
-

Select Interface:

+ + @@ -144,6 +144,18 @@ $selected_interface = $_GET['interface'] ?? array_keys($interface_data)[0] ?? nu

MAC Address:

@@ -209,16 +216,6 @@ $selected_interface = $_GET['interface'] ?? array_keys($interface_data)[0] ?? nu
-
- - -
@@ -247,6 +244,14 @@ $selected_interface = $_GET['interface'] ?? array_keys($interface_data)[0] ?? nu } }); }); + + // Tab switching functionality + document.querySelectorAll('.tab-button').forEach(button => { + button.addEventListener('click', function() { + const interfaceName = this.getAttribute('data-interface'); + window.location.href = '?interface=' + encodeURIComponent(interfaceName); + }); + }); \ No newline at end of file diff --git a/html/style.css b/html/style.css index c540339..2bbcb19 100644 --- a/html/style.css +++ b/html/style.css @@ -553,14 +553,52 @@ main { backdrop-filter: blur(10px); } -/* Horizontal network interfaces */ -.network-interfaces-horizontal { +.interface-tabs { + margin-bottom: 20px; + padding: 10px 0; + border-bottom: 1px solid rgba(92, 158, 255, 0.2); +} + +.interface-list { display: flex; flex-wrap: wrap; - gap: 20px; + gap: 8px; +} + +.tab-button { + padding: 10px 20px; + background: rgba(13, 27, 45, 0.7); + border: 1px solid rgba(92, 158, 255, 0.3); + border-radius: 8px; + color: var(--text-primary); + cursor: pointer; + transition: all 0.3s ease; + font-weight: 500; + backdrop-filter: blur(5px); +} + +.tab-button:hover { + background: rgba(0, 168, 255, 0.2); + border-color: var(--accent-blue-light); +} + +.tab-button.active { + background: linear-gradient(90deg, var(--accent-blue), var(--accent-blue-light)); + color: white; + border-color: var(--accent-blue-light); + box-shadow: 0 4px 12px rgba(0, 168, 255, 0.3); +} + +/* Activation buttons */ +.activation-buttons { + display: flex; + gap: 10px; + margin-bottom: 20px; justify-content: flex-start; } +/* ... existing CSS ... */ + .interface-card { flex: 1 1 300px; min-width: 300px; @@ -651,4 +689,23 @@ main { width: 100%; min-width: auto; } -} \ No newline at end of file + + .interface-list { + flex-direction: column; + align-items: stretch; + } + + .tab-button { + width: 100%; + text-align: center; + } + + .activation-buttons { + flex-direction: column; + align-items: stretch; + } + + .activation-buttons button { + width: 100%; + } +}