mirror of
https://github.com/jokob-sk/NetAlertX.git
synced 2026-04-15 22:51:37 -07:00
FE+BE: deviceDetials migration to graphQL endpoints
Signed-off-by: jokob-sk <jokob.sk@gmail.com>
This commit is contained in:
+18
-2
@@ -416,9 +416,25 @@ async function renderSmallBoxes() {
|
||||
showSpinner();
|
||||
|
||||
// Get data from the server
|
||||
const response = await fetch(`php/server/devices.php?action=getServerDeviceData&mac=${getMac()}&period=${period}`);
|
||||
const protocol = window.location.protocol.replace(':', '');
|
||||
const host = window.location.hostname;
|
||||
const apiToken = getSetting("API_TOKEN");
|
||||
const port = getSetting("GRAPHQL_PORT"); // same port your Flask app runs on
|
||||
|
||||
const apiBase = `${protocol}://${host}:${port}`;
|
||||
const url = `${apiBase}/device/${getMac()}?period=${encodeURIComponent(period)}`;
|
||||
|
||||
const response = await fetch(url, {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Authorization": `Bearer ${apiToken}`,
|
||||
"Content-Type": "application/json"
|
||||
}
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`Error fetching device data: ${response.statusText}`);
|
||||
const text = await response.text();
|
||||
throw new Error(`Error fetching device data: ${response.status} ${text}`);
|
||||
}
|
||||
|
||||
const deviceData = await response.json();
|
||||
|
||||
+95
-60
@@ -1,8 +1,7 @@
|
||||
<?php
|
||||
//------------------------------------------------------------------------------
|
||||
// check if authenticated
|
||||
require_once $_SERVER['DOCUMENT_ROOT'] . '/php/templates/security.php';
|
||||
?>
|
||||
//------------------------------------------------------------------------------
|
||||
// check if authenticated
|
||||
require_once $_SERVER["DOCUMENT_ROOT"] . "/php/templates/security.php"; ?>
|
||||
|
||||
|
||||
<div class="row" id="deviceDetailsEdit">
|
||||
@@ -20,7 +19,7 @@
|
||||
id="btnDelete"
|
||||
onclick="askDeleteDevice()">
|
||||
<i class="fas fa-trash-alt"></i>
|
||||
<?= lang('DevDetail_button_Delete');?>
|
||||
<?= lang("DevDetail_button_Delete") ?>
|
||||
</button>
|
||||
<button type="button"
|
||||
class="btn btn-primary pa-btn"
|
||||
@@ -28,7 +27,7 @@
|
||||
id="btnSave"
|
||||
onclick="setDeviceData()" >
|
||||
<i class="fas fa-save"></i>
|
||||
<?= lang('DevDetail_button_Save');?>
|
||||
<?= lang("DevDetail_button_Save") ?>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -37,36 +36,78 @@
|
||||
|
||||
<script defer>
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
// Get plugin and settings data from API endpoints
|
||||
function getDeviceData(){
|
||||
// -------------------------------------------------------------------
|
||||
// Get plugin and settings data from API endpoints
|
||||
function getDeviceData() {
|
||||
|
||||
mac = getMac()
|
||||
|
||||
console.log(mac);
|
||||
|
||||
const protocol = window.location.protocol.replace(':', '');
|
||||
const host = window.location.hostname;
|
||||
const apiToken = getSetting("API_TOKEN");
|
||||
const port = getSetting("GRAPHQL_PORT");
|
||||
|
||||
const apiBase = `${protocol}://${host}:${port}`;
|
||||
const url = `${apiBase}/device/${mac}?period=${encodeURIComponent(period)}`;
|
||||
|
||||
// get data from server
|
||||
$.get('php/server/devices.php?action=getServerDeviceData&mac='+ mac + '&period='+ period, function(data) {
|
||||
|
||||
// show loading dialog
|
||||
showSpinner()
|
||||
|
||||
var deviceData = JSON.parse(data);
|
||||
$.ajax({
|
||||
url: url,
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Authorization": `Bearer ${apiToken}`
|
||||
},
|
||||
dataType: "json",
|
||||
success: function(deviceData) {
|
||||
|
||||
// some race condition, need to implement delay
|
||||
setTimeout(() => {
|
||||
$.get('php/server/query_json.php', {
|
||||
file: 'table_settings.json',
|
||||
// nocache: Date.now()
|
||||
},
|
||||
function(res) {
|
||||
|
||||
settingsData = res["data"];
|
||||
const query = `
|
||||
query($filters: [FilterOptionsInput]) {
|
||||
settings(filters: $filters) {
|
||||
settings {
|
||||
setKey
|
||||
setName
|
||||
setDescription
|
||||
setType
|
||||
setOptions
|
||||
setGroup
|
||||
setValue
|
||||
setEvents
|
||||
setOverriddenByEnv
|
||||
}
|
||||
count
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
// I need to get a subset of settings only (performance improvement), but include both NEWDEV and CUSTPROP settings
|
||||
const variables = {
|
||||
filters: [
|
||||
{ filterColumn: "setGroup", filterValue: "NEWDEV" },
|
||||
{ filterColumn: "setGroup", filterValue: "CUSTPROP" }
|
||||
]
|
||||
};
|
||||
|
||||
const graphQlUrl = `${apiBase}/graphql`;
|
||||
|
||||
$.ajax({
|
||||
url: graphQlUrl,
|
||||
method: "POST",
|
||||
contentType: "application/json",
|
||||
headers: { "Authorization": `Bearer ${apiToken}` },
|
||||
data: JSON.stringify({ query, variables }),
|
||||
success: function(response) {
|
||||
|
||||
const settingsData = response.data.settings.settings;
|
||||
|
||||
// columns to hide
|
||||
hiddenFields = ["NEWDEV_devScan", "NEWDEV_devPresentLastScan" ]
|
||||
hiddenFields = ["NEWDEV_devScan", "NEWDEV_devPresentLastScan"]
|
||||
// columns to disable/readonly - conditional depending if a new dummy device is created
|
||||
disabledFields = mac == "new" ? ["NEWDEV_devLastNotification", "NEWDEV_devFirstConnection", "NEWDEV_devLastConnection"] : ["NEWDEV_devLastNotification", "NEWDEV_devFirstConnection", "NEWDEV_devLastConnection", "NEWDEV_devMac", "NEWDEV_devLastIP", "NEWDEV_devSyncHubNode", "NEWDEV_devFQDN" ];
|
||||
disabledFields = mac == "new" ? ["NEWDEV_devLastNotification", "NEWDEV_devFirstConnection", "NEWDEV_devLastConnection"] : ["NEWDEV_devLastNotification", "NEWDEV_devFirstConnection", "NEWDEV_devLastConnection", "NEWDEV_devMac", "NEWDEV_devLastIP", "NEWDEV_devSyncHubNode", "NEWDEV_devFQDN"];
|
||||
|
||||
// Grouping of fields into categories with associated documentation links
|
||||
const fieldGroups = {
|
||||
@@ -169,12 +210,14 @@
|
||||
groupSettings.forEach(setting => {
|
||||
const column = $('<div>'); // Create a column for each setting (Bootstrap column)
|
||||
|
||||
console.log(setting);
|
||||
|
||||
// Get the field data (replace 'NEWDEV_' prefix from the key)
|
||||
fieldData = deviceData[setting.setKey.replace('NEWDEV_', '')]
|
||||
fieldData = fieldData == null ? "" : fieldData;
|
||||
fieldOptionsOverride = null;
|
||||
|
||||
// console.log(setting.setKey);
|
||||
console.log(setting.setKey);
|
||||
// console.log(fieldData);
|
||||
|
||||
// Additional form elements like the random MAC address button for devMac
|
||||
@@ -209,9 +252,8 @@
|
||||
if (
|
||||
Array.isArray(fieldData) &&
|
||||
(setting.setKey == "NEWDEV_devChildrenDynamic" ||
|
||||
setting.setKey == "NEWDEV_devChildrenNicsDynamic" )
|
||||
)
|
||||
{
|
||||
setting.setKey == "NEWDEV_devChildrenNicsDynamic")
|
||||
) {
|
||||
fieldDataNew = []
|
||||
fieldData.forEach(child => {
|
||||
fieldDataNew.push(child.devMac)
|
||||
@@ -261,19 +303,16 @@
|
||||
|
||||
hideSpinner();
|
||||
|
||||
})
|
||||
|
||||
}, 100);
|
||||
});
|
||||
|
||||
}
|
||||
}}); // $.get callback
|
||||
}, 100); // setTimeout
|
||||
} // ajax success
|
||||
}); // $.ajax
|
||||
} // getDeviceData
|
||||
|
||||
|
||||
|
||||
|
||||
// ----------------------------------------
|
||||
// Handle the read-only fields
|
||||
function handleReadOnly(settingsData, disabledFields) {
|
||||
// ----------------------------------------
|
||||
// Handle the read-only fields
|
||||
function handleReadOnly(settingsData, disabledFields) {
|
||||
settingsData.forEach(setting => {
|
||||
const element = $(`#${setting.setKey}`);
|
||||
if (disabledFields.includes(setting.setKey)) {
|
||||
@@ -282,11 +321,11 @@
|
||||
element.prop('readonly', false);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Save device data to DB
|
||||
function setDeviceData(direction = '', refreshCallback = '') {
|
||||
// -----------------------------------------------------------------------------
|
||||
// Save device data to DB
|
||||
function setDeviceData(direction = '', refreshCallback = '') {
|
||||
// Check MAC
|
||||
mac = getMac()
|
||||
|
||||
@@ -303,7 +342,7 @@
|
||||
const newMac = $('#NEWDEV_devMac').val()
|
||||
|
||||
// Validate MAC and Last IP
|
||||
if (mac === '' || !isValidMac(newMac) || !( isValidIPv4(devLastIP) || isValidIPv6(devLastIP) )) {
|
||||
if (mac === '' || !isValidMac(newMac) || !(isValidIPv4(devLastIP) || isValidIPv6(devLastIP))) {
|
||||
showMessage(getString("DeviceEdit_ValidMacIp"), 5000, "modal_red");
|
||||
return;
|
||||
}
|
||||
@@ -368,7 +407,7 @@
|
||||
"Content-Type": "application/json"
|
||||
},
|
||||
data: JSON.stringify(payload),
|
||||
success: function (resp) {
|
||||
success: function(resp) {
|
||||
|
||||
if (resp && resp.success) {
|
||||
showMessage("Device saved successfully");
|
||||
@@ -390,7 +429,7 @@
|
||||
|
||||
hideSpinner();
|
||||
},
|
||||
error: function (xhr) {
|
||||
error: function(xhr) {
|
||||
if (xhr.status === 403) {
|
||||
showMessage("Unauthorized - invalid API token");
|
||||
} else {
|
||||
@@ -399,27 +438,27 @@
|
||||
hideSpinner();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------------
|
||||
// Disables or enables network configuration for the root node
|
||||
function toggleNetworkConfiguration(disable) {
|
||||
//-----------------------------------------------------------------------------------
|
||||
// Disables or enables network configuration for the root node
|
||||
function toggleNetworkConfiguration(disable) {
|
||||
if (disable) {
|
||||
// Completely disable the NEWDEV_devParentMAC <select> and NEWDEV_devParentPort
|
||||
$('#NEWDEV_devParentMAC').prop('disabled', true).val("").prop('selectedIndex', 0);
|
||||
$('#NEWDEV_devParentMAC').empty() // Remove all options
|
||||
.append('<option value="">Root Node</option>')
|
||||
$('#NEWDEV_devParentPort').prop('disabled', true);
|
||||
$('#NEWDEV_devParentPort').prop('readonly', true );
|
||||
$('#NEWDEV_devParentMAC').prop('readonly', true );
|
||||
$('#NEWDEV_devParentPort').prop('readonly', true);
|
||||
$('#NEWDEV_devParentMAC').prop('readonly', true);
|
||||
} else {
|
||||
// Enable the NEWDEV_devParentMAC <select> and NEWDEV_devParentPort
|
||||
$('#NEWDEV_devParentMAC').prop('disabled', false);
|
||||
$('#NEWDEV_devParentPort').prop('disabled', false);
|
||||
$('#NEWDEV_devParentPort').prop('readonly', false );
|
||||
$('#NEWDEV_devParentMAC').prop('readonly', false );
|
||||
}
|
||||
$('#NEWDEV_devParentPort').prop('readonly', false);
|
||||
$('#NEWDEV_devParentMAC').prop('readonly', false);
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------
|
||||
// INIT with polling for panel element visibility
|
||||
@@ -427,8 +466,7 @@
|
||||
|
||||
var deviceDetailsPageInitialized = false;
|
||||
|
||||
function initdeviceDetailsPage()
|
||||
{
|
||||
function initdeviceDetailsPage() {
|
||||
// Only proceed if .plugin-content is visible
|
||||
if (!$('#panDetails:visible').length) {
|
||||
return; // exit early if nothing is visible
|
||||
@@ -456,12 +494,9 @@ function deviceDetailsPageUpdater() {
|
||||
// if visible, load immediately, if not start updater
|
||||
if (!$('#panDetails:visible').length) {
|
||||
deviceDetailsPageUpdater();
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
getDeviceData();
|
||||
}
|
||||
|
||||
|
||||
|
||||
</script>
|
||||
@@ -31,7 +31,6 @@
|
||||
$action = $_REQUEST['action'];
|
||||
switch ($action) {
|
||||
// check server/api_server/api_server_start.py for equivalents
|
||||
case 'getServerDeviceData': getServerDeviceData(); break; // equivalent: get_device_data
|
||||
case 'deleteDevice': deleteDevice(); break; // equivalent: delete_device(mac)
|
||||
case 'deleteAllWithEmptyMACs': deleteAllWithEmptyMACs(); break; // equivalent: delete_all_with_empty_macs
|
||||
|
||||
@@ -55,150 +54,6 @@
|
||||
}
|
||||
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Query Device Data
|
||||
//------------------------------------------------------------------------------
|
||||
function getServerDeviceData() {
|
||||
global $db;
|
||||
|
||||
// Request Parameters
|
||||
$periodDate = getDateFromPeriod();
|
||||
$mac = $_REQUEST['mac'];
|
||||
|
||||
// Check for "new" MAC case
|
||||
if ($mac === "new") {
|
||||
$now = date('Y-m-d H:i');
|
||||
$deviceData = [
|
||||
"devMac" => "",
|
||||
"devName" => "",
|
||||
"devOwner" => "",
|
||||
"devType" => "",
|
||||
"devVendor" => "",
|
||||
"devFavorite" => 0,
|
||||
"devGroup" => "",
|
||||
"devComments" => "",
|
||||
"devFirstConnection" => $now,
|
||||
"devLastConnection" => $now,
|
||||
"devLastIP" => "",
|
||||
"devStaticIP" => 0,
|
||||
"devScan" => 0,
|
||||
"devLogEvents" => 0,
|
||||
"devAlertEvents" => 0,
|
||||
"devAlertDown" => 0,
|
||||
"devParentRelType" => "default",
|
||||
"devReqNicsOnline" => 0,
|
||||
"devSkipRepeated" => 0,
|
||||
"devLastNotification" => "",
|
||||
"devPresentLastScan" => 0,
|
||||
"devIsNew" => 1,
|
||||
"devLocation" => "",
|
||||
"devIsArchived" => 0,
|
||||
"devParentMAC" => "",
|
||||
"devParentPort" => "",
|
||||
"devIcon" => "",
|
||||
"devGUID" => "",
|
||||
"devSite" => "",
|
||||
"devSSID" => "",
|
||||
"devSyncHubNode" => "",
|
||||
"devSourcePlugin" => "",
|
||||
"devCustomProps" => "",
|
||||
"devStatus" => "Unknown",
|
||||
"devIsRandomMAC" => false,
|
||||
"devSessions" => 0,
|
||||
"devEvents" => 0,
|
||||
"devDownAlerts" => 0,
|
||||
"devPresenceHours" => 0,
|
||||
"devFQDN" => ""
|
||||
];
|
||||
echo json_encode($deviceData);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// Get current date (used in presence calc)
|
||||
$currentdate = date("Y-m-d H:i:s");
|
||||
|
||||
// Fetch Device Info + Children + Events Stats
|
||||
$sql =<<<SQL
|
||||
SELECT
|
||||
d.rowid,
|
||||
d.*,
|
||||
CASE
|
||||
WHEN d.devAlertDown != 0 AND d.devPresentLastScan = 0 THEN "Down"
|
||||
WHEN d.devPresentLastScan = 1 THEN "On-line"
|
||||
ELSE "Off-line"
|
||||
END AS devStatus,
|
||||
|
||||
-- Event counters
|
||||
(SELECT COUNT(*) FROM Sessions
|
||||
WHERE ses_MAC = d.devMac AND (
|
||||
ses_DateTimeConnection >= $periodDate OR
|
||||
ses_DateTimeDisconnection >= $periodDate OR
|
||||
ses_StillConnected = 1
|
||||
)
|
||||
) AS devSessions,
|
||||
|
||||
(SELECT COUNT(*) FROM Events
|
||||
WHERE eve_MAC = d.devMac AND
|
||||
eve_DateTime >= $periodDate AND
|
||||
eve_EventType NOT IN ("Connected", "Disconnected")
|
||||
) AS devEvents,
|
||||
|
||||
(SELECT COUNT(*) FROM Events
|
||||
WHERE eve_MAC = d.devMac AND
|
||||
eve_DateTime >= $periodDate AND
|
||||
eve_EventType = "Device Down"
|
||||
) AS devDownAlerts,
|
||||
|
||||
(SELECT CAST(( MAX (0, SUM (julianday (IFNULL (ses_DateTimeDisconnection,'$currentdate'))
|
||||
- julianday (CASE WHEN ses_DateTimeConnection < $periodDate
|
||||
THEN $periodDate
|
||||
ELSE ses_DateTimeConnection END)) *24 )) AS INT)
|
||||
FROM Sessions
|
||||
WHERE ses_MAC = d.devMac AND
|
||||
ses_DateTimeConnection IS NOT NULL AND
|
||||
(ses_DateTimeDisconnection IS NOT NULL OR ses_StillConnected = 1) AND
|
||||
(
|
||||
ses_DateTimeConnection >= $periodDate OR
|
||||
ses_DateTimeDisconnection >= $periodDate OR
|
||||
ses_StillConnected = 1
|
||||
)
|
||||
) AS devPresenceHours
|
||||
|
||||
FROM Devices d
|
||||
WHERE d.devMac = "$mac" OR CAST(d.rowid AS TEXT) = "$mac"
|
||||
SQL;
|
||||
|
||||
$row = $db->query($sql)->fetchArray(SQLITE3_ASSOC);
|
||||
$deviceData = $row;
|
||||
$mac = $deviceData['devMac'];
|
||||
|
||||
$deviceData['devFirstConnection'] = formatDate($deviceData['devFirstConnection']);
|
||||
$deviceData['devLastConnection'] = formatDate($deviceData['devLastConnection']);
|
||||
$deviceData['devIsRandomMAC'] = isRandomMAC($mac);
|
||||
|
||||
// Fetch children once and split in PHP
|
||||
$sql = 'SELECT rowid, * FROM Devices WHERE devParentMAC = "' . $mac . '" ORDER BY devPresentLastScan DESC';
|
||||
$result = $db->query($sql);
|
||||
$children = [];
|
||||
$childrenNics = [];
|
||||
|
||||
while ($row = $result->fetchArray(SQLITE3_ASSOC)) {
|
||||
$children[] = $row;
|
||||
if ($row['devParentRelType'] === 'nic') {
|
||||
$childrenNics[] = $row;
|
||||
}
|
||||
}
|
||||
|
||||
$deviceData['devChildrenDynamic'] = $children;
|
||||
$deviceData['devChildrenNicsDynamic'] = $childrenNics;
|
||||
|
||||
// Return JSON
|
||||
echo json_encode($deviceData);
|
||||
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Delete Device
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
@@ -366,9 +366,9 @@ class Query(ObjectType):
|
||||
return DeviceResult(devices=devices, count=total_count)
|
||||
|
||||
# --- SETTINGS ---
|
||||
settings = Field(SettingResult)
|
||||
settings = Field(SettingResult, filters=List(FilterOptionsInput))
|
||||
|
||||
def resolve_settings(root, info):
|
||||
def resolve_settings(root, info, filters=None):
|
||||
try:
|
||||
with open(folder + "table_settings.json", "r") as f:
|
||||
settings_data = json.load(f)["data"]
|
||||
@@ -379,7 +379,18 @@ class Query(ObjectType):
|
||||
mylog("trace", f"[graphql_schema] settings_data: {settings_data}")
|
||||
|
||||
# Convert to Setting objects
|
||||
settings = [Setting(**setting) for setting in settings_data]
|
||||
settings = [Setting(**s) for s in settings_data]
|
||||
|
||||
# Apply dynamic filters (OR)
|
||||
if filters:
|
||||
filtered_settings = []
|
||||
for s in settings:
|
||||
for f in filters:
|
||||
if f.filterColumn and f.filterValue is not None:
|
||||
if str(getattr(s, f.filterColumn, "")).lower() == str(f.filterValue).lower():
|
||||
filtered_settings.append(s)
|
||||
break # match one filter is enough (OR)
|
||||
settings = filtered_settings
|
||||
|
||||
return SettingResult(settings=settings, count=len(settings))
|
||||
|
||||
|
||||
Reference in New Issue
Block a user