diff --git a/front/js/common.js b/front/js/common.js
index 85b0aeb7..e2e71e36 100755
--- a/front/js/common.js
+++ b/front/js/common.js
@@ -144,7 +144,16 @@ function cacheSettings()
if(options_params != [])
{
// handles only strings of length == 1
- resolvedOptions = `["${resolveParams(options_params, resolvedOptions[0])}"]`
+
+ resolved = resolveParams(options_params, resolvedOptions[0])
+
+ if(resolved.includes('"')) // check if list of strings
+ {
+ resolvedOptions = `[${resolved}]`
+ } else // one value only
+ {
+ resolvedOptions = `["${resolved}"]`
+ }
}
}
}
@@ -1027,8 +1036,18 @@ function resolveParams(params, template) {
// If the parameter type is 'setting', retrieve setting value
if (param.type == "setting") {
var value = getSetting(param.value);
+
+ // Remove brackets and single quotes, replace them with double quotes
+ value = value.replace('[','').replace(']','').replace(/'/g, '"');
+
+ // Split the string into an array, remove empty elements
+ const arr = value.split(',').filter(Boolean);
+
+ // Join the array elements with commas
+ const result = arr.join(', ');
+
// Replace placeholder with setting value
- template = template.replace("{" + param.name + "}", value);
+ template = template.replace("{" + param.name + "}", result);
} else {
// If the parameter type is not 'setting', use the provided value
template = template.replace("{" + param.name + "}", param.value);
diff --git a/front/js/settings_utils.js b/front/js/settings_utils.js
index ffd9641b..d431194a 100755
--- a/front/js/settings_utils.js
+++ b/front/js/settings_utils.js
@@ -539,6 +539,7 @@ function getParam(targetId, key, skipCache = false) {
// optionsArray = getSettingOptions(set['Code_Name'] )
valuesArray = createArray(set['Value']);
+
// create unique ID
var targetLocation = set['Code_Name'] + "_initSettingDropdown";
diff --git a/front/js/ui_components.js b/front/js/ui_components.js
index f78b4aeb..c362cfa1 100755
--- a/front/js/ui_components.js
+++ b/front/js/ui_components.js
@@ -95,9 +95,29 @@ function initSettingDropdown(settingKey, // Identifier for the setting
{
var optionsHtml = ""
+
+
+ if(settingKey == 'SYNC_plugins' || settingKey == 'VNDRPDT_WATCH')
+ {
+ console.log('getSettingOptions(settingKey)');
+ console.log(getSettingOptions(settingKey));
+
+ }
+
optionsArray = createArray(getSettingOptions(settingKey))
+
+ if(settingKey == 'SYNC_plugins' || settingKey == 'VNDRPDT_WATCH')
+ {
+ console.log('settingKey');
+ console.log(settingKey);
+ console.log('valuesArray');
+ console.log(valuesArray);
+ console.log('optionsArray');
+ console.log(optionsArray);
+ }
+
// check if the result is a SQL query
if(isSQLQuery(optionsArray[0]))
{
diff --git a/front/plugins/node_sync/README.md b/front/plugins/node_sync/README.md
deleted file mode 100755
index b6e106be..00000000
--- a/front/plugins/node_sync/README.md
+++ /dev/null
@@ -1,13 +0,0 @@
-## Overview
-
-A simple setting-based plugin for setting the password.
-
-### Usage
-
-- Head to **Settings** > **Set Password** to adjust the default values.
-
-### Notes
-
-- The default password is 123456.
-- When enabled a login dialog is displayed. If facing issues, you can always disable the login by setting SETPWD_enable_password=False in your app.conf file.
-- SHA256 hash is used
\ No newline at end of file
diff --git a/front/plugins/node_sync/config.json b/front/plugins/node_sync/config.json
deleted file mode 100755
index ac5736be..00000000
--- a/front/plugins/node_sync/config.json
+++ /dev/null
@@ -1,259 +0,0 @@
-{
- "code_name": "node_sync",
- "template_type": "database-entry",
- "unique_prefix": "NODESYNC",
- "plugin_type": "system",
- "enabled": true,
- "data_source": "template",
- "show_ui": false,
- "localized": ["display_name", "description", "icon"],
- "display_name": [{
- "language_code": "en_us",
- "string": "Node synchronization"
- }],
- "description": [{
- "language_code": "en_us",
- "string": "Plugin to synchronize multiple NetAlertX instances."
- }],
- "icon": [
- {
- "language_code": "en_us",
- "string": ""
- }
- ],
- "params" : [
- {
- "name" : "password",
- "type" : "setting",
- "value" : "SETPWD_password"
- }
- ],
- "settings":[
- {
- "function": "enable",
- "type": "boolean",
- "default_value": false,
- "options": [],
- "localized": ["name", "description"],
- "name": [
- {
- "language_code": "en_us",
- "string": "Enable login"
- }
- ],
- "description": [
- {
- "language_code": "en_us",
- "string": "When enabled a login dialog is displayed. If facing issues, you can always disable the login by setting SETPWD_enable_password=False in your app.conf file."
- }
- ]
- },
- {
- "function": "plugins",
- "type": "text.multiselect",
- "maxLength": 50,
- "default_value": [
- ],
- "options": ["{value}"],
- "options_params" : [
- {
- "name" : "value",
- "type" : "setting",
- "value" : "LOADED_PLUGINS"
- }
- ],
- "localized": ["name", "description"],
- "name": [
- {
- "language_code": "en_us",
- "string": "Plugins"
- }
- ],
- "description": [
- {
- "language_code": "en_us",
- "string": "Plugins to synchronize. Only plugins that produce some kind of output in last_result.log can be synchronized."
- }
- ]
- },
- {
- "function": "target_url",
- "type": "text",
- "maxLength": 50,
- "default_value": "",
- "options": [],
- "localized": ["name", "description"],
- "name": [
- {
- "language_code": "en_us",
- "string": "Target URL"
- }
- ],
- "description": [
- {
- "language_code": "en_us",
- "string": "Target URL to send the data to."
- }
- ]
- },
- {
- "function": "password",
- "type": "password.SHA256",
- "maxLength": 50,
- "default_value": "",
- "options": [],
- "localized": ["name", "description"],
- "name": [
- {
- "language_code": "en_us",
- "string": "Password"
- },
- {
- "language_code": "es_es",
- "string": "Contraseña"
- }
- ],
- "description": [
- {
- "language_code": "en_us",
- "string": "The password "
- }
- ]
- },{
- "function": "RUN",
- "events": ["run"],
- "type": "text.select",
- "default_value":"schedule",
- "options": ["disabled", "once", "schedule", "always_after_scan", "on_new_device"],
- "localized": ["name", "description"],
- "name" :[{
- "language_code":"en_us",
- "string" : "When to run"
- },
- {
- "language_code":"es_es",
- "string" : "Cuándo ejecutar"
- },
- {
- "language_code":"de_de",
- "string" : "Wann laufen"
- }],
- "description": [{
- "language_code":"en_us",
- "string" : "When the backup should be created. A daily or weekly SCHEDULE is a good option."
- },
- {
- "language_code":"es_es",
- "string" : "Cuándo se debe crear la copia de seguridad. Un SCHEDULE diario o semanal es una buena opción."
- },
- {
- "language_code":"de_de",
- "string" : "Wann das Backup erstellt werden soll. Ein täglicher oder wöchentlicher SCHEDULE ist eine gute Option."
- }]
-},
- {
- "function": "CMD",
- "type": "readonly",
- "default_value": "python3 /app/front/plugins/csv_backup/script.py overwrite={overwrite} location={location}",
- "options": [],
- "localized": ["name", "description"],
- "name": [
- {
- "language_code": "en_us",
- "string": "Command"
- },
- {
- "language_code": "es_es",
- "string": "Comando"
- },
- {
- "language_code": "de_de",
- "string": "Befehl"
- }
- ],
- "description": [
- {
- "language_code": "en_us",
- "string": "Command to run. This can not be changed"
- },
- {
- "language_code": "es_es",
- "string": "Comando a ejecutar. Esto no se puede cambiar"
- },
- {
- "language_code": "de_de",
- "string": "Befehl zum Ausführen. Dies kann nicht geändert werden"
- }
- ]
- },
- {
- "function": "RUN_SCHD",
- "type": "text",
- "default_value":"0 2 * * 3",
- "options": [],
- "localized": ["name", "description"],
- "name" : [{
- "language_code":"en_us",
- "string" : "Schedule"
- },
- {
- "language_code":"es_es",
- "string" : "Schedule"
- },
- {
- "language_code":"de_de",
- "string" : "Schedule"
- }],
- "description": [{
- "language_code":"en_us",
- "string" : "Only enabled if you select schedule in the CSVBCKP_RUN setting. Make sure you enter the schedule in the correct cron-like format (e.g. validate at crontab.guru). For example entering 0 4 * * * will run the scan after 4 am in the TIMEZONE you set above. Will be run NEXT time the time passes."
- },
- {
- "language_code":"es_es",
- "string" : "Solo está habilitado si selecciona schedule en la configuración CSVBCKP_RUN. Asegúrese de ingresar la programación en el formato similar a cron correcto (por ejemplo, valide en crontab.guru). Por ejemplo, ingresar 0 4 * * * ejecutará el escaneo después de las 4 a.m. en el TIMEZONE código> que configuró arriba. Se ejecutará la PRÓXIMA vez que pase el tiempo."
- },
- {
- "language_code":"de_de",
- "string" : "Nur aktiviert, wenn Sie schedule in der CSVBCKP_RUN-Einstellung auswählen. Stellen Sie sicher, dass Sie den Zeitplan im richtigen Cron-ähnlichen Format eingeben (z. B. validieren unter crontab.guru). Wenn Sie beispielsweise 0 4 * * * eingeben, wird der Scan nach 4 Uhr morgens in der TIMEZONE ausgeführt. Code> den Sie oben festgelegt haben. Wird das NÄCHSTE Mal ausgeführt, wenn die Zeit vergeht."
- }]
- },
- {
- "function": "RUN_TIMEOUT",
- "type": "integer",
- "default_value": 30,
- "options": [],
- "localized": ["name", "description"],
- "name": [
- {
- "language_code": "en_us",
- "string": "Run timeout"
- },
- {
- "language_code": "es_es",
- "string": "Tiempo límite de ejecución"
- },
- {
- "language_code": "de_de",
- "string": "Zeitüberschreitung"
- }
- ],
- "description": [
- {
- "language_code": "en_us",
- "string": "Maximum time in seconds to wait for the script to finish. If this time is exceeded the script is aborted."
- },
- {
- "language_code": "es_es",
- "string": "Tiempo máximo en segundos para esperar a que finalice el script. Si se supera este tiempo, el script se cancela."
- },
- {
- "language_code": "de_de",
- "string": "Maximale Zeit in Sekunden, die auf den Abschluss des Skripts gewartet werden soll. Bei Überschreitung dieser Zeit wird das Skript abgebrochen."
- }
- ]
- }
-
-
- ]
-
- }
diff --git a/front/plugins/node_sync/ignore_plugin b/front/plugins/node_sync/ignore_plugin
deleted file mode 100755
index e69de29b..00000000
diff --git a/front/plugins/node_sync/script.py b/front/plugins/node_sync/script.py
deleted file mode 100755
index 97ef2081..00000000
--- a/front/plugins/node_sync/script.py
+++ /dev/null
@@ -1,100 +0,0 @@
-#!/usr/bin/env python
-
-import os
-import pathlib
-import argparse
-import sys
-import hashlib
-import csv
-import sqlite3
-from io import StringIO
-from datetime import datetime
-
-# Register NetAlertX directories
-INSTALL_PATH="/app"
-sys.path.extend([f"{INSTALL_PATH}/front/plugins", f"{INSTALL_PATH}/server"])
-
-from plugin_helper import Plugin_Object, Plugin_Objects, decodeBase64
-from logger import mylog, append_line_to_file
-from helper import timeNowTZ
-from const import logPath, applicationPath, fullDbPath
-
-
-CUR_PATH = str(pathlib.Path(__file__).parent.resolve())
-LOG_FILE = os.path.join(CUR_PATH, 'script.log')
-RESULT_FILE = os.path.join(CUR_PATH, 'last_result.log')
-
-pluginName = 'NODESYNC'
-
-def main():
-
- # the script expects a parameter in the format of devices=device1,device2,...
- parser = argparse.ArgumentParser(description='Export devices data to CSV')
- parser.add_argument('overwrite', action="store", help="Specify 'TRUE' to overwrite an existing file, or 'FALSE' to create a new file")
- parser.add_argument('location', action="store", help="The directory where the CSV file will be saved")
- values = parser.parse_args()
-
- overwrite = values.overwrite.split('=')[1]
-
- if (overwrite.upper() == "TRUE"):
- overwrite = True
- else:
- overwrite = False
-
- mylog('verbose', [f'[{pluginName}] In script'])
-
- # Connect to the App database
- conn = sqlite3.connect(fullDbPath)
- cursor = conn.cursor()
-
- # Execute your SQL query
- cursor.execute("SELECT * FROM Devices")
-
- # Get column names
- columns = [desc[0] for desc in cursor.description]
-
- if overwrite:
- filename = 'devices.csv'
- else:
- timestamp = datetime.now().strftime('%Y%m%d%H%M%S')
- filename = f'devices_{timestamp}.csv'
-
- fullPath = os.path.join(values.location.split('=')[1], filename)
-
- mylog('verbose', ['[CSVBCKP] Writing file ', fullPath])
-
- # Create a CSV file in the specified location
- with open(fullPath, 'w', newline='') as csvfile:
- # Initialize the CSV writer
- csv_writer = csv.writer(csvfile, delimiter=',', quoting=csv.QUOTE_MINIMAL)
-
- # Wrap the header values in double quotes and write the header row
- csv_writer.writerow([ '"' + col + '"' for col in columns])
-
- # Fetch and write data rows
- for row in cursor.fetchall():
- # Wrap each value in double quotes and write the row
- csv_writer.writerow(['"' + str(value) + '"' for value in row])
-
- # Close the database connection
- conn.close()
-
- # Open the CSV file for reading
- with open(fullPath, 'r') as file:
- data = file.read()
-
- # Replace all occurrences of """ with "
- data = data.replace('"""', '"')
-
- # Open the CSV file for writing
- with open(fullPath, 'w') as file:
- file.write(data)
-
- return 0
-
-
-#===============================================================================
-# BEGIN
-#===============================================================================
-if __name__ == '__main__':
- main()
\ No newline at end of file
diff --git a/front/plugins/node_sync/server.php b/front/plugins/node_sync/server.php
deleted file mode 100755
index e69de29b..00000000
diff --git a/front/plugins/sync/README.md b/front/plugins/sync/README.md
new file mode 100755
index 00000000..4f03eff6
--- /dev/null
+++ b/front/plugins/sync/README.md
@@ -0,0 +1,11 @@
+## Overview
+
+Synchronization plugin to synchronize multiple app instances. The plugin sends encrypted `last_result.log` files for individual plugins.
+
+### Usage
+
+- Head to **Settings** > **Sync Hub** to adjust the default values.
+
+### Notes
+
+- TBC
\ No newline at end of file
diff --git a/front/plugins/sync/config.json b/front/plugins/sync/config.json
new file mode 100755
index 00000000..2b96df65
--- /dev/null
+++ b/front/plugins/sync/config.json
@@ -0,0 +1,545 @@
+{
+ "code_name": "sync",
+ "template_type": "database-entry",
+ "unique_prefix": "SYNC",
+ "plugin_type": "system",
+ "enabled": true,
+ "data_source": "template",
+ "show_ui": false,
+ "localized": ["display_name", "description", "icon"],
+ "display_name": [{
+ "language_code": "en_us",
+ "string": "Sync Hub"
+ }],
+ "description": [{
+ "language_code": "en_us",
+ "string": "Plugin to synchronize multiple NetAlertX instances."
+ }],
+ "icon": [
+ {
+ "language_code": "en_us",
+ "string": ""
+ }
+ ],
+ "params" : [],
+ "database_column_definitions": [
+ {
+ "column": "Index",
+ "css_classes": "col-sm-2",
+ "show": false,
+ "type": "label",
+ "default_value": "",
+ "options": [],
+ "localized": [
+ "name"
+ ],
+ "name": [
+ {
+ "language_code": "en_us",
+ "string": "N/A"
+ },
+ {
+ "language_code": "es_es",
+ "string": "N/A"
+ }
+ ]
+ },
+ {
+ "column": "Plugin",
+ "css_classes": "col-sm-2",
+ "show": false,
+ "type": "label",
+ "default_value": "",
+ "options": [],
+ "localized": [
+ "name"
+ ],
+ "name": [
+ {
+ "language_code": "en_us",
+ "string": "N/A"
+ },
+ {
+ "language_code": "es_es",
+ "string": "N/A"
+ }
+ ]
+ },
+ {
+ "column": "Object_PrimaryID",
+ "css_classes": "col-sm-2",
+ "show": true,
+ "type": "label",
+ "default_value": "",
+ "options": [],
+ "localized": [
+ "name"
+ ],
+ "name": [
+ {
+ "language_code": "en_us",
+ "string": "Plugin"
+ }
+ ]
+ },
+ {
+ "column": "Object_SecondaryID",
+ "css_classes": "col-sm-2",
+ "show": false,
+ "type": "label",
+ "default_value": "",
+ "options": [],
+ "localized": [
+ "name"
+ ],
+ "name": [
+ {
+ "language_code": "en_us",
+ "string": "Time"
+ }
+ ]
+ },
+ {
+ "column": "DateTimeCreated",
+ "css_classes": "col-sm-2",
+ "show": true,
+ "type": "label",
+ "default_value": "",
+ "options": [],
+ "localized": [
+ "name"
+ ],
+ "name": [
+ {
+ "language_code": "en_us",
+ "string": "Sent when"
+ }
+ ]
+ },
+ {
+ "column": "DateTimeChanged",
+ "css_classes": "col-sm-2",
+ "show": false,
+ "type": "label",
+ "default_value": "",
+ "options": [],
+ "localized": [
+ "name"
+ ],
+ "name": [
+ {
+ "language_code": "en_us",
+ "string": "Changed"
+ }
+ ]
+ },
+ {
+ "column": "Watched_Value1",
+ "css_classes": "col-sm-2",
+ "show": true,
+ "type": "label",
+ "default_value":"",
+ "options": [
+ ],
+ "localized": [
+ "name"
+ ],
+ "name": [
+ {
+ "language_code": "en_us",
+ "string": "Node name"
+ }
+ ]
+ },
+ {
+ "column": "Watched_Value2",
+ "css_classes": "col-sm-8",
+ "show": true,
+ "type": "label",
+ "default_value": "",
+ "options": [],
+ "localized": [
+ "name"
+ ],
+ "name": [
+ {
+ "language_code": "en_us",
+ "string": "Status code"
+ }
+ ]
+ },
+ {
+ "column": "Watched_Value3",
+ "css_classes": "col-sm-2",
+ "show": true,
+ "type": "textarea_readonly",
+ "default_value": "",
+ "options": [],
+ "localized": [
+ "name"
+ ],
+ "name": [
+ {
+ "language_code": "en_us",
+ "string": "Response"
+ }
+ ]
+ },
+ {
+ "column": "Watched_Value4",
+ "css_classes": "col-sm-2",
+ "show": false,
+ "type": "label",
+ "default_value": "",
+ "options": [],
+ "localized": [
+ "name"
+ ],
+ "name": [
+ {
+ "language_code": "en_us",
+ "string": "N/A"
+ }
+ ]
+ },
+ {
+ "column": "UserData",
+ "css_classes": "col-sm-2",
+ "show": false,
+ "type": "textbox_save",
+ "default_value": "",
+ "options": [],
+ "localized": [
+ "name"
+ ],
+ "name": [
+ {
+ "language_code": "en_us",
+ "string": "Comments"
+ }
+ ]
+ },
+ {
+ "column": "Status",
+ "css_classes": "col-sm-1",
+ "show": false,
+ "type": "replace",
+ "default_value": "",
+ "options": [
+ {
+ "equals": "watched-not-changed",
+ "replacement": "
hub. If this instance is sending the data, select node"
+ }
+ ]
+ },
+ {
+ "function": "RUN",
+ "events": ["run"],
+ "type": "text.select",
+ "display_condition": {
+ "type" : "setting",
+ "name" : "SYNC_instance_type",
+ "value": "hub"
+ },
+ "default_value":"disabled",
+ "options": ["disabled", "once", "schedule", "always_after_scan", "on_new_device", "on_notification"],
+ "localized": ["name", "description"],
+ "name" :[{
+ "language_code":"en_us",
+ "string" : "When to run"
+ },
+ {
+ "language_code":"es_es",
+ "string" : "Cuándo ejecutar"
+ },
+ {
+ "language_code":"de_de",
+ "string" : "Wann laufen"
+ }],
+ "description": [{
+ "language_code":"en_us",
+ "string" : "When the node sync should run. Data might be lost if you run the sync less frequently. Good options are always_after_scan, on_new_device, on_notification"
+ }]
+ },
+ {
+ "function": "target_url",
+ "type": "text",
+ "display_condition": {
+ "type" : "setting",
+ "name" : "SYNC_instance_type",
+ "value": "hub"
+ },
+ "maxLength": 50,
+ "default_value": "",
+ "options": [],
+ "localized": ["name", "description"],
+ "name": [
+ {
+ "language_code": "en_us",
+ "string": "Hub URL"
+ }
+ ],
+ "description": [
+ {
+ "language_code": "en_us",
+ "string": "Target hub URL to send the data to."
+ }
+ ]
+ },
+ {
+ "function": "node_name",
+ "type": "text",
+ "display_condition": {
+ "type" : "setting",
+ "name" : "SYNC_instance_type",
+ "value": "node"
+ },
+ "maxLength": 50,
+ "default_value": "",
+ "options": [],
+ "localized": ["name", "description"],
+ "name": [
+ {
+ "language_code": "en_us",
+ "string": "Node name"
+ }
+ ],
+ "description": [
+ {
+ "language_code": "en_us",
+ "string": "Use a unique node name, without spaces or special characters, such as Node_Vlan01"
+ }
+ ]
+ },
+ {
+ "function": "plugins",
+ "type": "text.multiselect",
+ "display_condition": {
+ "type" : "setting",
+ "name" : "SYNC_instance_type",
+ "value": "node"
+ },
+ "maxLength": 50,
+ "default_value": [
+ ],
+ "options": ["{value}"],
+ "options_params" : [
+ {
+ "name" : "value",
+ "type" : "setting",
+ "value" : "LOADED_PLUGINS"
+ }
+ ],
+ "localized": ["name", "description"],
+ "name": [
+ {
+ "language_code": "en_us",
+ "string": "Plugins"
+ }
+ ],
+ "description": [
+ {
+ "language_code": "en_us",
+ "string": "Plugins to synchronize. Only plugins that produce some kind of output in last_result.log can be synchronized."
+ }
+ ]
+ },
+ {
+ "function": "api_token",
+ "type": "text",
+ "maxLength": 50,
+ "default_value": "",
+ "options": [],
+ "localized": ["name", "description"],
+ "name": [
+ {
+ "language_code": "en_us",
+ "string": "API token"
+ }
+ ],
+ "description": [
+ {
+ "language_code": "en_us",
+ "string": "API token to secure communication. The API token needs to be the same on the hub and on the nodes."
+ }
+ ]
+ },
+ {
+ "function": "CMD",
+ "type": "readonly",
+ "default_value": "python3 /app/front/plugins/sync/sync.py",
+ "options": [],
+ "localized": ["name", "description"],
+ "name": [
+ {
+ "language_code": "en_us",
+ "string": "Command"
+ },
+ {
+ "language_code": "es_es",
+ "string": "Comando"
+ },
+ {
+ "language_code": "de_de",
+ "string": "Befehl"
+ }
+ ],
+ "description": [
+ {
+ "language_code": "en_us",
+ "string": "Command to run. This can not be changed"
+ },
+ {
+ "language_code": "es_es",
+ "string": "Comando a ejecutar. Esto no se puede cambiar"
+ },
+ {
+ "language_code": "de_de",
+ "string": "Befehl zum Ausführen. Dies kann nicht geändert werden"
+ }
+ ]
+ },
+ {
+ "function": "RUN_SCHD",
+ "type": "text",
+ "display_condition": {
+ "type" : "setting",
+ "name" : "SYNC_instance_type",
+ "value": "hub"
+ },
+ "default_value":"0 2 * * 3",
+ "options": [],
+ "localized": ["name", "description"],
+ "name" : [{
+ "language_code":"en_us",
+ "string" : "Schedule"
+ },
+ {
+ "language_code":"es_es",
+ "string" : "Schedule"
+ },
+ {
+ "language_code":"de_de",
+ "string" : "Schedule"
+ }],
+ "description": [{
+ "language_code":"en_us",
+ "string" : "Only enabled if you select schedule in the SYNC_RUN setting. Make sure you enter the schedule in the correct cron-like format (e.g. validate at crontab.guru). For example entering 0 4 * * * will run the scan after 4 am in the TIMEZONE you set above. Will be run NEXT time the time passes."
+ },
+ {
+ "language_code":"es_es",
+ "string" : "Solo está habilitado si selecciona schedule en la configuración SYNC_RUN. Asegúrese de ingresar la programación en el formato similar a cron correcto (por ejemplo, valide en crontab.guru). Por ejemplo, ingresar 0 4 * * * ejecutará el escaneo después de las 4 a.m. en el TIMEZONE código> que configuró arriba. Se ejecutará la PRÓXIMA vez que pase el tiempo."
+ },
+ {
+ "language_code":"de_de",
+ "string" : "Nur aktiviert, wenn Sie schedule in der SYNC_RUN-Einstellung auswählen. Stellen Sie sicher, dass Sie den Zeitplan im richtigen Cron-ähnlichen Format eingeben (z. B. validieren unter crontab.guru). Wenn Sie beispielsweise 0 4 * * * eingeben, wird der Scan nach 4 Uhr morgens in der TIMEZONE ausgeführt. Code> den Sie oben festgelegt haben. Wird das NÄCHSTE Mal ausgeführt, wenn die Zeit vergeht."
+ }]
+ },
+ {
+ "function": "RUN_TIMEOUT",
+ "type": "integer",
+ "default_value": 30,
+ "options": [],
+ "localized": ["name", "description"],
+ "name": [
+ {
+ "language_code": "en_us",
+ "string": "Run timeout"
+ },
+ {
+ "language_code": "es_es",
+ "string": "Tiempo límite de ejecución"
+ },
+ {
+ "language_code": "de_de",
+ "string": "Zeitüberschreitung"
+ }
+ ],
+ "description": [
+ {
+ "language_code": "en_us",
+ "string": "Maximum time in seconds to wait for the script to finish. If this time is exceeded the script is aborted."
+ },
+ {
+ "language_code": "es_es",
+ "string": "Tiempo máximo en segundos para esperar a que finalice el script. Si se supera este tiempo, el script se cancela."
+ },
+ {
+ "language_code": "de_de",
+ "string": "Maximale Zeit in Sekunden, die auf den Abschluss des Skripts gewartet werden soll. Bei Überschreitung dieser Zeit wird das Skript abgebrochen."
+ }
+ ]
+ }
+
+
+ ]
+
+ }
diff --git a/front/plugins/sync/hub.php b/front/plugins/sync/hub.php
new file mode 100755
index 00000000..7796963f
--- /dev/null
+++ b/front/plugins/sync/hub.php
@@ -0,0 +1,47 @@
+
diff --git a/front/plugins/sync/sync.py b/front/plugins/sync/sync.py
new file mode 100755
index 00000000..9d005be6
--- /dev/null
+++ b/front/plugins/sync/sync.py
@@ -0,0 +1,104 @@
+#!/usr/bin/env python
+
+import os
+import pathlib
+import sys
+import hashlib
+import requests
+from plugin_helper import Plugin_Object, Plugin_Objects, decodeBase64
+from plugin_utils import get_plugins_configs
+from logger import mylog
+from helper import get_setting_value
+
+# Define the installation path and extend the system path for plugin imports
+INSTALL_PATH = "/app"
+sys.path.extend([f"{INSTALL_PATH}/front/plugins", f"{INSTALL_PATH}/server"])
+
+# Define the current path and log file paths
+CUR_PATH = str(pathlib.Path(__file__).parent.resolve())
+LOG_FILE = os.path.join(CUR_PATH, 'script.log')
+RESULT_FILE = os.path.join(CUR_PATH, 'last_result.log')
+
+pluginName = 'SYNC'
+
+# Function to encrypt data using a password
+def encrypt_data(data, password):
+ key = hashlib.sha256(password.encode()).digest()
+ cipher = hashlib.pbkdf2_hmac('sha256', data.encode(), key, 100000)
+ return cipher.hex()
+
+def main():
+ mylog('verbose', [f'[{pluginName}] In script'])
+
+
+ # Initialize the Plugin obj output file
+ plugin_objects = Plugin_Objects(RESULT_FILE)
+
+ # Retrieve configuration settings
+ plugins_to_sync = get_setting_value('SYNC_plugins')
+ api_token = get_setting_value('SYNC_api_token') # Use an API token instead of a password
+ hub_url = get_setting_value('SYNC_hub_url')
+ node_name = get_setting_value('SYNC_node_name')
+
+ # Get all plugin configurations
+ all_plugins = get_plugins_configs()
+
+
+ index = 0
+ for plugin in all_plugins:
+ pref = plugin["unique_prefix"]
+
+ if pref in plugins_to_sync:
+ index += 1
+ mylog('verbose', [f'[{pluginName}] synching "{pref}" ({index}/{len(plugins_to_sync)})'])
+
+ # Construct the file path for the plugin's last_result.log file
+ plugin_folder = plugin["code_name"]
+ file_path = f"{INSTALL_PATH}/front/plugins/{plugin_folder}/last_result.log"
+
+ if os.path.exists(file_path):
+ # Read the content of the log file
+ with open(file_path, 'r') as f:
+ newLines = f.read()
+ # Encrypt the log data using the API token
+ encrypted_data = encrypt_data(newLines, api_token)
+
+ # Prepare the data payload for the POST request
+ data = {
+ 'data': encrypted_data,
+ 'plugin_folder': plugin_folder,
+ 'node_name': node_name
+ }
+
+ # Set the authorization header with the API token
+ headers = {'Authorization': f'Bearer {api_token}'}
+ api_endpoint = f"{hub_url}/plugins/sync/hub.php"
+ response = requests.post(api_endpoint, data=data, headers=headers)
+
+ mylog('verbose', [f'[{pluginName}] response: "{response}"'])
+
+ if response.status_code == 200:
+ mylog('verbose', [f'[{pluginName}] Data for "{plugin_folder}" sent successfully'])
+ else:
+ mylog('error', [f'[{pluginName}] Failed to send data for "{plugin_folder}"'])
+
+ # log result
+ plugin_objects.add_object(
+ primaryId = pref,
+ secondaryId = timeNowTZ(),
+ watched1 = node_name,
+ watched2 = response.status_code,
+ watched3 = response.text,
+ watched4 = '',
+ extra = '',
+ foreignKey = '')
+ else:
+ mylog('verbose', [f'[{pluginName}] {plugin_folder}/last_result.log not found'])
+
+ # log result
+ plugin_objects.write_result_file()
+
+ return 0
+
+if __name__ == '__main__':
+ main()