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'; ?>
+
+
-
Network Configuration
-
-
-
-
-
-
-
-
-
-
-
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%;
+ }
+}