mirror of
https://github.com/jokob-sk/NetAlertX.git
synced 2026-04-12 21:21:36 -07:00
Compare commits
36 Commits
2f70e2e8d8
...
v24.9.26
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
15a7779d6e | ||
|
|
2784f2ebeb | ||
|
|
d46046beea | ||
|
|
6233f4d646 | ||
|
|
31411e0a14 | ||
|
|
8d824af3bd | ||
|
|
f05f0d625a | ||
|
|
2fec3b6607 | ||
|
|
f285a28887 | ||
|
|
11cb47fada | ||
|
|
d8b413b5e7 | ||
|
|
656bba7ff7 | ||
|
|
a2cf8c1167 | ||
|
|
737cb07403 | ||
|
|
3febbc21cb | ||
|
|
7e14fae29c | ||
|
|
a16fe4561b | ||
|
|
f2afe9d681 | ||
|
|
f8c0a5a1ef | ||
|
|
631e992411 | ||
|
|
feafaff218 | ||
|
|
f6a06842cc | ||
|
|
0cc3ede86c | ||
|
|
aa277136c6 | ||
|
|
82ccb0c0b6 | ||
|
|
30750a9449 | ||
|
|
5278af48c5 | ||
|
|
77f19c3575 | ||
|
|
10df7363d6 | ||
|
|
06e49f7adb | ||
|
|
9fcbd9d64e | ||
|
|
c6888a79fd | ||
|
|
ef458903b7 | ||
|
|
b544734209 | ||
|
|
815810dc7a | ||
|
|
552d79eee8 |
@@ -8,7 +8,7 @@ There are 3 artifacts that can be used to backup the application:
|
||||
| File | Description | Limitations |
|
||||
|-----------------------|-------------------------------|-------------------------------|
|
||||
| `/db/app.db` | Database file(s) | The database file might be in an uncommitted state or corrupted |
|
||||
| `/config/app.conf` | Configuration file | Doesn't contain settings from the Maintenance section |
|
||||
| `/config/app.conf` | Configuration file | Can be overridden with the [`APP_CONF_OVERRIDE` env variable](https://github.com/jokob-sk/NetAlertX/tree/main/dockerfiles#docker-environment-variables). |
|
||||
| `/config/devices.csv` | CSV file containing device information | Doesn't contain historical data |
|
||||
|
||||
## Data and backup storage
|
||||
|
||||
@@ -518,6 +518,60 @@ Required attributes are:
|
||||
| (optional) `"override_value"` | Used to determine a user-defined override for the setting. Useful for template-based plugins, where you can choose to leave the current value or override it with the value defined in the setting. (Work in progress) |
|
||||
| (optional) `"events"` | Used to trigger the plugin. Usually used on the `RUN` setting. Not fully tested in all scenarios. Will show a play button next to the setting. After clicking, an event is generated for the backend in the `Parameters` database table to process the front-end event on the next run. |
|
||||
|
||||
### UI Component Types Documentation
|
||||
|
||||
This section outlines the structure and types of UI components, primarily used to build HTML forms or interactive elements dynamically. Each UI component has a `"type"` which defines its structure, behavior, and rendering options.
|
||||
|
||||
#### UI Component JSON Structure
|
||||
The UI component is defined as a JSON object containing a list of `elements`. Each element specifies how it should behave, with properties like `elementType`, `elementOptions`, and any associated `transformers` to modify the data. The example below demonstrates how a component with two elements (`span` and `select`) is structured:
|
||||
|
||||
```json
|
||||
{
|
||||
"function": "dev_Icon",
|
||||
"type": {
|
||||
"dataType": "string",
|
||||
"elements": [
|
||||
{
|
||||
"elementType": "span",
|
||||
"elementOptions": [
|
||||
{ "cssClasses": "input-group-addon iconPreview" },
|
||||
{ "getStringKey": "Gen_SelectToPreview" },
|
||||
{ "customId": "NEWDEV_dev_Icon_preview" }
|
||||
],
|
||||
"transformers": []
|
||||
},
|
||||
{
|
||||
"elementType": "select",
|
||||
"elementHasInputValue": 1,
|
||||
"elementOptions": [
|
||||
{ "cssClasses": "col-xs-12" },
|
||||
{
|
||||
"onChange": "updateIconPreview(this)"
|
||||
},
|
||||
{ "customParams": "NEWDEV_dev_Icon,NEWDEV_dev_Icon_preview" }
|
||||
],
|
||||
"transformers": []
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
### Rendering Logic
|
||||
|
||||
The code snippet provided demonstrates how the elements are iterated over to generate their corresponding HTML. Depending on the `elementType`, different HTML tags (like `<select>`, `<input>`, `<textarea>`, `<button>`, etc.) are created with the respective attributes such as `onChange`, `my-data-type`, and `class` based on the provided `elementOptions`. Events can also be attached to elements like buttons or select inputs.
|
||||
|
||||
### Key Element Types
|
||||
|
||||
- **`select`**: Renders a dropdown list. Additional options like `isMultiSelect` and event handlers (e.g., `onChange`) can be attached.
|
||||
- **`input`**: Handles various types of input fields, including checkboxes, text, and others, with customizable attributes like `readOnly`, `placeholder`, etc.
|
||||
- **`button`**: Generates clickable buttons with custom event handlers (`onClick`), icons, or labels.
|
||||
- **`textarea`**: Creates a multi-line input box for text input.
|
||||
- **`span`**: Used for inline text or content with customizable classes and data attributes.
|
||||
|
||||
Each element may also have associated events (e.g., running a scan or triggering a notification) defined under `Events`.
|
||||
|
||||
|
||||
##### Supported settings `function` values
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ showSpinner()
|
||||
$(document).ready(function() {
|
||||
|
||||
// Load JSON data from the provided URL
|
||||
$.getJSON('/api/table_appevents.json', function(data) {
|
||||
$.getJSON('api/table_appevents.json', function(data) {
|
||||
// Process the JSON data and generate UI dynamically
|
||||
processData(data)
|
||||
|
||||
|
||||
@@ -1097,6 +1097,11 @@ input[readonly] {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
#settingsPage .form-control
|
||||
{
|
||||
min-height: 42px;
|
||||
}
|
||||
|
||||
#settingsPage .select2-selection
|
||||
{
|
||||
background-color: rgb(96, 96, 96);
|
||||
@@ -1112,11 +1117,41 @@ input[readonly] {
|
||||
display: inline-grid;
|
||||
}
|
||||
|
||||
|
||||
/* Basic style for the div elements */
|
||||
#settingsPage .setting_overriden_by_env {
|
||||
position: relative;
|
||||
/* width: 300px;
|
||||
height: 200px; */
|
||||
background-color: #f3f3f3;
|
||||
border: 1px solid #ccc;
|
||||
margin: 20px;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
/* Style for the overlay */
|
||||
#settingsPage .setting_overriden_by_env::after {
|
||||
content: "Overridden with ENV variable";
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: rgba(0, 0, 0, 0.6); /* semi-transparent black overlay */
|
||||
color: white;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 16px;
|
||||
font-weight: 700;
|
||||
z-index: 11;
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------- */
|
||||
/* Devices page */
|
||||
/* ----------------------------------------------------------------- */
|
||||
|
||||
#txtIconFA {
|
||||
.iconPreview {
|
||||
min-width: 40px;
|
||||
}
|
||||
|
||||
|
||||
@@ -100,7 +100,7 @@
|
||||
<div class="col-lg-12 col-sm-12 col-xs-12">
|
||||
<!-- <div class="box-transparent"> -->
|
||||
<div id="navDevice" class="nav-tabs-custom">
|
||||
<ul class="nav nav-tabs" style="fon t-size:16px;">
|
||||
<ul class="nav nav-tabs" style="font-size:16px;">
|
||||
<li> <a id="tabDetails" href="#panDetails" data-toggle="tab"> <?= lang('DevDetail_Tab_Details');?> </a></li>
|
||||
<li> <a id="tabTools" href="#panTools" data-toggle="tab"> <?= lang('DevDetail_Tab_Tools');?> </a></li>
|
||||
<li> <a id="tabSessions" href="#panSessions" data-toggle="tab"> <?= lang('DevDetail_Tab_Sessions');?> </a></li>
|
||||
@@ -197,8 +197,8 @@
|
||||
</label>
|
||||
<div class="col-sm-9">
|
||||
<div class="input-group">
|
||||
<span class="input-group-addon" id="txtIconFA"></span>
|
||||
<input class="form-control" id="txtIcon" type="text" value="--" readonly>
|
||||
<span class="input-group-addon iconPreview" id="txtIconPreview" my-customid="txtIconPreview"></span>
|
||||
<input class="form-control" id="txtIcon" my-customid="txtIcon" my-customparams="txtIcon,txtIconPreview" type="text" value="--" readonly>
|
||||
<span class="input-group-addon" title='<?= lang('DevDetail_button_AddIcon_Tooltip');?>'><i class="fa fa-square-plus pointer" onclick="askAddIcon();"></i></span>
|
||||
<span class="input-group-addon" title='<?= lang('DevDetail_button_OverwriteIcons_Tooltip');?>'><i class="fa fa-copy pointer" onclick="askOverwriteIconType();"></i></span>
|
||||
<div class="input-group-btn">
|
||||
@@ -755,7 +755,7 @@ function main () {
|
||||
|
||||
// Show device icon as it changes
|
||||
$('#txtIcon').on('change input', function() {
|
||||
updateIconPreview('#txtIcon')
|
||||
updateIconPreview(this)
|
||||
});
|
||||
|
||||
|
||||
@@ -1136,7 +1136,7 @@ function initializeCalendar () {
|
||||
showSpinner()
|
||||
} else {
|
||||
setTimeout(() => {
|
||||
updateIconPreview('#txtIcon')
|
||||
updateIconPreview($('#txtIcon'))
|
||||
}, 500);
|
||||
|
||||
hideSpinner()
|
||||
@@ -1445,10 +1445,10 @@ function setDeviceData (direction='', refreshCallback='') {
|
||||
|
||||
// update data to server
|
||||
$.get('php/server/devices.php?action=setDeviceData&mac='+ mac
|
||||
+ '&name=' + encodeURIComponent($('#txtName').val())
|
||||
+ '&owner=' + encodeURIComponent($('#txtOwner').val())
|
||||
+ '&name=' + encodeURIComponent($('#txtName').val().replace(/'/g, ""))
|
||||
+ '&owner=' + encodeURIComponent($('#txtOwner').val().replace(/'/g, ""))
|
||||
+ '&type=' + $('#txtDeviceType').val()
|
||||
+ '&vendor=' + encodeURIComponent($('#txtVendor').val())
|
||||
+ '&vendor=' + encodeURIComponent($('#txtVendor').val().replace(/'/g, ""))
|
||||
+ '&icon=' + encodeURIComponent($('#txtIcon').val())
|
||||
+ '&favorite=' + ($('#chkFavorite')[0].checked * 1)
|
||||
+ '&group=' + encodeURIComponent($('#txtGroup').val())
|
||||
@@ -1667,7 +1667,7 @@ function addAsBase64 () {
|
||||
|
||||
$('#txtIcon').val(iconHtmlBase64);
|
||||
|
||||
updateIconPreview('#txtIcon')
|
||||
updateIconPreview($('#txtIcon'))
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -78,7 +78,7 @@ if (isset ($_SESSION["login"]) == FALSE || $_SESSION["login"] != 1)
|
||||
<meta http-equiv="Pragma" content="no-cache" />
|
||||
<meta http-equiv="Expires" content="0" />
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<title>Net Alert X | Log in</title>
|
||||
<title>NetAlert X | Log in</title>
|
||||
<!-- Tell the browser to be responsive to screen width -->
|
||||
<meta content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" name="viewport">
|
||||
<!-- Bootstrap 3.3.7 -->
|
||||
@@ -104,7 +104,7 @@ if ($ENABLED_DARKMODE === True) {
|
||||
<body class="hold-transition login-page">
|
||||
<div class="login-box login-custom">
|
||||
<div class="login-logo">
|
||||
<a href="/index2.php">Net <b>Alert</b><sup>x</sup></a>
|
||||
<a href="/index2.php">Net<b>Alert</b><sup>x</sup></a>
|
||||
</div>
|
||||
<!-- /.login-logo -->
|
||||
<div class="login-box-body">
|
||||
|
||||
@@ -667,7 +667,11 @@ const handleElementOptions = (codeName, elementOptions, transformers, val) => {
|
||||
let valRes = val;
|
||||
let sourceIds = [];
|
||||
let getStringKey = "";
|
||||
let onClick = "alert('Not implemented');";
|
||||
let onClick = "console.log('onClick - Not implemented');";
|
||||
let onChange = "console.log('onChange - Not implemented');";
|
||||
let customParams = "";
|
||||
let customId = "";
|
||||
|
||||
|
||||
elementOptions.forEach((option) => {
|
||||
if (option.prefillValue) {
|
||||
@@ -711,6 +715,15 @@ const handleElementOptions = (codeName, elementOptions, transformers, val) => {
|
||||
if (option.onClick) {
|
||||
onClick = option.onClick;
|
||||
}
|
||||
if (option.onChange) {
|
||||
onChange = option.onChange;
|
||||
}
|
||||
if (option.customParams) {
|
||||
customParams = option.customParams;
|
||||
}
|
||||
if (option.customId) {
|
||||
customId = option.customId;
|
||||
}
|
||||
});
|
||||
|
||||
if (transformers.includes("sha256")) {
|
||||
@@ -731,6 +744,9 @@ const handleElementOptions = (codeName, elementOptions, transformers, val) => {
|
||||
valRes,
|
||||
getStringKey,
|
||||
onClick,
|
||||
onChange,
|
||||
customParams,
|
||||
customId
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -68,23 +68,66 @@ function initDeviceSelectors(devicesListAll_JSON) {
|
||||
}
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// ----------------------------------------------
|
||||
// Updates the icon preview
|
||||
function updateIconPreview (inputId) {
|
||||
// update icon
|
||||
iconInput = $(inputId)
|
||||
function updateIconPreview(elem) {
|
||||
// Retrieve and parse custom parameters from the element
|
||||
let params = $(elem).attr("my-customparams")?.split(',').map(param => param.trim());
|
||||
|
||||
value = iconInput.val()
|
||||
// console.log(params);
|
||||
|
||||
iconInput.on('change input', function() {
|
||||
$('#txtIconFA').html(atob(value))
|
||||
});
|
||||
if (params && params.length >= 2) {
|
||||
var inputElementID = params[0];
|
||||
var targetElementID = params[1];
|
||||
} else {
|
||||
console.error("Invalid parameters passed to updateIconPreview function");
|
||||
return;
|
||||
}
|
||||
|
||||
$('#txtIconFA').html(atob(value))
|
||||
|
||||
// Get the input element using the inputElementID
|
||||
let iconInput = $("#" + inputElementID);
|
||||
|
||||
if (iconInput.length === 0) {
|
||||
console.error("Icon input element not found");
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the initial value and update the target element
|
||||
let value = iconInput.val();
|
||||
if (!value) {
|
||||
console.error("Input value is empty or not defined");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!targetElementID) {
|
||||
targetElementID = "txtIcon";
|
||||
}
|
||||
|
||||
// Check if the target element exists, if not find an element with matching custom attribute
|
||||
let targetElement = $('#' + targetElementID);
|
||||
if (targetElement.length === 0) {
|
||||
// Look for an element with my-custom-id attribute equal to targetElementID
|
||||
targetElement = $('[my-customid="' + targetElementID + '"]');
|
||||
if (targetElement.length === 0) {
|
||||
console.error("Neither target element with ID nor element with custom attribute found");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Update the target element with decoded base64 value
|
||||
targetElement.html(atob(value));
|
||||
|
||||
// Add event listener to update the icon on input change
|
||||
iconInput.on('change input', function () {
|
||||
let newValue = $(this).val();
|
||||
$('#' + targetElementID).html(atob(newValue));
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Generic function to copy text to clipboard
|
||||
function copyToClipboard(buttonElement) {
|
||||
|
||||
@@ -1034,7 +1034,7 @@ var mouse = $.widget("ui.mouse", {
|
||||
return this.mouseDelayMet;
|
||||
},
|
||||
|
||||
// These are placeholder methods, to be overriden by extending plugin
|
||||
// These are placeholder methods, to be overridden by extending plugin
|
||||
_mouseStart: function(/* event */) {},
|
||||
_mouseDrag: function(/* event */) {},
|
||||
_mouseStop: function(/* event */) {},
|
||||
|
||||
@@ -101,19 +101,20 @@
|
||||
for (let j = i * elementsPerColumn; j < Math.min((i + 1) * elementsPerColumn, columns.length); j++) {
|
||||
|
||||
const setTypeObject = JSON.parse(columns[j].Type.replace(/'/g, '"'));
|
||||
// console.log(setTypeObject); 🔽
|
||||
// const lastElementObj = setTypeObject.elements[setTypeObject.elements.length - 1]
|
||||
|
||||
// get the element with the input value(s)
|
||||
let elementsWithInputValue = setTypeObject.elements.filter(element => element.elementHasInputValue === 1);
|
||||
let elements = setTypeObject.elements.filter(element => element.elementHasInputValue === 1);
|
||||
|
||||
// if none found, take last
|
||||
if(elementsWithInputValue.length == 0)
|
||||
if(elements.length == 0)
|
||||
{
|
||||
elementsWithInputValue = setTypeObject.elements[setTypeObject.elements.length - 1]
|
||||
elementWithInputValue = setTypeObject.elements[setTypeObject.elements.length - 1]
|
||||
} else
|
||||
{
|
||||
elementWithInputValue = elements[0]
|
||||
}
|
||||
|
||||
const { elementType, elementOptions = [], transformers = [] } = elementsWithInputValue;
|
||||
const { elementType, elementOptions = [], transformers = [] } = elementWithInputValue;
|
||||
const {
|
||||
inputType,
|
||||
readOnly,
|
||||
@@ -127,26 +128,28 @@
|
||||
editable,
|
||||
valRes,
|
||||
getStringKey,
|
||||
onClick
|
||||
onClick,
|
||||
onChange,
|
||||
customParams,
|
||||
customId
|
||||
} = handleElementOptions('none', elementOptions, transformers, val = "");
|
||||
|
||||
// console.log(setTypeObject);
|
||||
// console.log(inputType);
|
||||
|
||||
// render based on element type
|
||||
if (elementsWithInputValue.elementType === 'select') {
|
||||
if (elementType === 'select') {
|
||||
|
||||
targetLocation = columns[j].Code_Name + "_generateSetOptions"
|
||||
|
||||
generateOptionsOrSetOptions(columns[j].Code_Name, [], targetLocation, generateOptions)
|
||||
|
||||
// Handle Icons as tehy need a preview
|
||||
console.log(columns[j].Code_Name)
|
||||
// Handle Icons as they need a preview
|
||||
if(columns[j].Code_Name == 'NEWDEV_dev_Icon')
|
||||
{
|
||||
input = `
|
||||
<span class="input-group-addon" id="txtIconFA"></span>
|
||||
<span class="input-group-addon iconPreview" my-customid="NEWDEV_dev_Icon_preview"></span>
|
||||
<select class="form-control"
|
||||
onChange="updateIconPreview('#NEWDEV_dev_Icon')"
|
||||
onChange="updateIconPreview(this)"
|
||||
my-customparams="NEWDEV_dev_Icon,NEWDEV_dev_Icon_preview"
|
||||
id="${columns[j].Code_Name}"
|
||||
data-my-column="${columns[j].Code_Name}"
|
||||
data-my-targetColumns="${columns[j].Code_Name.replace('NEWDEV_','')}" >
|
||||
@@ -164,7 +167,7 @@
|
||||
}
|
||||
|
||||
|
||||
} else if (elementsWithInputValue.elementType === 'input'){
|
||||
} else if (elementType === 'input'){
|
||||
|
||||
// Add classes specifically for checkboxes
|
||||
inputType === 'checkbox' ? inputClass = 'checkbox' : inputClass = 'form-control';
|
||||
@@ -172,6 +175,7 @@
|
||||
|
||||
input = `<input class="${inputClass}"
|
||||
id="${columns[j].Code_Name}"
|
||||
my-customid="${columns[j].Code_Name}"
|
||||
data-my-column="${columns[j].Code_Name}"
|
||||
data-my-targetColumns="${columns[j].Code_Name.replace('NEWDEV_','')}"
|
||||
type="${inputType}">`
|
||||
|
||||
@@ -783,7 +783,7 @@
|
||||
setCache(key, target.replaceAll(":","_")+'_id') // _id is added so it doesn't conflict with AdminLTE tab behavior
|
||||
}
|
||||
|
||||
// get the tab id from the cookie (already overriden by the target)
|
||||
// get the tab id from the cookie (already overridden by the target)
|
||||
if(!emptyArr.includes(getCache(key)))
|
||||
{
|
||||
selectedTab = getCache(key);
|
||||
|
||||
@@ -53,8 +53,8 @@
|
||||
<link rel="stylesheet" href="lib/AdminLTE/bower_components/select2/dist/css/select2.min.css">
|
||||
|
||||
<!-- NetAlertX -->
|
||||
<script src="js/handle_version.js"></script>
|
||||
<script defer src="js/ui_components.js?v=<?php include 'php/templates/version.php'; ?>"></script>
|
||||
<script defer src="js/handle_version.js"></script>
|
||||
<script src="js/ui_components.js?v=<?php include 'php/templates/version.php'; ?>"></script>
|
||||
|
||||
|
||||
<!-- Select2 JavaScript -->
|
||||
|
||||
@@ -295,6 +295,7 @@
|
||||
"Gen_Save": "",
|
||||
"Gen_Saved": "",
|
||||
"Gen_Search": "",
|
||||
"Gen_SelectToPreview": "",
|
||||
"Gen_Selected_Devices": "",
|
||||
"Gen_Switch": "",
|
||||
"Gen_Upd": "",
|
||||
|
||||
@@ -307,6 +307,7 @@
|
||||
"Gen_Save": "Speichern",
|
||||
"Gen_Saved": "Gespeichert",
|
||||
"Gen_Search": "Suchen",
|
||||
"Gen_SelectToPreview": "",
|
||||
"Gen_Selected_Devices": "Ausgewählte Geräte:",
|
||||
"Gen_Switch": "Umschalten",
|
||||
"Gen_Upd": "Aktualisierung erfolgreich",
|
||||
@@ -777,4 +778,4 @@
|
||||
"settings_update_item_warning": "",
|
||||
"test_event_icon": "fa-vial-circle-check",
|
||||
"test_event_tooltip": "Save your changes at first before you test your settings."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -295,6 +295,7 @@
|
||||
"Gen_Save": "Save",
|
||||
"Gen_Saved": "Saved",
|
||||
"Gen_Search": "Search",
|
||||
"Gen_SelectToPreview": "Select to preview",
|
||||
"Gen_Selected_Devices": "Selected Devices:",
|
||||
"Gen_Switch": "Switch",
|
||||
"Gen_Upd": "Updated successfully",
|
||||
|
||||
@@ -66,8 +66,8 @@
|
||||
"BackDevices_Restore_okay": "Restauración ejecutado con éxito.",
|
||||
"BackDevices_darkmode_disabled": "Darkmode Desactivado",
|
||||
"BackDevices_darkmode_enabled": "Darkmode Activado",
|
||||
"CLEAR_NEW_FLAG_description": "",
|
||||
"CLEAR_NEW_FLAG_name": "",
|
||||
"CLEAR_NEW_FLAG_description": "Si está habilitado (<code>0</code> está desactivado), los dispositivos marcados como <b>Nuevo dispositivo</b> se desmarcarán si el límite de tiempo (especificado en horas) excede su tiempo de <b>primera sesión</b>.",
|
||||
"CLEAR_NEW_FLAG_name": "Eliminar la nueva bandera",
|
||||
"DAYS_TO_KEEP_EVENTS_description": "Esta es una configuración de mantenimiento. Esto especifica el número de días de entradas de eventos que se guardarán. Todos los eventos anteriores se eliminarán periódicamente.",
|
||||
"DAYS_TO_KEEP_EVENTS_name": "Eliminar eventos anteriores a",
|
||||
"DevDetail_Copy_Device_Title": "<i class=\"fa fa-copy\"></i> Copiar detalles del dispositivo",
|
||||
@@ -305,6 +305,7 @@
|
||||
"Gen_Save": "Guardar",
|
||||
"Gen_Saved": "Guardado",
|
||||
"Gen_Search": "Buscar",
|
||||
"Gen_SelectToPreview": "Seleccionar para previsualizar",
|
||||
"Gen_Selected_Devices": "Dispositivos seleccionados:",
|
||||
"Gen_Switch": "Cambiar",
|
||||
"Gen_Upd": "Actualizado correctamente",
|
||||
@@ -315,8 +316,8 @@
|
||||
"Gen_Work_In_Progress": "Trabajo en curso, un buen momento para hacer comentarios en https://github.com/jokob-sk/NetAlertX/issues",
|
||||
"General_display_name": "General",
|
||||
"General_icon": "<i class=\"fa fa-gears\"></i>",
|
||||
"HRS_TO_KEEP_NEWDEV_description": "Esta es una configuración de mantenimiento. Si está habilitado (<code>0</code> está deshabilitado), los dispositivos marcados como <b>Nuevo dispositivo</b> se eliminarán si su <b>Primera sesión</b> el tiempo era anterior a las horas especificadas en esta configuración. Utilice esta configuración si desea eliminar automáticamente <b>Nuevos dispositivos</b> después de <code>X</code> horas.",
|
||||
"HRS_TO_KEEP_NEWDEV_name": "Guardar nuevos dispositivos para",
|
||||
"HRS_TO_KEEP_NEWDEV_description": "Se trata de una configuración de mantenimiento <b>BORRAR dispositivos</b>. Si está activado (<code>0</code> está desactivado), los dispositivos marcados como <b>Nuevo dispositivo</b> se eliminarán si su fecha de <b>primera sesión</b> es anterior a las horas especificadas en este ajuste. Use este ajuste si desea eliminar automáticamente <b>Nuevos dispositivos</b> después de <code>X</code> horas.",
|
||||
"HRS_TO_KEEP_NEWDEV_name": "Eliminar nuevos dispositivos después",
|
||||
"HelpFAQ_Cat_Detail": "Detalles",
|
||||
"HelpFAQ_Cat_Detail_300_head": "¿Qué significa? ",
|
||||
"HelpFAQ_Cat_Detail_300_text_a": "significa un dispositivo de red (un dispositivo del tipo AP, Gateway, Firewall, Hypervisor, Powerline, Switch, WLAN, PLC, Router,Adaptador LAN USB, Adaptador WIFI USB o Internet). Los tipos personalizados pueden añadirse mediante el ajuste <code>NETWORK_DEVICE_TYPES</code>.",
|
||||
@@ -775,4 +776,4 @@
|
||||
"settings_update_item_warning": "Actualice el valor a continuación. Tenga cuidado de seguir el formato anterior. <b>O la validación no se realiza.</b>",
|
||||
"test_event_icon": "fa-vial-circle-check",
|
||||
"test_event_tooltip": "Guarda tus cambios antes de probar nuevos ajustes."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -295,6 +295,7 @@
|
||||
"Gen_Save": "Enregistrer",
|
||||
"Gen_Saved": "Enregistré",
|
||||
"Gen_Search": "Recherche",
|
||||
"Gen_SelectToPreview": "",
|
||||
"Gen_Selected_Devices": "Appareils sélectionnés :",
|
||||
"Gen_Switch": "Basculer",
|
||||
"Gen_Upd": "Mise à jour réussie",
|
||||
@@ -696,4 +697,4 @@
|
||||
"settings_update_item_warning": "Mettre à jour la valeur ci-dessous. Veillez à bien suivre le même format qu'auparavant. <b>Il n'y a pas de pas de contrôle.</b>",
|
||||
"test_event_icon": "fa-vial-circle-check",
|
||||
"test_event_tooltip": "Enregistrer d'abord vos modifications avant de tester vôtre paramétrage."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -295,6 +295,7 @@
|
||||
"Gen_Save": "Salva",
|
||||
"Gen_Saved": "Salvato",
|
||||
"Gen_Search": "Cerca",
|
||||
"Gen_SelectToPreview": "",
|
||||
"Gen_Selected_Devices": "Dispositivi selezionati:",
|
||||
"Gen_Switch": "Cambia",
|
||||
"Gen_Upd": "Aggiornato correttamente",
|
||||
@@ -696,4 +697,4 @@
|
||||
"settings_update_item_warning": "Aggiorna il valore qui sotto. Fai attenzione a seguire il formato precedente. <b>La convalida non viene eseguita.</b>",
|
||||
"test_event_icon": "fa-vial-circle-check",
|
||||
"test_event_tooltip": "Salva le modifiche prima di provare le nuove impostazioni."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -295,6 +295,7 @@
|
||||
"Gen_Save": "Lagre",
|
||||
"Gen_Saved": "Lagret",
|
||||
"Gen_Search": "Søk",
|
||||
"Gen_SelectToPreview": "",
|
||||
"Gen_Selected_Devices": "Valgte Enheter:",
|
||||
"Gen_Switch": "Bytt",
|
||||
"Gen_Upd": "Oppdatering vellykket",
|
||||
@@ -696,4 +697,4 @@
|
||||
"settings_update_item_warning": "Oppdater verdien nedenfor. Pass på å følge forrige format. <b>Validering etterpå utføres ikke.</b>",
|
||||
"test_event_icon": "fa-vial-circle-check",
|
||||
"test_event_tooltip": "Lagre endringene først, før du tester innstillingene dine."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -295,6 +295,7 @@
|
||||
"Gen_Save": "Zapisz",
|
||||
"Gen_Saved": "Zapisano",
|
||||
"Gen_Search": "Szukaj",
|
||||
"Gen_SelectToPreview": "",
|
||||
"Gen_Selected_Devices": "Wybierz Urządzenia:",
|
||||
"Gen_Switch": "Switch",
|
||||
"Gen_Upd": "Zaktualizowane poprawnie",
|
||||
@@ -696,4 +697,4 @@
|
||||
"settings_update_item_warning": "Zaktualizuj poniższą wartość. Zachowaj ostrożność i postępuj zgodnie z poprzednim formatem. <b>Walidacja nie jest wykonywana.</b>",
|
||||
"test_event_icon": "fa-vial-circle-check",
|
||||
"test_event_tooltip": "Zapisz zmiany zanim będziesz testować swoje ustawienia."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -295,6 +295,7 @@
|
||||
"Gen_Save": "Salvar",
|
||||
"Gen_Saved": "Salvo",
|
||||
"Gen_Search": "Procurar",
|
||||
"Gen_SelectToPreview": "",
|
||||
"Gen_Selected_Devices": "Dispositivos selecionados:",
|
||||
"Gen_Switch": "Trocar",
|
||||
"Gen_Upd": "Atualizado com sucesso",
|
||||
@@ -696,4 +697,4 @@
|
||||
"settings_update_item_warning": "",
|
||||
"test_event_icon": "",
|
||||
"test_event_tooltip": ""
|
||||
}
|
||||
}
|
||||
|
||||
@@ -295,6 +295,7 @@
|
||||
"Gen_Save": "Сохранить",
|
||||
"Gen_Saved": "Сохранено",
|
||||
"Gen_Search": "Поиск",
|
||||
"Gen_SelectToPreview": "",
|
||||
"Gen_Selected_Devices": "Выбранные устройства:",
|
||||
"Gen_Switch": "Переключить",
|
||||
"Gen_Upd": "Успешное обновление",
|
||||
@@ -696,4 +697,4 @@
|
||||
"settings_update_item_warning": "Обновить значение ниже. Будьте осторожны, следуя предыдущему формату. <b>Проверка не выполняется.</b>",
|
||||
"test_event_icon": "fa-vial-circle-check",
|
||||
"test_event_tooltip": "Сначала сохраните изменения, прежде чем проверять настройки."
|
||||
}
|
||||
}
|
||||
|
||||
@@ -295,6 +295,7 @@
|
||||
"Gen_Save": "Kaydet",
|
||||
"Gen_Saved": "Kaydedildi",
|
||||
"Gen_Search": "",
|
||||
"Gen_SelectToPreview": "",
|
||||
"Gen_Selected_Devices": "Seçilmiş Cihazlar:",
|
||||
"Gen_Switch": "",
|
||||
"Gen_Upd": "Başarılı bir şekilde güncellendi",
|
||||
|
||||
@@ -295,6 +295,7 @@
|
||||
"Gen_Save": "保存",
|
||||
"Gen_Saved": "已保存",
|
||||
"Gen_Search": "搜索",
|
||||
"Gen_SelectToPreview": "",
|
||||
"Gen_Selected_Devices": "选定的设备:",
|
||||
"Gen_Switch": "交换",
|
||||
"Gen_Upd": "已成功更新",
|
||||
@@ -696,4 +697,4 @@
|
||||
"settings_update_item_warning": "更新下面的值。请注意遵循先前的格式。<b>未执行验证。</b>",
|
||||
"test_event_icon": "",
|
||||
"test_event_tooltip": "在测试设置之前,请先保存更改。"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -198,7 +198,7 @@
|
||||
"elementOptions": [
|
||||
{ "sourceSuffixes": ["_in"] },
|
||||
{ "separator": "" },
|
||||
{ "cssClasses": "col-sm-2" },
|
||||
{ "cssClasses": "col-xs-12" },
|
||||
{ "onClick": "addList(this, false)" },
|
||||
{ "getStringKey": "Gen_Add" }
|
||||
],
|
||||
@@ -219,7 +219,7 @@
|
||||
"elementOptions": [
|
||||
{ "sourceSuffixes": [] },
|
||||
{ "separator": "" },
|
||||
{ "cssClasses": "col-sm-3" },
|
||||
{ "cssClasses": "col-xs-6" },
|
||||
{ "onClick": "removeFromList(this)" },
|
||||
{ "getStringKey": "Gen_Remove_Last" }
|
||||
],
|
||||
@@ -230,7 +230,7 @@
|
||||
"elementOptions": [
|
||||
{ "sourceSuffixes": [] },
|
||||
{ "separator": "" },
|
||||
{ "cssClasses": "col-sm-3" },
|
||||
{ "cssClasses": "col-xs-6" },
|
||||
{ "onClick": "removeAllOptions(this)" },
|
||||
{ "getStringKey": "Gen_Remove_All" }
|
||||
],
|
||||
@@ -261,7 +261,7 @@
|
||||
"description": [
|
||||
{
|
||||
"language_code": "en_us",
|
||||
"string": "All the newly discovered device names are clened up by applying the following REGEX expression in this order. All the below are replaced by a blank string."
|
||||
"string": "All the newly discovered device names are cleaned up by applying the following REGEX expression in this order. All the below are replaced by a blank string."
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -977,7 +977,27 @@
|
||||
"type": {
|
||||
"dataType": "string",
|
||||
"elements": [
|
||||
{ "elementType": "select", "elementOptions": [], "transformers": [] }
|
||||
{
|
||||
"elementType": "span",
|
||||
"elementOptions": [
|
||||
{ "cssClasses": "input-group-addon iconPreview" },
|
||||
{ "getStringKey": "Gen_SelectToPreview" },
|
||||
{ "customId": "NEWDEV_dev_Icon_preview" }
|
||||
],
|
||||
"transformers": []
|
||||
},
|
||||
{
|
||||
"elementType": "select",
|
||||
"elementHasInputValue": 1,
|
||||
"elementOptions": [
|
||||
{ "cssClasses": "col-xs-12" },
|
||||
{
|
||||
"onChange": "updateIconPreview(this)"
|
||||
},
|
||||
{ "customParams": "NEWDEV_dev_Icon,NEWDEV_dev_Icon_preview" }
|
||||
],
|
||||
"transformers": []
|
||||
}
|
||||
]
|
||||
},
|
||||
"default_value": "",
|
||||
|
||||
@@ -1,41 +1,63 @@
|
||||
## Overview
|
||||
|
||||
Synchronization plugin to synchronize multiple app instances. The Plugin can sychronize 2 types of data:
|
||||
The synchronization plugin is designed to synchronize data across multiple instances of the app. It supports the following data synchronization modes:
|
||||
|
||||
1. 💻 Devices: The plugin sends an encrypted `table_devices.json` file to synchronize the whole Devices DB table.
|
||||
1. 🔌 Plugin data: The plugin sends encrypted `last_result.log` files for individual plugins.
|
||||
1. **💻 Devices**: Sends an encrypted `table_devices.json` file to synchronize the entire Devices database table.
|
||||
2. **🔌 Plugin Data**: Sends encrypted `last_result.log` files for individual plugins.
|
||||
|
||||
> [!TIP]
|
||||
> `[n]` indicates a setting that is usually specified for the node instance. `[n,h]` indicates a setting used both, on the node and on the hub instance.
|
||||
> **Note:** `[n]` indicates a setting specified for the node instance, and `[n,h]` indicates a setting used on both the node and the hub instances.
|
||||
|
||||
### Synchronizing 💻 Devices data or 🔌 Plugins data
|
||||
### Synchronization Modes
|
||||
|
||||
Most of the setups will probably only use 💻 Devices synchronization. 🔌 Plugins data will be probably used in only special use cases.
|
||||
The plugin operates in three different modes based on the configuration settings:
|
||||
|
||||
#### [n] Node (Source) Settings
|
||||
1. **Mode 1: PUSH (NODE)** - Sends data from the node to the hub.
|
||||
- This mode is activated if `SYNC_hub_url` is set and either `SYNC_devices` or `SYNC_plugins` is enabled.
|
||||
- **Actions**:
|
||||
- Sends `table_devices.json` to the hub if `SYNC_devices` is enabled.
|
||||
- Sends individual plugin `last_result.log` files to the hub if `SYNC_plugins` is enabled.
|
||||
|
||||
- When to run [n,h] `SYNC_RUN`
|
||||
- Schedule [n,h] `SYNC_RUN_SCHD`
|
||||
- API token [n,h] `SYNC_api_token`
|
||||
- Encryption Key [n,h] `SYNC_encryption_key`
|
||||
- Node name [n] `SYNC_node_name`
|
||||
- Hub URL [n] `SYNC_hub_url`
|
||||
- Sync Devices [n] `SYNC_devices` or Sync Plugins [n] `SYNC_plugins` (or both)
|
||||
2. **Mode 2: PULL (HUB)** - Retrieves data from nodes to the hub.
|
||||
- This mode is activated if `SYNC_nodes` is set.
|
||||
- **Actions**:
|
||||
- Retrieves data from configured nodes using the API and saves it locally for further processing.
|
||||
|
||||
#### [h] Hub (Target) Settings
|
||||
3. **Mode 3: RECEIVE (HUB)** - Processes received data on the hub.
|
||||
- Activated when data is received in Mode 2 and is ready to be processed.
|
||||
- **Actions**:
|
||||
- Decodes received data files, processes them, and updates the Devices table accordingly.
|
||||
|
||||
- When to run [n,h] `SYNC_RUN`
|
||||
- Schedule [n,h] `SYNC_RUN_SCHD`
|
||||
- API token [n,h] `SYNC_api_token`
|
||||
- Encryption Key [n,h] `SYNC_encryption_key`
|
||||
### Settings
|
||||
|
||||
#### Node (Source) Settings `[n]`
|
||||
|
||||
- **When to Run** `[n,h]`: `SYNC_RUN`
|
||||
- **Schedule** `[n,h]`: `SYNC_RUN_SCHD`
|
||||
- **API Token** `[n,h]`: `SYNC_api_token`
|
||||
- **Encryption Key** `[n,h]`: `SYNC_encryption_key`
|
||||
- **Node Name** `[n]`: `SYNC_node_name`
|
||||
- **Hub URL** `[n]`: `SYNC_hub_url`
|
||||
- **Sync Devices** `[n]`: `SYNC_devices`
|
||||
- **Sync Plugins** `[n]`: `SYNC_plugins`
|
||||
|
||||
#### Hub (Target) Settings `[h]`
|
||||
|
||||
- **When to Run** `[n,h]`: `SYNC_RUN`
|
||||
- **Schedule** `[n,h]`: `SYNC_RUN_SCHD`
|
||||
- **API Token** `[n,h]`: `SYNC_api_token`
|
||||
- **Encryption Key** `[n,h]`: `SYNC_encryption_key`
|
||||
- **Nodes to Pull From** `[h]`: `SYNC_nodes`
|
||||
|
||||
### Usage
|
||||
|
||||
- Head to **Settings** > **Sync Hub** to adjust the default values.
|
||||
1. **Adjust Settings**:
|
||||
- Navigate to **Settings** > **Sync Hub** to modify default settings.
|
||||
2. **Data Flow**:
|
||||
- Nodes send or receive data based on the specified modes, either pushing data to the hub or pulling from nodes.
|
||||
|
||||
### Notes
|
||||
|
||||
- If a MAC address already exists on the hub, the device will be skipped in the data coming from this SYNC plugin.
|
||||
- Existing devices on the hub will not be updated by the data received from this SYNC plugin if their MAC addresses are already present.
|
||||
- It is recommended to use Device synchronization primarily. Plugin data synchronization is more suitable for specific use cases.
|
||||
|
||||

|
||||

|
||||
|
||||
@@ -163,16 +163,15 @@
|
||||
},
|
||||
"maxLength": 50,
|
||||
"default_value": [
|
||||
"PGkgY2xhc3M9J2ZhIGZhLXdpZmknPjwvaT4=",
|
||||
"PGkgY2xhc3M9ImZhIGZhLWNvbXB1dGVyIj48L2k+",
|
||||
"PGkgY2xhc3M9ImZhIGZhLWV0aGVybmV0Ij48L2k+",
|
||||
"PGkgY2xhc3M9ImZhIGZhLWdhbWVwYWQiPjwvaT4",
|
||||
"PGkgY2xhc3M9ImZhIGZhLWdhbWVwYWQiPjwvaT4=",
|
||||
"PGkgY2xhc3M9ImZhIGZhLWdsb2JlIj48L2k+",
|
||||
"PGkgY2xhc3M9ImZhIGZhLWxhcHRvcCI+PC9pPg==",
|
||||
"PGkgY2xhc3M9ImZhIGZhLWxpZ2h0YnVsYiI+PC9pPg==",
|
||||
"PGkgY2xhc3M9ImZhIGZhLXNoaWVsZCI+PC9pPg==",
|
||||
"PGkgY2xhc3M9ImZhIGZhLXdpZmkiPjwvaT4",
|
||||
"PGkgY2xhc3M9J2ZhIGZhLWdhbWVwYWQnPjwvaT4"
|
||||
"PGkgY2xhc3M9ImZhIGZhLXdpZmkiPjwvaT4=",
|
||||
"PGkgY2xhc3M9ImZhIGZhLWdhbWVwYWQiPjwvaT4="
|
||||
],
|
||||
"options": [],
|
||||
"localized": [],
|
||||
|
||||
@@ -42,7 +42,7 @@ def main():
|
||||
fake_mac = string_to_mac_hash(fake_dev)
|
||||
|
||||
plugin_objects.add_object(
|
||||
primaryId=fake_dev, # MAC (Device Name)
|
||||
primaryId=fake_mac, # MAC (Device Name)
|
||||
secondaryId="0.0.0.0", # IP Address (always 0.0.0.0)
|
||||
watched1=fake_dev, # Device Name
|
||||
watched2="",
|
||||
|
||||
@@ -99,7 +99,7 @@
|
||||
// Function to update the displayed data and timestamp based on the selected format and index
|
||||
function updateData(format, index) {
|
||||
// Fetch data from the API endpoint
|
||||
fetch('/api/table_notifications.json?nocache=' + Date.now())
|
||||
fetch('api/table_notifications.json?nocache=' + Date.now())
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (index < 0) {
|
||||
@@ -163,7 +163,7 @@
|
||||
const urlParams = new URLSearchParams(window.location.search);
|
||||
if (urlParams.has('guid')) {
|
||||
const guid = urlParams.get('guid');
|
||||
fetch('/api/table_notifications.json')
|
||||
fetch('api/table_notifications.json')
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
const index = findIndexByGUID(data.data, guid);
|
||||
|
||||
@@ -378,6 +378,7 @@ $settingsJSON_DB = json_encode($settings, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX
|
||||
|
||||
const valIn = set['Value'];
|
||||
const codeName = set['Code_Name'];
|
||||
const overriddenByEnv = set['OverriddenByEnv'] == 1;
|
||||
const setType = set['Type'];
|
||||
const isMetadata = codeName.includes('__metadata');
|
||||
// is this isn't a metadata entry, get corresponding metadata object from the dummy setting
|
||||
@@ -416,11 +417,11 @@ $settingsJSON_DB = json_encode($settings, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX
|
||||
<div class="table_cell setting_description">
|
||||
${getString(codeName + '_description', set['Description'])}
|
||||
</div>
|
||||
<div class="table_cell setting_input input-group col-sm-12">
|
||||
<div class="table_cell input-group setting_input ${overriddenByEnv ? "setting_overriden_by_env" : ""} input-group col-sm-12">
|
||||
`;
|
||||
|
||||
// OVERRIDE
|
||||
// surface settings override functionality if the setting is a template that can be overriden with user defined values
|
||||
// surface settings override functionality if the setting is a template that can be overridden with user defined values
|
||||
// if the setting is a json of the correct structure, handle like a template setting
|
||||
|
||||
let overrideHtml = "";
|
||||
@@ -428,7 +429,7 @@ $settingsJSON_DB = json_encode($settings, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX
|
||||
//pre-check if this is a json object that needs value extraction
|
||||
|
||||
let overridable = false; // indicates if the setting is overridable
|
||||
let override = false; // If the setting is set to be overriden by the user or by default
|
||||
let override = false; // If the setting is set to be overridden by the user or by default
|
||||
let readonly = ""; // helper variable to make text input readonly
|
||||
let disabled = ""; // helper variable to make checkbox input readonly
|
||||
|
||||
@@ -485,7 +486,10 @@ $settingsJSON_DB = json_encode($settings, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX
|
||||
editable,
|
||||
valRes,
|
||||
getStringKey,
|
||||
onClick
|
||||
onClick,
|
||||
onChange,
|
||||
customParams,
|
||||
customId
|
||||
} = handleElementOptions(codeName, elementOptions, transformers, valIn);
|
||||
|
||||
// override
|
||||
@@ -498,7 +502,15 @@ $settingsJSON_DB = json_encode($settings, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX
|
||||
let addCss = isOrdeable ? "select2 select2-hidden-accessible" : "";
|
||||
|
||||
|
||||
inputHtml += `<select onChange="settingsChanged()" my-data-type="${dataType}" my-editable="${editable}" class="form-control ${addCss}" name="${codeName}" id="${codeName}" ${multi}>
|
||||
inputHtml += `<select onChange="settingsChanged();${onChange}"
|
||||
my-data-type="${dataType}"
|
||||
my-editable="${editable}"
|
||||
class="form-control ${addCss}"
|
||||
name="${codeName}"
|
||||
id="${codeName}"
|
||||
my-customparams="${customParams}"
|
||||
my-customid="${customId}"
|
||||
${multi}>
|
||||
<option value="" id="${codeName + "_temp_"}"></option>
|
||||
</select>`;
|
||||
|
||||
@@ -513,8 +525,10 @@ $settingsJSON_DB = json_encode($settings, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX
|
||||
inputHtml += `
|
||||
<input
|
||||
class="${inputClass} ${cssClasses}"
|
||||
onChange="settingsChanged()"
|
||||
onChange="settingsChanged();${onChange}"
|
||||
my-data-type="${dataType}"
|
||||
my-customparams="${customParams}"
|
||||
my-customid="${customId}"
|
||||
id="${codeName}${suffix}"
|
||||
type="${inputType}"
|
||||
value="${val}"
|
||||
@@ -529,6 +543,8 @@ $settingsJSON_DB = json_encode($settings, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX
|
||||
inputHtml += `
|
||||
<button
|
||||
class="btn btn-primary ${cssClasses}"
|
||||
my-customparams="${customParams}"
|
||||
my-customid="${customId}"
|
||||
my-input-from="${sourceIds}"
|
||||
my-input-to="${codeName}"
|
||||
onclick="${onClick}">
|
||||
@@ -539,12 +555,25 @@ $settingsJSON_DB = json_encode($settings, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX
|
||||
inputHtml += `
|
||||
<textarea
|
||||
class="form-control input"
|
||||
my-customparams="${customParams}"
|
||||
my-customid="${customId}"
|
||||
my-data-type="${dataType}"
|
||||
id="${codeName}"
|
||||
${readOnly}>
|
||||
${val}
|
||||
</textarea>`;
|
||||
break;
|
||||
case 'span':
|
||||
inputHtml += `
|
||||
<span
|
||||
class="${cssClasses}"
|
||||
my-data-type="${dataType}"
|
||||
my-customparams="${customParams}"
|
||||
my-customid="${customId}"
|
||||
>
|
||||
${getString(getStringKey)}
|
||||
</span>`;
|
||||
break;
|
||||
|
||||
default:
|
||||
console.warn(`🟥Unknown element type: ${elementType}`);
|
||||
@@ -640,17 +669,21 @@ $settingsJSON_DB = json_encode($settings, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX
|
||||
// console.log(setTypeObject);
|
||||
|
||||
const dataType = setTypeObject.dataType;
|
||||
|
||||
|
||||
// get the element with the input value(s)
|
||||
let elementsWithInputValue = setTypeObject.elements.filter(element => element.elementHasInputValue === 1);
|
||||
let elements = setTypeObject.elements.filter(element => element.elementHasInputValue === 1);
|
||||
|
||||
// if none found, take last
|
||||
if(elementsWithInputValue.length == 0)
|
||||
if(elements.length == 0)
|
||||
{
|
||||
elementsWithInputValue = setTypeObject.elements[setTypeObject.elements.length - 1]
|
||||
elementWithInputValue = setTypeObject.elements[setTypeObject.elements.length - 1]
|
||||
} else
|
||||
{
|
||||
elementWithInputValue = elements[0]
|
||||
}
|
||||
|
||||
const { elementType, elementOptions = [], transformers = [] } = elementsWithInputValue;
|
||||
const { elementType, elementOptions = [], transformers = [] } = elementWithInputValue;
|
||||
const {
|
||||
inputType,
|
||||
readOnly,
|
||||
@@ -664,7 +697,10 @@ $settingsJSON_DB = json_encode($settings, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX
|
||||
editable,
|
||||
valRes,
|
||||
getStringKey,
|
||||
onClick
|
||||
onClick,
|
||||
onChange,
|
||||
customParams,
|
||||
customId
|
||||
} = handleElementOptions('none', elementOptions, transformers, val = "");
|
||||
|
||||
let value;
|
||||
|
||||
@@ -60,7 +60,7 @@ require 'php/templates/header.php';
|
||||
<script>
|
||||
function fetchData(callback) {
|
||||
$.ajax({
|
||||
url: '/api/user_notifications.json?nocache=' + Date.now(),
|
||||
url: 'api/user_notifications.json?nocache=' + Date.now(),
|
||||
method: 'GET',
|
||||
dataType: 'json',
|
||||
success: function(response) {
|
||||
|
||||
@@ -213,15 +213,16 @@ class DB():
|
||||
self.sql.execute(""" DROP TABLE IF EXISTS Settings;""")
|
||||
self.sql.execute("""
|
||||
CREATE TABLE "Settings" (
|
||||
"Code_Name" TEXT,
|
||||
"Display_Name" TEXT,
|
||||
"Description" TEXT,
|
||||
"Type" TEXT,
|
||||
"Options" TEXT,
|
||||
"RegEx" TEXT,
|
||||
"Value" TEXT,
|
||||
"Group" TEXT,
|
||||
"Events" TEXT
|
||||
"Code_Name" TEXT,
|
||||
"Display_Name" TEXT,
|
||||
"Description" TEXT,
|
||||
"Type" TEXT,
|
||||
"Options" TEXT,
|
||||
"RegEx" TEXT,
|
||||
"Group" TEXT,
|
||||
"Value" TEXT,
|
||||
"Events" TEXT,
|
||||
"OverriddenByEnv" INTEGER
|
||||
);
|
||||
""")
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ import subprocess
|
||||
import conf
|
||||
import os
|
||||
import re
|
||||
from helper import timeNowTZ, get_setting, get_setting_value, list_to_where, resolve_device_name_dig, resolve_device_name_pholus, get_device_name_nbtlookup, get_device_name_nslookup, check_IP_format
|
||||
from helper import timeNowTZ, get_setting, get_setting_value, list_to_where, resolve_device_name_dig, resolve_device_name_pholus, get_device_name_nbtlookup, get_device_name_nslookup, check_IP_format, sanitize_SQL_input
|
||||
from logger import mylog, print_log
|
||||
from const import vendorsPath, vendorsPathNewest, sql_generateGuid
|
||||
|
||||
@@ -192,12 +192,12 @@ def create_new_devices (db):
|
||||
{get_setting_value('NEWDEV_dev_NewDevice')},
|
||||
{get_setting_value('NEWDEV_dev_SkipRepeated')},
|
||||
{get_setting_value('NEWDEV_dev_ScanCycle')},
|
||||
'{get_setting_value('NEWDEV_dev_Owner')}',
|
||||
'{sanitize_SQL_input(get_setting_value('NEWDEV_dev_Owner'))}',
|
||||
{get_setting_value('NEWDEV_dev_Favorite')},
|
||||
'{get_setting_value('NEWDEV_dev_Group')}',
|
||||
'{get_setting_value('NEWDEV_dev_Comments')}',
|
||||
'{sanitize_SQL_input(get_setting_value('NEWDEV_dev_Group'))}',
|
||||
'{sanitize_SQL_input(get_setting_value('NEWDEV_dev_Comments'))}',
|
||||
{get_setting_value('NEWDEV_dev_LogEvents')},
|
||||
'{get_setting_value('NEWDEV_dev_Location')}'"""
|
||||
'{sanitize_SQL_input(get_setting_value('NEWDEV_dev_Location'))}'"""
|
||||
|
||||
# Fetch data from CurrentScan
|
||||
current_scan_data = sql.execute("SELECT cur_MAC, cur_Name, cur_Vendor, cur_IP, cur_SyncHubNodeName, cur_NetworkNodeMAC, cur_PORT, cur_NetworkSite, cur_SSID, cur_Type FROM CurrentScan").fetchall()
|
||||
@@ -232,19 +232,19 @@ def create_new_devices (db):
|
||||
)
|
||||
VALUES
|
||||
(
|
||||
'{cur_MAC}',
|
||||
'{cur_Name}',
|
||||
'{cur_Vendor}',
|
||||
'{cur_IP}',
|
||||
'{sanitize_SQL_input(cur_MAC)}',
|
||||
'{sanitize_SQL_input(cur_Name)}',
|
||||
'{sanitize_SQL_input(cur_Vendor)}',
|
||||
'{sanitize_SQL_input(cur_IP)}',
|
||||
?,
|
||||
?,
|
||||
'{cur_SyncHubNodeName}',
|
||||
'{sanitize_SQL_input(cur_SyncHubNodeName)}',
|
||||
{sql_generateGuid},
|
||||
'{cur_NetworkNodeMAC}',
|
||||
'{cur_PORT}',
|
||||
'{cur_NetworkSite}',
|
||||
'{cur_SSID}',
|
||||
'{cur_Type}',
|
||||
'{sanitize_SQL_input(cur_NetworkNodeMAC)}',
|
||||
'{sanitize_SQL_input(cur_PORT)}',
|
||||
'{sanitize_SQL_input(cur_NetworkSite)}',
|
||||
'{sanitize_SQL_input(cur_SSID)}',
|
||||
'{sanitize_SQL_input(cur_Type)}',
|
||||
{newDevDefaults}
|
||||
)"""
|
||||
|
||||
|
||||
@@ -319,7 +319,7 @@ def get_setting_value(key):
|
||||
set_type = 'Error: Not handled'
|
||||
set_value = 'Error: Not handled'
|
||||
|
||||
set_value = setting["Value"] # Setting value (Value (upper case) = user overriden default_value)
|
||||
set_value = setting["Value"] # Setting value (Value (upper case) = user overridden default_value)
|
||||
set_type = setting["Type"] # Setting type # lower case "type" - default json value vs uppper-case "Type" (= from user defined settings)
|
||||
|
||||
value = setting_value_to_python_type(set_type, set_value)
|
||||
@@ -806,6 +806,13 @@ def sanitize_string(input):
|
||||
return input
|
||||
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
def sanitize_SQL_input(val):
|
||||
if val is None:
|
||||
return ''
|
||||
return val.replace("'", "_")
|
||||
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
def generate_mac_links (html, deviceUrl):
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ from notification import write_notification
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
# managing application settings, ensuring SQL safety for user input, and updating internal configuration lists
|
||||
def ccd(key, default, config_dir, name, inputtype, options, group, events=None, desc="", regex="", setJsonMetadata=None, overrideTemplate=None, forceDefault=False):
|
||||
def ccd(key, default, config_dir, name, inputtype, options, group, events=None, desc="", regex="", setJsonMetadata=None, overrideTemplate=None, forceDefault=False, overriddenByEnv=0):
|
||||
if events is None:
|
||||
events = []
|
||||
if setJsonMetadata is None:
|
||||
@@ -50,8 +50,8 @@ def ccd(key, default, config_dir, name, inputtype, options, group, events=None,
|
||||
result = result.replace('\'', "{s-quote}")
|
||||
|
||||
# Create the tuples
|
||||
sql_safe_tuple = (key, name, desc, str(inputtype), options, regex, str(result), group, str(events))
|
||||
settings_tuple = (key, name, desc, inputtype, options, regex, result, group, str(events))
|
||||
sql_safe_tuple = (key, name, desc, str(inputtype), options, regex, str(result), group, str(events), overriddenByEnv)
|
||||
settings_tuple = (key, name, desc, inputtype, options, regex, result, group, str(events), overriddenByEnv)
|
||||
|
||||
# Update or append the tuples in the lists
|
||||
conf.mySettingsSQLsafe = update_or_append(conf.mySettingsSQLsafe, sql_safe_tuple, key)
|
||||
@@ -59,7 +59,7 @@ def ccd(key, default, config_dir, name, inputtype, options, group, events=None,
|
||||
|
||||
# Save metadata in dummy setting if not a metadata key
|
||||
if '__metadata' not in key:
|
||||
metadata_tuple = (f'{key}__metadata', "metadata name", "metadata desc", '{"dataType":"json", "elements": [{"elementType" : "textarea", "elementOptions" : [{"readonly": "true"}] ,"transformers": []}]}', '[]', "", json.dumps(setJsonMetadata), group, '[]')
|
||||
metadata_tuple = (f'{key}__metadata', "metadata name", "metadata desc", '{"dataType":"json", "elements": [{"elementType" : "textarea", "elementOptions" : [{"readonly": "true"}] ,"transformers": []}]}', '[]', "", json.dumps(setJsonMetadata), group, '[]', overriddenByEnv)
|
||||
conf.mySettingsSQLsafe = update_or_append(conf.mySettingsSQLsafe, metadata_tuple, f'{key}__metadata')
|
||||
conf.mySettings = update_or_append(conf.mySettings, metadata_tuple, f'{key}__metadata')
|
||||
|
||||
@@ -298,7 +298,7 @@ def importConfigs (db, all_plugins):
|
||||
# -----------------
|
||||
# Plugins END
|
||||
|
||||
# TODO check app_conf_override.json
|
||||
# HANDLE APP_CONF_OVERRIDE via app_conf_override.json
|
||||
# Assuming fullConfFolder is defined elsewhere
|
||||
app_conf_override_path = fullConfFolder + '/app_conf_override.json'
|
||||
|
||||
@@ -315,12 +315,12 @@ def importConfigs (db, all_plugins):
|
||||
# Log the value being passed
|
||||
# ccd(key, default, config_dir, name, inputtype, options, group, events=None, desc="", regex="", setJsonMetadata=None, overrideTemplate=None, forceDefault=False)
|
||||
mylog('debug', [f"[Config] Setting override {setting_name} with value: {value}"])
|
||||
ccd(setting_name, value, c_d, '_KEEP_', '_KEEP_', '_KEEP_', '_KEEP_', None, "_KEEP_", "", None, None, True)
|
||||
ccd(setting_name, value, c_d, '_KEEP_', '_KEEP_', '_KEEP_', '_KEEP_', None, "_KEEP_", "", None, None, True, 1)
|
||||
else:
|
||||
# Convert to string and log
|
||||
# ccd(key, default, config_dir, name, inputtype, options, group, events=None, desc="", regex="", setJsonMetadata=None, overrideTemplate=None, forceDefault=False)
|
||||
mylog('debug', [f"[Config] Setting override {setting_name} with value: {str(value)}"])
|
||||
ccd(setting_name, str(value), c_d, '_KEEP_', '_KEEP_', '_KEEP_', '_KEEP_', None, "_KEEP_", "", None, None, True)
|
||||
ccd(setting_name, str(value), c_d, '_KEEP_', '_KEEP_', '_KEEP_', '_KEEP_', None, "_KEEP_", "", None, None, True, 1)
|
||||
|
||||
except json.JSONDecodeError:
|
||||
mylog('none', [f"[Config] [ERROR] Setting override decoding JSON from {app_conf_override_path}"])
|
||||
@@ -348,8 +348,9 @@ def importConfigs (db, all_plugins):
|
||||
|
||||
# Insert settings into the DB
|
||||
sql.execute ("DELETE FROM Settings")
|
||||
# mylog('debug', [f"[Config] conf.mySettingsSQLsafe : '{conf.mySettingsSQLsafe}'"])
|
||||
sql.executemany ("""INSERT INTO Settings ("Code_Name", "Display_Name", "Description", "Type", "Options",
|
||||
"RegEx", "Value", "Group", "Events" ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)""", conf.mySettingsSQLsafe)
|
||||
"RegEx", "Value", "Group", "Events", "OverriddenByEnv" ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)""", conf.mySettingsSQLsafe)
|
||||
|
||||
db.commitDB()
|
||||
|
||||
|
||||
@@ -248,7 +248,11 @@ def execute_plugin(db, all_plugins, plugin, pluginsState = plugins_state() ):
|
||||
|
||||
for line in newLines:
|
||||
columns = line.split("|")
|
||||
# There have to be 9 or 13 columns
|
||||
# There have to be 9 or 13 columns
|
||||
if len(columns) not in [9, 13]:
|
||||
mylog('none', [f'[Plugins] Wrong number of input values, must be 9 or 13, got {len(columns)} from: {line}'])
|
||||
continue # Skip lines with incorrect number of columns
|
||||
|
||||
# Common part of the SQL parameters
|
||||
base_params = [
|
||||
0, # "Index" placeholder
|
||||
@@ -284,8 +288,6 @@ def execute_plugin(db, all_plugins, plugin, pluginsState = plugins_state() ):
|
||||
'null', # "HelpVal3"
|
||||
'null' # "HelpVal4"
|
||||
])
|
||||
else:
|
||||
mylog('none', [f'[Plugins] Wrong number of input values, must be 9 or 13, got {len(columns)} from: {line} '])
|
||||
|
||||
# Create a tuple containing values to be inserted into the database.
|
||||
# Each value corresponds to a column in the table in the order of the columns.
|
||||
|
||||
Reference in New Issue
Block a user