Merge pull request #1714 from netalertx/next_release

FE: Notifications sorting fix #1713
This commit is contained in:
Jokob @NetAlertX
2026-07-26 09:36:06 +10:00
committed by GitHub
33 changed files with 188 additions and 98 deletions

View File

@@ -95,7 +95,7 @@ body:
***Generally speaking, all bug reports should have logs provided.***
Tip: You can attach images or log files by clicking this area to highlight it and then dragging files in.
Additionally, any additional info? Screenshots? References? Anything that will give us more context about the issue you are encountering!
You can use `tail -100 /app/log/app.log` in the container if you have trouble getting to the log files or send them to netalertx@gmail.com with the issue number.
You can use `tail -100 /app/log/app.log` in the container if you have trouble getting to the log files or send them to support@netalertx.com with the issue number.
validations:
required: false
- type: textarea

View File

@@ -10,7 +10,7 @@ body:
attributes:
value: |
**Important:** For security reasons, please do **not** post sensitive security issues publicly in the issue tracker.
Instead, send details to our security contact email: [jokob@duck.com](mailto:jokob@duck.com).
Instead, send details to our security contact email: [support@netalertx.com](mailto:support@netalertx.com).
We appreciate your responsible disclosure.
- type: textarea
@@ -27,6 +27,6 @@ body:
attributes:
label: Have you sent this report via email to the security contact?
options:
- label: Yes, I have sent the details to jokob@duck.com
- label: Yes, I have sent the details to support@netalertx.com
required: true
- label: Not yet, I will send it after opening this issue

View File

@@ -60,7 +60,7 @@ representative at an online or offline event.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported to the community leaders responsible for enforcement at <jokob@duck.com>.
reported to the community leaders responsible for enforcement at <support@netalertx.com>.
All complaints will be reviewed and investigated promptly and fairly.
All community leaders are obligated to respect the privacy and security of the

View File

@@ -198,7 +198,7 @@ Thank you to everyone who appreciates this tool and donates.
- Bitcoin: `1N8tupjeCK12qRVU2XrV17WvKK7LCawyZM`
- Ethereum: `0x6e2749Cb42F4411bc98501406BdcD82244e3f9C7`
📧 Email me at [jokob@duck.com](mailto:jokob@duck.com?subject=NetAlertX) if you want to get in touch or if I should add other sponsorship platforms.
📧 Email me at [support@netalertx.com](mailto:support@netalertx.com?subject=NetAlertX) if you want to get in touch or if I should add other sponsorship platforms.
</details>

View File

@@ -6,7 +6,7 @@
#
# update_vendors.sh - Back module. IEEE Vendors db update
# ------------------------------------------------------------------------------
# Puche 2021 / 2022+ jokob jokob@duck.com GNU GPLv3
# Puche 2021 / 2022+ jokob support@netalertx.com GNU GPLv3
# ------------------------------------------------------------------------------
# ----------------------------------------------------------------------

View File

@@ -103,6 +103,6 @@ Sometimes specific log sections are needed to debug issues. The Devices and Curr
2. Wait for the issue to occur.
3. Search for `================ DEVICES table content ================` in your logs.
4. Search for `================ CurrentScan table content ================` in your logs.
5. Open a new issue and post (redacted) output into the issue description (or send to the netalertx@gmail.com email if sensitive data present).
5. Open a new issue and post (redacted) output into the issue description (or send to the support@netalertx.com email if sensitive data present).
6. Please set `LOG_LEVEL` to `debug` or lower.

View File

@@ -67,7 +67,7 @@ Sometimes specific log sections are needed to debug issues. The Devices and Curr
2. Wait for the issue to occur.
3. Search for `================ DEVICES table content ================` in your logs.
4. Search for `================ CurrentScan table content ================` in your logs.
5. Open a new issue and post (redacted) output into the issue description (or send to the netalertx@gmail.com email if sensitive data present).
5. Open a new issue and post (redacted) output into the issue description (or send to the support@netalertx.com email if sensitive data present).
6. Please set `LOG_LEVEL` to `debug` or lower.
## Common issues

View File

@@ -127,4 +127,4 @@ You can read or watch several [community configuration guides](https://docs.neta
- Bitcoin: `1N8tupjeCK12qRVU2XrV17WvKK7LCawyZM`
- Ethereum: `0x6e2749Cb42F4411bc98501406BdcD82244e3f9C7`
> 📧 Email me at [netalertx@gmail.com](mailto:netalertx@gmail.com?subject=NetAlertX Donations) if you want to get in touch or if I should add other sponsorship platforms.
> 📧 Email me at [support@netalertx.com](mailto:support@netalertx.com?subject=NetAlertX Donations) if you want to get in touch or if I should add other sponsorship platforms.

View File

@@ -24,7 +24,7 @@ The following device properties influence notifications. You can:
2. **Alert Down** - Alerts when a device goes down. This setting overrides a disabled **Alert Events** setting, so you will get a notification of a device going down even if you don't have **Alert Events** ticked. Disabling this will disable down and down reconnected notifications on the device.
3. **Can Sleep** - Marks the device as sleep-capable (e.g. a battery-powered sensor that deep-sleeps between readings). When enabled, offline periods within the **Alert down after (sleep)** (`NTFPRCS_sleep_time`) global window are shown as **Sleeping** (aqua badge 🌙) instead of **Down**, and no down alert is fired during that window. Once the window expires the device falls back to normal down-alert logic. ⚠ Requires **Alert Down** to be enabled — sleeping suppresses the alert during the window only.
4. **Skip repeated notifications**, if for example you know there is a temporary issue and want to pause the same notification for this device for a given time.
5. **Require NICs Online** - Indicates whether this device should be considered online only if all associated NICs (child devices with the `nic` relationship type) are online. When disabled, a single online NIC is sufficient to mark the parent device as online, regardless of the parent's own detected status. The nic relationship type is configured on the child (NIC) device.
5. **Require NICs Online** - Determines whether this device is considered online only when **all associated NICs** are online. To configure this, navigate to the child devices, assign the `nic` relationship, and set this device as the **Parent node**. If enabled, every associated NIC must be online for the device to be considered online. If disabled, the device is considered online when **any NIC** is online. Database column name: `devReqNicsOnline`.
> [!NOTE]
> Please read through the [NTFPRCS plugin](https://github.com/netalertx/NetAlertX/blob/main/front/plugins/notification_processing/README.md) documentation to understand how device and global settings influence the notification processing.

View File

@@ -91,9 +91,9 @@ By default, NetAlertX does **not** require login. Before exposing the UI in any
## 📣 Responsible Disclosure
If you discover a vulnerability or security concern, please report it **privately** to:
If you discover a vulnerability or security concern, please report it **privately** via GitHub security advisories or to:
📧 [jokob@duck.com](mailto:jokob@duck.com?subject=NetAlertX%20Security%20Disclosure)
📧 [support@netalertx.com](mailto:support@netalertx.com?subject=NetAlertX%20Security%20Disclosure)
We take security seriously and will work to patch confirmed issues promptly. Your help in responsible disclosure is appreciated!

View File

@@ -4,7 +4,7 @@
#
# app.css - Front module. CSS styles
#-------------------------------------------------------------------------------
# Puche 2021 / 2022+ jokob jokob@duck.com GNU GPLv3
# Puche 2021 / 2022+ jokob support@netalertx.com GNU GPLv3
----------------------------------------------------------------------------- */
/* -----------------------------------------------------------------------------

View File

@@ -8,7 +8,7 @@
* mergeUniqueArrays(), getSetting(), getString(), getCache(),
* setCache(), and all cache* functions from cache.js.
*-------------------------------------------------------------------------------
# jokob@duck.com GNU GPLv3
# support@netalertx.com GNU GPLv3
----------------------------------------------------------------------------- */
// -----------------------------------------------------------------------------

View File

@@ -7,7 +7,7 @@
* All cross-file calls (handleSuccess, showSpinner, etc.) are
* call-time dependencies resolved after page load.
*-------------------------------------------------------------------------------
# jokob@duck.com GNU GPLv3
# support@netalertx.com GNU GPLv3
----------------------------------------------------------------------------- */
// Cache version stamp — injected by header.php from the app's .VERSION file.

View File

@@ -4,7 +4,7 @@
*
* common.js - Front module. Common Javascript functions
*-------------------------------------------------------------------------------
# Puche 2021 / 2022+ jokob jokob@duck.com GNU GPLv3
# Puche 2021 / 2022+ jokob support@netalertx.com GNU GPLv3
----------------------------------------------------------------------------- */
// -----------------------------------------------------------------------------
@@ -1009,7 +1009,7 @@ function showSpinner(stringKey = "Loading", target = null) {
console.log(resolvedTarget);
console.log(`spinnerTarget=${resolvedTarget.attr("id")} class=${resolvedTarget.attr("class")} size=${resolvedTarget.outerWidth()}x${resolvedTarget.outerHeight()} parent=${resolvedTarget.parent().outerWidth()}x${resolvedTarget.parent().outerHeight()}`)
// console.log(`spinnerTarget=${resolvedTarget.attr("id")} class=${resolvedTarget.attr("class")} size=${resolvedTarget.outerWidth()}x${resolvedTarget.outerHeight()} parent=${resolvedTarget.parent().outerWidth()}x${resolvedTarget.parent().outerHeight()}`)
$("#loadingSpinnerText").text(text);

View File

@@ -1059,6 +1059,16 @@ function collectSetting(prefix, setCodeName, setType, settingsArray) {
// Map of handlers
const handlers = {
textarea: () => {
let value = $(`#${setCodeName}`).val();
// Only escape multiline plain text values
if (dataType === "string") {
value = JSON.stringify(value).slice(1, -1);
}
return applyTransformers(value, transformers);
},
datatableString: () => {
const value = collectTableData(`#${setCodeName}_table`);
return btoa(JSON.stringify(value));
@@ -1091,9 +1101,26 @@ function collectSetting(prefix, setCodeName, setType, settingsArray) {
},
none: () => "",
json: () => {
let value = $(`#${setCodeName}`).val();
value = applyTransformers(value, transformers);
return JSON.stringify(value, null, 2);
let value = $(`#${setCodeName}`).val();
value = applyTransformers(value, transformers);
try {
value = JSON.parse(value);
} catch (e) {
console.error(`[collectSetting] Invalid JSON for ${setCodeName}`, e);
return value;
}
value = JSON.stringify(value, null, 2)
.replace(/\btrue\b/g, "True")
.replace(/\bfalse\b/g, "False")
.replace(/\bnull\b/g, "None");
console.log("---------------")
console.log(value)
return value;
},
fallback: () => {
console.error(`[collectSetting] Couldn't determine how to handle (${setCodeName}|${dataType}|${opts.inputType})`);
@@ -1106,17 +1133,20 @@ function collectSetting(prefix, setCodeName, setType, settingsArray) {
let handlerKey;
if (dataType === "string" && elementType === "datatable") {
handlerKey = "datatableString";
} else if (dataType === "json") {
handlerKey = "json";
}
else if (elementType === "textarea") {
handlerKey = "textarea";
} else if (dataType === "string" ||
(dataType === "integer" && (opts.inputType === "number" || opts.inputType === "text"))) {
handlerKey = "simpleValue";
handlerKey = "simpleValue";
} else if (opts.inputType === "checkbox") {
handlerKey = "checkbox";
} else if (dataType === "array") {
handlerKey = "array";
} else if (dataType === "none") {
handlerKey = "none";
} else if (dataType === "json") {
handlerKey = "json";
} else {
handlerKey = "fallback";
}
@@ -1162,16 +1192,19 @@ function generateFormHtml(settingsData, set, overrideValue, overrideOptions, ori
const setKey = set['setKey'];
const setType = set['setType'];
// if (setKey == 'UNIFIAPI_site_name') {
// if (setKey == 'NTFPRCS_TEXT_TEMPLATE_down_devices') {
// console.log("==== DEBUG OUTPUT BELOW 1 ====");
// console.log("populateFromOverrides: " + populateFromOverrides);
// console.log(setType);
// console.log(setKey);
// console.log("overrideValue:" + overrideValue);
// console.log("inVal:" + inVal);
// console.log("set['setValue']:" + set['setValue']);
// }
// console.log("==== DEBUG OUTPUT BELOW 1 ====");
// console.log("populateFromOverrides: " + populateFromOverrides);
// console.log(setType);
// console.log(setKey);
// console.log("overrideValue:" + overrideValue);
// console.log("inVal:" + inVal);
// console.log("set['setValue']:" + set['setValue']);
// console.log("overrideValue stringify:", JSON.stringify(overrideValue));
// console.log("inVal stringify:", JSON.stringify(inVal));
// console.log("set['setValue'] stringify:", JSON.stringify(set['setValue']));
// }
// Parse the setType JSON string
// console.log(processQuotes(setType));
@@ -1212,7 +1245,7 @@ function generateFormHtml(settingsData, set, overrideValue, overrideOptions, ori
// Override value
let val = valRes;
// if (setKey == 'UNIFIAPI_site_name') {
// if (setKey == 'NTFPRCS_TEXT_TEMPLATE_down_devices') {
// console.log("==== DEBUG OUTPUT BELOW 2 ====");
// console.log(setType);

View File

@@ -4,7 +4,7 @@
*
* ui_components.js - Front module. Common UI components
*-------------------------------------------------------------------------------
# jokob jokob@duck.com GNU GPLv3
# jokob support@netalertx.com GNU GPLv3
----------------------------------------------------------------------------- */

View File

@@ -5,7 +5,7 @@
//
// db.php - Front module. Server side. DB common file
//------------------------------------------------------------------------------
# 2022 jokob jokob@duck.com GNU GPLv3
# 2022 jokob support@netalertx.com GNU GPLv3
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------

View File

@@ -5,7 +5,7 @@
//
// util.php - Front module. Server side. Settings and utility functions
//------------------------------------------------------------------------------
# Puche 2021 / 2022+ jokob jokob@duck.com GNU GPLv3
# Puche 2021 / 2022+ jokob support@netalertx.com GNU GPLv3
//------------------------------------------------------------------------------
require dirname(__FILE__).'/../templates/globals.php';

View File

@@ -4,7 +4,7 @@
#
# header.php - Front module. Common header to all the web pages
#-------------------------------------------------------------------------------
# Puche 2021 / 2022+ jokob jokob@duck.com GNU GPLv3
# Puche 2021 / 2022+ jokob support@netalertx.com GNU GPLv3
#--------------------------------------------------------------------------- -->
<?php

View File

@@ -4,7 +4,7 @@
#
# notificacion.php - Front module. Common notification & modal window
#-------------------------------------------------------------------------------
# Puche 2021 / 2022+ jokob jokob@duck.com GNU GPLv3
# Puche 2021 / 2022+ jokob support@netalertx.com GNU GPLv3
#--------------------------------------------------------------------------- -->
<!-- Modal Ok -->

View File

@@ -3,6 +3,7 @@
import subprocess
import os
import sys
import json
# Register NetAlertX directories
INSTALL_PATH = os.getenv('NETALERTX_APP', '/app')
@@ -79,45 +80,78 @@ def check_config():
# -------------------------------------------------------------------------------
def send(text):
# limit = 1024 * 1024 # 1MB limit (1024 bytes * 1024 bytes = 1MB)
"""
Send a Telegram notification.
"""
limit = get_setting_value('TELEGRAM_SIZE')
run_timeout = int(get_setting_value('TELEGRAM_RUN_TIMEOUT'))
curl_timeout = str(max(1, run_timeout - 1))
# Ensure the final payload, including the truncation marker,
# never exceeds TELEGRAM_SIZE.
truncation_marker = " (text was truncated)"
if len(text) > limit:
payloadData = text[:limit] + " (text was truncated)"
payload_data = (text[:max(0, limit - len(truncation_marker))] + truncation_marker)[:limit]
else:
payloadData = text
payload_data = text
payload = json.dumps({
"chat_id": get_setting_value('TELEGRAM_HOST'),
"text": payload_data,
"disable_notification": False
})
cmd = [
"curl",
"--location",
# Prevent curl from hanging indefinitely.
# Both values are intentionally below RUN_TIMEOUT.
"--connect-timeout", curl_timeout,
"--max-time", curl_timeout,
f"https://api.telegram.org/bot{get_setting_value('TELEGRAM_URL')}/sendMessage",
"--header",
"Content-Type: application/json",
"--data",
payload,
]
mylog("debug", ["Executing: Telegram sendMessage", "--data <json>"])
try:
# try runnning a subprocess
proc = subprocess.run(
cmd,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
text=True,
check=False,
)
req = """curl --location 'https://api.telegram.org/bot%s/sendMessage' \\
--header 'Content-Type: application/json' \\
--data '{
"chat_id": "%s",
"text": "%s",
"disable_notification": false
}'""" % (get_setting_value('TELEGRAM_URL'), get_setting_value('TELEGRAM_HOST'), payloadData)
mylog("debug", [proc.stdout])
mylog('debug', [req])
# curl execution failed
if proc.returncode != 0:
raise subprocess.CalledProcessError(
proc.returncode,
cmd,
output=proc.stdout,
)
p = subprocess.Popen(req, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, shell=True)
stdout, stderr = p.communicate()
# Telegram API returned an error
try:
response = json.loads(proc.stdout)
if isinstance(response, dict) and response.get("ok") is False:
raise RuntimeError(proc.stdout)
except json.JSONDecodeError:
# Ignore non-JSON responses and preserve existing behavior.
pass
# write stdout and stderr into .log files for debugging if needed
# Log the stdout and stderr
mylog('debug', [stdout, stderr])
return proc.stdout
# log result
result = stdout
except subprocess.CalledProcessError as e:
# An error occurred, handle it
mylog('none', [e.output])
# log result
result = e.output
return result
except OSError:
# Propagate filesystem/process execution errors.
raise
if __name__ == '__main__':

View File

@@ -154,21 +154,24 @@ def send(text_data, html_data, json_data):
}]
}
# Serialize once so the transmitted payload and HMAC signature always match
payload_json = json.dumps(_json_payload, separators=(',', ':'))
# DEBUG - Write the json payload into a log file for debugging
write_file(logPath + '/webhook_payload.json', json.dumps(_json_payload))
write_file(logPath + '/webhook_payload.json', payload_json)
# Using the Slack-Compatible Webhook endpoint for Discord so that the same payload can be used for both
# Consider: curl has the ability to load in data to POST from a file + piping
if (endpointUrl.startswith('https://discord.com/api/webhooks/') and not endpointUrl.endswith("/slack")):
_WEBHOOK_URL = f"{endpointUrl}/slack"
curlParams = ["curl", "-i", "-H", "Content-Type:application/json", "-d", json.dumps(_json_payload), _WEBHOOK_URL]
curlParams = ["curl", "-i", "-H", "Content-Type:application/json", "-d", payload_json, _WEBHOOK_URL]
else:
_WEBHOOK_URL = endpointUrl
curlParams = ["curl", "-i", "-X", requestMethod , "-H", "Content-Type:application/json", "-d", json.dumps(_json_payload), _WEBHOOK_URL]
curlParams = ["curl", "-i", "-X", requestMethod, "-H", "Content-Type:application/json", "-d", payload_json, _WEBHOOK_URL]
# Add HMAC signature if configured
if (secret != ''):
h = hmac.new(secret.encode("UTF-8"), json.dumps(_json_payload, separators=(',', ':')).encode(), hashlib.sha256).hexdigest()
h = hmac.new(secret.encode("UTF-8"), payload_json.encode("UTF-8"), hashlib.sha256).hexdigest()
curlParams.insert(4, "-H")
curlParams.insert(5, f"X-Webhook-Signature: sha256={h}")

View File

@@ -130,15 +130,11 @@ def cleanup_database(
histCount = get_setting_value("DBCLNP_NOTIFI_HIST")
mylog("verbose", f"[{pluginName}] Notifications: Trim to {histCount}")
delete_query = f"""DELETE FROM Notifications
WHERE "Index" NOT IN (
SELECT "Index"
FROM (
SELECT "Index",
ROW_NUMBER() OVER(PARTITION BY "index" ORDER BY dateTimeCreated DESC) AS row_num
FROM Notifications
) AS ranked_objects
WHERE row_num <= {histCount}
);"""
WHERE "Index" NOT IN (
SELECT "Index" FROM Notifications
ORDER BY dateTimeCreated DESC
LIMIT {histCount}
);"""
cursor.execute(delete_query)
mylog("verbose", [f"[{pluginName}] Notifications deleted rows: {cursor.rowcount}"])

View File

@@ -1888,7 +1888,7 @@
"description": [
{
"language_code": "en_us",
"string": "Indicates whether this device should be considered online only if all associated NICs (devices with the <code>nic</code> relationship type) are online. If disabled, the device is considered online if any NIC is online. Database column name: <code>devReqNicsOnline</code>."
"string": "Determines whether this device is considered online only when <strong>all associated NICs</strong> are online. To configure this, navigate to the child devices, assign the <code>nic</code> relationship, and set this device as the <strong>Parent node</strong>. If enabled, every associated NIC must be online for the device to be considered online. If disabled, the device is considered online when <strong>any NIC</strong> is online. Database column name: <code>devReqNicsOnline</code>."
}
]
},

View File

@@ -210,7 +210,7 @@
"type": {
"dataType": "string",
"elements": [
{ "elementType": "input", "elementOptions": [], "transformers": [] }
{ "elementType": "textarea", "elementOptions": [], "transformers": [] }
]
},
"default_value": "",
@@ -225,7 +225,7 @@
"description": [
{
"language_code": "en_us",
"string": "Custom text template for new device notifications. Use <code>{FieldName}</code> placeholders, e.g. <code>{devName} ({eveMac}) - {eveIp}</code>. Leave empty for default formatting. Available fields: <code>{devName}</code>, <code>{eveMac}</code>, <code>{devVendor}</code>, <code>{eveIp}</code>, <code>{eveDateTime}</code>, <code>{eveEventType}</code>, <code>{devComments}</code>."
"string": "Custom text template for new device notifications. Use <code>{FieldName}</code> placeholders, e.g. <code>{devName} ({eveMac}) - {eveIp}</code>. Leave empty for default formatting. Available fields: <code>{devName}</code>, <code>{eveMac}</code>, <code>{devVendor}</code>, <code>{eveIp}</code>, <code>{eveDateTime}</code>, <code>{eveEventType}</code>, <code>{devComments}</code>, <code>{devVlan}</code>, <code>{devSite}</code>, <code>{devSSID}</code>."
}
]
},
@@ -234,7 +234,7 @@
"type": {
"dataType": "string",
"elements": [
{ "elementType": "input", "elementOptions": [], "transformers": [] }
{ "elementType": "textarea", "elementOptions": [], "transformers": [] }
]
},
"default_value": "",
@@ -249,7 +249,7 @@
"description": [
{
"language_code": "en_us",
"string": "Custom text template for down device notifications. Use <code>{FieldName}</code> placeholders, e.g. <code>{devName} ({eveMac}) - {eveIp}</code>. Leave empty for default formatting. Available fields: <code>{devName}</code>, <code>{eveMac}</code>, <code>{devVendor}</code>, <code>{eveIp}</code>, <code>{eveDateTime}</code>, <code>{eveEventType}</code>, <code>{devComments}</code>."
"string": "Custom text template for down device notifications. Use <code>{FieldName}</code> placeholders, e.g. <code>{devName} ({eveMac}) - {eveIp}</code>. Leave empty for default formatting. Available fields: <code>{devName}</code>, <code>{eveMac}</code>, <code>{devVendor}</code>, <code>{eveIp}</code>, <code>{eveDateTime}</code>, <code>{eveEventType}</code>, <code>{devComments}</code>, <code>{devVlan}</code>, <code>{devSite}</code>, <code>{devSSID}</code>."
}
]
},
@@ -258,7 +258,7 @@
"type": {
"dataType": "string",
"elements": [
{ "elementType": "input", "elementOptions": [], "transformers": [] }
{ "elementType": "textarea", "elementOptions": [], "transformers": [] }
]
},
"default_value": "",
@@ -273,7 +273,7 @@
"description": [
{
"language_code": "en_us",
"string": "Custom text template for reconnected device notifications. Use <code>{FieldName}</code> placeholders, e.g. <code>{devName} ({eveMac}) reconnected at {eveDateTime}</code>. Leave empty for default formatting. Available fields: <code>{devName}</code>, <code>{eveMac}</code>, <code>{devVendor}</code>, <code>{eveIp}</code>, <code>{eveDateTime}</code>, <code>{eveEventType}</code>, <code>{devComments}</code>."
"string": "Custom text template for reconnected device notifications. Use <code>{FieldName}</code> placeholders, e.g. <code>{devName} ({eveMac}) reconnected at {eveDateTime}</code>. Leave empty for default formatting. Available fields: <code>{devName}</code>, <code>{eveMac}</code>, <code>{devVendor}</code>, <code>{eveIp}</code>, <code>{eveDateTime}</code>, <code>{eveEventType}</code>, <code>{devComments}</code>, <code>{devVlan}</code>, <code>{devSite}</code>, <code>{devSSID}</code>."
}
]
},
@@ -282,7 +282,7 @@
"type": {
"dataType": "string",
"elements": [
{ "elementType": "input", "elementOptions": [], "transformers": [] }
{ "elementType": "textarea", "elementOptions": [], "transformers": [] }
]
},
"default_value": "",
@@ -297,7 +297,7 @@
"description": [
{
"language_code": "en_us",
"string": "Custom text template for event notifications. Use <code>{FieldName}</code> placeholders, e.g. <code>{devName} ({eveMac}) {eveEventType} at {eveDateTime}</code>. Leave empty for default formatting. Available fields: <code>{devName}</code>, <code>{eveMac}</code>, <code>{devVendor}</code>, <code>{eveIp}</code>, <code>{eveDateTime}</code>, <code>{eveEventType}</code>, <code>{devComments}</code>."
"string": "Custom text template for event notifications. Use <code>{FieldName}</code> placeholders, e.g. <code>{devName} ({eveMac}) {eveEventType} at {eveDateTime}</code>. Leave empty for default formatting. Available fields: <code>{devName}</code>, <code>{eveMac}</code>, <code>{devVendor}</code>, <code>{eveIp}</code>, <code>{eveDateTime}</code>, <code>{eveEventType}</code>, <code>{devComments}</code>, <code>{devVlan}</code>, <code>{devSite}</code>, <code>{devSSID}</code>."
}
]
},
@@ -306,7 +306,7 @@
"type": {
"dataType": "string",
"elements": [
{ "elementType": "input", "elementOptions": [], "transformers": [] }
{ "elementType": "textarea", "elementOptions": [], "transformers": [] }
]
},
"default_value": "",

View File

@@ -20,6 +20,7 @@ __version__ = 0.1 # Initial version
__version__ = 0.2 # Rephrased error messages, improved logging and code logic
__version__ = 0.3 # Refactored data collection into a class, improved code clarity with comments
__version__ = 0.4 # Fix for https://github.com/netalertx/NetAlertX/issues/1595 - Omada Controller versions >= 6.2.0.0 removed the v1 clients endpoint
__version__ = 0.5 # Fix for https://github.com/netalertx/NetAlertX/issues/1717 - preventing None as IP
import os
import sys
@@ -168,6 +169,10 @@ class OmadaHelper:
entry["ip_address"] = data.get("ip")
entry["name"] = data.get("name")
# Preventing None as IP
if entry["ip_address"] is None or entry["ip_address"] == "None":
entry["ip_address"] = "null"
# Assign the last datetime the device/client was seen on the network
last_seen = OmadaHelper.timestamp_to_datetime(data.get("lastSeen", 0), timezone)
entry["last_seen"] = last_seen.get("response_result") if isinstance(last_seen, dict) and last_seen.get("response_type") == "success" else ""

View File

@@ -1171,7 +1171,8 @@
"devSite",
"devType",
"devParentMAC",
"devParentPort"
"devParentPort",
"devVlan"
],
"localized": [
"name",

View File

@@ -439,7 +439,7 @@ $settingsJSON_DB = json_encode($settings, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX
// constructing final HTML for the setting
setHtml = ""
if(set["setGroup"] == prefix)
if(set["setGroup"] == prefix )
{
// hide metadata by default by assigning it a special class
isMetadata ? metadataClass = 'metadata' : metadataClass = '';

View File

@@ -87,6 +87,12 @@ require 'php/templates/header.php';
result = result.split('+')[0]; // Remove timezone offset
}
// Keep sortable value for DataTables
if (type === "sort" || type === "type" || type !== "display") {
return result;
}
// Display localized date only
result = localizeTimestamp(result);
return result;

View File

@@ -7,7 +7,7 @@ set -euo pipefail
#
# update_vendors.sh - Back module. IEEE Vendors db update
# ------------------------------------------------------------------------------
# Puche 2021 / 2022+ jokob jokob@duck.com GNU GPLv3
# Puche 2021 / 2022+ jokob support@netalertx.com GNU GPLv3
# ------------------------------------------------------------------------------
# ----------------------------------------------------------------------

View File

@@ -6,7 +6,7 @@
#
# Back module. Network scanner
# -------------------------------------------------------------------------------
# Puche 2021 / 2022+ jokob jokob@duck.com GNU GPLv3
# Puche 2021 / 2022+ jokob support@netalertx.com GNU GPLv3
# -------------------------------------------------------------------------------

View File

@@ -52,7 +52,10 @@ SQL_TEMPLATES = {
devLastIP as eveIp,
eveDateTime,
eveEventType,
devComments
devComments,
devVlan,
devSite,
devSSID
FROM Events_Devices
WHERE evePendingAlertEmail = 1
AND eveEventType = 'New Device' {condition}
@@ -66,7 +69,10 @@ SQL_TEMPLATES = {
eveIp,
eveDateTime,
eveEventType,
devComments
devComments,
devVlan,
devSite,
devSSID
FROM Events_Devices AS down_events
WHERE evePendingAlertEmail = 1
AND down_events.eveEventType = 'Device Down'
@@ -88,7 +94,10 @@ SQL_TEMPLATES = {
reconnected_devices.eveIp,
reconnected_devices.eveDateTime,
reconnected_devices.eveEventType,
devComments
devComments,
devVlan,
devSite,
devSSID
FROM Events_Devices AS reconnected_devices
WHERE reconnected_devices.eveEventType = 'Down Reconnected'
AND reconnected_devices.evePendingAlertEmail = 1
@@ -109,7 +118,10 @@ SQL_TEMPLATES = {
devLastIP as eveIp,
eveDateTime,
eveEventType,
devComments
devComments,
devVlan,
devSite,
devSSID
FROM Events_Devices
WHERE evePendingAlertEmail = 1
AND eveEventType IN ({event_types}) {condition}

View File

@@ -446,7 +446,7 @@ class DeviceInstance:
# Join all sub-selects with commas
query = "SELECT\n " + ",\n ".join(sub_queries)
mylog('none', [f'[getNamedTotals] query {query}'])
mylog('trace', [f'[getNamedTotals] query {query}'])
json_obj = get_table_json(sql, query, parameters=None)
return json_obj