mirror of
https://github.com/jokob-sk/NetAlertX.git
synced 2026-04-03 16:51:24 -07:00
BE: /nettoos/interfaces endpoint
Signed-off-by: jokob-sk <jokob.sk@gmail.com>
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
# Net Tools API Endpoints
|
# Net Tools API Endpoints
|
||||||
|
|
||||||
The Net Tools API provides **network diagnostic utilities**, including Wake-on-LAN, traceroute, speed testing, DNS resolution, nmap scanning, and internet connection information.
|
The Net Tools API provides **network diagnostic utilities**, including Wake-on-LAN, traceroute, speed testing, DNS resolution, nmap scanning, internet connection information, and network interface info.
|
||||||
|
|
||||||
All endpoints require **authorization** via Bearer token.
|
All endpoints require **authorization** via Bearer token.
|
||||||
|
|
||||||
@@ -190,6 +190,51 @@ All endpoints require **authorization** via Bearer token.
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
### 7. Network Interfaces
|
||||||
|
|
||||||
|
* **GET** `/nettools/interfaces`
|
||||||
|
Fetches the list of network interfaces on the system, including IPv4/IPv6 addresses, MAC, MTU, state (up/down), and RX/TX byte counters.
|
||||||
|
|
||||||
|
**Response** (success):
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"success": true,
|
||||||
|
"interfaces": {
|
||||||
|
"eth0": {
|
||||||
|
"name": "eth0",
|
||||||
|
"short": "eth0",
|
||||||
|
"type": "ethernet",
|
||||||
|
"state": "up",
|
||||||
|
"mtu": 1500,
|
||||||
|
"mac": "00:11:32:EF:A5:6B",
|
||||||
|
"ipv4": ["192.168.1.82/24"],
|
||||||
|
"ipv6": ["fe80::211:32ff:feef:a56c/64"],
|
||||||
|
"rx_bytes": 18488221,
|
||||||
|
"tx_bytes": 1443944
|
||||||
|
},
|
||||||
|
"lo": {
|
||||||
|
"name": "lo",
|
||||||
|
"short": "lo",
|
||||||
|
"type": "loopback",
|
||||||
|
"state": "up",
|
||||||
|
"mtu": 65536,
|
||||||
|
"mac": null,
|
||||||
|
"ipv4": ["127.0.0.1/8"],
|
||||||
|
"ipv6": ["::1/128"],
|
||||||
|
"rx_bytes": 123456,
|
||||||
|
"tx_bytes": 123456
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Error Responses**:
|
||||||
|
|
||||||
|
* Command failure or parsing error → HTTP 500
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## Example `curl` Requests
|
## Example `curl` Requests
|
||||||
|
|
||||||
**Wake-on-LAN**:
|
**Wake-on-LAN**:
|
||||||
@@ -242,11 +287,20 @@ curl "http://<server_ip>:<GRAPHQL_PORT>/nettools/internetinfo" \
|
|||||||
-H "Authorization: Bearer <API_TOKEN>"
|
-H "Authorization: Bearer <API_TOKEN>"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
**Network Interfaces**:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
curl "http://<server_ip>:<GRAPHQL_PORT>/nettools/interfaces" \
|
||||||
|
-H "Authorization: Bearer <API_TOKEN>"
|
||||||
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## MCP Tools
|
## MCP Tools
|
||||||
|
|
||||||
Network tools are available as **MCP Tools** for AI assistant integration:
|
Network tools are available as **MCP Tools** for AI assistant integration:
|
||||||
- `wol_wake_device`, `trigger_scan`, `get_open_ports`
|
|
||||||
|
* `wol_wake_device`, `trigger_scan`, `get_open_ports`
|
||||||
|
|
||||||
📖 See [MCP Server Bridge API](API_MCP.md) for AI integration details.
|
📖 See [MCP Server Bridge API](API_MCP.md) for AI integration details.
|
||||||
|
|
||||||
|
|||||||
@@ -31,76 +31,107 @@ function getExternalIp() {
|
|||||||
// Network
|
// Network
|
||||||
// ----------------------------------------------------------
|
// ----------------------------------------------------------
|
||||||
|
|
||||||
//Network stats
|
|
||||||
// Server IP
|
|
||||||
|
|
||||||
|
|
||||||
|
// ----------------------------------------------------
|
||||||
|
// Network Stats (General)
|
||||||
|
// ----------------------------------------------------
|
||||||
|
|
||||||
|
// External IP
|
||||||
$externalIp = getExternalIp();
|
$externalIp = getExternalIp();
|
||||||
|
|
||||||
// Check Server name
|
// Server Name
|
||||||
if (!empty(gethostname())) { $network_NAME = gethostname(); } else { $network_NAME = lang('Systeminfo_Network_Server_Name_String'); }
|
$network_NAME = gethostname() ?: lang('Systeminfo_Network_Server_Name_String');
|
||||||
// Check HTTPS
|
|
||||||
if (isset($_SERVER['HTTPS'])) { $network_HTTPS = 'Yes (HTTPS)'; } else { $network_HTTPS = lang('Systeminfo_Network_Secure_Connection_String'); }
|
// HTTPS Check
|
||||||
// Check Query String
|
$network_HTTPS = isset($_SERVER['HTTPS']) ? 'Yes (HTTPS)' : lang('Systeminfo_Network_Secure_Connection_String');
|
||||||
if (empty($_SERVER['QUERY_STRING'])) { $network_QueryString = lang('Systeminfo_Network_Server_Query_String'); } else { $network_QueryString = $_SERVER['QUERY_STRING']; }
|
|
||||||
// Check HTTP referer
|
// Query String
|
||||||
if (empty($_SERVER['HTTP_REFERER'])) { $network_referer = lang('Systeminfo_Network_HTTP_Referer_String'); } else { $network_referer = $_SERVER['HTTP_REFERER']; }
|
$network_QueryString = !empty($_SERVER['QUERY_STRING'])
|
||||||
//Network Hardware stat
|
? $_SERVER['QUERY_STRING']
|
||||||
$network_result = shell_exec("cat /proc/net/dev | tail -n +3 | awk '{print $1}'");
|
: lang('Systeminfo_Network_Server_Query_String');
|
||||||
$net_interfaces = explode("\n", trim($network_result));
|
|
||||||
$network_result = shell_exec("cat /proc/net/dev | tail -n +3 | awk '{print $2}'");
|
// Referer
|
||||||
$net_interfaces_rx = explode("\n", trim($network_result));
|
$network_referer = !empty($_SERVER['HTTP_REFERER'])
|
||||||
$network_result = shell_exec("cat /proc/net/dev | tail -n +3 | awk '{print $10}'");
|
? $_SERVER['HTTP_REFERER']
|
||||||
$net_interfaces_tx = explode("\n", trim($network_result));
|
: lang('Systeminfo_Network_HTTP_Referer_String');
|
||||||
|
|
||||||
|
|
||||||
// Network Hardware ----------------------------------------------------------
|
// ----------------------------------------------------
|
||||||
echo '<div class="box box-solid">
|
// Network Hardware Stats (FAST VERSION)
|
||||||
<div class="box-header">
|
// ----------------------------------------------------
|
||||||
<h3 class="box-title sysinfo_headline"><i class="fa fa-sitemap fa-rotate-270"></i> ' . lang('Systeminfo_Network_Hardware') . '</h3>
|
|
||||||
</div>
|
// ----------------------------------------------------
|
||||||
<div class="box-body">
|
// Network Stats (General)
|
||||||
<table id="networkTable" class="table table-bordered table-hover">
|
// ----------------------------------------------------
|
||||||
<thead>
|
|
||||||
<tr>
|
// External IP
|
||||||
|
$externalIp = getExternalIp();
|
||||||
|
|
||||||
|
// Server Name
|
||||||
|
$network_NAME = gethostname() ?: lang('Systeminfo_Network_Server_Name_String');
|
||||||
|
|
||||||
|
// HTTPS Check
|
||||||
|
$network_HTTPS = isset($_SERVER['HTTPS']) ? 'Yes (HTTPS)' : lang('Systeminfo_Network_Secure_Connection_String');
|
||||||
|
|
||||||
|
// Query String
|
||||||
|
$network_QueryString = !empty($_SERVER['QUERY_STRING'])
|
||||||
|
? $_SERVER['QUERY_STRING']
|
||||||
|
: lang('Systeminfo_Network_Server_Query_String');
|
||||||
|
|
||||||
|
// Referer
|
||||||
|
$network_referer = !empty($_SERVER['HTTP_REFERER'])
|
||||||
|
? $_SERVER['HTTP_REFERER']
|
||||||
|
: lang('Systeminfo_Network_HTTP_Referer_String');
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// ----------------------------------------------------
|
||||||
|
// Network Stats (General)
|
||||||
|
// ----------------------------------------------------
|
||||||
|
|
||||||
|
// External IP
|
||||||
|
$externalIp = getExternalIp();
|
||||||
|
|
||||||
|
// Server Name
|
||||||
|
$network_NAME = gethostname() ?: lang('Systeminfo_Network_Server_Name_String');
|
||||||
|
|
||||||
|
// HTTPS Check
|
||||||
|
$network_HTTPS = isset($_SERVER['HTTPS']) ? 'Yes (HTTPS)' : lang('Systeminfo_Network_Secure_Connection_String');
|
||||||
|
|
||||||
|
// Query String
|
||||||
|
$network_QueryString = !empty($_SERVER['QUERY_STRING'])
|
||||||
|
? $_SERVER['QUERY_STRING']
|
||||||
|
: lang('Systeminfo_Network_Server_Query_String');
|
||||||
|
|
||||||
|
// Referer
|
||||||
|
$network_referer = !empty($_SERVER['HTTP_REFERER'])
|
||||||
|
? $_SERVER['HTTP_REFERER']
|
||||||
|
: lang('Systeminfo_Network_HTTP_Referer_String');
|
||||||
|
|
||||||
|
echo '
|
||||||
|
<div class="box box-solid">
|
||||||
|
<div class="box-header">
|
||||||
|
<h3 class="box-title sysinfo_headline">
|
||||||
|
<i class="fa fa-sitemap fa-rotate-270"></i>' . lang('Systeminfo_Network_Hardware') .'
|
||||||
|
</h3>
|
||||||
|
</div>
|
||||||
|
<div class="box-body">
|
||||||
|
<table id="networkTable" class="table table-bordered table-hover">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
<th>' . lang('Systeminfo_Network_Hardware_Interface_Name') . '</th>
|
<th>' . lang('Systeminfo_Network_Hardware_Interface_Name') . '</th>
|
||||||
<th>' . lang('Systeminfo_Network_Hardware_Interface_Mask') . '</th>
|
<th>' . lang('Systeminfo_Network_Hardware_Interface_Mask') . '</th>
|
||||||
<th>' . lang('Systeminfo_Network_Hardware_Interface_RX') . '</th>
|
<th>' . lang('Systeminfo_Network_Hardware_Interface_RX') . '</th>
|
||||||
<th>' . lang('Systeminfo_Network_Hardware_Interface_TX') . '</th>
|
<th>' . lang('Systeminfo_Network_Hardware_Interface_TX') . '</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>';
|
<tbody>
|
||||||
|
<tr><td colspan="4">Loading...</td></tr>
|
||||||
for ($x = 0; $x < sizeof($net_interfaces); $x++) {
|
</tbody>
|
||||||
$interface_name = str_replace(':', '', $net_interfaces[$x]);
|
</table>
|
||||||
$interface_ip_temp = exec('ip addr show ' . $interface_name . ' | grep "inet "');
|
</div>
|
||||||
$interface_ip_arr = explode(' ', trim($interface_ip_temp));
|
</div>';
|
||||||
|
|
||||||
if (!isset($interface_ip_arr[1])) {
|
|
||||||
$interface_ip_arr[1] = '--';
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($net_interfaces_rx[$x] == 0) {
|
|
||||||
$temp_rx = 0;
|
|
||||||
} else {
|
|
||||||
$temp_rx = number_format(round(($net_interfaces_rx[$x] / 1024 / 1024), 2), 2, ',', '.');
|
|
||||||
}
|
|
||||||
if ($net_interfaces_tx[$x] == 0) {
|
|
||||||
$temp_tx = 0;
|
|
||||||
} else {
|
|
||||||
$temp_tx = number_format(round(($net_interfaces_tx[$x] / 1024 / 1024), 2), 2, ',', '.');
|
|
||||||
}
|
|
||||||
echo '<tr>';
|
|
||||||
echo '<td>' . $interface_name . '</td>';
|
|
||||||
echo '<td>' . $interface_ip_arr[1] . '</td>';
|
|
||||||
echo '<td>' . $temp_rx . ' MB</td>';
|
|
||||||
echo '<td>' . $temp_tx . ' MB</td>';
|
|
||||||
echo '</tr>';
|
|
||||||
}
|
|
||||||
|
|
||||||
echo ' </tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>';
|
|
||||||
|
|
||||||
// Available IPs ----------------------------------------------------------
|
// Available IPs ----------------------------------------------------------
|
||||||
echo '<div class="box box-solid">
|
echo '<div class="box box-solid">
|
||||||
@@ -131,7 +162,7 @@ echo '<div class="box box-solid">
|
|||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-sm-3 sysinfo_network_a">' . lang('Systeminfo_Network_IP_Server') . '</div>
|
<div class="col-sm-3 sysinfo_network_a">' . lang('Systeminfo_Network_IP_Server') . '</div>
|
||||||
<div class="col-sm-9 sysinfo_network_b">' . $_SERVER['SERVER_ADDR'] . '</div>
|
<div class="col-sm-9 sysinfo_network_b">' . $_SERVER['SERVER_ADDR'] . '</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-sm-3 sysinfo_network_a">' . lang('Systeminfo_Network_Server_Name') . '</div>
|
<div class="col-sm-3 sysinfo_network_a">' . lang('Systeminfo_Network_Server_Name') . '</div>
|
||||||
<div class="col-sm-9 sysinfo_network_b">' . $network_NAME . '</div>
|
<div class="col-sm-9 sysinfo_network_b">' . $network_NAME . '</div>
|
||||||
@@ -139,11 +170,11 @@ echo '<div class="box box-solid">
|
|||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-sm-3 sysinfo_network_a">' . lang('Systeminfo_Network_Connection_Port') . '</div>
|
<div class="col-sm-3 sysinfo_network_a">' . lang('Systeminfo_Network_Connection_Port') . '</div>
|
||||||
<div class="col-sm-9 sysinfo_network_b">' . $_SERVER['REMOTE_PORT'] . '</div>
|
<div class="col-sm-9 sysinfo_network_b">' . $_SERVER['REMOTE_PORT'] . '</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-sm-3 sysinfo_network_a">' . lang('Systeminfo_Network_Secure_Connection') . '</div>
|
<div class="col-sm-3 sysinfo_network_a">' . lang('Systeminfo_Network_Secure_Connection') . '</div>
|
||||||
<div class="col-sm-9 sysinfo_network_b">' . $network_HTTPS . '</div>
|
<div class="col-sm-9 sysinfo_network_b">' . $network_HTTPS . '</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-sm-3 sysinfo_network_a">' . lang('Systeminfo_Network_Server_Version') . '</div>
|
<div class="col-sm-3 sysinfo_network_a">' . lang('Systeminfo_Network_Server_Version') . '</div>
|
||||||
<div class="col-sm-9 sysinfo_network_b">' . $_SERVER['SERVER_SOFTWARE'] . '</div>
|
<div class="col-sm-9 sysinfo_network_b">' . $_SERVER['SERVER_SOFTWARE'] . '</div>
|
||||||
@@ -151,7 +182,7 @@ echo '<div class="box box-solid">
|
|||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-sm-3 sysinfo_gerneral_a">' . lang('Systeminfo_Network_Request_URI') . '</div>
|
<div class="col-sm-3 sysinfo_gerneral_a">' . lang('Systeminfo_Network_Request_URI') . '</div>
|
||||||
<div class="col-sm-9 sysinfo_gerneral_b">' . $_SERVER['REQUEST_URI'] . '</div>
|
<div class="col-sm-9 sysinfo_gerneral_b">' . $_SERVER['REQUEST_URI'] . '</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-sm-3 sysinfo_network_a">' . lang('Systeminfo_Network_Server_Query') . '</div>
|
<div class="col-sm-3 sysinfo_network_a">' . lang('Systeminfo_Network_Server_Query') . '</div>
|
||||||
<div class="col-sm-9 sysinfo_network_b">' . $network_QueryString . '</div>
|
<div class="col-sm-9 sysinfo_network_b">' . $network_QueryString . '</div>
|
||||||
@@ -159,11 +190,11 @@ echo '<div class="box box-solid">
|
|||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-sm-3 sysinfo_network_a">' . lang('Systeminfo_Network_HTTP_Host') . '</div>
|
<div class="col-sm-3 sysinfo_network_a">' . lang('Systeminfo_Network_HTTP_Host') . '</div>
|
||||||
<div class="col-sm-9 sysinfo_network_b">' . $_SERVER['HTTP_HOST'] . '</div>
|
<div class="col-sm-9 sysinfo_network_b">' . $_SERVER['HTTP_HOST'] . '</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-sm-3 sysinfo_network_a">' . lang('Systeminfo_Network_HTTP_Referer') . '</div>
|
<div class="col-sm-3 sysinfo_network_a">' . lang('Systeminfo_Network_HTTP_Referer') . '</div>
|
||||||
<div class="col-sm-9 sysinfo_network_b">' . $network_referer . '</div>
|
<div class="col-sm-9 sysinfo_network_b">' . $network_referer . '</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-sm-3 sysinfo_network_a">' . lang('Systeminfo_Network_MIME') . '</div>
|
<div class="col-sm-3 sysinfo_network_a">' . lang('Systeminfo_Network_MIME') . '</div>
|
||||||
<div class="col-sm-9 sysinfo_network_b">' . $_SERVER['HTTP_ACCEPT'] . '</div>
|
<div class="col-sm-9 sysinfo_network_b">' . $_SERVER['HTTP_ACCEPT'] . '</div>
|
||||||
@@ -171,11 +202,11 @@ echo '<div class="box box-solid">
|
|||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-sm-3 sysinfo_network_a">' . lang('Systeminfo_Network_Accept_Language') . '</div>
|
<div class="col-sm-3 sysinfo_network_a">' . lang('Systeminfo_Network_Accept_Language') . '</div>
|
||||||
<div class="col-sm-9 sysinfo_network_b">' . $_SERVER['HTTP_ACCEPT_LANGUAGE'] . '</div>
|
<div class="col-sm-9 sysinfo_network_b">' . $_SERVER['HTTP_ACCEPT_LANGUAGE'] . '</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-sm-3 sysinfo_network_a">' . lang('Systeminfo_Network_Accept_Encoding') . '</div>
|
<div class="col-sm-3 sysinfo_network_a">' . lang('Systeminfo_Network_Accept_Encoding') . '</div>
|
||||||
<div class="col-sm-9 sysinfo_network_b">' . $_SERVER['HTTP_ACCEPT_ENCODING'] . '</div>
|
<div class="col-sm-9 sysinfo_network_b">' . $_SERVER['HTTP_ACCEPT_ENCODING'] . '</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-sm-3 sysinfo_network_a">' . lang('Systeminfo_Network_Request_Method') . '</div>
|
<div class="col-sm-3 sysinfo_network_a">' . lang('Systeminfo_Network_Request_Method') . '</div>
|
||||||
<div class="col-sm-9 sysinfo_network_b">' . $_SERVER['REQUEST_METHOD'] . '</div>
|
<div class="col-sm-9 sysinfo_network_b">' . $_SERVER['REQUEST_METHOD'] . '</div>
|
||||||
@@ -183,7 +214,7 @@ echo '<div class="box box-solid">
|
|||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-sm-3 sysinfo_network_a">' . lang('Systeminfo_Network_Request_Time') . '</div>
|
<div class="col-sm-3 sysinfo_network_a">' . lang('Systeminfo_Network_Request_Time') . '</div>
|
||||||
<div class="col-sm-9 sysinfo_network_b">' . $_SERVER['REQUEST_TIME'] . '</div>
|
<div class="col-sm-9 sysinfo_network_b">' . $_SERVER['REQUEST_TIME'] . '</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>';
|
</div>';
|
||||||
|
|
||||||
@@ -241,14 +272,14 @@ function fetchUsedIps(callback) {
|
|||||||
`,
|
`,
|
||||||
variables: {
|
variables: {
|
||||||
options: {
|
options: {
|
||||||
status: "all_devices"
|
status: "all_devices"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
success: function(response) {
|
success: function(response) {
|
||||||
|
|
||||||
console.log(response);
|
console.log(response);
|
||||||
|
|
||||||
const usedIps = (response?.data?.devices?.devices || [])
|
const usedIps = (response?.data?.devices?.devices || [])
|
||||||
.map(d => d.devLastIP)
|
.map(d => d.devLastIP)
|
||||||
.filter(ip => ip && ip.includes('.'));
|
.filter(ip => ip && ip.includes('.'));
|
||||||
@@ -270,12 +301,12 @@ function renderAvailableIpsTable(allIps, usedIps) {
|
|||||||
destroy: true,
|
destroy: true,
|
||||||
data: availableIps,
|
data: availableIps,
|
||||||
columns: [
|
columns: [
|
||||||
{
|
{
|
||||||
title: getString("Gen_Subnet"),
|
title: getString("Gen_Subnet"),
|
||||||
data: "subnet"
|
data: "subnet"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: getString("Systeminfo_AvailableIps"),
|
title: getString("Systeminfo_AvailableIps"),
|
||||||
data: "ip",
|
data: "ip",
|
||||||
render: function (data, type, row, meta) {
|
render: function (data, type, row, meta) {
|
||||||
return `
|
return `
|
||||||
@@ -292,6 +323,87 @@ function renderAvailableIpsTable(allIps, usedIps) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Helper: Convert CIDR to subnet mask
|
||||||
|
function cidrToMask(cidr) {
|
||||||
|
return ((0xFFFFFFFF << (32 - cidr)) >>> 0)
|
||||||
|
.toString(16)
|
||||||
|
.match(/.{1,2}/g)
|
||||||
|
.map(h => parseInt(h, 16))
|
||||||
|
.join('.');
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatDataSize(bytes) {
|
||||||
|
if (!bytes) bytes = 0; // ensure it's a number
|
||||||
|
|
||||||
|
const mb = bytes / 1024 / 1024; // convert bytes to MB
|
||||||
|
|
||||||
|
// Format number with 2 decimals and thousands separators
|
||||||
|
return mb.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 }) + " MB";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function loadInterfaces() {
|
||||||
|
const apiToken = getSetting("API_TOKEN"); // replace with dynamic token if available
|
||||||
|
|
||||||
|
const host = window.location.hostname;
|
||||||
|
const port = getSetting("GRAPHQL_PORT");
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
url: "http://" + host + ":" + port + "/nettools/interfaces",
|
||||||
|
type: "GET",
|
||||||
|
headers: {
|
||||||
|
"Authorization": "Bearer " + apiToken,
|
||||||
|
"Content-Type": "application/json"
|
||||||
|
},
|
||||||
|
success: function(data) {
|
||||||
|
const tbody = $("#networkTable tbody");
|
||||||
|
tbody.empty();
|
||||||
|
|
||||||
|
console.log(data);
|
||||||
|
|
||||||
|
|
||||||
|
if (!data.success || !data.interfaces || Object.keys(data.interfaces).length === 0) {
|
||||||
|
tbody.append('<tr><td colspan="4">No interfaces found</td></tr>');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$.each(data.interfaces, function(iface_name, iface) {
|
||||||
|
|
||||||
|
const rx_mb = formatDataSize(iface.rx_bytes);
|
||||||
|
const tx_mb = formatDataSize(iface.tx_bytes);
|
||||||
|
|
||||||
|
// const rx_mb = (iface.rx_bytes ?? 0) / 1024 / 1024;
|
||||||
|
// const tx_mb = (iface.tx_bytes ?? 0) / 1024 / 1024;
|
||||||
|
|
||||||
|
let cidr_display = "";
|
||||||
|
if (iface.ipv4 && iface.ipv4.length > 0) {
|
||||||
|
const ip_info = iface.ipv4[0];
|
||||||
|
const ip = ip_info.ip || "--";
|
||||||
|
const mask = cidrToMask(ip_info.cidr || 24);
|
||||||
|
cidr_display = mask + " / " + iface.ipv4;
|
||||||
|
}
|
||||||
|
|
||||||
|
tbody.append(`
|
||||||
|
<tr>
|
||||||
|
<td>${iface_name}</td>
|
||||||
|
<td>${cidr_display}</td>
|
||||||
|
<td>${rx_mb}</td>
|
||||||
|
<td>${tx_mb}</td>
|
||||||
|
</tr>
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
error: function(xhr) {
|
||||||
|
const tbody = $("#networkTable tbody");
|
||||||
|
tbody.empty();
|
||||||
|
tbody.append('<tr><td colspan="4">Failed to fetch interfaces</td></tr>');
|
||||||
|
console.error("Error fetching interfaces:", xhr.responseText);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// INIT
|
// INIT
|
||||||
$(document).ready(function() {
|
$(document).ready(function() {
|
||||||
|
|
||||||
@@ -301,6 +413,8 @@ $(document).ready(function() {
|
|||||||
renderAvailableIpsTable(allIps, usedIps);
|
renderAvailableIpsTable(allIps, usedIps);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
loadInterfaces();
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
// Available IPs datatable
|
// Available IPs datatable
|
||||||
$('#networkTable').DataTable({
|
$('#networkTable').DataTable({
|
||||||
@@ -309,7 +423,7 @@ $(document).ready(function() {
|
|||||||
initComplete: function(settings, json) {
|
initComplete: function(settings, json) {
|
||||||
hideSpinner(); // Called after the DataTable is fully initialized
|
hideSpinner(); // Called after the DataTable is fully initialized
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}, 200);
|
}, 200);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -58,7 +58,8 @@ from .nettools_endpoint import ( # noqa: E402 [flake8 lint suppression]
|
|||||||
speedtest,
|
speedtest,
|
||||||
nslookup,
|
nslookup,
|
||||||
nmap_scan,
|
nmap_scan,
|
||||||
internet_info
|
internet_info,
|
||||||
|
network_interfaces
|
||||||
)
|
)
|
||||||
from .dbquery_endpoint import read_query, write_query, update_query, delete_query # noqa: E402 [flake8 lint suppression]
|
from .dbquery_endpoint import read_query, write_query, update_query, delete_query # noqa: E402 [flake8 lint suppression]
|
||||||
from .sync_endpoint import handle_sync_post, handle_sync_get # noqa: E402 [flake8 lint suppression]
|
from .sync_endpoint import handle_sync_post, handle_sync_get # noqa: E402 [flake8 lint suppression]
|
||||||
@@ -535,6 +536,13 @@ def api_internet_info():
|
|||||||
return internet_info()
|
return internet_info()
|
||||||
|
|
||||||
|
|
||||||
|
@app.route("/nettools/interfaces", methods=["GET"])
|
||||||
|
def api_network_interfaces():
|
||||||
|
if not is_authorized():
|
||||||
|
return jsonify({"success": False, "message": "ERROR: Not authorized", "error": "Forbidden"}), 403
|
||||||
|
return network_interfaces()
|
||||||
|
|
||||||
|
|
||||||
@app.route('/mcp/sse/nettools/trigger-scan', methods=['POST'])
|
@app.route('/mcp/sse/nettools/trigger-scan', methods=['POST'])
|
||||||
@app.route("/nettools/trigger-scan", methods=["GET"])
|
@app.route("/nettools/trigger-scan", methods=["GET"])
|
||||||
def api_trigger_scan():
|
def api_trigger_scan():
|
||||||
|
|||||||
@@ -277,3 +277,90 @@ def internet_info():
|
|||||||
"details": str(e),
|
"details": str(e),
|
||||||
}
|
}
|
||||||
), 500
|
), 500
|
||||||
|
|
||||||
|
|
||||||
|
def network_interfaces():
|
||||||
|
"""
|
||||||
|
API endpoint to fetch network interface info using `nmap --iflist`.
|
||||||
|
Returns JSON with interface info and RX/TX bytes.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
# Run Nmap
|
||||||
|
nmap_output = subprocess.run(
|
||||||
|
["nmap", "--iflist"],
|
||||||
|
capture_output=True,
|
||||||
|
text=True,
|
||||||
|
check=True,
|
||||||
|
).stdout.strip()
|
||||||
|
|
||||||
|
# Read /proc/net/dev for RX/TX
|
||||||
|
rx_tx = {}
|
||||||
|
with open("/proc/net/dev") as f:
|
||||||
|
for line in f.readlines()[2:]:
|
||||||
|
if ":" not in line:
|
||||||
|
continue
|
||||||
|
iface, data = line.split(":")
|
||||||
|
iface = iface.strip()
|
||||||
|
cols = data.split()
|
||||||
|
rx_bytes = int(cols[0])
|
||||||
|
tx_bytes = int(cols[8])
|
||||||
|
rx_tx[iface] = {"rx": rx_bytes, "tx": tx_bytes}
|
||||||
|
|
||||||
|
interfaces = {}
|
||||||
|
|
||||||
|
for line in nmap_output.splitlines():
|
||||||
|
line = line.strip()
|
||||||
|
if not line:
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Skip header line
|
||||||
|
if line.startswith("DEV") or line.startswith("----"):
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Regex to parse: DEV (SHORT) IP/MASK TYPE UP MTU MAC
|
||||||
|
match = re.match(
|
||||||
|
r"^(\S+)\s+\(([^)]*)\)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s*(\S*)",
|
||||||
|
line
|
||||||
|
)
|
||||||
|
if not match:
|
||||||
|
continue
|
||||||
|
|
||||||
|
dev, short, ipmask, type_, state, mtu_str, mac = match.groups()
|
||||||
|
|
||||||
|
# Only parse MTU if it's a number
|
||||||
|
try:
|
||||||
|
mtu = int(mtu_str)
|
||||||
|
except ValueError:
|
||||||
|
mtu = None
|
||||||
|
|
||||||
|
if dev not in interfaces:
|
||||||
|
interfaces[dev] = {
|
||||||
|
"name": dev,
|
||||||
|
"short": short,
|
||||||
|
"type": type_,
|
||||||
|
"state": state.lower(),
|
||||||
|
"mtu": mtu,
|
||||||
|
"mac": mac if mac else None,
|
||||||
|
"ipv4": [],
|
||||||
|
"ipv6": [],
|
||||||
|
"rx_bytes": rx_tx.get(dev, {}).get("rx", 0),
|
||||||
|
"tx_bytes": rx_tx.get(dev, {}).get("tx", 0),
|
||||||
|
}
|
||||||
|
|
||||||
|
# Parse IP/MASK
|
||||||
|
if ipmask != "(none)/0":
|
||||||
|
if ":" in ipmask:
|
||||||
|
interfaces[dev]["ipv6"].append(ipmask)
|
||||||
|
else:
|
||||||
|
interfaces[dev]["ipv4"].append(ipmask)
|
||||||
|
|
||||||
|
return jsonify({"success": True, "interfaces": interfaces}), 200
|
||||||
|
|
||||||
|
except (subprocess.CalledProcessError, ValueError, FileNotFoundError) as e:
|
||||||
|
return jsonify(
|
||||||
|
{
|
||||||
|
"success": False,
|
||||||
|
"error": "Failed to fetch network interface info",
|
||||||
|
"details": str(e),
|
||||||
|
}
|
||||||
|
), 500
|
||||||
|
|||||||
@@ -208,3 +208,32 @@ def test_internet_info_endpoint(client, api_token):
|
|||||||
assert data.get("success") is False
|
assert data.get("success") is False
|
||||||
assert "error" in data
|
assert "error" in data
|
||||||
assert "details" in data
|
assert "details" in data
|
||||||
|
|
||||||
|
|
||||||
|
def test_interfaces_endpoint(client, api_token):
|
||||||
|
# Call the /nettools/interfaces endpoint
|
||||||
|
resp = client.get("/nettools/interfaces", headers=auth_headers(api_token))
|
||||||
|
data = resp.json
|
||||||
|
|
||||||
|
# Assertions
|
||||||
|
if resp.status_code == 200:
|
||||||
|
assert data.get("success") is True
|
||||||
|
assert "interfaces" in data
|
||||||
|
interfaces = data["interfaces"]
|
||||||
|
assert isinstance(interfaces, dict)
|
||||||
|
for if_name, iface in interfaces.items():
|
||||||
|
assert "name" in iface
|
||||||
|
assert "short" in iface
|
||||||
|
assert "type" in iface
|
||||||
|
assert "state" in iface
|
||||||
|
assert "mtu" in iface
|
||||||
|
assert "mac" in iface
|
||||||
|
assert "ipv4" in iface and isinstance(iface["ipv4"], list)
|
||||||
|
assert "ipv6" in iface and isinstance(iface["ipv6"], list)
|
||||||
|
assert "rx_bytes" in iface
|
||||||
|
assert "tx_bytes" in iface
|
||||||
|
else:
|
||||||
|
# Handle failure
|
||||||
|
assert data.get("success") is False
|
||||||
|
assert "error" in data
|
||||||
|
assert "details" in data
|
||||||
|
|||||||
Reference in New Issue
Block a user