FE: enhance settings save functionality with reload wait detection and background processing

This commit is contained in:
Jokob @NetAlertX
2026-06-21 00:57:17 +00:00
parent 6256149fe1
commit 102a62c8b7
5 changed files with 94 additions and 9 deletions

View File

@@ -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

View File

@@ -807,6 +807,7 @@
"settings_publishers_label": "Publishers",
"settings_readonly": "Can't READ or WRITE <code>app.conf</code>. Try restarting the container and read the <a href=\"https://docs.netalertx.com/FILE_PERMISSIONS\" target=\"_blank\">file permissions documentation</a>",
"settings_saved": "<br/>Settings saved. <br/> Reloading… <br/><i class=\"ion ion-ios-loop-strong fa-spin fa-2x fa-fw\"></i> <br/>",
"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. <b>Validation is not performed.</b>",

View File

@@ -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')

View File

@@ -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 = []

View File

@@ -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: