diff --git a/docker-compose.yml b/docker-compose.yml
index e874efe9..23cd5a55 100755
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -26,7 +26,7 @@ services:
- ${APP_DATA_LOCATION}/pihole/etc-pihole/pihole-FTL.db:/etc/pihole/pihole-FTL.db
- ${DEV_LOCATION}/server:/app/server
- ${DEV_LOCATION}/dockerfiles:/app/dockerfiles
- - ${APP_DATA_LOCATION}/netalertx/php.ini:/etc/php/8.2/fpm/php.ini
+ # - ${APP_DATA_LOCATION}/netalertx/php.ini:/etc/php/8.2/fpm/php.ini
- ${DEV_LOCATION}/install:/app/install
- ${DEV_LOCATION}/front/css:/app/front/css
- ${DEV_LOCATION}/front/img:/app/front/img
diff --git a/front/css/app.css b/front/css/app.css
index 6c39b26a..1d2bbd9f 100755
--- a/front/css/app.css
+++ b/front/css/app.css
@@ -492,28 +492,34 @@
/* -----------------------------------------------------------------------------
Notification float banner
----------------------------------------------------------------------------- */
-.pa_alert_notification {
+.notification_modal {
+
text-align: center;
- font-size: large;
- font-weight: bold;
- color: #258744;
-
- background-color: #d4edda;
- border-color: #c3e6cb;
- border-radius: 5px;
-
- max-width: 1000px;
- /* 80% wrapper 1250px */
+ left: 0;
+ right: 0;
width: 80%;
z-index: 9999;
-
position: fixed;
- top: 30px;
- margin: auto;
- transform: translate(0, 0);
-
+ top: 100px;
display: none;
+ margin-left: auto;
+ margin-right: auto;
}
+
+.modal_green
+{
+ color: #258744;
+ background-color: #d4edda;
+ border-color: #c3e6cb;
+}
+
+.modal_grey
+{
+ color: white;
+ background-color: darkgrey;
+ border-color: #000000;
+}
+
/* ticker setup */
.ticker-li
{
@@ -979,6 +985,11 @@ input[readonly] {
border-color: #258744;
}
+.settings-sticky-bottom-section .form-group
+{
+ margin-bottom: 0px;
+}
+
.clear-filter
{
opacity: 0.5;
diff --git a/front/js/modal.js b/front/js/modal.js
index ad0786bc..5efcbfab 100755
--- a/front/js/modal.js
+++ b/front/js/modal.js
@@ -186,17 +186,24 @@ function modalWarningOK() {
}
// -----------------------------------------------------------------------------
-function showMessage(textMessage = "") {
+function showMessage(textMessage = "", timeout = 3000, colorClass = "modal_green") {
if (textMessage.toLowerCase().includes("error")) {
// show error
alert(textMessage);
} else {
- // show temporal notification
+ // show temporary notification
+ $("#notification").removeClass(); // remove all classes
+ $("#notification").addClass("alert alert-dimissible notification_modal"); // add default ones
+ $("#notification").addClass(colorClass); // add color modifiers
+
+ // message
$("#alert-message").html(textMessage);
+
+ // timeout
$("#notification").fadeIn(1, function () {
window.setTimeout(function () {
$("#notification").fadeOut(500);
- }, 3000);
+ }, timeout);
});
}
}
diff --git a/front/js/settings_utils.js b/front/js/settings_utils.js
index 78818c58..ffd9641b 100755
--- a/front/js/settings_utils.js
+++ b/front/js/settings_utils.js
@@ -201,10 +201,94 @@
}
}
+// -------------------------------------------------------------------
+// Validation
+// -------------------------------------------------------------------
+function settingsCollectedCorrectly(settingsArray, settingsJSON_DB) {
+
+ // check if the required UI_LANG setting is in the array - if not something went wrong
+ $.each(settingsArray, function(index, value) {
+ if (value[1] == "UI_LANG") {
+ if(isEmpty(value[3]) == true)
+ {
+ console.log(`⚠ Error: Required setting UI_LANG not found`);
+ showModalOk('ERROR', getString('settings_missing_block'));
+
+ return false;
+ }
+ }
+ });
+
+ const settingsCodeNames = settingsJSON_DB.map(setting => setting.Code_Name);
+ const detailedCodeNames = settingsArray.map(item => item[1]);
+
+ const missingCodeNamesOnPage = detailedCodeNames.filter(codeName => !settingsCodeNames.includes(codeName));
+ const missingCodeNamesInDB = settingsCodeNames.filter(codeName => !detailedCodeNames.includes(codeName));
+
+ // check if the number of settings on the page and in the DB are the same
+ if (missingCodeNamesOnPage.length !== missingCodeNamesInDB.length) {
+
+ console.log(`⚠ Error: The following settings are missing in the DB or on the page (Reload page to fix):`);
+ console.log(missingCodeNamesOnPage);
+ console.log(missingCodeNamesInDB);
+
+ showModalOk('ERROR', getString('settings_missing_block'));
+
+ return false;
+ }
+
+ // all OK
+ return true;
+}
// -------------------------------------------------------------------
// Manipulating Editable List options
// -------------------------------------------------------------------
+// ---------------------------------------------------------
+function addList(element)
+{
+
+ const fromId = $(element).attr('my-input-from');
+ const toId = $(element).attr('my-input-to');
+
+ input = $(`#${fromId}`).val();
+ $(`#${toId}`).append($("").attr("value", input).text(input));
+
+ // clear input
+ $(`#${fromId}`).val("");
+
+ settingsChanged();
+}
+// ---------------------------------------------------------
+function removeFromList(element)
+{
+ settingsChanged();
+ $(`#${$(element).attr('my-input')}`).find("option:last").remove();
+
+}
+// ---------------------------------------------------------
+function addInterface()
+{
+ ipMask = $('#ipMask').val();
+ ipInterface = $('#ipInterface').val();
+
+ full = ipMask + " --interface=" + ipInterface;
+
+ console.log(full)
+
+ if(ipMask == "" || ipInterface == "")
+ {
+ showModalOk ('Validation error', 'Specify both, the network mask and the interface');
+ } else {
+ $('#SCAN_SUBNETS').append($('').attr('value', full).text(full));
+
+ $('#ipMask').val('');
+ $('#ipInterface').val('');
+
+ settingsChanged();
+ }
+}
+
// -------------------------------------------------------------------
// Function to remove an item from the select element
@@ -305,32 +389,175 @@ function filterRows(inputText) {
});
}
-setTimeout(() => {
+ setTimeout(() => {
- // Event listener for input change
- $('#settingsSearch').on('input', function() {
- var searchText = $(this).val();
- // hide the setting overview dashboard
- $('#settingsOverview').collapse('hide');
+ // Event listener for input change
+ $('#settingsSearch').on('input', function() {
+ var searchText = $(this).val();
+ // hide the setting overview dashboard
+ $('#settingsOverview').collapse('hide');
- filterRows(searchText);
+ filterRows(searchText);
+ });
+
+ // Event listener for input focus
+ // var firstFocus = true;
+ $('#settingsSearch').on('focus', function() {
+ openAllSettings()
+ });
+
+
+
+ }, 1000);
+
+
+
+ // -----------------------------------------------------------------------------
+ // handling events on the backend initiated by the front end START
+ // -----------------------------------------------------------------------------
+
+ modalEventStatusId = 'modal-message-front-event'
+
+ // --------------------------------------------------------
+ // Calls a backend function to add a front-end event (specified by the attributes 'data-myevent' and 'data-myparam-plugin' on the passed element) to an execution queue
+ function addToExecutionQueue(element)
+ {
+
+ // value has to be in format event|param. e.g. run|ARPSCAN
+ action = `${getGuid()}|${$(element).attr('data-myevent')}|${$(element).attr('data-myparam-plugin')}`
+
+ $.ajax({
+ method: "POST",
+ url: "php/server/util.php",
+ data: { function: "addToExecutionQueue", action: action },
+ success: function(data, textStatus) {
+ // showModalOk ('Result', data );
+
+ // show message
+ showModalOk(getString("general_event_title"), `${getString("general_event_description")}
`);
+
+ updateModalState()
+ }
+ })
+ }
+
+ // --------------------------------------------------------
+ // Updating the execution queue in in modal pop-up
+ function updateModalState() {
+ setTimeout(function() {
+ // Fetch the content from the log file using an AJAX request
+ $.ajax({
+ url: '/log/execution_queue.log',
+ type: 'GET',
+ success: function(data) {
+ // Update the content of the HTML element (e.g., a div with id 'logContent')
+ $('#'+modalEventStatusId).html(data);
+
+ updateModalState();
+ },
+ error: function() {
+ // Handle error, such as the file not being found
+ $('#logContent').html('Error: Log file not found.');
+ }
+ });
+ }, 2000);
+ }
+
+
+ // -----------------------------------------------------------------------------
+ // handling events on the backend initiated by the front end END
+ // -----------------------------------------------------------------------------
+
+
+// ---------------------------------------------------------
+// UNUSED?
+function getParam(targetId, key, skipCache = false) {
+
+ skipCacheQuery = "";
+
+ if(skipCache)
+ {
+ skipCacheQuery = "&skipcache";
+ }
+
+ // get parameter value
+ $.get('php/server/parameters.php?action=get&defaultValue=0¶meter='+ key + skipCacheQuery, function(data) {
+
+ var result = data;
+
+ result = result.replaceAll('"', '');
+
+ document.getElementById(targetId).innerHTML = result.replaceAll('"', '');
});
+}
- // Event listener for input focus
- // var firstFocus = true;
- $('#settingsSearch').on('focus', function() {
- openAllSettings()
- });
+ // -----------------------------------------------------------------------------
+ // Show/hide the metadata settings
+ // -----------------------------------------------------------------------------
+ function toggleMetadata(element)
+ {
+ const id = $(element).attr('my-to-toggle');
+
+ $(`#${id}`).toggle();
+ }
+
+
+
+ // ---------------------------------------------------------
+ // Helper methods
+ // ---------------------------------------------------------
+ // Toggle readonly mode of the target element specified by the id in the "my-input-toggle-readonly" attribute
+ function overrideToggle(element) {
+ settingsChanged();
+
+ targetId = $(element).attr("my-input-toggle-readonly");
+
+ inputElement = $(`#${targetId}`)[0];
+
+ if (!inputElement) {
+ console.error("Input element not found!");
+ return;
+ }
+
+ if (inputElement.type === "text" || inputElement.type === "password") {
+ inputElement.readOnly = !inputElement.readOnly;
+ } else if (inputElement.type === "checkbox") {
+ inputElement.disabled = !inputElement.disabled;
+ } else {
+ console.warn("Unsupported input type. Only text, password, and checkbox inputs are supported.");
+ }
+
+ }
+
+
+ // ---------------------------------------------------------
+ // generate a list of options for a input select
+ function generateInputOptions(pluginsData, set, input, isMultiSelect = false)
+ {
+ multi = isMultiSelect ? "multiple" : "";
+
+ // optionsArray = getSettingOptions(set['Code_Name'] )
+ valuesArray = createArray(set['Value']);
+
+ // create unique ID
+ var targetLocation = set['Code_Name'] + "_initSettingDropdown";
+
+ // execute AJAX callabck + SQL query resolution
+ initSettingDropdown(set['Code_Name'] , valuesArray, targetLocation, generateDropdownOptions)
+
+ // main selection dropdown wrapper
+ input += `
+ `;
+
+ return input;
+ }
-
-}, 1000);
-
-
-
-
-
-
-
-
-
diff --git a/front/php/server/db.php b/front/php/server/db.php
index 40e7b21f..48243f8e 100755
--- a/front/php/server/db.php
+++ b/front/php/server/db.php
@@ -12,6 +12,8 @@
// DB File Path
$DBFILE = dirname(__FILE__).'/../../../db/app.db';
+$db_locked = false;
+
//------------------------------------------------------------------------------
// Connect DB
//------------------------------------------------------------------------------
@@ -20,12 +22,21 @@ function SQLite3_connect ($trytoreconnect) {
try
{
// connect to database
+
+ global $db_locked;
+
+ $db_locked = false;
+
// return new SQLite3($DBFILE, SQLITE3_OPEN_READONLY);
return new SQLite3($DBFILE, SQLITE3_OPEN_READWRITE);
}
catch (Exception $exception)
{
// sqlite3 throws an exception when it is unable to connect
+ global $db_locked;
+
+ $db_locked = true;
+
// try to reconnect one time after 3 seconds
if($trytoreconnect)
diff --git a/front/php/server/dbHelper.php b/front/php/server/dbHelper.php
index 134395e3..7cf6239e 100755
--- a/front/php/server/dbHelper.php
+++ b/front/php/server/dbHelper.php
@@ -269,16 +269,26 @@ function delete($columnName, $id, $dbtable)
// check if the database is locked
//------------------------------------------------------------------------------
function checkLock() {
- global $db;
- try {
- $db->exec('BEGIN EXCLUSIVE TRANSACTION');
- $db->exec('COMMIT');
- echo 0; // Not locked
- return 0;
- } catch (Exception $e) {
- echo 1; // Locked
- return 1;
+ global $DBFILE, $db_locked;
+
+ $file = fopen($DBFILE, 'r+');
+
+ if (!$file or $db_locked) {
+ echo 1; // Could not open the file
+ return;
}
+
+ if (flock($file, LOCK_EX | LOCK_NB)) {
+ // Lock acquired, meaning the database is not locked by another process
+ flock($file, LOCK_UN); // Release the lock
+ echo 0; // Not locked
+ } else {
+ // Could not acquire lock, meaning the database is locked
+ echo 1; // Locked
+ }
+
+ fclose($file);
}
+
?>
diff --git a/front/php/server/util.php b/front/php/server/util.php
index ee9fbaa4..2e221281 100755
--- a/front/php/server/util.php
+++ b/front/php/server/util.php
@@ -387,8 +387,10 @@ function saveSettings()
// Replace the original file with the temporary file
rename($tempConfPath, $fullConfPath);
- displayMessage("
Settings saved to the app.conf file.
A time-stamped backup of the previous file created.
Reloading...
",
- FALSE, TRUE, TRUE, TRUE);
+ // displayMessage(lang('settings_saved'),
+ // FALSE, TRUE, TRUE, TRUE);
+
+ echo "OK";
}
diff --git a/front/php/templates/language/en_us.json b/front/php/templates/language/en_us.json
index dc18e630..da6a8fec 100755
--- a/front/php/templates/language/en_us.json
+++ b/front/php/templates/language/en_us.json
@@ -657,7 +657,7 @@
"settings_imported": "Last time settings were imported from the app.conf file",
"settings_imported_label": "Settings imported",
"settings_missing": "Not all settings loaded! High load on the database or app startup sequence. Click the 🔄 reload button in the top.",
- "settings_missing_block": "Not all settings were loaded correctly. This is probably caused by a high load on the database. Click the 🔄 reload button in the top.",
+ "settings_missing_block": "Error: Settings not loaded correctly. Click the reload button 🔄 at the top, alternatively, check the browser log for details (F12).",
"settings_old": "Importing settings and re-initializing...",
"settings_other_scanners": "Other, non-device scanner plugins that are currently enabled.",
"settings_other_scanners_icon": "fa-solid fa-recycle",
@@ -665,7 +665,7 @@
"settings_publishers": "Enabled notification gateways - publishers, that will send a notification depending on your settings.",
"settings_publishers_icon": "fa-solid fa-comment-dots",
"settings_publishers_label": "Publishers",
- "settings_saved": "
Settings saved to the app.conf file.
A time-stamped backup of the previous file created.
Reloading...
",
+ "settings_saved": "
Settings saved.
Reloading...
",
"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/php/templates/notification.php b/front/php/templates/notification.php
index 44948790..eb05804d 100755
--- a/front/php/templates/notification.php
+++ b/front/php/templates/notification.php
@@ -147,7 +147,7 @@
-