FE+BE: init check work, removed legacy setDeviceData

Signed-off-by: jokob-sk <jokob.sk@gmail.com>
This commit is contained in:
jokob-sk
2025-12-18 09:02:57 +11:00
parent bc76c04f9e
commit daa720ab94
4 changed files with 325 additions and 271 deletions

View File

@@ -14,21 +14,21 @@
<!-- Buttons --> <!-- Buttons -->
<div class="col-xs-12"> <div class="col-xs-12">
<div class="pull-right"> <div class="pull-right">
<button type="button" <button type="button"
class="btn btn-default pa-btn pa-btn-delete" class="btn btn-default pa-btn pa-btn-delete"
style="margin-left:0px;" style="margin-left:0px;"
id="btnDelete" id="btnDelete"
onclick="askDeleteDevice()"> onclick="askDeleteDevice()">
<i class="fas fa-trash-alt"></i> <i class="fas fa-trash-alt"></i>
<?= lang('DevDetail_button_Delete');?> <?= lang('DevDetail_button_Delete');?>
</button> </button>
<button type="button" <button type="button"
class="btn btn-primary pa-btn" class="btn btn-primary pa-btn"
style="margin-left:6px; " style="margin-left:6px; "
id="btnSave" id="btnSave"
onclick="setDeviceData()" > onclick="setDeviceData()" >
<i class="fas fa-save"></i> <i class="fas fa-save"></i>
<?= lang('DevDetail_button_Save');?> <?= lang('DevDetail_button_Save');?>
</button> </button>
</div> </div>
</div> </div>
@@ -36,8 +36,8 @@
<script defer> <script defer>
// ------------------------------------------------------------------- // -------------------------------------------------------------------
// Get plugin and settings data from API endpoints // Get plugin and settings data from API endpoints
function getDeviceData(){ function getDeviceData(){
@@ -45,8 +45,8 @@
console.log(mac); console.log(mac);
// get data from server // get data from server
$.get('php/server/devices.php?action=getServerDeviceData&mac='+ mac + '&period='+ period, function(data) { $.get('php/server/devices.php?action=getServerDeviceData&mac='+ mac + '&period='+ period, function(data) {
// show loading dialog // show loading dialog
showSpinner() showSpinner()
@@ -55,30 +55,30 @@
// some race condition, need to implement delay // some race condition, need to implement delay
setTimeout(() => { setTimeout(() => {
$.get('php/server/query_json.php', { $.get('php/server/query_json.php', {
file: 'table_settings.json', file: 'table_settings.json',
// nocache: Date.now() // nocache: Date.now()
}, },
function(res) { function(res) {
settingsData = res["data"]; settingsData = res["data"];
// columns to hide // 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 // 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 // Grouping of fields into categories with associated documentation links
const fieldGroups = { const fieldGroups = {
// Group for device main information // Group for device main information
DevDetail_MainInfo_Title: { DevDetail_MainInfo_Title: {
data: ["devMac", "devLastIP", "devName", "devOwner", "devType", "devVendor", "devGroup", "devIcon", "devLocation", "devComments"], data: ["devMac", "devLastIP", "devName", "devOwner", "devType", "devVendor", "devGroup", "devIcon", "devLocation", "devComments"],
docs: "https://github.com/jokob-sk/NetAlertX/blob/main/docs/DEVICE_MANAGEMENT.md", docs: "https://github.com/jokob-sk/NetAlertX/blob/main/docs/DEVICE_MANAGEMENT.md",
iconClass: "fa fa-pencil", iconClass: "fa fa-pencil",
inputGroupClasses: "field-group main-group col-lg-4 col-sm-6 col-xs-12", inputGroupClasses: "field-group main-group col-lg-4 col-sm-6 col-xs-12",
labelClasses: "col-sm-4 col-xs-12 control-label", labelClasses: "col-sm-4 col-xs-12 control-label",
inputClasses: "col-sm-8 col-xs-12 input-group" inputClasses: "col-sm-8 col-xs-12 input-group"
}, },
// Group for event and alert settings // Group for event and alert settings
DevDetail_EveandAl_Title: { DevDetail_EveandAl_Title: {
data: ["devAlertEvents", "devAlertDown", "devSkipRepeated", "devReqNicsOnline", "devChildrenNicsDynamic"], data: ["devAlertEvents", "devAlertDown", "devSkipRepeated", "devReqNicsOnline", "devChildrenNicsDynamic"],
@@ -150,14 +150,14 @@
// Loop over each field group to generate sections for each category // Loop over each field group to generate sections for each category
Object.entries(fieldGroups).forEach(([groupName, obj]) => { Object.entries(fieldGroups).forEach(([groupName, obj]) => {
const groupDiv = $('<div>').addClass(obj.inputGroupClasses); // Create a div for each group with responsive Bootstrap classes const groupDiv = $('<div>').addClass(obj.inputGroupClasses); // Create a div for each group with responsive Bootstrap classes
// Add group title and documentation link // Add group title and documentation link
groupDiv.append(`<h5><i class="${obj.iconClass}"></i> ${getString(groupName)} groupDiv.append(`<h5><i class="${obj.iconClass}"></i> ${getString(groupName)}
<span class="helpIconSmallTopRight"> <span class="helpIconSmallTopRight">
<a target="_blank" href="${obj.docs}"> <a target="_blank" href="${obj.docs}">
<i class="fa fa-circle-question"></i> <i class="fa fa-circle-question"></i>
</a> </a>
</span> </span>
</h5> </h5>
<hr> <hr>
`); `);
@@ -174,8 +174,8 @@
fieldData = fieldData == null ? "" : fieldData; fieldData = fieldData == null ? "" : fieldData;
fieldOptionsOverride = null; fieldOptionsOverride = null;
// console.log(setting.setKey); // console.log(setting.setKey);
// console.log(fieldData); // console.log(fieldData);
// Additional form elements like the random MAC address button for devMac // Additional form elements like the random MAC address button for devMac
let inlineControl = ""; let inlineControl = "";
@@ -183,7 +183,7 @@
if (setting.setKey == "NEWDEV_devMac" && deviceData["devIsRandomMAC"] == true) { if (setting.setKey == "NEWDEV_devMac" && deviceData["devIsRandomMAC"] == true) {
inlineControl += `<span class="input-group-addon pointer" inlineControl += `<span class="input-group-addon pointer"
title="${getString("RandomMAC_hover")}"> title="${getString("RandomMAC_hover")}">
<a href="https://github.com/jokob-sk/NetAlertX/blob/main/docs/RANDOM_MAC.md" target="_blank"> <a href="https://github.com/jokob-sk/NetAlertX/blob/main/docs/RANDOM_MAC.md" target="_blank">
<i class="fa-solid fa-shuffle"></i> <i class="fa-solid fa-shuffle"></i>
</a> </a>
</span>`; </span>`;
@@ -204,15 +204,15 @@
<i class="fa-solid fa-dice" ></i> <i class="fa-solid fa-dice" ></i>
</span>`; </span>`;
} }
// handle devChildrenDynamic or NEWDEV_devChildrenNicsDynamic - selected values and options are the same // handle devChildrenDynamic or NEWDEV_devChildrenNicsDynamic - selected values and options are the same
if ( if (
Array.isArray(fieldData) && Array.isArray(fieldData) &&
(setting.setKey == "NEWDEV_devChildrenDynamic" || (setting.setKey == "NEWDEV_devChildrenDynamic" ||
setting.setKey == "NEWDEV_devChildrenNicsDynamic" ) setting.setKey == "NEWDEV_devChildrenNicsDynamic" )
) )
{ {
fieldDataNew = [] fieldDataNew = []
fieldData.forEach(child => { fieldData.forEach(child => {
fieldDataNew.push(child.devMac) fieldDataNew.push(child.devMac)
}) })
@@ -222,10 +222,10 @@
// Generate the input field HTML // Generate the input field HTML
const inputFormHtml = `<div class="form-group col-xs-12"> const inputFormHtml = `<div class="form-group col-xs-12">
<label id="${setting.setKey}_label" class="${obj.labelClasses}" > ${setting.setName} <label id="${setting.setKey}_label" class="${obj.labelClasses}" > ${setting.setName}
<i my-set-key="${setting.setKey}" <i my-set-key="${setting.setKey}"
title="${getString("Settings_Show_Description")}" title="${getString("Settings_Show_Description")}"
class="fa fa-circle-info pointer helpIconSmallTopRight" class="fa fa-circle-info pointer helpIconSmallTopRight"
onclick="showDescriptionPopup(this)"> onclick="showDescriptionPopup(this)">
</i> </i>
</label> </label>
@@ -247,29 +247,29 @@
updateAllIconPreviews(); updateAllIconPreviews();
// update readonly fields // update readonly fields
handleReadOnly(settingsData, disabledFields); handleReadOnly(settingsData, disabledFields);
}; };
// console.log(relevantSettings) // console.log(relevantSettings)
generateSimpleForm(relevantSettings); generateSimpleForm(relevantSettings);
toggleNetworkConfiguration(mac == 'Internet') toggleNetworkConfiguration(mac == 'Internet')
initSelect2(); initSelect2();
initHoverNodeInfo(); initHoverNodeInfo();
hideSpinner(); hideSpinner();
}) })
}, 100); }, 100);
}); });
} }
// ---------------------------------------- // ----------------------------------------
// Handle the read-only fields // Handle the read-only fields
@@ -306,62 +306,102 @@
showSpinner(); showSpinner();
// Update data to server using POST const apiToken = getSetting("API_TOKEN"); // dynamic token
$.post('php/server/devices.php?action=setDeviceData', { const host = window.location.hostname;
mac: $('#NEWDEV_devMac').val(), const port = getSetting("GRAPHQL_PORT");
name: encodeURIComponent($('#NEWDEV_devName').val().replace(/'/g, "")),
owner: encodeURIComponent($('#NEWDEV_devOwner').val().replace(/'/g, "")), mac = $('#NEWDEV_devMac').val();
type: $('#NEWDEV_devType').val().replace(/'/g, ""),
vendor: encodeURIComponent($('#NEWDEV_devVendor').val().replace(/'/g, "")), // Build payload for new endpoint
icon: encodeURIComponent($('#NEWDEV_devIcon').val()), const payload = {
favorite: ($('#NEWDEV_devFavorite')[0].checked * 1), devName: $('#NEWDEV_devName').val().replace(/'/g, ""),
group: encodeURIComponent($('#NEWDEV_devGroup').val().replace(/'/g, "")), devOwner: $('#NEWDEV_devOwner').val().replace(/'/g, ""),
location: encodeURIComponent($('#NEWDEV_devLocation').val().replace(/'/g, "")), devType: $('#NEWDEV_devType').val().replace(/'/g, ""),
comments: encodeURIComponent(encodeSpecialChars($('#NEWDEV_devComments').val())), devVendor: $('#NEWDEV_devVendor').val().replace(/'/g, ""),
networknode: $('#NEWDEV_devParentMAC').val(), devIcon: $('#NEWDEV_devIcon').val(),
networknodeport: $('#NEWDEV_devParentPort').val(),
ssid: $('#NEWDEV_devSSID').val(), devFavorite: ($('#NEWDEV_devFavorite')[0].checked * 1),
networksite: $('#NEWDEV_devSite').val(), devGroup: $('#NEWDEV_devGroup').val().replace(/'/g, ""),
staticIP: ($('#NEWDEV_devStaticIP')[0].checked * 1), devLocation: $('#NEWDEV_devLocation').val().replace(/'/g, ""),
scancycle: "1", devComments: encodeSpecialChars($('#NEWDEV_devComments').val()),
alertevents: ($('#NEWDEV_devAlertEvents')[0].checked * 1),
alertdown: ($('#NEWDEV_devAlertDown')[0].checked * 1), devParentMAC: $('#NEWDEV_devParentMAC').val(),
skiprepeated: $('#NEWDEV_devSkipRepeated').val().split(' ')[0], devParentPort: $('#NEWDEV_devParentPort').val(),
relType: $('#NEWDEV_devParentRelType').val().replace(/'/g, ""), devParentRelType: $('#NEWDEV_devParentRelType').val().replace(/'/g, ""),
reqNics: ($('#NEWDEV_devReqNicsOnline')[0].checked * 1),
newdevice: ($('#NEWDEV_devIsNew')[0].checked * 1), devSSID: $('#NEWDEV_devSSID').val(),
archived: ($('#NEWDEV_devIsArchived')[0].checked * 1), devSite: $('#NEWDEV_devSite').val(),
devFirstConnection: ($('#NEWDEV_devFirstConnection').val()),
devLastConnection: ($('#NEWDEV_devLastConnection').val()), devStaticIP: ($('#NEWDEV_devStaticIP')[0].checked * 1),
devCustomProps: btoa(JSON.stringify(collectTableData("#NEWDEV_devCustomProps_table"))), devScan: 1,
ip: ($('#NEWDEV_devLastIP').val()),
devAlertEvents: ($('#NEWDEV_devAlertEvents')[0].checked * 1),
devAlertDown: ($('#NEWDEV_devAlertDown')[0].checked * 1),
devSkipRepeated: $('#NEWDEV_devSkipRepeated').val().split(' ')[0],
devReqNicsOnline: ($('#NEWDEV_devReqNicsOnline')[0].checked * 1),
devIsNew: ($('#NEWDEV_devIsNew')[0].checked * 1),
devIsArchived: ($('#NEWDEV_devIsArchived')[0].checked * 1),
devFirstConnection: $('#NEWDEV_devFirstConnection').val(),
devLastConnection: $('#NEWDEV_devLastConnection').val(),
devLastIP: $('#NEWDEV_devLastIP').val(),
devCustomProps: btoa(
JSON.stringify(collectTableData("#NEWDEV_devCustomProps_table"))
),
createNew: createNew createNew: createNew
}, function(msg) { };
showMessage(msg);
// Remove navigation prompt "Are you sure you want to leave..."
window.onbeforeunload = null;
somethingChanged = false;
// refresh API $.ajax({
updateApi("devices,appevents"); url: "http://" + host + ":" + port + "/device/" + encodeURIComponent(mac),
type: "POST",
headers: {
"Authorization": "Bearer " + apiToken,
"Content-Type": "application/json"
},
data: JSON.stringify(payload),
success: function (resp) {
// Callback function if (resp && resp.success) {
if (typeof refreshCallback == 'function') { showMessage("Device saved successfully");
refreshCallback(direction); } else {
showMessage("Device update returned an unexpected response");
}
// Remove navigation prompt
window.onbeforeunload = null;
somethingChanged = false;
// Refresh API
updateApi("devices,appevents");
// Callback
if (typeof refreshCallback === "function") {
refreshCallback(direction);
}
hideSpinner();
},
error: function (xhr) {
if (xhr.status === 403) {
showMessage("Unauthorized invalid API token");
} else {
showMessage("Failed to save device (" + xhr.status + ")");
}
hideSpinner();
} }
// Everything loaded
hideSpinner();
}); });
} }
//----------------------------------------------------------------------------------- //-----------------------------------------------------------------------------------
// Disables or enables network configuration for the root node // Disables or enables network configuration for the root node
function toggleNetworkConfiguration(disable) { function toggleNetworkConfiguration(disable) {
if (disable) { if (disable) {
// Completely disable the NEWDEV_devParentMAC <select> and NEWDEV_devParentPort // Completely disable the NEWDEV_devParentMAC <select> and NEWDEV_devParentPort
$('#NEWDEV_devParentMAC').prop('disabled', true).val("").prop('selectedIndex', 0); $('#NEWDEV_devParentMAC').prop('disabled', true).val("").prop('selectedIndex', 0);
$('#NEWDEV_devParentMAC').empty() // Remove all options $('#NEWDEV_devParentMAC').empty() // Remove all options
.append('<option value="">Root Node</option>') .append('<option value="">Root Node</option>')
$('#NEWDEV_devParentPort').prop('disabled', true); $('#NEWDEV_devParentPort').prop('disabled', true);
@@ -380,7 +420,7 @@
// INIT with polling for panel element visibility // INIT with polling for panel element visibility
// ----------------------------------------------- // -----------------------------------------------
var deviceDetailsPageInitialized = false; var deviceDetailsPageInitialized = false;
function initdeviceDetailsPage() function initdeviceDetailsPage()
{ {

View File

@@ -1,7 +1,7 @@
<?php <?php
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// NetAlertX // NetAlertX
// Open Source Network Guard / WIFI & LAN intrusion detector // Open Source Network Guard / WIFI & LAN intrusion detector
// //
// devices.php - Front module. Server side. Manage Devices // devices.php - Front module. Server side. Manage Devices
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
@@ -32,17 +32,16 @@
switch ($action) { switch ($action) {
// check server/api_server/api_server_start.py for equivalents // check server/api_server/api_server_start.py for equivalents
case 'getServerDeviceData': getServerDeviceData(); break; // equivalent: get_device_data case 'getServerDeviceData': getServerDeviceData(); break; // equivalent: get_device_data
case 'setDeviceData': setDeviceData(); break; // equivalent: set_device_data
case 'deleteDevice': deleteDevice(); break; // equivalent: delete_device(mac) case 'deleteDevice': deleteDevice(); break; // equivalent: delete_device(mac)
case 'deleteAllWithEmptyMACs': deleteAllWithEmptyMACs(); break; // equivalent: delete_all_with_empty_macs case 'deleteAllWithEmptyMACs': deleteAllWithEmptyMACs(); break; // equivalent: delete_all_with_empty_macs
case 'deleteAllDevices': deleteAllDevices(); break; // equivalent: delete_devices(macs) case 'deleteAllDevices': deleteAllDevices(); break; // equivalent: delete_devices(macs)
case 'deleteUnknownDevices': deleteUnknownDevices(); break; // equivalent: delete_unknown_devices case 'deleteUnknownDevices': deleteUnknownDevices(); break; // equivalent: delete_unknown_devices
case 'deleteEvents': deleteEvents(); break; // equivalent: delete_events case 'deleteEvents': deleteEvents(); break; // equivalent: delete_events
case 'deleteEvents30': deleteEvents30(); break; // equivalent: delete_events_30 case 'deleteEvents30': deleteEvents30(); break; // equivalent: delete_events_30
case 'deleteActHistory': deleteActHistory(); break; // equivalent: delete_online_history case 'deleteActHistory': deleteActHistory(); break; // equivalent: delete_online_history
case 'deleteDeviceEvents': deleteDeviceEvents(); break; // equivalent: delete_device_events(mac) case 'deleteDeviceEvents': deleteDeviceEvents(); break; // equivalent: delete_device_events(mac)
case 'resetDeviceProps': resetDeviceProps(); break; // equivalent: reset_device_props case 'resetDeviceProps': resetDeviceProps(); break; // equivalent: reset_device_props
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
@@ -54,11 +53,11 @@
case 'copyFromDevice': copyFromDevice(); break; // equivalent: copy_device(mac_from, mac_to) case 'copyFromDevice': copyFromDevice(); break; // equivalent: copy_device(mac_from, mac_to)
case 'wakeonlan': wakeonlan(); break; // equivalent: wakeonlan case 'wakeonlan': wakeonlan(); break; // equivalent: wakeonlan
default: logServerConsole ('Action: '. $action); break; // equivalent: default: logServerConsole ('Action: '. $action); break; // equivalent:
} }
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// Query Device Data // Query Device Data
@@ -125,47 +124,47 @@ function getServerDeviceData() {
// Fetch Device Info + Children + Events Stats // Fetch Device Info + Children + Events Stats
$sql =<<<SQL $sql =<<<SQL
SELECT SELECT
d.rowid, d.rowid,
d.*, d.*,
CASE CASE
WHEN d.devAlertDown != 0 AND d.devPresentLastScan = 0 THEN "Down" WHEN d.devAlertDown != 0 AND d.devPresentLastScan = 0 THEN "Down"
WHEN d.devPresentLastScan = 1 THEN "On-line" WHEN d.devPresentLastScan = 1 THEN "On-line"
ELSE "Off-line" ELSE "Off-line"
END AS devStatus, END AS devStatus,
-- Event counters -- Event counters
(SELECT COUNT(*) FROM Sessions (SELECT COUNT(*) FROM Sessions
WHERE ses_MAC = d.devMac AND ( WHERE ses_MAC = d.devMac AND (
ses_DateTimeConnection >= $periodDate OR ses_DateTimeConnection >= $periodDate OR
ses_DateTimeDisconnection >= $periodDate OR ses_DateTimeDisconnection >= $periodDate OR
ses_StillConnected = 1 ses_StillConnected = 1
) )
) AS devSessions, ) AS devSessions,
(SELECT COUNT(*) FROM Events (SELECT COUNT(*) FROM Events
WHERE eve_MAC = d.devMac AND WHERE eve_MAC = d.devMac AND
eve_DateTime >= $periodDate AND eve_DateTime >= $periodDate AND
eve_EventType NOT IN ("Connected", "Disconnected") eve_EventType NOT IN ("Connected", "Disconnected")
) AS devEvents, ) AS devEvents,
(SELECT COUNT(*) FROM Events (SELECT COUNT(*) FROM Events
WHERE eve_MAC = d.devMac AND WHERE eve_MAC = d.devMac AND
eve_DateTime >= $periodDate AND eve_DateTime >= $periodDate AND
eve_EventType = "Device Down" eve_EventType = "Device Down"
) AS devDownAlerts, ) AS devDownAlerts,
(SELECT CAST(( MAX (0, SUM (julianday (IFNULL (ses_DateTimeDisconnection,'$currentdate')) (SELECT CAST(( MAX (0, SUM (julianday (IFNULL (ses_DateTimeDisconnection,'$currentdate'))
- julianday (CASE WHEN ses_DateTimeConnection < $periodDate - julianday (CASE WHEN ses_DateTimeConnection < $periodDate
THEN $periodDate THEN $periodDate
ELSE ses_DateTimeConnection END)) *24 )) AS INT) ELSE ses_DateTimeConnection END)) *24 )) AS INT)
FROM Sessions FROM Sessions
WHERE ses_MAC = d.devMac AND WHERE ses_MAC = d.devMac AND
ses_DateTimeConnection IS NOT NULL AND ses_DateTimeConnection IS NOT NULL AND
(ses_DateTimeDisconnection IS NOT NULL OR ses_StillConnected = 1) AND (ses_DateTimeDisconnection IS NOT NULL OR ses_StillConnected = 1) AND
( (
ses_DateTimeConnection >= $periodDate OR ses_DateTimeConnection >= $periodDate OR
ses_DateTimeDisconnection >= $periodDate OR ses_DateTimeDisconnection >= $periodDate OR
ses_StillConnected = 1 ses_StillConnected = 1
) )
) AS devPresenceHours ) AS devPresenceHours
@@ -271,31 +270,31 @@ function setDeviceData() {
WHERE devMac = '$mac'"; WHERE devMac = '$mac'";
} else { // An INSERT } else { // An INSERT
$sql = "INSERT INTO Devices ( $sql = "INSERT INTO Devices (
devMac, devMac,
devName, devName,
devOwner, devOwner,
devType, devType,
devVendor, devVendor,
devIcon, devIcon,
devFavorite, devFavorite,
devGroup, devGroup,
devLocation, devLocation,
devComments, devComments,
devParentMAC, devParentMAC,
devParentPort, devParentPort,
devSSID, devSSID,
devSite, devSite,
devStaticIP, devStaticIP,
devScan, devScan,
devAlertEvents, devAlertEvents,
devAlertDown, devAlertDown,
devParentRelType, devParentRelType,
devReqNicsOnline, devReqNicsOnline,
devSkipRepeated, devSkipRepeated,
devIsNew, devIsNew,
devIsArchived, devIsArchived,
devLastConnection, devLastConnection,
devFirstConnection, devFirstConnection,
devLastIP, devLastIP,
devGUID, devGUID,
devCustomProps, devCustomProps,
@@ -369,7 +368,7 @@ function deleteDevice() {
// Delete all devices with empty MAC addresses // Delete all devices with empty MAC addresses
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
function deleteAllWithEmptyMACs() { function deleteAllWithEmptyMACs() {
global $db; global $db;
// sql // sql
$sql = 'DELETE FROM Devices WHERE devMac=""'; $sql = 'DELETE FROM Devices WHERE devMac=""';
@@ -388,7 +387,7 @@ function deleteAllWithEmptyMACs() {
// Delete all devices with empty MAC addresses // Delete all devices with empty MAC addresses
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
function deleteUnknownDevices() { function deleteUnknownDevices() {
global $db; global $db;
// sql // sql
$sql = 'DELETE FROM Devices WHERE devName="(unknown)" OR devName="(name not found)"'; $sql = 'DELETE FROM Devices WHERE devName="(unknown)" OR devName="(name not found)"';
@@ -430,7 +429,7 @@ function resetDeviceProps() {
// sql // sql
$sql = 'UPDATE Devices set devCustomProps = "'.getSettingValue("NEWDEV_devCustomProps").'" WHERE devMac="' . $_REQUEST['mac'] .'"'; $sql = 'UPDATE Devices set devCustomProps = "'.getSettingValue("NEWDEV_devCustomProps").'" WHERE devMac="' . $_REQUEST['mac'] .'"';
// execute sql // execute sql
$result = $db->query($sql); $result = $db->query($sql);
@@ -443,7 +442,7 @@ function resetDeviceProps() {
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// Delete all devices // Delete all devices
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
function deleteAllDevices() { function deleteAllDevices() {
global $db; global $db;
@@ -462,7 +461,7 @@ function deleteAllDevices() {
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// Delete all Events // Delete all Events
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
function deleteEvents() { function deleteEvents() {
global $db; global $db;
@@ -527,18 +526,18 @@ function ExportCSV() {
header("Content-disposition: attachment; filename=\"devices.csv\""); header("Content-disposition: attachment; filename=\"devices.csv\"");
global $db; global $db;
$func_result = $db->query("SELECT * FROM Devices"); $func_result = $db->query("SELECT * FROM Devices");
// prepare CSV header row // prepare CSV header row
$columns = getDevicesColumns(); $columns = getDevicesColumns();
// wrap the headers with " (quotes) // wrap the headers with " (quotes)
$resultCSV = '"'.implode('","', $columns).'"'."\n"; $resultCSV = '"'.implode('","', $columns).'"'."\n";
// retrieve the devices from the DB // retrieve the devices from the DB
while ($row = $func_result->fetchArray(SQLITE3_ASSOC)) { while ($row = $func_result->fetchArray(SQLITE3_ASSOC)) {
// loop through columns and add values to the string // loop through columns and add values to the string
$index = 0; $index = 0;
foreach ($columns as $columnName) { foreach ($columns as $columnName) {
// Escape special chars (e.g.quotes) inside fields by replacing them with html definitions // Escape special chars (e.g.quotes) inside fields by replacing them with html definitions
@@ -552,14 +551,14 @@ function ExportCSV() {
$resultCSV .= ','; $resultCSV .= ',';
} }
$index++; $index++;
} }
// add a new line for the next row // add a new line for the next row
$resultCSV .= "\n"; $resultCSV .= "\n";
} }
//write the built CSV string //write the built CSV string
echo $resultCSV; echo $resultCSV;
} }
@@ -568,7 +567,7 @@ function ExportCSV() {
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
function ImportCSV() { function ImportCSV() {
global $db; global $db;
$file = '../../../config/devices.csv'; $file = '../../../config/devices.csv';
$data = ""; $data = "";
$skipped = ""; $skipped = "";
@@ -587,9 +586,9 @@ function ImportCSV() {
} else if (file_exists($file)) { // try to get the data form the file } else if (file_exists($file)) { // try to get the data form the file
// Read the CSV file // Read the CSV file
$data = file_get_contents($file); $data = file_get_contents($file);
} else { } else {
echo lang('BackDevices_DBTools_ImportCSVMissing'); echo lang('BackDevices_DBTools_ImportCSVMissing');
} }
if($data != "") if($data != "")
@@ -600,20 +599,20 @@ function ImportCSV() {
return str_replace("\n", " ", $matches[0]); // Replace with a space return str_replace("\n", " ", $matches[0]); // Replace with a space
}, $data); }, $data);
$lines = explode("\n", $data); $lines = explode("\n", $data);
// Get the column headers from the first line of the CSV // Get the column headers from the first line of the CSV
$header = str_getcsv(array_shift($lines)); $header = str_getcsv(array_shift($lines));
$header = array_map('trim', $header); $header = array_map('trim', $header);
// Delete everything form the DB table // Delete everything form the DB table
$sql = 'DELETE FROM Devices'; $sql = 'DELETE FROM Devices';
$result = $db->query($sql); $result = $db->query($sql);
// Build the SQL statement // Build the SQL statement
$sql = "INSERT INTO Devices (" . implode(', ', $header) . ") VALUES "; $sql = "INSERT INTO Devices (" . implode(', ', $header) . ") VALUES ";
// Parse data from CSV file line by line (max 10000 lines) // Parse data from CSV file line by line (max 10000 lines)
$index = 0; $index = 0;
foreach($lines as $row) { foreach($lines as $row) {
$rowArray = str_getcsv($row); $rowArray = str_getcsv($row);
@@ -642,7 +641,7 @@ function ImportCSV() {
// Import successful // Import successful
echo lang('BackDevices_DBTools_ImportCSV') . " (Skipped lines: " . $skipped . ") "; echo lang('BackDevices_DBTools_ImportCSV') . " (Skipped lines: " . $skipped . ") ";
} else { } else {
// An error occurred while writing to the DB, display the last error message // An error occurred while writing to the DB, display the last error message
echo lang('BackDevices_DBTools_ImportCSVError') . "\n" . $error . "\n" . $sql . "\n\n" . $result; echo lang('BackDevices_DBTools_ImportCSVError') . "\n" . $error . "\n" . $sql . "\n\n" . $result;
} }
} }
@@ -665,21 +664,21 @@ function getDevicesTotals() {
// combined query // combined query
$result = $db->query( $result = $db->query(
'SELECT 'SELECT
(SELECT COUNT(*) FROM Devices '. getDeviceCondition ('my').') as devices, (SELECT COUNT(*) FROM Devices '. getDeviceCondition ('my').') as devices,
(SELECT COUNT(*) FROM Devices '. getDeviceCondition ('connected').') as connected, (SELECT COUNT(*) FROM Devices '. getDeviceCondition ('connected').') as connected,
(SELECT COUNT(*) FROM Devices '. getDeviceCondition ('favorites').') as favorites, (SELECT COUNT(*) FROM Devices '. getDeviceCondition ('favorites').') as favorites,
(SELECT COUNT(*) FROM Devices '. getDeviceCondition ('new').') as new, (SELECT COUNT(*) FROM Devices '. getDeviceCondition ('new').') as new,
(SELECT COUNT(*) FROM Devices '. getDeviceCondition ('down').') as down, (SELECT COUNT(*) FROM Devices '. getDeviceCondition ('down').') as down,
(SELECT COUNT(*) FROM Devices '. getDeviceCondition ('archived').') as archived (SELECT COUNT(*) FROM Devices '. getDeviceCondition ('archived').') as archived
'); ');
$row = $result -> fetchArray (SQLITE3_NUM); $row = $result -> fetchArray (SQLITE3_NUM);
$resultJSON = json_encode (array ($row[0], $row[1], $row[2], $row[3], $row[4], $row[5])); $resultJSON = json_encode (array ($row[0], $row[1], $row[2], $row[3], $row[4], $row[5]));
// save to cache // save to cache
setCache("getDevicesTotals", $resultJSON ); setCache("getDevicesTotals", $resultJSON );
} }
echo ($resultJSON); echo ($resultJSON);
} }
@@ -769,19 +768,19 @@ function updateNetworkLeaf()
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// Wake-on-LAN // Wake-on-LAN
// Inspired by @leiweibau: https://github.com/leiweibau/Pi.Alert/commit/30427c7fea180670c71a2b790699e5d9e9e88ffd // Inspired by @leiweibau: https://github.com/leiweibau/Pi.Alert/commit/30427c7fea180670c71a2b790699e5d9e9e88ffd
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
function wakeonlan() { function wakeonlan() {
$WOL_HOST_IP = $_REQUEST['ip']; $WOL_HOST_IP = $_REQUEST['ip'];
$WOL_HOST_MAC = $_REQUEST['mac']; $WOL_HOST_MAC = $_REQUEST['mac'];
if (!filter_var($WOL_HOST_IP, FILTER_VALIDATE_IP)) { if (!filter_var($WOL_HOST_IP, FILTER_VALIDATE_IP)) {
echo "Invalid IP! ". lang('BackDevDetail_Tools_WOL_error'); exit; echo "Invalid IP! ". lang('BackDevDetail_Tools_WOL_error'); exit;
} }
elseif (!filter_var($WOL_HOST_MAC, FILTER_VALIDATE_MAC)) { elseif (!filter_var($WOL_HOST_MAC, FILTER_VALIDATE_MAC)) {
echo "Invalid MAC! ". lang('BackDevDetail_Tools_WOL_error'); exit; echo "Invalid MAC! ". lang('BackDevDetail_Tools_WOL_error'); exit;
} }
exec('wakeonlan '.$WOL_HOST_MAC , $output); exec('wakeonlan '.$WOL_HOST_MAC , $output);
@@ -792,14 +791,14 @@ function wakeonlan() {
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// Copy from device // Copy from device
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
function copyFromDevice() { function copyFromDevice() {
$MAC_FROM = $_REQUEST['macFrom']; $MAC_FROM = $_REQUEST['macFrom'];
$MAC_TO = $_REQUEST['macTo']; $MAC_TO = $_REQUEST['macTo'];
global $db; global $db;
// clean-up temporary table // clean-up temporary table
$sql = "DROP TABLE IF EXISTS temp_devices "; $sql = "DROP TABLE IF EXISTS temp_devices ";
$result = $db->query($sql); $result = $db->query($sql);
@@ -810,7 +809,7 @@ function copyFromDevice() {
// update temporary table with the correct target MAC // update temporary table with the correct target MAC
$sql = "UPDATE temp_devices SET devMac = '". $MAC_TO . "';"; $sql = "UPDATE temp_devices SET devMac = '". $MAC_TO . "';";
$result = $db->query($sql); $result = $db->query($sql);
// delete previous entry // delete previous entry
$sql = "DELETE FROM Devices WHERE devMac = '". $MAC_TO . "';"; $sql = "DELETE FROM Devices WHERE devMac = '". $MAC_TO . "';";
$result = $db->query($sql); $result = $db->query($sql);
@@ -828,8 +827,8 @@ function copyFromDevice() {
echo 'OK'; echo 'OK';
} else { } else {
echo lang('BackDevices_Device_UpdDevError'); echo lang('BackDevices_Device_UpdDevError');
} }
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
@@ -845,7 +844,7 @@ function getDeviceCondition ($deviceStatus) {
case 'down': return 'WHERE devIsArchived=0 AND devAlertDown !=0 AND devPresentLastScan=0'; break; case 'down': return 'WHERE devIsArchived=0 AND devAlertDown !=0 AND devPresentLastScan=0'; break;
case 'archived': return 'WHERE devIsArchived=1'; break; case 'archived': return 'WHERE devIsArchived=1'; break;
default: return 'WHERE 1=0'; break; default: return 'WHERE 1=0'; break;
} }
} }

View File

@@ -345,8 +345,7 @@ function formatDataSize(bytes) {
function loadInterfaces() { function loadInterfaces() {
const apiToken = getSetting("API_TOKEN"); // replace with dynamic token if available const apiToken = getSetting("API_TOKEN");
const host = window.location.hostname; const host = window.location.hostname;
const port = getSetting("GRAPHQL_PORT"); const port = getSetting("GRAPHQL_PORT");

View File

@@ -500,96 +500,112 @@ class DeviceInstance:
def setDeviceData(self, mac, data): def setDeviceData(self, mac, data):
"""Update or create a device.""" """Update or create a device."""
if data.get("createNew", False): try:
sql = """ if data.get("createNew", False):
INSERT INTO Devices ( sql = """
devMac, devName, devOwner, devType, devVendor, devIcon, INSERT INTO Devices (
devFavorite, devGroup, devLocation, devComments, devMac, devName, devOwner, devType, devVendor, devIcon,
devParentMAC, devParentPort, devSSID, devSite, devFavorite, devGroup, devLocation, devComments,
devStaticIP, devScan, devAlertEvents, devAlertDown, devParentMAC, devParentPort, devSSID, devSite,
devParentRelType, devReqNicsOnline, devSkipRepeated, devStaticIP, devScan, devAlertEvents, devAlertDown,
devIsNew, devIsArchived, devLastConnection, devParentRelType, devReqNicsOnline, devSkipRepeated,
devFirstConnection, devLastIP, devGUID, devCustomProps, devIsNew, devIsArchived, devLastConnection,
devSourcePlugin devFirstConnection, devLastIP, devGUID, devCustomProps,
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) devSourcePlugin
""" ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
"""
values = ( values = (
mac, mac,
data.get("devName", ""), data.get("devName", ""),
data.get("devOwner", ""), data.get("devOwner", ""),
data.get("devType", ""), data.get("devType", ""),
data.get("devVendor", ""), data.get("devVendor", ""),
data.get("devIcon", ""), data.get("devIcon", ""),
data.get("devFavorite", 0), data.get("devFavorite", 0),
data.get("devGroup", ""), data.get("devGroup", ""),
data.get("devLocation", ""), data.get("devLocation", ""),
data.get("devComments", ""), data.get("devComments", ""),
data.get("devParentMAC", ""), data.get("devParentMAC", ""),
data.get("devParentPort", ""), data.get("devParentPort", ""),
data.get("devSSID", ""), data.get("devSSID", ""),
data.get("devSite", ""), data.get("devSite", ""),
data.get("devStaticIP", 0), data.get("devStaticIP", 0),
data.get("devScan", 0), data.get("devScan", 0),
data.get("devAlertEvents", 0), data.get("devAlertEvents", 0),
data.get("devAlertDown", 0), data.get("devAlertDown", 0),
data.get("devParentRelType", "default"), data.get("devParentRelType", "default"),
data.get("devReqNicsOnline", 0), data.get("devReqNicsOnline", 0),
data.get("devSkipRepeated", 0), data.get("devSkipRepeated", 0),
data.get("devIsNew", 0), data.get("devIsNew", 0),
data.get("devIsArchived", 0), data.get("devIsArchived", 0),
data.get("devLastConnection", timeNowDB()), data.get("devLastConnection", timeNowDB()),
data.get("devFirstConnection", timeNowDB()), data.get("devFirstConnection", timeNowDB()),
data.get("devLastIP", ""), data.get("devLastIP", ""),
data.get("devGUID", ""), data.get("devGUID", ""),
data.get("devCustomProps", ""), data.get("devCustomProps", ""),
data.get("devSourcePlugin", "DUMMY"), data.get("devSourcePlugin", "DUMMY"),
) )
else: else:
sql = """ sql = """
UPDATE Devices SET UPDATE Devices SET
devName=?, devOwner=?, devType=?, devVendor=?, devIcon=?, devName=?, devOwner=?, devType=?, devVendor=?, devIcon=?,
devFavorite=?, devGroup=?, devLocation=?, devComments=?, devFavorite=?, devGroup=?, devLocation=?, devComments=?,
devParentMAC=?, devParentPort=?, devSSID=?, devSite=?, devParentMAC=?, devParentPort=?, devSSID=?, devSite=?,
devStaticIP=?, devScan=?, devAlertEvents=?, devAlertDown=?, devStaticIP=?, devScan=?, devAlertEvents=?, devAlertDown=?,
devParentRelType=?, devReqNicsOnline=?, devSkipRepeated=?, devParentRelType=?, devReqNicsOnline=?, devSkipRepeated=?,
devIsNew=?, devIsArchived=?, devCustomProps=? devIsNew=?, devIsArchived=?, devCustomProps=?
WHERE devMac=? WHERE devMac=?
""" """
values = ( values = (
data.get("devName", ""), data.get("devName", ""),
data.get("devOwner", ""), data.get("devOwner", ""),
data.get("devType", ""), data.get("devType", ""),
data.get("devVendor", ""), data.get("devVendor", ""),
data.get("devIcon", ""), data.get("devIcon", ""),
data.get("devFavorite", 0), data.get("devFavorite", 0),
data.get("devGroup", ""), data.get("devGroup", ""),
data.get("devLocation", ""), data.get("devLocation", ""),
data.get("devComments", ""), data.get("devComments", ""),
data.get("devParentMAC", ""), data.get("devParentMAC", ""),
data.get("devParentPort", ""), data.get("devParentPort", ""),
data.get("devSSID", ""), data.get("devSSID", ""),
data.get("devSite", ""), data.get("devSite", ""),
data.get("devStaticIP", 0), data.get("devStaticIP", 0),
data.get("devScan", 0), data.get("devScan", 0),
data.get("devAlertEvents", 0), data.get("devAlertEvents", 0),
data.get("devAlertDown", 0), data.get("devAlertDown", 0),
data.get("devParentRelType", "default"), data.get("devParentRelType", "default"),
data.get("devReqNicsOnline", 0), data.get("devReqNicsOnline", 0),
data.get("devSkipRepeated", 0), data.get("devSkipRepeated", 0),
data.get("devIsNew", 0), data.get("devIsNew", 0),
data.get("devIsArchived", 0), data.get("devIsArchived", 0),
data.get("devCustomProps", ""), data.get("devCustomProps", ""),
mac, mac,
) )
conn = get_temp_db_connection() conn = get_temp_db_connection()
cur = conn.cursor() cur = conn.cursor()
cur.execute(sql, values) cur.execute(sql, values)
conn.commit() conn.commit()
conn.close() conn.close()
return {"success": True} return {"success": True}
except Exception as e:
if conn:
conn.rollback()
# Optional: your existing logger
mylog("none", f"[DeviceInstance] setDeviceData({mac}) failed: {e}")
return {
"success": False,
"error": str(e)
}
finally:
if conn:
conn.close()
def deleteDeviceByMAC(self, mac): def deleteDeviceByMAC(self, mac):
"""Delete a device by MAC.""" """Delete a device by MAC."""