Merge branch 'next_release' of https://github.com/netalertx/NetAlertX into next_release

This commit is contained in:
Jokob @NetAlertX
2026-03-15 01:42:23 +00:00
158 changed files with 7576 additions and 2892 deletions

View File

@@ -400,11 +400,11 @@
"description": [
{
"language_code": "en_us",
"string": "The Webhook payload data format for the <code>body</code> > <code>attachments</code> > <code>text</code> attribute in the payload json. See an example of the payload <a target=\"_blank\" href=\"https://github.com/jokob-sk/NetAlertX/blob/main/front/report_templates/webhook_json_sample.json\">here</a>. (e.g.: for discord use <code>text</code>)"
"string": "The Webhook payload data format for the <code>body</code> > <code>attachments</code> > <code>text</code> attribute in the payload json. See an example of the payload <a target=\"_blank\" href=\"https://github.com/netalertx/NetAlertX/blob/main/front/report_templates/webhook_json_sample.json\">here</a>. (e.g.: for discord use <code>text</code>)"
},
{
"language_code": "es_es",
"string": "El formato de datos de carga de Webhook para el atributo <code>body</code> > <code>attachments</code> > <code>text</code> en el json de carga. Vea un ejemplo de la carga <a target=\"_blank\" href=\"https://github.com/jokob-sk/NetAlertX/blob/main/front/report_templates/webhook_json_sample.json\">aquí</a>. (por ejemplo: para discord use <code>text</code>)"
"string": "El formato de datos de carga de Webhook para el atributo <code>body</code> > <code>attachments</code> > <code>text</code> en el json de carga. Vea un ejemplo de la carga <a target=\"_blank\" href=\"https://github.com/netalertx/NetAlertX/blob/main/front/report_templates/webhook_json_sample.json\">aquí</a>. (por ejemplo: para discord use <code>text</code>)"
}
]
},

View File

@@ -96,6 +96,15 @@ def cleanup_database(
cursor.execute(sql)
mylog("verbose", [f"[{pluginName}] Events deleted rows: {cursor.rowcount}"])
# -----------------------------------------------------
# Sessions (derived snapshot — trimmed to the same window as Events so the
# two tables stay in sync without introducing a separate setting)
mylog("verbose", f"[{pluginName}] Sessions: Delete all older than {str(DAYS_TO_KEEP_EVENTS)} days (reuses DAYS_TO_KEEP_EVENTS)")
sql = f"""DELETE FROM Sessions WHERE ses_DateTimeConnection <= date('now', '-{str(DAYS_TO_KEEP_EVENTS)} day')"""
mylog("verbose", [f"[{pluginName}] SQL : {sql}"])
cursor.execute(sql)
mylog("verbose", [f"[{pluginName}] Sessions deleted rows: {cursor.rowcount}"])
# -----------------------------------------------------
# Plugins_History
mylog("verbose", f"[{pluginName}] Plugins_History: Trim to {str(PLUGINS_KEEP_HIST)} per Plugin")
@@ -199,9 +208,19 @@ def cleanup_database(
cursor.execute("PRAGMA wal_checkpoint(FULL);")
mylog("verbose", [f"[{pluginName}] WAL checkpoint executed to truncate file."])
# Refresh query-planner statistics after bulk deletes so SQLite chooses
# the right indexes on the next scan cycle (fixes CPU scaling with DB size)
cursor.execute("ANALYZE;")
mylog("verbose", [f"[{pluginName}] ANALYZE completed"])
mylog("verbose", [f"[{pluginName}] Shrink Database"])
cursor.execute("VACUUM;")
# Lightweight incremental ANALYZE at connection close — near-zero cost,
# only re-analyzes tables whose statistics have drifted >25%
cursor.execute("PRAGMA optimize;")
mylog("verbose", [f"[{pluginName}] PRAGMA optimize completed"])
conn.close()

View File

@@ -1191,6 +1191,41 @@
}
]
},
{
"function": "devCanSleep",
"type": {
"dataType": "integer",
"elements": [
{
"elementType": "input",
"elementOptions": [
{
"type": "checkbox"
}
],
"transformers": []
}
]
},
"default_value": 0,
"options": [],
"localized": [
"name",
"description"
],
"name": [
{
"language_code": "en_us",
"string": "Can Sleep"
}
],
"description": [
{
"language_code": "en_us",
"string": "When enabled, the device will appear as <em>Sleeping</em> instead of <em>Down</em> or <em>Off-line</em> while it has been absent for less than the <b>Sleep Window</b> (<code>NTFPRCS_sleep_time</code>). Once the sleep window expires the device becomes subject to the normal <code>Alert Down</code> condition. Database column name: <code>devCanSleep</code>."
}
]
},
{
"function": "devSkipRepeated",
"type": {
@@ -1676,7 +1711,7 @@
"description": [
{
"language_code": "en_us",
"string": "The name of the Sync Node. Uneditable - Auto-populated via the <a href=\"https://github.com/jokob-sk/NetAlertX/blob/main/front/plugins/sync/README.md\" target=\"_blank\">Sync plugin</a> if enabled. Database column name: <code>devSyncHubNode</code>."
"string": "The name of the Sync Node. Uneditable - Auto-populated via the <a href=\"https://github.com/netalertx/NetAlertX/blob/main/front/plugins/sync/README.md\" target=\"_blank\">Sync plugin</a> if enabled. Database column name: <code>devSyncHubNode</code>."
}
]
},

View File

@@ -105,6 +105,34 @@
}
]
},
{
"function": "sleep_time",
"type": {
"dataType": "integer",
"elements": [
{
"elementType": "input",
"elementOptions": [{ "type": "number" }],
"transformers": []
}
]
},
"default_value": 30,
"options": [],
"localized": ["name", "description"],
"name": [
{
"language_code": "en_us",
"string": "Sleep Window"
}
],
"description": [
{
"language_code": "en_us",
"string": "How many minutes a device with <code>Can Sleep</code> enabled is shown as <em>Sleeping</em> before it becomes subject to the <code>Alert Down</code> condition. Changes take effect after saving settings."
}
]
},
{
"function": "new_dev_condition",
"type": {
@@ -152,8 +180,6 @@
"string": "You can specify a SQL where condition to filter out Events from notifications. For example <code>AND devLastIP NOT LIKE '192.168.3.%'</code> will always exclude any Event notifications for all devices with the IP starting with <code>192.168.3.%</code>."
}
]
<<<<<<< Updated upstream
=======
},
{
"function": "TEXT_SECTION_HEADERS",
@@ -298,7 +324,6 @@
"string": "Custom text template for plugin event notifications. Use <code>{FieldName}</code> placeholders, e.g. <code>{Plugin}: {Object_PrimaryId} - {Status}</code>. Leave empty for default formatting. Available fields: <code>{Plugin}</code>, <code>{Object_PrimaryId}</code>, <code>{Object_SecondaryId}</code>, <code>{DateTimeChanged}</code>, <code>{Watched_Value1}</code>, <code>{Watched_Value2}</code>, <code>{Watched_Value3}</code>, <code>{Watched_Value4}</code>, <code>{Status}</code>."
}
]
>>>>>>> Stashed changes
}
],

View File

@@ -224,7 +224,7 @@
"description": [
{
"language_code": "en_us",
"string": "Omada SDN site IDs. For now, we only process the first site listed since NetAlertX's other probes won't traverse across NAT and routers. But if needed please submit an issue in github with your specific use case for consideration: <code>https://github.com/jokob-sk/NetAlertX/issues </code> "
"string": "Omada SDN site IDs. For now, we only process the first site listed since NetAlertX's other probes won't traverse across NAT and routers. But if needed please submit an issue in github with your specific use case for consideration: <code>https://github.com/netalertx/NetAlertX/issues </code> "
}
]
},

View File

@@ -483,11 +483,11 @@
"description": [
{
"language_code": "en_us",
"string": "A list of <code>snmpwalk</code> commands to execute against IP addresses of routers/switches with SNMP turned on. <br/> <br/> Example with the router on the IP <code>192.168.1.1</code> (the <code>-OXsq</code> is a required parameter): <br/> <code>snmpwalk -v 2c -c public -OXsq 192.168.1.1 .1.3.6.1.2.1.3.1.1.2</code> <br/><br/> Only IPv4 supported. Authentication is not supported. More info on the plugin <a href='https://github.com/jokob-sk/NetAlertX/tree/main/front/plugins/snmp_discovery' target='_blank'>here</a>."
"string": "A list of <code>snmpwalk</code> commands to execute against IP addresses of routers/switches with SNMP turned on. <br/> <br/> Example with the router on the IP <code>192.168.1.1</code> (the <code>-OXsq</code> is a required parameter): <br/> <code>snmpwalk -v 2c -c public -OXsq 192.168.1.1 .1.3.6.1.2.1.3.1.1.2</code> <br/><br/> Only IPv4 supported. Authentication is not supported. More info on the plugin <a href='https://github.com/netalertx/NetAlertX/tree/main/front/plugins/snmp_discovery' target='_blank'>here</a>."
},
{
"language_code": "es_es",
"string": "Una lista de comandos <code>snmpwalk</code> para ejecutar en direcciones IP de computadoras/conmutadores con SNMP activado. <br/> <br/> Ejemplo con el enrutador en la IP <code>192.168.1.1</code>: <br/> <code>snmpwalk -v 2c -c public -OXsq 192.168.1.1 .1.3.6.1. 2.1.3.1.1.2</code> <br/><br/> Solo se admite IPv4. No se admite la autenticación. Más información sobre el complemento <a href='https://github.com/jokob-sk/NetAlertX/tree/main/front/plugins/snmp_discovery' target='_blank'>aquí</a>."
"string": "Una lista de comandos <code>snmpwalk</code> para ejecutar en direcciones IP de computadoras/conmutadores con SNMP activado. <br/> <br/> Ejemplo con el enrutador en la IP <code>192.168.1.1</code>: <br/> <code>snmpwalk -v 2c -c public -OXsq 192.168.1.1 .1.3.6.1. 2.1.3.1.1.2</code> <br/><br/> Solo se admite IPv4. No se admite la autenticación. Más información sobre el complemento <a href='https://github.com/netalertx/NetAlertX/tree/main/front/plugins/snmp_discovery' target='_blank'>aquí</a>."
}
]
},

View File

@@ -222,27 +222,30 @@ def main():
extra = '',
foreignKey = device['devGUID'])
# Resolve the actual columns that exist in the Devices table once.
# This automatically excludes computed/virtual fields (e.g. devStatus,
# devIsSleeping) and 'rowid' without needing a maintained exclusion list.
cursor.execute("PRAGMA table_info(Devices)")
db_columns = {row[1] for row in cursor.fetchall()}
# Filter out existing devices
new_devices = [device for device in device_data if device['devMac'] not in existing_mac_addresses]
# Remove 'rowid' key if it exists
for device in new_devices:
device.pop('rowid', None)
device.pop('devStatus', None)
mylog('verbose', [f'[{pluginName}] All devices: "{len(device_data)}"'])
mylog('verbose', [f'[{pluginName}] New devices: "{len(new_devices)}"'])
# Prepare the insert statement
if new_devices:
# creating insert statement, removing 'rowid', 'devStatus' as handled on the target and devStatus is resolved on the fly
columns = ', '.join(k for k in new_devices[0].keys() if k not in ['rowid', 'devStatus'])
placeholders = ', '.join('?' for k in new_devices[0] if k not in ['rowid', 'devStatus'])
# Only keep keys that are real columns in the target DB; computed
# or unknown fields are silently dropped regardless of source schema.
insert_cols = [k for k in new_devices[0].keys() if k in db_columns]
columns = ', '.join(insert_cols)
placeholders = ', '.join('?' for _ in insert_cols)
sql = f'INSERT INTO Devices ({columns}) VALUES ({placeholders})'
# Extract values for the new devices
values = [tuple(device.values()) for device in new_devices]
# Extract only the whitelisted column values for each device
values = [tuple(device.get(col) for col in insert_cols) for device in new_devices]
mylog('verbose', [f'[{pluginName}] Inserting Devices SQL : "{sql}"'])
mylog('verbose', [f'[{pluginName}] Inserting Devices VALUES: "{values}"'])

View File

@@ -443,7 +443,8 @@
"Device_TableHead_ReqNicsOnline",
"Device_TableHead_Vlan",
"Device_TableHead_IPv4",
"Device_TableHead_IPv6"
"Device_TableHead_IPv6",
"Device_TableHead_Flapping"
],
"localized": ["name", "description"],
"name": [

View File

@@ -9,7 +9,7 @@
"display_name": [
{
"language_code": "en_us",
"string": "Website monitor"
"string": "Services & Web monitor"
},
{
"language_code": "es_es",