From 102a62c8b7edae2a8752f5a04e05f0fe7c18d6cd Mon Sep 17 00:00:00 2001 From: "Jokob @NetAlertX" <96159884+jokob-sk@users.noreply.github.com> Date: Sun, 21 Jun 2026 00:57:17 +0000 Subject: [PATCH] FE: enhance settings save functionality with reload wait detection and background processing --- front/php/server/util.php | 61 ++++++++++++++++++++++++- front/php/templates/language/en_us.json | 1 + front/settings.php | 30 ++++++++---- server/conf.py | 1 + server/initialise.py | 10 ++++ 5 files changed, 94 insertions(+), 9 deletions(-) diff --git a/front/php/server/util.php b/front/php/server/util.php index c9b7b38c..75c8d86c 100755 --- a/front/php/server/util.php +++ b/front/php/server/util.php @@ -166,15 +166,74 @@ function saveSettings() copy($fullConfPath, $fullConfPath . ".bak"); } + // Detect whether the frontend needs to block-wait for backend reload + $requiresReloadWait = getReloadWaitRequired($decodedSettings); + // Open the file for writing without changing permissions $file = fopen($fullConfPath, "w") or die("Unable to open file!"); fwrite($file, $txt); fclose($file); - echo "OK"; + echo json_encode(['success' => true, 'requiresReloadWait' => $requiresReloadWait]); } +// ------------------------------------------------------------------------------------------- +// Determines if the frontend must wait (block) for the backend to finish reloading after a +// settings save. Blocking is required when LOADED_PLUGINS changes or UI_WAIT_FOR_SETTINGS +// is explicitly enabled. Defaults to true (safe) on any parsing error. +function getReloadWaitRequired($decodedSettings) { + $newLoadedPlugins = null; + $uiWaitForSettings = false; + + foreach ($decodedSettings as $setting) { + if ($setting[1] === 'LOADED_PLUGINS') { + $newLoadedPlugins = $setting[3]; + } + if ($setting[1] === 'UI_WAIT_FOR_SETTINGS') { + $uiWaitForSettings = ($setting[3] === true || $setting[3] === 1 + || strtolower((string)$setting[3]) === 'true'); + } + } + + if ($uiWaitForSettings) { + return true; + } + + $oldLoadedPlugins = getSettingValue('LOADED_PLUGINS'); + + // If the old value couldn't be read, default to blocking (safe). + if (strpos((string)$oldLoadedPlugins, 'Could not') !== false) { + return true; + } + + return normalizePluginList($oldLoadedPlugins) !== normalizePluginList($newLoadedPlugins); +} + +// ------------------------------------------------------------------------------------------- +// Normalise a plugin list value (PHP array, JSON array, or Python-style list) to a sorted +// JSON string for reliable equality comparison. +function normalizePluginList($value) { + if ($value === null || $value === '') { + return '[]'; + } + if (is_array($value)) { + $arr = $value; + } else { + $arr = json_decode($value, true); + if (!is_array($arr)) { + // Handle Python-style single-quoted lists: ['A','B'] + $jsonStr = str_replace("'", '"', (string)$value); + $arr = json_decode($jsonStr, true); + } + if (!is_array($arr)) { + return trim((string)$value); + } + } + sort($arr); + return json_encode($arr); +} + // ------------------------------------------------------------------------------------------- // 🔺----- API ENDPOINTS SUPERSEDED -----🔺 // check server/api_server/api_server_start.py for equivalents diff --git a/front/php/templates/language/en_us.json b/front/php/templates/language/en_us.json index 102c7cb2..cfefcac5 100755 --- a/front/php/templates/language/en_us.json +++ b/front/php/templates/language/en_us.json @@ -807,6 +807,7 @@ "settings_publishers_label": "Publishers", "settings_readonly": "Can't READ or WRITE app.conf. Try restarting the container and read the file permissions documentation", "settings_saved": "
Settings saved.
Reloading…

", + "settings_saved_background": "Settings saved. Changes are being applied in the background.", "settings_system_icon": "fa-solid fa-gear", "settings_system_label": "System", "settings_update_item_warning": "Update the value below. Be careful to follow the previous format. Validation is not performed.", diff --git a/front/settings.php b/front/settings.php index 03d3e291..0be479ce 100755 --- a/front/settings.php +++ b/front/settings.php @@ -615,19 +615,33 @@ $settingsJSON_DB = json_encode($settings, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX settings: JSON.stringify(settingsArray) }, success: function(data, textStatus) { - if(data == "OK") - { - // showMessage (getString("settings_saved"), 5000, "modal_grey"); + // Parse response: support both legacy "OK" string and new JSON format + let saveSucceeded = false; + let requiresReloadWait = true; // safe default + + if (data === "OK") { + saveSucceeded = true; + } else { + let parsed = null; + try { parsed = (typeof data === 'object') ? data : JSON.parse(data); } catch(e) {} + if (parsed && parsed.success === true) { + saveSucceeded = true; + requiresReloadWait = parsed.requiresReloadWait === true; + } + } + + if (saveSucceeded) { // Remove navigation prompt "Are you sure you want to leave..." window.onbeforeunload = null; - // Reloads the current page - // setTimeout("clearCache()", 5000); - write_notification(`[Settings] Settings saved by the user`, 'info') - clearCache() - } else{ + if (requiresReloadWait) { + clearCache() + } else { + showMessage(getString("settings_saved_background"), 5000, "modal_green"); + } + } else { // something went wrong write_notification("[Important] Please take a screenshot of the Console tab in the browser (F12) and next error. Submit it (with the nginx and php error logs) as a new issue here: https://github.com/netalertx/NetAlertX/issues", 'interrupt') write_notification(data, 'interrupt') diff --git a/server/conf.py b/server/conf.py index 4eda3e84..c28dfc9c 100755 --- a/server/conf.py +++ b/server/conf.py @@ -32,6 +32,7 @@ SCAN_SUBNETS = ["192.168.1.0/24 --interface=eth1", "192.168.1.0/24 --interface=e LOG_LEVEL = "verbose" TIMEZONE = "Europe/Berlin" UI_LANG = "English (en_us)" +UI_WAIT_FOR_SETTINGS = False UI_PRESENCE = ["online", "offline", "archived"] UI_MY_DEVICES = ["online", "offline", "archived", "new", "down"] UI_NOT_RANDOM_MAC = [] diff --git a/server/initialise.py b/server/initialise.py index bf53c494..0cc06a21 100755 --- a/server/initialise.py +++ b/server/initialise.py @@ -452,6 +452,16 @@ def importConfigs(pm, db, all_plugins): "UI", ) + conf.UI_WAIT_FOR_SETTINGS = ccd( + "UI_WAIT_FOR_SETTINGS", + False, + c_d, + "Wait for settings reload", + '{"dataType":"boolean", "elements": [{"elementType" : "checkbox", "elementOptions" : [] ,"transformers": []}]}', + "[]", + "UI", + ) + # Init timezone in case it changed and handle invalid values try: if conf.TIMEZONE not in all_timezones: