mirror of
https://github.com/jokob-sk/NetAlertX.git
synced 2026-04-15 22:51:37 -07:00
Merge branch 'main' into fix-events
This commit is contained in:
@@ -9,7 +9,6 @@ Check the the HTTP response of the failing backend call by following these steps
|
|||||||
|
|
||||||
- Copy the URL causing the error and enter it in the address bar of your browser directly and hit enter. The copied URLs could look something like this (notice the query strings at the end):
|
- Copy the URL causing the error and enter it in the address bar of your browser directly and hit enter. The copied URLs could look something like this (notice the query strings at the end):
|
||||||
- `http://<server>:20211/api/table_devices.json?nocache=1704141103121`
|
- `http://<server>:20211/api/table_devices.json?nocache=1704141103121`
|
||||||
- `http://<server>:20211/php/server/devices.php?action=getDevicesTotals`
|
|
||||||
|
|
||||||
|
|
||||||
- Post the error response in the existing issue thread on GitHub or create a new issue and include the redacted response of the failing query.
|
- Post the error response in the existing issue thread on GitHub or create a new issue and include the redacted response of the failing query.
|
||||||
|
|||||||
@@ -1,5 +1,13 @@
|
|||||||
# Reverse Proxy Configuration
|
# Reverse Proxy Configuration
|
||||||
|
|
||||||
|
> [!TIP]
|
||||||
|
> You will need to specify the `BACKEND_API_URL` setting if you are running reverse proxies. This is the URL that points to the backend server url (including your `GRAPHQL_PORT`)
|
||||||
|
>
|
||||||
|
> 
|
||||||
|
> 
|
||||||
|
|
||||||
|
## NGINX HTTP Configuration (Direct Path)
|
||||||
|
|
||||||
> Submitted by amazing [cvc90](https://github.com/cvc90) 🙏
|
> Submitted by amazing [cvc90](https://github.com/cvc90) 🙏
|
||||||
|
|
||||||
> [!NOTE]
|
> [!NOTE]
|
||||||
@@ -10,8 +18,6 @@
|
|||||||
|
|
||||||
<br/>
|
<br/>
|
||||||
|
|
||||||
## NGINX HTTP Configuration (Direct Path)
|
|
||||||
|
|
||||||
1. On your NGINX server, create a new file called /etc/nginx/sites-available/netalertx
|
1. On your NGINX server, create a new file called /etc/nginx/sites-available/netalertx
|
||||||
|
|
||||||
2. In this file, paste the following code:
|
2. In this file, paste the following code:
|
||||||
|
|||||||
Binary file not shown.
|
After Width: | Height: | Size: 18 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 42 KiB |
@@ -418,8 +418,8 @@ async function renderSmallBoxes() {
|
|||||||
// Get data from the server
|
// Get data from the server
|
||||||
const apiToken = getSetting("API_TOKEN");
|
const apiToken = getSetting("API_TOKEN");
|
||||||
|
|
||||||
const apiBase = getApiBase();
|
const apiBaseUrl = getApiBase();
|
||||||
const url = `${apiBase}/device/${getMac()}?period=${encodeURIComponent(period)}`;
|
const url = `${apiBaseUrl}/device/${getMac()}?period=${encodeURIComponent(period)}`;
|
||||||
|
|
||||||
const response = await fetch(url, {
|
const response = await fetch(url, {
|
||||||
method: "GET",
|
method: "GET",
|
||||||
|
|||||||
@@ -46,8 +46,8 @@ function getDeviceData() {
|
|||||||
const apiToken = getSetting("API_TOKEN");
|
const apiToken = getSetting("API_TOKEN");
|
||||||
let period = $("#period").val()
|
let period = $("#period").val()
|
||||||
|
|
||||||
const apiBase = getApiBase();
|
const apiBaseUrl = getApiBase();
|
||||||
const url = `${apiBase}/device/${mac}?period=${encodeURIComponent(period)}`;
|
const url = `${apiBaseUrl}/device/${mac}?period=${encodeURIComponent(period)}`;
|
||||||
|
|
||||||
// get data from server
|
// get data from server
|
||||||
$.ajax({
|
$.ajax({
|
||||||
@@ -89,7 +89,7 @@ function getDeviceData() {
|
|||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
|
||||||
const graphQlUrl = `${apiBase}/graphql`;
|
const graphQlUrl = `${apiBaseUrl}/graphql`;
|
||||||
|
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: graphQlUrl,
|
url: graphQlUrl,
|
||||||
@@ -354,7 +354,7 @@ function setDeviceData(direction = '', refreshCallback = '') {
|
|||||||
showSpinner();
|
showSpinner();
|
||||||
|
|
||||||
const apiToken = getSetting("API_TOKEN"); // dynamic token
|
const apiToken = getSetting("API_TOKEN"); // dynamic token
|
||||||
const apiBase = getApiBase();
|
const apiBaseUrl = getApiBase();
|
||||||
|
|
||||||
mac = $('#NEWDEV_devMac').val();
|
mac = $('#NEWDEV_devMac').val();
|
||||||
|
|
||||||
@@ -402,7 +402,7 @@ function setDeviceData(direction = '', refreshCallback = '') {
|
|||||||
|
|
||||||
|
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: `${apiBase}/device/${encodeURIComponent(mac)}`,
|
url: `${apiBaseUrl}/device/${encodeURIComponent(mac)}`,
|
||||||
type: "POST",
|
type: "POST",
|
||||||
headers: {
|
headers: {
|
||||||
"Authorization": "Bearer " + apiToken,
|
"Authorization": "Bearer " + apiToken,
|
||||||
|
|||||||
@@ -50,8 +50,8 @@ function loadEventsData() {
|
|||||||
|
|
||||||
const apiToken = getSetting("API_TOKEN");
|
const apiToken = getSetting("API_TOKEN");
|
||||||
|
|
||||||
const apiBase = getApiBase();
|
const apiBaseUrl = getApiBase();
|
||||||
const url = `${apiBase}/dbquery/read`;
|
const url = `${apiBaseUrl}/dbquery/read`;
|
||||||
|
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: url,
|
url: url,
|
||||||
|
|||||||
@@ -38,8 +38,8 @@
|
|||||||
function loadPresenceData() {
|
function loadPresenceData() {
|
||||||
const apiToken = getSetting("API_TOKEN");
|
const apiToken = getSetting("API_TOKEN");
|
||||||
|
|
||||||
const apiBase = getApiBase();
|
const apiBaseUrl = getApiBase();
|
||||||
const url = `${apiBase}/sessions/calendar`;
|
const url = `${apiBaseUrl}/sessions/calendar`;
|
||||||
|
|
||||||
$('#calendar').fullCalendar('removeEventSources');
|
$('#calendar').fullCalendar('removeEventSources');
|
||||||
|
|
||||||
|
|||||||
@@ -105,8 +105,8 @@ function loadSessionsData() {
|
|||||||
// Build API base
|
// Build API base
|
||||||
const apiToken = getSetting("API_TOKEN");
|
const apiToken = getSetting("API_TOKEN");
|
||||||
|
|
||||||
const apiBase = getApiBase();
|
const apiBaseUrl = getApiBase();
|
||||||
const url = `${apiBase}/sessions/${getMac()}?period=${encodeURIComponent(period)}`;
|
const url = `${apiBaseUrl}/sessions/${getMac()}?period=${encodeURIComponent(period)}`;
|
||||||
|
|
||||||
// Call API with Authorization header
|
// Call API with Authorization header
|
||||||
$.ajax({
|
$.ajax({
|
||||||
|
|||||||
@@ -210,20 +210,17 @@
|
|||||||
<div id="scanoutput" style="margin-top: 30px;"></div>
|
<div id="scanoutput" style="margin-top: 30px;"></div>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
// Build base URL dynamically
|
|
||||||
const apiBase = getApiBase();
|
const apiToken = getSetting("API_TOKEN");
|
||||||
const apiToken = getSetting("API_TOKEN");
|
const apiBaseUrl = getApiBase();
|
||||||
|
|
||||||
// ----------------------------------------------------------------
|
// ----------------------------------------------------------------
|
||||||
function manualnmapscan(targetip, mode) {
|
function manualnmapscan(targetip, mode) {
|
||||||
$("#scanoutput").empty();
|
$("#scanoutput").empty();
|
||||||
|
|
||||||
// Build base URL dynamically
|
|
||||||
const apiBase = getApiBase();
|
|
||||||
|
|
||||||
$.ajax({
|
$.ajax({
|
||||||
method: "POST",
|
method: "POST",
|
||||||
url: `${apiBase}/nettools/nmap`,
|
url: `${apiBaseUrl}/nettools/nmap`,
|
||||||
contentType: "application/json",
|
contentType: "application/json",
|
||||||
dataType: "json",
|
dataType: "json",
|
||||||
data: JSON.stringify({
|
data: JSON.stringify({
|
||||||
@@ -262,7 +259,7 @@
|
|||||||
|
|
||||||
$.ajax({
|
$.ajax({
|
||||||
method: "GET",
|
method: "GET",
|
||||||
url: `${apiBase}/nettools/speedtest`,
|
url: `${apiBaseUrl}/nettools/speedtest`,
|
||||||
headers: {
|
headers: {
|
||||||
"Authorization": "Bearer " + apiToken,
|
"Authorization": "Bearer " + apiToken,
|
||||||
"Content-Type": "application/json"
|
"Content-Type": "application/json"
|
||||||
@@ -313,7 +310,7 @@
|
|||||||
|
|
||||||
$.ajax({
|
$.ajax({
|
||||||
method: "POST",
|
method: "POST",
|
||||||
url: `${apiBase}/nettools/traceroute`,
|
url: `${apiBaseUrl}/nettools/traceroute`,
|
||||||
headers: {
|
headers: {
|
||||||
"Authorization": "Bearer " + apiToken,
|
"Authorization": "Bearer " + apiToken,
|
||||||
"Content-Type": "application/json"
|
"Content-Type": "application/json"
|
||||||
@@ -359,11 +356,9 @@
|
|||||||
|
|
||||||
$("#nslookupoutput").empty();
|
$("#nslookupoutput").empty();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
$.ajax({
|
$.ajax({
|
||||||
method: "POST",
|
method: "POST",
|
||||||
url: `${apiBase}/nettools/nslookup`,
|
url: `${apiBaseUrl}/nettools/nslookup`,
|
||||||
headers: {
|
headers: {
|
||||||
"Authorization": "Bearer " + apiToken,
|
"Authorization": "Bearer " + apiToken,
|
||||||
"Content-Type": "application/json"
|
"Content-Type": "application/json"
|
||||||
@@ -447,7 +442,7 @@
|
|||||||
|
|
||||||
$.ajax({
|
$.ajax({
|
||||||
method: "POST",
|
method: "POST",
|
||||||
url: `${apiBase}/nettools/wakeonlan`,
|
url: `${apiBaseUrl}/nettools/wakeonlan`,
|
||||||
headers: {
|
headers: {
|
||||||
"Authorization": "Bearer " + apiToken,
|
"Authorization": "Bearer " + apiToken,
|
||||||
"Content-Type": "application/json"
|
"Content-Type": "application/json"
|
||||||
@@ -489,7 +484,7 @@
|
|||||||
|
|
||||||
$.ajax({
|
$.ajax({
|
||||||
method: "POST",
|
method: "POST",
|
||||||
url: `${apiBase}/device/copy`,
|
url: `${apiBaseUrl}/device/copy`,
|
||||||
headers: {
|
headers: {
|
||||||
"Authorization": "Bearer " + apiToken,
|
"Authorization": "Bearer " + apiToken,
|
||||||
"Content-Type": "application/json"
|
"Content-Type": "application/json"
|
||||||
@@ -582,14 +577,14 @@
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build base URL dynamically
|
|
||||||
const apiBase = getApiBase();
|
|
||||||
const apiToken = getSetting("API_TOKEN"); // optional token if needed
|
const apiToken = getSetting("API_TOKEN"); // optional token if needed
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Delete device events
|
// Delete device events
|
||||||
$.ajax({
|
$.ajax({
|
||||||
method: "DELETE",
|
method: "DELETE",
|
||||||
url: `${apiBase}/device/${encodeURIComponent(mac)}/events/delete`,
|
url: `${apiBaseUrl}/device/${encodeURIComponent(mac)}/events/delete`,
|
||||||
headers: {
|
headers: {
|
||||||
"Authorization": "Bearer " + apiToken
|
"Authorization": "Bearer " + apiToken
|
||||||
},
|
},
|
||||||
@@ -634,12 +629,9 @@
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build base URL dynamically
|
|
||||||
const apiBase = getApiBase();
|
|
||||||
|
|
||||||
$.ajax({
|
$.ajax({
|
||||||
method: "POST",
|
method: "POST",
|
||||||
url: `${apiBase}/device/${encodeURIComponent(mac)}/reset-props`,
|
url: `${apiBaseUrl}/device/${encodeURIComponent(mac)}/reset-props`,
|
||||||
dataType: "json",
|
dataType: "json",
|
||||||
headers: {
|
headers: {
|
||||||
"Authorization": "Bearer " + apiToken
|
"Authorization": "Bearer " + apiToken
|
||||||
@@ -669,7 +661,7 @@
|
|||||||
|
|
||||||
$.ajax({
|
$.ajax({
|
||||||
method: "GET",
|
method: "GET",
|
||||||
url: `${apiBase}/nettools/internetinfo`,
|
url: `${apiBaseUrl}/nettools/internetinfo`,
|
||||||
headers: {
|
headers: {
|
||||||
"Authorization": "Bearer " + apiToken,
|
"Authorization": "Bearer " + apiToken,
|
||||||
"Content-Type": "application/json"
|
"Content-Type": "application/json"
|
||||||
|
|||||||
@@ -44,7 +44,6 @@
|
|||||||
case 'ExportCSV': ExportCSV(); break; // equivalent: export_devices
|
case 'ExportCSV': ExportCSV(); break; // equivalent: export_devices
|
||||||
case 'ImportCSV': ImportCSV(); break; // equivalent: import_csv
|
case 'ImportCSV': ImportCSV(); break; // equivalent: import_csv
|
||||||
|
|
||||||
case 'getDevicesTotals': getDevicesTotals(); break; // equivalent: devices_totals
|
|
||||||
case 'getDevicesListCalendar': getDevicesListCalendar(); break; // equivalent: devices_by_status
|
case 'getDevicesListCalendar': getDevicesListCalendar(); break; // equivalent: devices_by_status
|
||||||
|
|
||||||
case 'updateNetworkLeaf': updateNetworkLeaf(); break; // equivalent: update_device_column(mac, column_name, column_value)
|
case 'updateNetworkLeaf': updateNetworkLeaf(); break; // equivalent: update_device_column(mac, column_name, column_value)
|
||||||
@@ -338,41 +337,6 @@ function ImportCSV() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
// Query total numbers of Devices by status
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
function getDevicesTotals() {
|
|
||||||
|
|
||||||
$resultJSON = "";
|
|
||||||
|
|
||||||
if(getCache("getDevicesTotals") != "")
|
|
||||||
{
|
|
||||||
$resultJSON = getCache("getDevicesTotals");
|
|
||||||
} else
|
|
||||||
{
|
|
||||||
global $db;
|
|
||||||
|
|
||||||
// combined query
|
|
||||||
$result = $db->query(
|
|
||||||
'SELECT
|
|
||||||
(SELECT COUNT(*) FROM Devices '. getDeviceCondition ('my').') as devices,
|
|
||||||
(SELECT COUNT(*) FROM Devices '. getDeviceCondition ('connected').') as connected,
|
|
||||||
(SELECT COUNT(*) FROM Devices '. getDeviceCondition ('favorites').') as favorites,
|
|
||||||
(SELECT COUNT(*) FROM Devices '. getDeviceCondition ('new').') as new,
|
|
||||||
(SELECT COUNT(*) FROM Devices '. getDeviceCondition ('down').') as down,
|
|
||||||
(SELECT COUNT(*) FROM Devices '. getDeviceCondition ('archived').') as archived
|
|
||||||
');
|
|
||||||
|
|
||||||
$row = $result -> fetchArray (SQLITE3_NUM);
|
|
||||||
$resultJSON = json_encode (array ($row[0], $row[1], $row[2], $row[3], $row[4], $row[5]));
|
|
||||||
|
|
||||||
// save to cache
|
|
||||||
setCache("getDevicesTotals", $resultJSON );
|
|
||||||
}
|
|
||||||
|
|
||||||
echo ($resultJSON);
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
// Determine if Random MAC
|
// Determine if Random MAC
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|||||||
@@ -567,7 +567,7 @@ def publish_notifications(db, mqtt_client):
|
|||||||
try:
|
try:
|
||||||
payload = json.loads(payload_str) # Deserialize JSON string
|
payload = json.loads(payload_str) # Deserialize JSON string
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
mylog('minimal', [f"[{pluginName}] ⚠ ERROR decoding JSON for notification GUID {notification["GUID"]}: {e}"])
|
mylog('minimal', [f"[{pluginName}] ⚠ ERROR decoding JSON for notification GUID {notification['GUID']}: {e}"])
|
||||||
continue # skip this notification
|
continue # skip this notification
|
||||||
else:
|
else:
|
||||||
# fallback generic payload (like webhook does)
|
# fallback generic payload (like webhook does)
|
||||||
|
|||||||
+50
-15
@@ -352,27 +352,62 @@ function initializeCalendar () {
|
|||||||
|
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
|
/**
|
||||||
|
* Fetch device totals from the API and update dashboard counters.
|
||||||
|
*
|
||||||
|
* This function:
|
||||||
|
* - Stops the automatic refresh timer
|
||||||
|
* - Calls the `/devices/totals` API endpoint using Bearer token authentication
|
||||||
|
* - Updates the device summary boxes (all, connected, favorites, new, down, archived)
|
||||||
|
* - Restarts the refresh timer after completion
|
||||||
|
*
|
||||||
|
* Expected API response format:
|
||||||
|
* [
|
||||||
|
* devices, // Total devices
|
||||||
|
* connected, // Currently connected devices
|
||||||
|
* favorites, // Favorite devices
|
||||||
|
* new, // Newly discovered devices
|
||||||
|
* down, // Devices marked as down
|
||||||
|
* archived // Archived / hidden devices
|
||||||
|
* ]
|
||||||
|
*/
|
||||||
function getDevicesTotals () {
|
function getDevicesTotals () {
|
||||||
// stop timer
|
// stop timer
|
||||||
stopTimerRefreshData();
|
stopTimerRefreshData();
|
||||||
|
|
||||||
// get totals and put in boxes
|
const apiToken = getSetting("API_TOKEN");
|
||||||
$.get('php/server/devices.php?action=getDevicesTotals', function(data) {
|
const apiBaseUrl = getApiBase();
|
||||||
var totalsDevices = JSON.parse(data);
|
const totalsUrl = `${apiBaseUrl}/devices/totals`;
|
||||||
|
|
||||||
$('#devicesAll').html (totalsDevices[0].toLocaleString());
|
$.ajax({
|
||||||
$('#devicesConnected').html (totalsDevices[1].toLocaleString());
|
url: totalsUrl,
|
||||||
$('#devicesFavorites').html (totalsDevices[2].toLocaleString());
|
method: "GET",
|
||||||
$('#devicesNew').html (totalsDevices[3].toLocaleString());
|
headers: {
|
||||||
$('#devicesDown').html (totalsDevices[4].toLocaleString());
|
"Authorization": `Bearer ${apiToken}`
|
||||||
$('#devicesHidden').html (totalsDevices[5].toLocaleString());
|
},
|
||||||
|
success: function (totalsDevices) {
|
||||||
|
|
||||||
// Timer for refresh data
|
$('#devicesAll').html (totalsDevices[0].toLocaleString());
|
||||||
newTimerRefreshData (getDevicesTotals);
|
$('#devicesConnected').html (totalsDevices[1].toLocaleString());
|
||||||
} );
|
$('#devicesFavorites').html (totalsDevices[2].toLocaleString());
|
||||||
|
$('#devicesNew').html (totalsDevices[3].toLocaleString());
|
||||||
|
$('#devicesDown').html (totalsDevices[4].toLocaleString());
|
||||||
|
$('#devicesHidden').html (totalsDevices[5].toLocaleString());
|
||||||
|
|
||||||
|
// Timer for refresh data
|
||||||
|
newTimerRefreshData(getDevicesTotals);
|
||||||
|
},
|
||||||
|
error: function (xhr) {
|
||||||
|
console.error("Failed to load device totals:", xhr.responseText);
|
||||||
|
|
||||||
|
// Ensure refresh loop continues even on failure
|
||||||
|
newTimerRefreshData(getDevicesTotals);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
function getDevicesPresence (status) {
|
function getDevicesPresence (status) {
|
||||||
// Save status selected
|
// Save status selected
|
||||||
@@ -423,12 +458,12 @@ function getDevicesPresence (status) {
|
|||||||
|
|
||||||
const apiToken = getSetting("API_TOKEN");
|
const apiToken = getSetting("API_TOKEN");
|
||||||
|
|
||||||
const apiBase = getApiBase();
|
const apiBaseUrl = getApiBase();
|
||||||
|
|
||||||
// -----------------------------
|
// -----------------------------
|
||||||
// Load Devices as Resources
|
// Load Devices as Resources
|
||||||
// -----------------------------
|
// -----------------------------
|
||||||
const devicesUrl = `${apiBase}/devices/by-status?status=${deviceStatus}`;
|
const devicesUrl = `${apiBaseUrl}/devices/by-status?status=${deviceStatus}`;
|
||||||
|
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: devicesUrl,
|
url: devicesUrl,
|
||||||
@@ -451,7 +486,7 @@ function getDevicesPresence (status) {
|
|||||||
// -----------------------------
|
// -----------------------------
|
||||||
// Load Events
|
// Load Events
|
||||||
// -----------------------------
|
// -----------------------------
|
||||||
const eventsUrl = `${apiBase}/sessions/calendar?start=${startDate}&end=${endDate}`;
|
const eventsUrl = `${apiBaseUrl}/sessions/calendar?start=${startDate}&end=${endDate}`;
|
||||||
|
|
||||||
$('#calendar').fullCalendar('removeEventSources');
|
$('#calendar').fullCalendar('removeEventSources');
|
||||||
$('#calendar').fullCalendar('addEventSource', {
|
$('#calendar').fullCalendar('addEventSource', {
|
||||||
|
|||||||
Reference in New Issue
Block a user