DOCS+FE+BE: cleaner versioning retrieval, ICMP, plugin debug docs

Signed-off-by: jokob-sk <jokob.sk@gmail.com>
This commit is contained in:
jokob-sk
2026-01-28 21:56:20 +11:00
parent e46f556df7
commit ecd0ca89c7
10 changed files with 80 additions and 47 deletions

View File

@@ -1,7 +1,7 @@
# Troubleshooting plugins
> [!TIP]
> Before troubleshooting, please ensure you have the right [Debugging and LOG_LEVEL set](./DEBUG_TIPS.md).
> Before troubleshooting, please ensure you have the right [Debugging and LOG_LEVEL set](./DEBUG_TIPS.md) in Settings.
## High-level overview
@@ -22,10 +22,25 @@ For a more in-depth overview on how plugins work check the [Plugins development
#### Incorrect input data
Input data from the plugin might cause mapping issues in specific edge cases. Look for a corresponding section in the `app.log` file, for example notice the first line of the execution run of the `PIHOLE` plugin below:
Input data from the plugin might cause mapping issues in specific edge cases. Look for a corresponding section in the `app.log` file, and search for `[Scheduler] run for PLUGINNAME: YES`, so for ICMP you would look for `[Scheduler] run for ICMP: YES`. You can find examples of useful logs below. If your issue is related to a plugin, and you don't include a log section with this data, we can't help you to resolve your issue.
##### ICMP log example
```
17:31:05 [Scheduler] - Scheduler run for PIHOLE: YES
20:39:04 [Scheduler] run for ICMP: YES
20:39:04 [ICMP] fping skipping 192.168.1.124 : [2], timed out (NaN avg, 100% loss)
20:39:04 [ICMP] adding 192.168.1.123 from 192.168.1.123 : [2], 64 bytes, 20.1 ms (8.22 avg, 0% loss)
20:39:04 [ICMP] fping skipping 192.168.1.157 : [1], timed out (NaN avg, 100% loss)
20:39:04 [ICMP] adding 192.168.1.79 from 192.168.1.79 : [2], 64 bytes, 48.3 ms (60.9 avg, 0% loss)
20:39:04 [ICMP] fping skipping 192.168.1.128 : [2], timed out (NaN avg, 100% loss)
20:39:04 [ICMP] fping skipping 192.168.1.129 : [2], timed out (NaN avg, 100% loss)
```
##### PIHOLE log example
```
17:31:05 [Scheduler] run for PIHOLE: YES
17:31:05 [Plugin utils] ---------------------------------------------
17:31:05 [Plugin utils] display_name: PiHole (Device sync)
17:31:05 [Plugins] CMD: SELECT n.hwaddr AS Object_PrimaryID, {s-quote}null{s-quote} AS Object_SecondaryID, datetime() AS DateTime, na.ip AS Watched_Value1, n.lastQuery AS Watched_Value2, na.name AS Watched_Value3, n.macVendor AS Watched_Value4, {s-quote}null{s-quote} AS Extra, n.hwaddr AS ForeignKey FROM EXTERNAL_PIHOLE.Network AS n LEFT JOIN EXTERNAL_PIHOLE.Network_Addresses AS na ON na.network_id = n.id WHERE n.hwaddr NOT LIKE {s-quote}ip-%{s-quote} AND n.hwaddr is not {s-quote}00:00:00:00:00:00{s-quote} AND na.ip is not null
@@ -60,7 +75,7 @@ Input data from the plugin might cause mapping issues in specific edge cases. Lo
17:31:05 [API] Update API starting
17:31:06 [API] Updating table_plugins_history.json file in /api
```
> [!NOTE]
> The debug output between the 🔻red arrows🔺 is important for debugging (arrows added only to highlight the section on this page, they are not available in the actual debug log)
In the above output notice the section logging how many events are produced by the plugin:
@@ -80,12 +95,11 @@ These values, if formatted correctly, will also show up in the UI:
![Plugins table](./img/DEBUG_PLUGINS/plugin_objects_pihole.png)
### Sharing application state
Sometimes specific log sections are needed to debug issues. The Devices and CurrentScan table data is sometimes needed to figure out what's wrong.
1. Please set `LOG_LEVEL` to `trace` (Disable it once you have the info as this produces big log files).
1. Please set `LOG_LEVEL` to `trace` in the Settings (Disable it once you have the info as this produces big log files).
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.

View File

@@ -42,7 +42,9 @@ ARPSCAN_DURATION=30
### ✅ Add ICMP (Ping) Scanning
Enable the `ICMP` scan plugin to complement ARP detection. ICMP is often more reliable for detecting active hosts, especially when ARP fails.
Enable the `ICMP` scan plugin to complement ARP detection. ICMP is often more reliable for detecting active hosts, especially when ARP fails.
> [!IMPORTANT] If using AdGuard/Pi-hole: If devices still show offline after enabling ICMP, temporarily disable your content blocker. If the issue disappears, whitelist the NetAlertX host IP in your blocker's settings to prevent pings from being dropped.
### ✅ Use Multiple Detection Methods
@@ -52,7 +54,7 @@ A combined approach greatly improves detection robustness:
* `ICMP` (ping)
* `NMAPDEV` (nmap)
This hybrid strategy increases reliability, especially for down detection and alerting. See [other plugins](./PLUGINS.md) that might be compatible with your setup. See benefits and drawbacks of individual scan methods in their respective docs.
This hybrid strategy increases reliability, especially for down detection and alerting. See [other plugins](./PLUGINS.md) that might be compatible with your setup. See benefits and drawbacks of individual scan methods in their respective docs.
## Results
@@ -76,4 +78,4 @@ After increasing the ARP timeout and adding ICMP scanning (on select IP ranges),
Let us know in the [NetAlertX Discussions](https://github.com/jokob-sk/NetAlertX/discussions) if you have further feedback or edge cases.
See also [Remote Networks](./REMOTE_NETWORKS.md) for more advanced setups.
See also [Remote Networks](./REMOTE_NETWORKS.md) for more advanced setups.

View File

@@ -57,7 +57,7 @@
</div>
<div class="box-body">
<div class="chart">
<script src="lib/chart.js/Chart.js?v=<?php include 'php/templates/version.php'; ?>"></script>
<script src="lib/chart.js/Chart.js"></script>
<!-- presence chart -->
<?php
require 'php/components/graph_online_history.php';

View File

@@ -128,14 +128,31 @@ class NetAlertXStateManager {
}
/**
* Handle state update from SSE
* Handle state update from SSE or Polling
*/
handleStateUpdate(appState) {
try {
if (document.getElementById("state")) {
const cleanState = appState["currentState"].replaceAll('"', "");
document.getElementById("state").innerHTML = cleanState;
// 1. Update the main status text
if (appState["currentState"]) {
const cleanState = appState["currentState"].replace(/"/g, "");
$("#state").html(cleanState);
}
// 2. Update Version placeholders
const version = appState["appVersion"] || "UNKNOWN";
$('[data-plc="version"]')
.html(version)
.attr('data-version', version);
// 3. Update Build Timestamp placeholders
const buildTime = appState["buildTimestamp"] !== undefined ? appState["buildTimestamp"] : "";
const displayTime = (buildTime === 0) ? "UNKNOWN" : buildTime;
$('[data-plc="build-timestamp"]')
.html(displayTime)
.attr('data-build-time', buildTime);
console.log("[NetAlertX State] UI updated via jQuery");
} catch (e) {
console.error("[NetAlertX State] Failed to update state display:", e);
}

View File

@@ -66,21 +66,24 @@ $db->close();
<a href="https://docs.netalertx.com/VERSIONS" target="_blank"> <span><i class="fa fa-circle-question"></i></a></span>
</div>
<div class="db_info_table_cell">
<div class="version" id="version" data-build-time="<?php echo file_get_contents( "buildtimestamp.txt");?>">
<?php echo '<span id="new-version-text" class="myhidden"><i class="fa-solid fa-rocket fa-beat"></i> ' .lang('Maintenance_new_version').'</span>'.'<span id="current-version-text" class="myhidden">' .lang('Maintenance_current_version').'</span>';?>
<div class="version" id="version" data-build-time="">
<span id="new-version-text" class="myhidden"><i class="fa-solid fa-rocket fa-beat"></i>
<?php echo lang('Maintenance_new_version');?> </span> <span id="current-version-text" class="myhidden">
<?php echo lang('Maintenance_current_version') ;?> </span>
</div>
</div>
</div>
<div class="db_info_table_row">
<div class="db_info_table_cell" style="min-width: 140px"><?= lang('Maintenance_built_on');?></div>
<div class="db_info_table_cell">
<?php echo date("Y-m-d", ((int)file_get_contents( "buildtimestamp.txt")));?>
<span data-plc="build-timestamp"></span>
</div>
</div>
<div class="db_info_table_row">
<div class="db_info_table_cell" style="min-width: 140px"><?= lang('Maintenance_Running_Version');?></div>
<div class="db_info_table_cell">
<?php include 'php/templates/version.php'; ?>
<span data-plc="version"></span>
</div>
</div>
<div class="db_info_table_row">

View File

@@ -1,21 +0,0 @@
<?php
#---------------------------------------------------------------------------------#
# NetAlertX #
# Open Source Network Guard / WIFI & LAN intrusion detector #
# #
# build.php - Templates module Template to display the current build version #
#---------------------------------------------------------------------------------#
# Puche 2021 pi.alert.application@gmail.com GNU GPLv3 #
# jokob-sk 2022 jokob.sk@gmail.com GNU GPLv3 #
# leiweibau 2022 https://github.com/leiweibau GNU GPLv3 #
# cvc90 2023 https://github.com/cvc90 GNU GPLv3 #
#---------------------------------------------------------------------------------#
$file = "/app/front/buildtimestamp.txt";
if (file_exists($file)) {
echo date("Y-m-d", ((int)file_get_contents($file)));
}
else {
echo "File not found";
}
?>

View File

@@ -32,8 +32,8 @@
| <a href="https://docs.netalertx.com/" class="pointer" target="_blank" title="Documentation"><i class="fa fa-book"></i></a>
| <a href="https://github.com/jokob-sk/NetAlertX/issues" class="pointer" target="_blank"><i class="fa fa-bug" title="Report a bug"></i></a>
| <a href="https://discord.com/invite/NczTUTWyRr" class="pointer" target="_blank"><i class="fa-brands fa-discord" title="Join Discord"></i></a>
| <?= lang('Maintenance_built_on');?>: <?php include 'php/templates/build.php'; ?>
| Version: <?php include 'php/templates/version.php'; ?>
| <?= lang('Maintenance_built_on');?>: <span data-plc="build-timestamp"></span>
| Version: <span data-plc="version"></span>
|
</div>
</footer>

View File

@@ -13,7 +13,7 @@ INSTALL_PATH = os.getenv("NETALERTX_APP", "/app")
sys.path.extend([f"{INSTALL_PATH}/front/plugins", f"{INSTALL_PATH}/server"])
from logger import mylog # noqa: E402 [flake8 lint suppression]
from helper import get_setting_value, get_env_setting_value # noqa: E402 [flake8 lint suppression]
from helper import get_setting_value, get_env_setting_value, getBuildTimeStampAndVersion # noqa: E402 [flake8 lint suppression]
from db.db_helper import get_date_from_period # noqa: E402 [flake8 lint suppression]
from app_state import updateState # noqa: E402 [flake8 lint suppression]
@@ -1809,6 +1809,9 @@ def start_server(graphql_port, app_state):
)
thread.start()
# Pass Application "VERSION" into the app_state
buildTimestamp, newBuildVersion = getBuildTimeStampAndVersion()
# Update the state to indicate the server has started
app_state = updateState("Process: Idle", None, None, None, 1)

View File

@@ -42,7 +42,8 @@ class app_state_class:
graphQLServerStarted=0,
processScan=False,
pluginsStates=None,
appVersion=None
appVersion=None,
buildTimestamp=None
):
"""
Initialize the application state, optionally overwriting previous values.
@@ -59,6 +60,7 @@ class app_state_class:
processScan (bool, optional): Initial processScan flag.
pluginsStates (dict, optional): Initial plugin states to merge with previous state.
appVersion (str, optional): Application version.
buildTimestamp (str, optional): ABuild timestamp.
"""
# json file containing the state to communicate with the frontend
stateFile = apiPath + "app_state.json"
@@ -86,6 +88,7 @@ class app_state_class:
self.currentState = previousState.get("currentState", "Init")
self.pluginsStates = previousState.get("pluginsStates", {})
self.appVersion = previousState.get("appVersion", "")
self.buildTimestamp = previousState.get("buildTimestamp", "")
else: # init first time values
self.settingsSaved = 0
self.settingsImported = 0
@@ -97,6 +100,7 @@ class app_state_class:
self.currentState = "Init"
self.pluginsStates = {}
self.appVersion = ""
self.buildTimestamp = ""
# Overwrite with provided parameters if supplied
if settingsSaved is not None:
@@ -127,6 +131,8 @@ class app_state_class:
self.pluginsStates[plugin] = state
if appVersion is not None:
self.appVersion = appVersion
if buildTimestamp is not None:
self.buildTimestamp = buildTimestamp
# check for new version every hour and if currently not running new version
if self.isNewVersion is False and self.isNewVersionChecked + 3600 < int(
timeNow().timestamp()
@@ -154,7 +160,13 @@ class app_state_class:
# Broadcast state change via SSE if available
try:
broadcast_state_update(self.currentState, self.settingsImported, timestamp=self.lastUpdated)
broadcast_state_update(
self.currentState,
self.settingsImported,
timestamp=self.lastUpdated,
appVersion=self.appVersion,
buildTimestamp=self.buildTimestamp
)
except Exception as e:
mylog("none", [f"[app_state] SSE broadcast: {e}"])
@@ -170,7 +182,8 @@ def updateState(newState = None,
graphQLServerStarted = None,
processScan = None,
pluginsStates=None,
appVersion=None):
appVersion=None,
buildTimestamp=None):
"""
Convenience method to create or update the app state.
@@ -183,6 +196,7 @@ def updateState(newState = None,
processScan (bool, optional): Flag indicating if a scan is active.
pluginsStates (dict, optional): Plugin state updates.
appVersion (str, optional): Application version.
buildTimestamp (str, optional): Build timestamp.
Returns:
app_state_class: Updated state object.
@@ -195,7 +209,8 @@ def updateState(newState = None,
graphQLServerStarted,
processScan,
pluginsStates,
appVersion
appVersion,
buildTimestamp
)

View File

@@ -715,7 +715,7 @@ def importConfigs(pm, db, all_plugins):
# settingsImported = None (timestamp),
# showSpinner = False (1/0),
# graphQLServerStarted = 1 (1/0))
updateState("Config imported", conf.lastImportedConfFile, conf.lastImportedConfFile, False, 1, None, None, new_version)
updateState("Config imported", conf.lastImportedConfFile, conf.lastImportedConfFile, False, 1, None, None, new_version, buildTimestamp)
msg = '[Config] Imported new settings config'
mylog('minimal', msg)