Compare commits

..

34 Commits

Author SHA1 Message Date
jokob-sk
4bb87fe8df Merge branch 'main' of https://github.com/jokob-sk/NetAlertX
Some checks failed
Code checks / check-url-paths (push) Has been cancelled
docker / docker_dev (push) Has been cancelled
Deploy MkDocs / deploy (push) Has been cancelled
2025-04-13 10:29:20 +10:00
jokob-sk
71bcbbe7f9 fixes 2025-04-13 10:29:07 +10:00
Massimo Pissarello
f941133304 Translated using Weblate (Italian)
Some checks failed
Code checks / check-url-paths (push) Has been cancelled
docker / docker_dev (push) Has been cancelled
Deploy MkDocs / deploy (push) Has been cancelled
Currently translated at 100.0% (743 of 743 strings)

Translation: NetAlertX/core
Translate-URL: https://hosted.weblate.org/projects/pialert/core/it/
2025-04-11 07:07:45 +02:00
Jokob @NetAlertX
470997fcde Merge pull request #1045 from LeoRX/patch-1
Some checks are pending
Code checks / check-url-paths (push) Waiting to run
docker / docker_dev (push) Waiting to run
Deploy MkDocs / deploy (push) Waiting to run
Update DOCKER_COMPOSE.md
2025-04-11 10:43:27 +10:00
LeoRX
d6b2ac587f Update DOCKER_COMPOSE.md
Fix spelling
2025-04-11 09:47:18 +10:00
jokob-sk
e6962e0393 docs + auto lables on GH issues
Some checks failed
Code checks / check-url-paths (push) Has been cancelled
docker / docker_dev (push) Has been cancelled
Deploy MkDocs / deploy (push) Has been cancelled
2025-04-06 10:48:27 +10:00
Jokob @NetAlertX
426dd48540 Update setup-help.yml 2025-04-06 10:39:58 +10:00
Jokob @NetAlertX
7da11d167d Update i-have-an-issue.yml 2025-04-06 10:38:39 +10:00
jokob-sk
40e090c5c6 docs 2025-04-06 09:38:23 +10:00
jokob-sk
3ccb165658 docs + code checks + deviceDetails delay 2025-04-06 08:40:25 +10:00
jokob-sk
a12da278c6 docs + #1042
Some checks are pending
URL Path Check / check-url-paths (push) Waiting to run
docker / docker_dev (push) Waiting to run
Deploy MkDocs / deploy (push) Waiting to run
2025-04-05 08:04:31 +11:00
Jokob @NetAlertX
ffb0d0238d Merge pull request #1041 from Tlaloc-Es/patch-1
Some checks are pending
URL Path Check / check-url-paths (push) Waiting to run
docker / docker_dev (push) Waiting to run
Deploy MkDocs / deploy (push) Waiting to run
Update manager.py
2025-04-04 19:43:15 +11:00
Joseba Fuentes
599603d9ff Update manager.py 2025-04-04 10:25:41 +02:00
Safeguard
befb58619b Translated using Weblate (Russian)
Some checks are pending
URL Path Check / check-url-paths (push) Waiting to run
docker / docker_dev (push) Waiting to run
Deploy MkDocs / deploy (push) Waiting to run
Currently translated at 100.0% (743 of 743 strings)

Translation: NetAlertX/core
Translate-URL: https://hosted.weblate.org/projects/pialert/core/ru/
2025-04-04 02:33:44 +02:00
Safeguard
f83cdc766b Translated using Weblate (Russian)
Some checks are pending
URL Path Check / check-url-paths (push) Waiting to run
docker / docker_dev (push) Waiting to run
Deploy MkDocs / deploy (push) Waiting to run
Currently translated at 100.0% (743 of 743 strings)

Translation: NetAlertX/core
Translate-URL: https://hosted.weblate.org/projects/pialert/core/ru/
2025-04-03 12:13:45 +02:00
jokob-sk
6fb1547fc4 wf work
Some checks are pending
URL Path Check / check-url-paths (push) Waiting to run
docker / docker_dev (push) Waiting to run
Deploy MkDocs / deploy (push) Waiting to run
2025-04-03 07:52:50 +11:00
jokob-sk
ea9a07d29e Merge branch 'main' of https://github.com/jokob-sk/NetAlertX 2025-04-03 07:52:33 +11:00
jokob-sk
2889be28e4 wf work 2025-04-03 07:51:59 +11:00
Sylvain Pichon
46a8bb66e7 Translated using Weblate (French)
Some checks failed
URL Path Check / check-url-paths (push) Has been cancelled
docker / docker_dev (push) Has been cancelled
Deploy MkDocs / deploy (push) Has been cancelled
Currently translated at 100.0% (743 of 743 strings)

Translation: NetAlertX/core
Translate-URL: https://hosted.weblate.org/projects/pialert/core/fr/
2025-04-01 10:18:08 +02:00
GitHub Actions
578a6d0d48 Add release tweet for v25.4.1 - 🔀 Workflows - automate device management
Some checks are pending
URL Path Check / check-url-paths (push) Waiting to run
docker / docker_dev (push) Waiting to run
Deploy MkDocs / deploy (push) Waiting to run
2025-03-31 21:48:09 +00:00
jokob-sk
e1f9ca05b7 wf docs 2025-04-01 08:20:00 +11:00
Максим Горпиніч
4aaf86f0fc Translated using Weblate (Ukrainian)
Some checks are pending
URL Path Check / check-url-paths (push) Waiting to run
docker / docker_dev (push) Waiting to run
Deploy MkDocs / deploy (push) Waiting to run
Currently translated at 100.0% (743 of 743 strings)

Translation: NetAlertX/core
Translate-URL: https://hosted.weblate.org/projects/pialert/core/uk/
2025-03-31 13:27:45 +02:00
jokob-sk
9bb21ad303 wf docs 2025-03-31 18:46:55 +11:00
jokob-sk
e1197eb3f8 wf work + docs 2025-03-31 18:05:15 +11:00
jokob-sk
2c445ccaeb wf work + docs 2025-03-31 18:04:56 +11:00
jokob-sk
8a07f7067b Merge branch 'main' of https://github.com/jokob-sk/NetAlertX
Some checks are pending
URL Path Check / check-url-paths (push) Waiting to run
docker / docker_dev (push) Waiting to run
Deploy MkDocs / deploy (push) Waiting to run
2025-03-31 08:12:57 +11:00
jokob-sk
d86c2a5023 Move ObjectGUID to the end 2025-03-31 08:12:32 +11:00
Максим Горпиніч
2b51674e52 Translated using Weblate (Ukrainian)
Currently translated at 100.0% (737 of 737 strings)

Translation: NetAlertX/core
Translate-URL: https://hosted.weblate.org/projects/pialert/core/uk/
2025-03-30 19:39:24 +02:00
Sylvain Pichon
eb6820dd93 Translated using Weblate (French)
Currently translated at 100.0% (737 of 737 strings)

Translation: NetAlertX/core
Translate-URL: https://hosted.weblate.org/projects/pialert/core/fr/
2025-03-30 19:39:24 +02:00
Ettore Atalan
b156246cb0 Translated using Weblate (German)
Currently translated at 89.2% (658 of 737 strings)

Translation: NetAlertX/core
Translate-URL: https://hosted.weblate.org/projects/pialert/core/de/
2025-03-30 19:39:24 +02:00
Hosted Weblate
716c6a4046 Merge branch 'origin/main' into Weblate.
Some checks are pending
URL Path Check / check-url-paths (push) Waiting to run
docker / docker_dev (push) Waiting to run
Deploy MkDocs / deploy (push) Waiting to run
2025-03-30 08:30:09 +02:00
Massimo Pissarello
114b5a2621 Translated using Weblate (Italian)
Currently translated at 100.0% (737 of 737 strings)

Translation: NetAlertX/core
Translate-URL: https://hosted.weblate.org/projects/pialert/core/it/
2025-03-30 08:30:09 +02:00
jokob-sk
02b19c833e Merge branch 'main' of https://github.com/jokob-sk/NetAlertX 2025-03-30 17:29:56 +11:00
jokob-sk
e0c06548ba wf work + fa upgrade + css cleanup + network tweaks 2025-03-30 17:29:22 +11:00
70 changed files with 842 additions and 1909 deletions

View File

@@ -59,12 +59,15 @@ body:
validations:
required: false
- type: dropdown
id: installation_type
attributes:
label: What installation are you running?
options:
- Production (netalertx)
- Dev (netalertx-dev)
- Home Assistant (addon)
- Home Assistant fa (full-access addon)
- Bare-metal (community only support - Check Discord)
validations:
required: true
- type: textarea

View File

@@ -44,12 +44,15 @@ body:
validations:
required: false
- type: dropdown
id: installation_type
attributes:
label: What installation are you running?
options:
- Production (netalertx)
- Dev (netalertx-dev)
- Home Assistant (addon)
- Home Assistant fa (full-access addon)
- Bare-metal (community only support - Check Discord)
validations:
required: true
- type: textarea

2
.github/tweet.md vendored Executable file
View File

@@ -0,0 +1,2 @@
🎉 New release: **v25.4.1 - 🔀 Workflows - automate device management ** is live! 🚀
Check it out here: https://github.com/jokob-sk/NetAlertX/releases/tag/v25.4.1

View File

@@ -1,4 +1,4 @@
name: URL Path Check
name: Code checks
on:
push:
branches:
@@ -25,4 +25,9 @@ jobs:
else
echo "✅ No absolute path URLs found."
fi
- name: Check Python syntax
run: |
set -e
echo "🔍 Checking Python syntax..."
find . -name "*.py" -print0 | xargs -0 -n1 python3 -m py_compile

43
.github/workflows/label-issues.yml vendored Executable file
View File

@@ -0,0 +1,43 @@
name: Label Issues by Installation Type
on:
issues:
types: [opened]
permissions:
issues: write
jobs:
add-label:
runs-on: ubuntu-latest
steps:
- name: Get issue content
uses: actions/github-script@v7
with:
script: |
const body = context.payload.issue.body;
const lowerBody = body.toLowerCase();
let labelsToAdd = [];
if (lowerBody.includes('bare-metal')) {
labelsToAdd.push('bare-metal ❗');
}
if (lowerBody.includes('home assistant')) {
labelsToAdd.push('Home Assistant 🏠');
}
if (lowerBody.includes('production (netalertx)') || lowerBody.includes('dev (netalertx-dev)')) {
labelsToAdd.push('Docker 🐋');
}
if (labelsToAdd.length > 0) {
await github.rest.issues.addLabels({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
labels: labelsToAdd
});
}

View File

@@ -41,6 +41,10 @@ Send notifications to more than 80+ services, including Telegram via [Apprise](h
Feed your data and device changes into [Home Assistant](https://github.com/jokob-sk/NetAlertX/blob/main/docs/HOME_ASSISTANT.md), read [API endpoints](https://github.com/jokob-sk/NetAlertX/blob/main/docs/API.md), or use [Webhooks](https://github.com/jokob-sk/NetAlertX/blob/main/docs/WEBHOOK_N8N.md) to setup custom automation flows. You can also
build your own scanners with the [Plugin system](https://github.com/jokob-sk/NetAlertX/tree/main/docs/PLUGINS.md#readme) in as little as [15 minutes](https://www.youtube.com/watch?v=cdbxlwiWhv8).
### Workflows
The [workflows module](https://github.com/jokob-sk/NetAlertX/blob/main/docs/WORKFLOWS.md) in NetAlertX allows to automate repetitive tasks, making network management more efficient. Whether you need to assign newly discovered devices to a specific Network Node, auto-group devices from a given vendor, unarchive a device if detected online, or automatically delete devices, this module provides the flexibility to tailor the automations to your needs.
## 📚 Documentation
<!--- --------------------------------------------------------------------- --->

View File

@@ -2,7 +2,7 @@
## High-level overview
If a Plugin supplies data to the main app it's doine either vie a SQL query or via a script that updates the `last_result.log` file in the plugin folder (`front/plugins/<plugin>`).
If a Plugin supplies data to the main app it's done either vie a SQL query or via a script that updates the `last_result.log` file in the plugin log folder (`app/log/plugins/`).
For a more in-depth overview on how plugins work check the [Plugins development docs](https://github.com/jokob-sk/NetAlertX/blob/main/docs/PLUGINS.md).

View File

@@ -1,7 +1,7 @@
# `docker-compose.yaml` Examples
> [!NOTE]
> The container needs to run in `network_mode:"host"`.
> The container needs to run in `network_mode:"host"`. This also means that not all functionality is supported on a Windows host as Docker for Windows doesn't support this networking option.
### Example 1
@@ -103,3 +103,37 @@ DEV_LOCATION=/path/to/local/source/code
```
To run the container execute: `sudo docker-compose --env-file /path/to/.env up`
### Example 4: Docker swarm
Notice how the host network is defined in a swarm setup:
```yaml
services:
netalertx:
# Use the below line if you want to test the latest dev image
# image: "jokobsk/netalertx-dev:latest"
image: "ghcr.io/jokob-sk/netalertx:latest"
volumes:
- /mnt/MYSERVER/netalertx/config:/config:rw
- /mnt/MYSERVER/netalertx/db:/netalertx/db:rw
- /mnt/MYSERVER/netalertx/logs:/netalertx/front/log:rw
environment:
- TZ=Europe/London
- PORT=20211
networks:
- outside
deploy:
mode: replicated
replicas: 1
restart_policy:
condition: on-failure
networks:
outside:
external:
name: "host"
```

View File

@@ -3,7 +3,7 @@
NetAlertX comes with MQTT support, allowing you to show all detected devices as devices in Home Assistant. It also supplies a collection of stats, such as number of online devices.
> [!TIP]
> You can install NetAlertX also as a Home Assistant addon [![Home Assistant](https://img.shields.io/badge/Repo-blue?logo=home-assistant&style=for-the-badge&color=0aa8d2&logoColor=fff&label=Add)](https://my.home-assistant.io/redirect/supervisor_add_addon_repository/?repository_url=https%3A%2F%2Fgithub.com%2Falexbelgium%2Fhassio-addons) via the [alexbelgium/hassio-addons](https://github.com/alexbelgium/hassio-addons/). This is only possible if you run a supervised instance of Home Assistant. If not, you can still run NetAlertX in a separate Docker container and follow this guide to configure MQTT.
> You can install NetAlertX also as a Home Assistant addon [![Home Assistant](https://img.shields.io/badge/Repo-blue?logo=home-assistant&style=for-the-badge&color=0aa8d2&logoColor=fff&label=Add)](https://my.home-assistant.io/redirect/supervisor_add_addon_repository/?repository_url=https%3A%2F%2Fgithub.com%2Falexbelgium%2Fhassio-addons) via the [alexbelgium/hassio-addons](https://github.com/alexbelgium/hassio-addons/) repository. This is only possible if you run a supervised instance of Home Assistant. If not, you can still run NetAlertX in a separate Docker container and follow this guide to configure MQTT.
## ⚠ Note

View File

@@ -2,7 +2,7 @@
## Installation options
NetAlertX can be installed several ways. The best supported option is Docker, followed by a supervised the Home Assistant instance, as an Unraid app and lastly on bare metal.
NetAlertX can be installed several ways. The best supported option is Docker, followed by a supervised Home Assistant instance, as an Unraid app, and lastly, on bare metal.
- [[Installation] Docker (recommended)](https://github.com/jokob-sk/NetAlertX/blob/main/dockerfiles/README.md)
- [[Installation] Home Assistant](https://github.com/alexbelgium/hassio-addons/tree/master/netalertx)

26
docs/LOGGING.md Executable file
View File

@@ -0,0 +1,26 @@
# Logging
NetAlertX comes with several logs that help to identify application issues.
For plugin-specific log debugging, please read the [Debug Plugins](./DEBUG_PLUGINS.md) guide.
When debugging any issue, increase the `LOG_LEVEL` Setting as per the [Debug tips](./DEBUG_TIPS.md) documentation.
## Main logs
You can find most of the logs exposed in the UI under _Maintenance -> Logs_.
If the UI is inaccessible, you can access them under `/app/log`.
![Logs](./img/LOGGING/maintenance_logs.png)
In the _Maintennace -> Logs_ you can **Purge logs**, download the full log file or Filter the lines with some substring to narrow down your search.
## Plugin logging
If a Plugin supplies data to the main app it's done either vie a SQL query or via a script that updates the `last_result.log` file in the plugin log folder (`app/log/plugins/`). These files are processed at the end of the scan and deleted on successful processing.
The data is in most of the cases then displayed in the application under _Integrations -> Plugins_ (or _Device -> Plugins_ if the plugin is supplying device-specific data).
![Plugin objects](./img/LOGGING/logging_integrations_plugins.png)

47
docs/NAME_RESOLUTION.md Executable file
View File

@@ -0,0 +1,47 @@
# Device Name Resolution
Name resolution in NetAlertX relies on multiple plugins to resolve device names from IP addresses. If you are seeing `(name not found)` as device names, follow these steps to diagnose and fix the issue.
## Required Plugins
For best results, ensure the following name resolution plugins are enabled:
- **AVAHISCAN** Uses mDNS/Avahi to resolve local network names.
- **NBTSCAN** Queries NetBIOS to find device names.
- **NSLOOKUP** Performs standard DNS lookups.
You can check which plugins are active in your _Settings_ section and enable any that are missing.
There are other plugins that can supply device names as well, but they rely on bespoke hardware and services. See [Plugins overview](./PLUGINS.md) for details and look for plugins with name discovery (🆎) features.
## Checking Logs
If names are not resolving, check the logs for errors or timeouts.
See how to explore logs in the [Logging guide](./LOGGING.md).
Logs will show which plugins attempted resolution and any failures encountered.
## Adjusting Timeout Settings
If resolution is slow or failing due to timeouts, increase the timeout settings in your configuration, for example.
```ini
NSLOOKUP_RUN_TIMEOUT = 30
```
Raising the timeout may help if your network has high latency or slow DNS responses.
## Checking Plugin Objects
Each plugin stores results in its respective object. You can inspect these objects to see if they contain valid name resolution data.
See [Logging guide](./LOGGING.md) and [Debug plugins](./DEBUG_PLUGINS.md) guides for details.
If the object contains no results, the issue may be with DNS settings or network access.
## Improving name resolution
For more details how to improve name resolution refer to the
[Reverse DNS Documentation](./REVERSE_DNS.md).

View File

@@ -21,6 +21,9 @@ There are 4 settings on the device for influencing 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. **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.
> [!NOTE]
> Please read through the [NTFPRCS plugin](https://github.com/jokob-sk/NetAlertX/blob/main/front/plugins/notification_processing/README.md) documentation to understand how device and global settings influence the notification processing.
## Plugin settings 🔌
![Plugin notification settings](./img/NOTIFICATIONS/Plugin-notification-settings.png)
@@ -38,7 +41,7 @@ Click the **Read more in the docs.** Link at the top of each plugin to get more
In Notification Processing settings, you can specify blanket rules. These allow you to specify exceptions to the Plugin and Device settings and will override those.
1. Notify on (`NTFPRCS_INCLUDED_SECTIONS`) allows you to specify which events trigger notifications. Usual setups will have `new_devices`, `down_devices`, and possibly `down_reconnected` set. Including `plugin` (dependenton the Plugin `<plugin>_WATCH` and `<plugin>_REPORT_ON` settings) and `events` (dependent on the on-device **Alert Events** setting) might be too noisy for most setups. More info in the [NTFPRCS plugin](/front/plugins/notification_processing/README.md) on what events these selections include.
1. Notify on (`NTFPRCS_INCLUDED_SECTIONS`) allows you to specify which events trigger notifications. Usual setups will have `new_devices`, `down_devices`, and possibly `down_reconnected` set. Including `plugin` (dependenton the Plugin `<plugin>_WATCH` and `<plugin>_REPORT_ON` settings) and `events` (dependent on the on-device **Alert Events** setting) might be too noisy for most setups. More info in the [NTFPRCS plugin](https://github.com/jokob-sk/NetAlertX/blob/main/front/plugins/notification_processing/README.md) on what events these selections include.
2. Alert down after (`NTFPRCS_alert_down_time`) is useful if you want to wait for some time before the system sends out a down notification for a device. This is related to the on-device **Alert down** setting and only devices with this checked will trigger a down notification.
3. A filter to allow you to set device-specific exceptions to New devices being added to the app.
4. A filter to allow you to set device-specific exceptions to generated Events.

View File

@@ -8,17 +8,38 @@ NetAlertX supports additional plugins to extend its functionality, each with its
## ⚡ Quick start
> [!TIP]
> You can load additional Plugins via the General -> `LOADED_PLUGINS` setting.
> You can load additional Plugins via the General -> `LOADED_PLUGINS` setting. You need to save the settings for the new plugins to load (cache/page reload may be necessary).
> ![Loaded plugins settings](./img/PLUGINS/loaded_plugins_setting.png)
1. Pick your `🔍 dev scanner` plugin (e.g. `ARPSCAN` or `NMAPDEV`), or import devices into the application with an `📥 importer` plugin. (See **Enabling plugins** below)
1. Pick your `🔍 dev scanner` plugin (e.g. `ARPSCAN` or `NMAPDEV`), or import devices into the application with an `📥 importer` plugin. (See **Enabling plugins** below)
2. Pick a `▶️ publisher` plugin, if you want to send notifications. If you don't see a publisher you'd like to use, look at the [📚_publisher_apprise](/front/plugins/_publisher_apprise/) plugin which is a proxy for over 80 notification services.
3. Setup your [Network topology diagram](./NETWORK_TREE.md)
4. Fine-tune [Notifications](./NOTIFICATIONS.md)
5. [Backup your setup](./BACKUPS.md)
6. Contribute and [Create custom plugins](./PLUGINS_DEV.md)
5. Setup [Workflows](./WORKFLOWS.md)
6. [Backup your setup](./BACKUPS.md)
7. Contribute and [Create custom plugins](./PLUGINS_DEV.md)
## 📑 Available Plugins
## Plugin types
| Plugin type | Icon | Description | When to run | Required | Data source [?](./PLUGINS_DEV.md) |
| -------------- | ---- | ---------------------------------------------------------------- | ----------------------------------- | -------- | ------------------------------------- |
| publisher | ▶️ | Sending notifications to services. | `on_notification` | ✖ | Script |
| dev scanner | 🔍 | Create devices in the app, manages online/offline device status. | `schedule` | ✖ | Script / SQLite DB |
| name discovery | 🆎 | Discovers names of devices via various protocols. | `before_name_updates`, `schedule` | ✖ | Script |
| importer | 📥 | Importing devices from another service. | `schedule` | ✖ | Script / SQLite DB |
| system | ⚙ | Providing core system functionality. | `schedule` / always on | ✖/✔ | Script / Template |
| other | ♻ | Other plugins | misc | ✖ | Script / Template |
## Features
| Icon | Description |
| ---- | ------------------------------------------------------------ |
| 🖧 | Auto-imports the network topology diagram |
| 🔄 | Has the option to sync some data back into the plugin source |
## Available Plugins
Device-detecting plugins insert values into the `CurrentScan` database table. The plugins that are not required are safe to ignore, however, it makes sense to have at least some device-detecting plugins enabled, such as `ARPSCAN` or `NMAPDEV`.
@@ -36,7 +57,7 @@ Device-detecting plugins insert values into the `CurrentScan` database table. T
| `DHCPLSS` | 🔍/📥/🆎| Import devices from DHCP leases | | | Script | [dhcp_leases](https://github.com/jokob-sk/NetAlertX/tree/main/front/plugins/dhcp_leases/) |
| `DHCPSRVS` | ♻ | DHCP servers | | | Script | [dhcp_servers](https://github.com/jokob-sk/NetAlertX/tree/main/front/plugins/dhcp_servers/) |
| `FREEBOX` | 🔍/♻/🆎| Pull data and names from Freebox/Iliadbox | | | Script | [freebox](https://github.com/jokob-sk/NetAlertX/tree/main/front/plugins/freebox/) |
| `ICMP` | 🔍 | ICMP (ping) status checker | | | Script | [icmp_scan](https://github.com/jokob-sk/NetAlertX/tree/main/front/plugins/icmp_scan/) |
| `ICMP` | | ICMP (ping) status checker | | | Script | [icmp_scan](https://github.com/jokob-sk/NetAlertX/tree/main/front/plugins/icmp_scan/) |
| `INTRNT` | 🔍 | Internet IP scanner | | | Script | [internet_ip](https://github.com/jokob-sk/NetAlertX/tree/main/front/plugins/internet_ip/) |
| `INTRSPD` | ♻ | Internet speed test | | | Script | [internet_speedtest](https://github.com/jokob-sk/NetAlertX/tree/main/front/plugins/internet_speedtest/) |
| `IPNEIGH` | 🔍 | Scan ARP (IPv4) and NDP (IPv6) tables | | | Script | [ipneigh](https://github.com/jokob-sk/NetAlertX/tree/main/front/plugins/ipneigh/) |
@@ -72,27 +93,9 @@ Device-detecting plugins insert values into the `CurrentScan` database table. T
> ❌ marked for removal
> ⌚It's recommended to use the same schedule interval for all plugins responsible for discovering new devices.
## Plugin types
| Plugin type | Icon | Description | When to run | Required | Data source [?](./PLUGINS_DEV.md) |
| -------------- | ---- | ---------------------------------------------------------------- | ----------------------------------- | -------- | ------------------------------------- |
| publisher | ▶️ | Sending notifications to services. | `on_notification` | ✖ | Script |
| dev scanner | 🔍 | Create devices in the app, manages online/offline device status. | `schedule` | ✖ | Script / SQLite DB |
| name discovery | 🆎 | Discovers names of devices via various protocols. | `before_name_updates`, `schedule` | ✖ | Script |
| importer | 📥 | Importing devices from another service. | `schedule` | ✖ | Script / SQLite DB |
| system | ⚙ | Providing core system functionality. | `schedule` / always on | ✖/✔ | Script / Template |
| other | ♻ | Other plugins | misc | ✖ | Script / Template |
## Features
| Icon | Description |
| ---- | ------------------------------------------------------------ |
| 🖧 | Auto-imports the network topology diagram |
| 🔄 | Has the option to sync some data back into the plugin source |
## ✅Enabling plugins
## Enabling plugins
Plugins can be enabled via Settings, and can be disabled as needed.

View File

@@ -31,7 +31,7 @@ The following workarounds should work for most complex network setups.
## Supplementing Plugins
You can use supplementary plugins that employ alternate methods. Protocols used by the `SNMPDSC` or `DHCPLSS` plugins are widely supported on different routers and can be effective as workarounds. Check the [plugins list](https://github.com/jokob-sk/NetAlertX/blob/main/docs/PLUGINS.md) to find a plugin that works with your router and network setup.
You can use supplementary plugins that employ alternate methods. Protocols used by the `SNMPDSC` or `DHCPLSS` plugins are widely supported on different routers and can be effective as workarounds. Check the [plugins list](./PLUGINS.md) to find a plugin that works with your router and network setup.
## Multiple NetAlertX Instances

View File

@@ -51,7 +51,7 @@ services:
![Project settings](./img/SYNOLOGY/07_Create_project.png)
7. Replace the paths to your volume and/or comment out unnecessary line(s):
7. Replace the paths to your volume and comment out unnecessary line(s):
- This is only an example, your paths will differ.

View File

@@ -1,49 +1,70 @@
# Debugging inaccessible UI
## 1. Port conflicts
The application uses the following default ports:
When opening an issue please:
- **Web UI**: `20211`
- **GraphQL API**: `20212`
The **Web UI** is served by an **nginx** server, while the **API backend** runs on a **Flask (Python)** server.
## Changing Ports
- To change the **Web UI** port, update the `PORT` environment variable in the `docker-compose.yml` file.
- To change the **GraphQL API** port, use the `GRAPHQL_PORT` setting, either directly or via Docker:
```yaml
APP_CONF_OVERRIDE={"GRAPHQL_PORT":"20212"}
```
For more information, check the [Docker installation guide](https://github.com/jokob-sk/NetAlertX/blob/main/dockerfiles/README.md).
## Possible issues and troubleshooting
Follow all of the below in order to disqualify potential causes of issues and to troubleshoot these problems faster.
### 1. Port conflicts
When opening an issue or debugging:
1. Include a screenshot of what you see when accessing `HTTP://<your rpi IP>/20211` (or your custom port)
1. [Follow steps 1, 2, 3, 4 on this page](./DEBUG_TIPS.md)
1. Execute the following in the container to see the processes and their ports and submit a screenshot of the result:
1. `sudo apk add lsof`
1. `sudo lsof -i`
1. Try running the `nginx` command in the container
1. if you get `nginx: [emerg] bind() to 0.0.0.0:20211 failed (98: Address in use)` try using a different port number
- `sudo apk add lsof`
- `sudo lsof -i`
1. Try running the `nginx` command in the container:
- if you get `nginx: [emerg] bind() to 0.0.0.0:20211 failed (98: Address in use)` try using a different port number
![lsof ports](./img/WEB_UI_PORT_DEBUG/container_port.png)
## 2. JavaScript issues
### 2. JavaScript issues
Check for browser console (F12 browser dev console) errors + check different browsers.
## 3. Clear the app cache and cached JavaScript files
### 3. Clear the app cache and cached JavaScript files
Refresh the browser cache (usually shoft + refresh), try a private window, or different browsers. Please also refresh the app cache by clicking the 🔃 (reload) button in the header of the application.
## 4. Disable proxy
### 4. Disable proxies
If you have any reverse proxy or similar, try disabling it.
## 5. Disable your firewall
### 5. Disable your firewall
If you are using a firewall, try to temporarily disabling it.
## 6. Post your docker start details
### 6. Post your docker start details
If you haven't, post your docker compose/run command.
## 7. Check for errors in your PHP/NGINX error logs
### 7. Check for errors in your PHP/NGINX error logs
In the container execute:
In the container execute and investigate:
`cat /var/log/nginx/error.log`
`cat /app/log/app.php_errors.log`
## 8. Make sure permissions are correct
### 8. Make sure permissions are correct
> [!TIP]
> You can try to start the container without mapping the `/app/config` and `/app/db` dirs and if the UI shows up then the issue is most likely related to your file system permissions or file ownership.

View File

@@ -1,44 +1,59 @@
# Workflows Overview
The workflows module in NetAlertX allows you to automate repetitive tasks, making network management more efficient. Whether you need to assign newly discovered devices to a specific Network Node, auto-group devices from a given vendor, unarchive a device if detected online, or automatically delete or archive devices, this module provides the flexibility to tailor the automations to your needs.
The workflows module in NetAlertX allows to automate repetitive tasks, making network management more efficient. Whether you need to assign newly discovered devices to a specific Network Node, auto-group devices from a given vendor, unarchive a device if detected online, or automatically delete devices, this module provides the flexibility to tailor the automations to your needs.
![Workflow example](./img/WORKFLOWS/workflows.png)
![Workflows diagram](./img/WORKFLOWS/workflows_diagram.png)
Below are a few examples that demonstrate how this module can be used to simplify network management tasks.
## Updating Workflows
> [!NOTE]
> In order to apply a workflow change, you must first **Save** the changes and then reload the application by clicking **Restart server**.
## Triggers
## Workflow components
### Triggers
![Trigger example](./img/WORKFLOWS/trigger.jpg)
Triggers define the event that activates a workflow. They monitor changes to objects within the system, such as updates to devices or the insertion of new entries. When the specified event occurs, the workflow is executed.
### Example Trigger:
> [!TIP]
> Workflows not running? Check the [Workflows debugging](./WORKFLOWS_DEBUGGING.md) guide how to troubleshoot triggers and conditions.
#### Example Trigger:
- **Object Type**: `Devices`
- **Event Type**: `update`
This trigger will activate when a `Device` object is updated.
## Conditions
### Conditions
![Conditions example](./img/WORKFLOWS/conditions.png)
Conditions determine whether a workflow should proceed based on certain criteria. These criteria can be set for specific fields, such as whether a device is from a certain vendor, or whether it is new or archived. You can combine conditions using logical operators (`AND`, `OR`).
> [!TIP]
> To better understand how to use specific Device fields, please read through the [Database overview](./DATABASE.md) guide.
### Example Condition:
#### Example Condition:
- **Logic**: `AND`
- **Field**: `devVendor`
- **Operator**: `contains`
- **Operator**: `contains` (case in-sensitive)
- **Value**: `Google`
This condition checks if the device's vendor is `Google`. The workflow will only proceed if the condition is true.
## Actions
### Actions
![Actions example](./img/WORKFLOWS/actions.jpg)
Actions define the tasks that the workflow will perform once the conditions are met. Actions can include updating fields or deleting devices.
### Example Action:
You can include multiple actions that should execute once the conditions are met.
#### Example Action:
- **Action Type**: `update_field`
- **Field**: `devIsNew`
- **Value**: `0`
@@ -50,13 +65,17 @@ Actions define the tasks that the workflow will perform once the conditions are
Below you can find a couple of configuration examples.
![Workflow example](./img/WORKFLOWS/workflows.png)
---
## Example 1: Assign Device to Network Node Based on IP
This workflow assigns newly added devices with IP addresses in the `192.168.1.*` range to the device with the MAC address `6c:6d:6d:6c:6c:6c`.
### Trigger:
- **Object Type**: `Devices`
- **Event Type**: `create`
- **Event Type**: `insert`
### Conditions:
- **Logic**: `AND`
@@ -71,12 +90,12 @@ Below you can find a couple of configuration examples.
- **Field**: `devNetworkNode`
- **Value**: `6c:6d:6d:6c:6c:6c`
This workflow assigns newly added devices with IP addresses in the `192.168.1.*` range to the device with the MAC address `6c:6d:6d:6c:6c:6c`.
---
## Example 2: Mark Device as Not New and Delete If from Google Vendor
This workflow automates the process of marking Google devices as not new and deleting them if they meet the criteria.
### Trigger:
- **Object Type**: `Devices`
- **Event Type**: `update`
@@ -107,10 +126,5 @@ This workflow assigns newly added devices with IP addresses in the `192.168.1.*`
This action deletes the device after it is marked as not new.
This workflow automates the process of marking Google devices as not new and deleting them if they meet the criteria.
---
### Conclusion
With workflows, NetAlertX can automatically adjust to network changes, saving time and reducing the manual overhead involved in maintaining your devices. You can create highly tailored automation rules to handle everything from basic updates to more complex device management.
> [!TIP]
> Share your workflows in [Discord](https://discord.com/invite/NczTUTWyRr) or [GitHub Discussions](https://github.com/jokob-sk/NetAlertX/discussions).

38
docs/WORKFLOWS_DEBUGGING.md Executable file
View File

@@ -0,0 +1,38 @@
# Workflows debugging and troubleshooting
> [!TIP]
> Before troubleshooting, please ensure you have [Debugging enabled](./DEBUG_TIPS.md).
Workflows are triggered by various events. These events are captured and listed in the _Integrations -> App Events_ section of the application.
## Troubleshooting triggers
> [!NOTE]
> Workflow events are processed once every 5 seconds. However, if a scan or other background tasks are running, this can cause a delay up to a few minutes.
If an event doesn't trigger a workflow as expected, check the _App Events_ section for the event. You can filter these by the ID of the device (`devMAC` or `devGUID`).
![App events search](./img/WORKFLOWS/workflows_app_events_search.png)
Once you find the _Event Guid_ and _Object GUID_, use them to find relevant debug entries.
Navigate to _Mainetenace -> Logs_ where you can filter the logs based on the _Event or Object GUID_.
![Log events search](./img/WORKFLOWS/workflows_logs_search.png)
Below you can find some example `app.log` entries that will help you understand why a Workflow was or was not triggered.
```bash
16:27:03 [WF] Checking if '13f0ce26-1835-4c48-ae03-cdaf38f328fe' triggers the workflow 'Sample Device Update Workflow'
16:27:03 [WF] self.triggered 'False' for event '[[155], ['13f0ce26-1835-4c48-ae03-cdaf38f328fe'], [0], ['2025-04-02 05:26:56'], ['Devices'], ['050b6980-7af6-4409-950d-08e9786b7b33'], ['DEVICES'], ['00:11:32:ef:a5:6c'], ['192.168.1.82'], ['050b6980-7af6-4409-950d-08e9786b7b33'], [None], [0], [0], ['devPresentLastScan'], ['online'], ['update'], [None], [None], [None], [None]] and trigger {"object_type": "Devices", "event_type": "insert"}'
16:27:03 [WF] Checking if '13f0ce26-1835-4c48-ae03-cdaf38f328fe' triggers the workflow 'Location Change'
16:27:03 [WF] self.triggered 'True' for event '[[155], ['13f0ce26-1835-4c48-ae03-cdaf38f328fe'], [0], ['2025-04-02 05:26:56'], ['Devices'], ['050b6980-7af6-4409-950d-08e9786b7b33'], ['DEVICES'], ['00:11:32:ef:a5:6c'], ['192.168.1.82'], ['050b6980-7af6-4409-950d-08e9786b7b33'], [None], [0], [0], ['devPresentLastScan'], ['online'], ['update'], [None], [None], [None], [None]] and trigger {"object_type": "Devices", "event_type": "update"}'
16:27:03 [WF] Event with GUID '13f0ce26-1835-4c48-ae03-cdaf38f328fe' triggered the workflow 'Location Change'
```
Note how one trigger executed, but the other didn't based on different `"event_type"` values. One is `"event_type": "insert"`, the other `"event_type": "update"`.
Given the Event is a update event (note `...['online'], ['update'], [None]...` in the event structure), the `"event_type": "insert"` trigger didn't execute.

Binary file not shown.

After

Width:  |  Height:  |  Size: 110 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 93 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 77 KiB

BIN
docs/img/WORKFLOWS/actions.jpg Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

BIN
docs/img/WORKFLOWS/conditions.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

BIN
docs/img/WORKFLOWS/trigger.jpg Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 89 KiB

After

Width:  |  Height:  |  Size: 129 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 166 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 164 KiB

View File

@@ -1517,10 +1517,9 @@ input[readonly] {
/* AdminLTE overrides */
#networkTree .box
{
border-top:1px;
/* border-top:1px; */
border-top-color:grey;
padding:0px;
padding-top:6px;
margin:0px;
align-items:center;
border-radius:20px;
@@ -1528,7 +1527,7 @@ input[readonly] {
/* display:flex; */
flex-direction:column;
justify-content:center;
display: inline-grid;
/* display: inline-grid; */
}
.helpIcon
{
@@ -1577,17 +1576,16 @@ input[readonly] {
width: auto;
}
#networkTree .netCollapse
{
display: block;
position: absolute;
margin-left: 170px;
font-size: large;
left: -15px;
right: 0;
margin-right: -3px;
}
#networkTree .highlightedNode
{
border: solid;
/* border: solid; */
border-color:cyan;
}
#networkTree .netStatus-Off-line i,
@@ -1598,7 +1596,6 @@ input[readonly] {
.spanNetworkTree {
display: inline-block;
/* width: 135px; */
white-space: nowrap;
overflow: hidden !important;
text-overflow: ellipsis;
@@ -1614,6 +1611,11 @@ input[readonly] {
/* margin-left: 0.2em; */
}
.networkTable .networkNodeTabHeaders a {
display: block;
height: 3em;
}
.networkTable .icon {
/* padding-left:2em; */
width:2em;
@@ -1632,7 +1634,6 @@ input[readonly] {
.networkNodeTabHeaders
{
max-width: 200px;
text-overflow: ellipsis;
overflow: hidden;
text-wrap: nowrap;
@@ -1710,13 +1711,6 @@ input[readonly] {
padding-top: 20px;
}
.login-page .login-custom
{
width:480px;
}
/*Hidden special button*/
@media (max-width: 365px) {
@@ -1863,6 +1857,11 @@ input[readonly] {
padding: 5px;
}
.workflows
{
max-width: 800px;
}
.workflows .col-sm-12, .workflows .col-sx-12
{
padding-right: 5px;
@@ -1975,6 +1974,7 @@ input[readonly] {
right: 0.1em;
}
.workflows .remove-condition
{
z-index: 1;

View File

@@ -266,7 +266,7 @@
})
}, 1);
}, 100);
});
}
@@ -338,6 +338,7 @@
// -----------------------------------------------------------------------------
// Save device data to DB
function setDeviceData(direction = '', refreshCallback = '') {
// Check MAC
if (mac === '') {

View File

@@ -74,10 +74,10 @@
<!-- box-header -->
<div class="box-header">
<div class=" col-md-9 ">
<div class=" col-sm-8 ">
<h3 id="tableDevicesTitle" class="box-title text-gray "></h3>
</div>
<div class="dummyDevice col-md-3 ">
<div class="dummyDevice col-sm-4 ">
<span id="multiEditPlc">
<!-- multi edit button placeholder -->
</span>

View File

@@ -2,10 +2,13 @@
<link rel="stylesheet" href="css/app.css">
<?php
require dirname(__FILE__).'/php/server/init.php';
//------------------------------------------------------------------------------
// check if authenticated
require_once $_SERVER['DOCUMENT_ROOT'] . '/php/templates/security.php';
// Be CAREFUL WHEN INCLUDING NEW PHP FILES
require_once $_SERVER['DOCUMENT_ROOT'] . '/php/server/db.php';
require_once $_SERVER['DOCUMENT_ROOT'] . '/php/templates/language/lang.php';
require_once $_SERVER['DOCUMENT_ROOT'] . '/php/templates/security.php';
$CookieSaveLoginName = 'NetAlertX_SaveLogin';
@@ -92,10 +95,8 @@ if (isset ($_SESSION["login"]) == FALSE || $_SESSION["login"] != 1)
<!-- iCheck -->
<link rel="stylesheet" href="lib/iCheck/square/blue.css">
<!-- Font Awesome -->
<link rel="stylesheet" href="lib/font-awesome/fontawesome.min.css">
<link rel="stylesheet" href="lib/font-awesome/solid.css">
<link rel="stylesheet" href="lib/font-awesome/brands.css">
<link rel="stylesheet" href="lib/font-awesome/v5-font-face.css">
<link rel="stylesheet" href="lib/font-awesome/all.min.css">
<!-- Favicon -->
<link id="favicon" rel="icon" type="image/x-icon" href="img/NetAlertX_logo.png">
@@ -112,7 +113,7 @@ switch ($UI_THEME) {
?>
<link rel="stylesheet" href="/css/offline-font.css">
</head>
<body class="hold-transition login-page">
<body class="hold-transition login-page col-sm-12 col-sx-12">
<div class="login-box login-custom">
<div class="login-logo">
<a href="/index2.php">Net<b>Alert</b><sup>x</sup></a>

View File

@@ -68,11 +68,13 @@ function showModalWarning(
callbackFunction = null,
triggeredBy = null
) {
prefix = "modal-warning";
// set captions
$("#modal-warning-title").html(title);
$("#modal-warning-message").html(message);
$("#modal-warning-cancel").html(btnCancel);
$("#modal-warning-OK").html(btnOK);
$(`#${prefix}-title`).html(title);
$(`#${prefix}-message`).html(message);
$(`#${prefix}-cancel`).html(btnCancel);
$(`#${prefix}-OK`).html(btnOK);
if (callbackFunction != null) {
modalCallbackFunction = callbackFunction;
@@ -83,7 +85,7 @@ function showModalWarning(
}
// Show modal
$("#modal-warning").modal("show");
$(`#${prefix}`).modal("show");
}
// -----------------------------------------------------------------------------
@@ -93,7 +95,8 @@ function showModalInput(
btnCancel = getString("Gen_Cancel"),
btnOK = getString("Gen_Okay"),
callbackFunction = null,
triggeredBy = null
triggeredBy = null,
defaultValue = ""
) {
prefix = "modal-input";
@@ -102,6 +105,7 @@ function showModalInput(
$(`#${prefix}-message`).html(message);
$(`#${prefix}-cancel`).html(btnCancel);
$(`#${prefix}-OK`).html(btnOK);
$(`#${prefix}-textarea`).val(defaultValue);
if (callbackFunction != null) {
modalCallbackFunction = callbackFunction;

9
front/lib/font-awesome/all.min.css vendored Executable file

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1,19 +0,0 @@
/*!
* Font Awesome Free 6.2.1 by @fontawesome - https://fontawesome.com
* License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
* Copyright 2022 Fonticons, Inc.
*/
:root, :host {
--fa-style-family-classic: 'Font Awesome 6 Free';
--fa-font-solid: normal 900 1em/1 'Font Awesome 6 Free'; }
@font-face {
font-family: 'Font Awesome 6 Free';
font-style: normal;
font-weight: 900;
font-display: block;
src: url("../webfonts/fa-solid-900.woff2") format("woff2"), url("../webfonts/fa-solid-900.ttf") format("truetype"); }
.fas,
.fa-solid {
font-weight: 900; }

View File

@@ -1,6 +0,0 @@
/*!
* Font Awesome Free 6.2.1 by @fontawesome - https://fontawesome.com
* License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
* Copyright 2022 Fonticons, Inc.
*/
:host,:root{--fa-style-family-classic:"Font Awesome 6 Free";--fa-font-solid:normal 900 1em/1 "Font Awesome 6 Free"}@font-face{font-family:"Font Awesome 6 Free";font-style:normal;font-weight:900;font-display:block;src:url(../webfonts/fa-solid-900.woff2) format("woff2"),url(../webfonts/fa-solid-900.ttf) format("truetype")}.fa-solid,.fas{font-weight:900}

View File

@@ -1,22 +0,0 @@
/*!
* Font Awesome Free 6.2.1 by @fontawesome - https://fontawesome.com
* License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
* Copyright 2022 Fonticons, Inc.
*/
@font-face {
font-family: 'Font Awesome 5 Brands';
font-display: block;
font-weight: 400;
src: url("../webfonts/fa-brands-400.woff2") format("woff2"), url("../webfonts/fa-brands-400.ttf") format("truetype"); }
@font-face {
font-family: 'Font Awesome 5 Free';
font-display: block;
font-weight: 900;
src: url("../webfonts/fa-solid-900.woff2") format("woff2"), url("../webfonts/fa-solid-900.ttf") format("truetype"); }
@font-face {
font-family: 'Font Awesome 5 Free';
font-display: block;
font-weight: 400;
src: url("../webfonts/fa-regular-400.woff2") format("woff2"), url("../webfonts/fa-regular-400.ttf") format("truetype"); }

View File

@@ -1,6 +0,0 @@
/*!
* Font Awesome Free 6.2.1 by @fontawesome - https://fontawesome.com
* License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
* Copyright 2022 Fonticons, Inc.
*/
@font-face{font-family:"Font Awesome 5 Brands";font-display:block;font-weight:400;src:url(../webfonts/fa-brands-400.woff2) format("woff2"),url(../webfonts/fa-brands-400.ttf) format("truetype")}@font-face{font-family:"Font Awesome 5 Free";font-display:block;font-weight:900;src:url(../webfonts/fa-solid-900.woff2) format("woff2"),url(../webfonts/fa-solid-900.ttf) format("truetype")}@font-face{font-family:"Font Awesome 5 Free";font-display:block;font-weight:400;src:url(../webfonts/fa-regular-400.woff2) format("woff2"),url(../webfonts/fa-regular-400.ttf) format("truetype")}

View File

@@ -6,8 +6,6 @@
// online / offline badges HTML snippets
define('badge_online', '<div class="badge bg-green text-white" style="width: 60px;">Online</div>');
define('badge_offline', '<div class="badge bg-red text-white" style="width: 60px;">Offline</div>');
define('circle_online', '<div class="badge bg-green text-white" style="width: 10px; height: 10px; padding:2px; margin-top: -25px;">&nbsp;</div>');
define('circle_offline', '<div class="badge bg-red text-white" style="width: 10px; height: 10px; padding:2px; margin-top: -25px;">&nbsp;</div>');
define('sortable_column', ' <span class="sort-btn" onclick="sortColumn(this)"><i class="fa-solid fa-arrow-up-short-wide"></i></span>');
?>
@@ -39,21 +37,18 @@
}
// online/offline status circle (red/green)
$node_badge = "";
if($node_status == 1) // 1 means online, 0 offline
$icon_style = "";
if($node_status == 0) // 1 means online, 0 offline
{
$node_badge = circle_online;
} else
{
$node_badge = circle_offline;
}
$icon_style = "style=\"color:var(--color-red);\"";
}
$decoded_icon = base64_decode($icon);
$idFromMac = str_replace(":", "_", $node_mac);
$str_tab_header = '<li class="networkNodeTabHeaders '.$activetab.' " >
<a href="#'.$idFromMac.'" data-mytabmac="'.$node_mac.'" id="'.$idFromMac.'_id" data-toggle="tab" title="'.$node_name.' ">' // _id is added so it doesn't conflict with AdminLTE tab behavior
.'<div class="icon">'.$decoded_icon.' </div> <span class="node-name">'.$node_name.'</span>' .$str_port.$node_badge.
.'<div class="icon" '.$icon_style.'>'.$decoded_icon.' </div> <span class="node-name">'.$node_name.'</span>' .$str_port.
'</a>
</li>';
@@ -697,34 +692,56 @@ function attachTreeEvents()
// ---------------------------------------------------------------------------
// Handle network node click - select correct tab in the bottom table
function handleNodeClick(nodeData)
function handleNodeClick(el)
{
const targetTabMAC = nodeData.data.mac;
const targetTabMAC = $(el).attr("data-mytreemacmain");
var targetTab = $(`a[data-mytabmac="${targetTabMAC}"]`);
// Simulate a click event on the target tab
targetTab.click();
if (targetTab.length) {
// Simulate a click event on the target tab
targetTab.click();
// Smooth scroll to the tab content
$('html, body').animate({
scrollTop: targetTab.offset().top - 50
}, 500); // Adjust the duration as needed
}
}
// ---------------------------------------------------------------------------
var myTree;
var visibleTreeArea = $(window).height()-155;
var nodeWidth = 120;
var emSize;
var nodeHeight;
var sizeCoefficient = 1.4
// var sizeCoefficient = 1.4
function pxToEm(px, element) {
var baseFontSize = parseFloat($(element || "body").css("font-size"));
return px / baseFontSize;
}
function emToPx(em, element) {
var baseFontSize = parseFloat($(element || "body").css("font-size"));
return Math.round(em * baseFontSize);
}
function initTree(myHierarchy)
{
// calculate the drawing area based on teh tree width and available screen size
var treeAreaHeight = visibleTreeArea > 800 ? 800 : visibleTreeArea;
let screenWidth = $('.content-header').width();
let treeWidth = (nodeWidth + 20) * parentNodesCount;
let treeAreaWidth = screenWidth < treeWidth ? treeWidth : screenWidth;
let baseFontSize = parseFloat($('html').css('font-size'));
let treeAreaHeight = ($(window).height() - 155); ;
// calculate the font size of the leaf nodes to fit everything into the tree area
leafNodesCount == 0 ? 1 : leafNodesCount;
emSize = pxToEm((treeAreaHeight/(leafNodesCount)).toFixed(2));
let screenWidthEm = pxToEm($('.networkTable').width());
// init the drawing area size
$("#networkTree").attr('style', `height:${treeAreaHeight}px; width:${treeAreaWidth}px`)
$("#networkTree").attr('style', `height:${treeAreaHeight}px; width:${emToPx(screenWidthEm)}px`)
if(myHierarchy.type == "")
{
@@ -733,13 +750,14 @@ function initTree(myHierarchy)
return;
}
// calculate the font size of the leaf nodes to fit everything into the tree area
leafNodesCount == 0 ? 1 : leafNodesCount;
emSize = ((treeAreaHeight/(25*leafNodesCount)).toFixed(2));
emSize = emSize > 1 ? 1 : emSize;
// handle if only a few nodes
emSize > 1 ? emSize = 1 : emSize = emSize;
// nodeHeight = ((emSize*100*0.30).toFixed(0))
nodeHeight = ((emSize*100*0.30).toFixed(0))
let nodeHeightPx = emToPx(emSize*1);
let nodeWidthPx = emToPx(screenWidthEm / (parentNodesCount));
// handle if only a few nodes
nodeWidthPx > 160 ? nodeWidthPx = 160 : nodeWidthPx = nodeWidthPx;
console.log(Treeviz);
@@ -747,8 +765,7 @@ function initTree(myHierarchy)
htmlId: "networkTree",
renderNode: nodeData => {
var fontSize = "font-size:"+emSize+"em;";
(!emptyArr.includes(nodeData.data.port )) ? port = nodeData.data.port : port = "";
(port == "" || port == 0 || port == 'None' ) ? portBckgIcon = `<i class="fa fa-wifi"></i>` : portBckgIcon = `<i class="fa fa-ethernet"></i>`;
@@ -761,10 +778,10 @@ function initTree(myHierarchy)
${atob(nodeData.data.icon)}
</div>` : "";
devicePort = `<div class="netPort"
style="width:${emSize*sizeCoefficient}em;height:${emSize*sizeCoefficient}em">
style="width:${emSize}em;height:${emSize}em">
${portHtml}</div>
<div class="portBckgIcon"
style="margin-left:-${emSize*sizeCoefficient}em;">
style="margin-left:-${emSize}em;">
${portBckgIcon}
</div>`;
collapseExpandIcon = nodeData.data.hiddenChildren ?
@@ -773,7 +790,7 @@ function initTree(myHierarchy)
// generate +/- icon if node has children nodes
collapseExpandHtml = nodeData.data.hasChildren ?
`<div class="netCollapse"
style="font-size:${emSize*sizeCoefficient}em;top:${emSize/6}em"
style="font-size:${nodeHeightPx/2}px;top:${nodeHeightPx/4}px"
data-mytreepath="${nodeData.data.path}"
data-mytreemac="${nodeData.data.mac}">
<i class="fa fa-${collapseExpandIcon} pointer"></i>
@@ -787,21 +804,24 @@ function initTree(myHierarchy)
// css indicating online/offline status
statusCss = ` netStatus-${nodeData.data.status}`;
return result = `<div class="box ${nodeData.data.hasChildren ? "pointer":""} ${statusCss} ${highlightedCss}"
return result = `<div
class="node-inner box ${nodeData.data.hasChildren ? "pointer":""} ${statusCss} ${highlightedCss}"
data-mytreemacmain="${nodeData.data.mac}"
style="height:${nodeData.settings.nodeHeight}px;${fontSize}"
style="height:${nodeHeightPx}px;font-size:${nodeHeightPx-5}px;"
onclick="handleNodeClick(this)"
>
<div class="netNodeText">
<strong>${devicePort} ${deviceIcon}
<span class="spanNetworkTree anonymizeDev" >${nodeData.data.name}</span>
</strong>
${collapseExpandHtml}
</div>
</div>`;
<span class="spanNetworkTree anonymizeDev" style="width:${nodeWidthPx-50}px">${nodeData.data.name}</span>
</strong>
</div>
</div>
${collapseExpandHtml}`;
},
mainAxisNodeSpacing: 'auto',
secondaryAxisNodeSpacing: 0.3,
nodeHeight: nodeHeight.toString(),
// secondaryAxisNodeSpacing: 0.3,
nodeHeight: nodeHeightPx,
nodeWidth: nodeWidthPx,
marginTop: '5',
isHorizontal : true,
hasZoom: true,
@@ -811,8 +831,8 @@ function initTree(myHierarchy)
hasFlatData: false,
relationnalField: "children",
linkWidth: (nodeData) => 3,
linkColor: (nodeData) => "#ffcc80",
onNodeClick: (nodeData) => handleNodeClick(nodeData),
linkColor: (nodeData) => "#ffcc80"
// onNodeClick: (nodeData) => handleNodeClick(nodeData),
});
console.log(deviceListGlobal);

View File

@@ -30,7 +30,7 @@
<div class="pull-right no-hidden-xs">
| <a href="https://gurubase.io/g/netalertx" class="pointer" target="_blank" title="Ask AI"><i class="fa-regular fa-comment-dots fa-flip-horizontal"></i></a>
| <a href="https://jokob-sk.github.io/NetAlertX/" 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-solid fa-bug" title="Report a bug"></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'; ?>

View File

@@ -59,10 +59,7 @@
<!-- Font Awesome -->
<link rel="stylesheet" href="lib/font-awesome/fontawesome.min.css">
<link rel="stylesheet" href="lib/font-awesome/solid.css">
<link rel="stylesheet" href="lib/font-awesome/brands.css">
<link rel="stylesheet" href="lib/font-awesome/v5-font-face.css">
<link rel="stylesheet" href="lib/font-awesome/all.min.css">
<!-- Ionicons -->
<link rel="stylesheet" href="lib/Ionicons/ionicons.min.css">
@@ -421,20 +418,15 @@
</li>
<!-- Integrations menu item -->
<li class=" treeview <?php if (in_array (basename($_SERVER['SCRIPT_NAME']), array('plugins.php', 'workflows.php', 'appEvents.php' ) ) ){ echo 'active menu-open'; } ?>">
<li class=" treeview <?php if (in_array (basename($_SERVER['SCRIPT_NAME']), array('plugins.php', 'appEvents.php' ) ) ){ echo 'active menu-open'; } ?>">
<a href="#">
<i class="fa fa-fw fa-plug"></i> <span><?= lang('Navigation_Integrations');?></span>
<span class="pull-right-container">
<i class="fa fa-angle-left pull-right"></i>
</span>
</a>
<ul class="treeview-menu " style="display: <?php if (in_array (basename($_SERVER['SCRIPT_NAME']), array('plugins.php', 'workflows.php', 'appEvents.php' ) ) ){ echo 'block'; } else {echo 'none';} ?>;">
<ul class="treeview-menu " style="display: <?php if (in_array (basename($_SERVER['SCRIPT_NAME']), array('plugins.php', 'appEvents.php' ) ) ){ echo 'block'; } else {echo 'none';} ?>;">
<li>
<div class="info-icon-nav"> </div>
<a href="workflows.php"><?= lang('Navigation_Workflows');?></a>
</li>
<li>
<div class="info-icon-nav"> </div>
<a href="appEvents.php"><?= lang('Navigation_AppEvents');?></a>
</li>
<li>
@@ -443,6 +435,11 @@
</ul>
</li>
<!-- workflows menu item -->
<li class=" <?php if (in_array (basename($_SERVER['SCRIPT_NAME']), array('workflows.php') ) ){ echo 'active'; } ?>">
<a href="workflows.php"><i class="fa fa-fw fa-shuffle"></i> <span><?= lang('Navigation_Workflows');?></span></a>
</li>
<!-- system info menu item -->
<li class=" <?php if (in_array (basename($_SERVER['SCRIPT_NAME']), array('systeminfo.php') ) ){ echo 'active'; } ?>">
<a href="systeminfo.php"><i class="fa fa-fw fa-info-circle"></i> <span><?= lang('Navigation_SystemInfo');?></span></a>

View File

@@ -683,9 +683,15 @@
"WF_Condition_value": "",
"WF_Conditions": "",
"WF_Conditions_logic_rules": "",
"WF_Duplicate": "",
"WF_Enabled": "",
"WF_Export": "",
"WF_Export_Copy": "",
"WF_Import": "",
"WF_Import_Copy": "",
"WF_Name": "",
"WF_Remove": "",
"WF_Remove_Copy": "",
"WF_Save": "",
"WF_Trigger": "",
"WF_Trigger_event_type": "",

8
front/php/templates/language/ca_ca.json Normal file → Executable file
View File

@@ -683,9 +683,15 @@
"WF_Condition_value": "",
"WF_Conditions": "",
"WF_Conditions_logic_rules": "",
"WF_Duplicate": "",
"WF_Enabled": "",
"WF_Export": "",
"WF_Export_Copy": "",
"WF_Import": "",
"WF_Import_Copy": "",
"WF_Name": "",
"WF_Remove": "",
"WF_Remove_Copy": "",
"WF_Save": "",
"WF_Trigger": "",
"WF_Trigger_event_type": "",
@@ -736,4 +742,4 @@
"settings_update_item_warning": "Actualitza el valor sota. Sigues curós de seguir el format anterior. <b>No hi ha validació.</b>",
"test_event_icon": "fa-vial-circle-check",
"test_event_tooltip": "Deseu els canvis primer abans de comprovar la configuració."
}
}

View File

@@ -683,9 +683,15 @@
"WF_Condition_value": "",
"WF_Conditions": "",
"WF_Conditions_logic_rules": "",
"WF_Duplicate": "",
"WF_Enabled": "",
"WF_Export": "",
"WF_Export_Copy": "",
"WF_Import": "",
"WF_Import_Copy": "",
"WF_Name": "",
"WF_Remove": "",
"WF_Remove_Copy": "",
"WF_Save": "",
"WF_Trigger": "",
"WF_Trigger_event_type": "",

46
front/php/templates/language/de_de.json Normal file → Executable file
View File

@@ -16,8 +16,8 @@
"About_Design": "Entworfen für:",
"About_Exit": "Abmelden",
"About_Title": "Netzwerksicherheitsscanner und Benachrichtigungsframework",
"AppEvents_AppEventProcessed": "",
"AppEvents_DateTimeCreated": "protokolliert",
"AppEvents_AppEventProcessed": "Verarbeitet",
"AppEvents_DateTimeCreated": "Protokolliert",
"AppEvents_Extra": "Extra",
"AppEvents_GUID": "Anwendungsereignis-GUID",
"AppEvents_Helper1": "Helfer 1",
@@ -30,7 +30,7 @@
"AppEvents_ObjectPlugin": "Verknüpfte Plugins",
"AppEvents_ObjectPrimaryID": "Primär ID",
"AppEvents_ObjectSecondaryID": "Sekundär ID",
"AppEvents_ObjectStatus": "Status (zum Log-Zeitpunkt)",
"AppEvents_ObjectStatus": "Protokollierter Status",
"AppEvents_ObjectStatusColumn": "Statusspalte",
"AppEvents_ObjectType": "Objekttyp",
"AppEvents_Plugin": "Plugin",
@@ -237,7 +237,7 @@
"Device_TableHead_Name": "Name",
"Device_TableHead_NetworkSite": "Netzwerkseite",
"Device_TableHead_Owner": "Eigentümer",
"Device_TableHead_Parent_MAC": "Übergeordnete MAC",
"Device_TableHead_Parent_MAC": "Übergeordneter Netzwerkknoten",
"Device_TableHead_Port": "Port",
"Device_TableHead_PresentLastScan": "Anwesenheit",
"Device_TableHead_RowID": "Zeilen ID",
@@ -499,7 +499,7 @@
"NTFY_display_name": "NTFY",
"NTFY_icon": "<i class=\"fa fa-terminal\"></i>",
"Navigation_About": "Über",
"Navigation_AppEvents": "",
"Navigation_AppEvents": "App-Ereignisse",
"Navigation_Devices": "Geräte",
"Navigation_Donations": "Spenden",
"Navigation_Events": "Ereignisse",
@@ -753,28 +753,34 @@
"WF_Action_field": "",
"WF_Action_type": "",
"WF_Action_value": "",
"WF_Actions": "",
"WF_Actions": "Aktionen",
"WF_Add": "",
"WF_Add_Condition": "",
"WF_Add_Group": "",
"WF_Condition_field": "",
"WF_Condition_operator": "",
"WF_Condition_value": "",
"WF_Conditions": "",
"WF_Conditions_logic_rules": "",
"WF_Add_Condition": "Bedingung hinzufügen",
"WF_Add_Group": "Gruppe hinzufügen",
"WF_Condition_field": "Feld",
"WF_Condition_operator": "Betreiber",
"WF_Condition_value": "Wert",
"WF_Conditions": "Bedingungen",
"WF_Conditions_logic_rules": "Logikregeln",
"WF_Duplicate": "",
"WF_Enabled": "",
"WF_Export": "",
"WF_Export_Copy": "",
"WF_Import": "",
"WF_Import_Copy": "",
"WF_Name": "",
"WF_Remove": "",
"WF_Remove_Copy": "",
"WF_Save": "",
"WF_Trigger": "",
"WF_Trigger_event_type": "",
"WF_Trigger_type": "",
"WF_Trigger": "Auslöser",
"WF_Trigger_event_type": "Ereignistyp",
"WF_Trigger_type": "Auslösertyp",
"Webhooks_display_name": "Webhooks",
"Webhooks_icon": "<i class=\"fa fa-circle-nodes\"></i>",
"add_icon_event_icon": "",
"add_icon_event_tooltip": "",
"add_icon_event_tooltip": "Neues Symbol hinzufügen",
"add_option_event_icon": "",
"add_option_event_tooltip": "",
"add_option_event_tooltip": "Neuen Wert hinzufügen",
"copy_icons_event_icon": "",
"copy_icons_event_tooltip": "Icons aller Geräte mit demselben Gerätetyp überschreiben",
"devices_old": "Aktualisiert...",
@@ -782,7 +788,7 @@
"general_event_title": "",
"go_to_node_event_icon": "",
"go_to_node_event_tooltip": "",
"new_version_available": "",
"new_version_available": "Es ist eine neue Version verfügbar.",
"report_guid": "",
"report_guid_missing": "",
"report_select_format": "Format auswählen:",
@@ -817,4 +823,4 @@
"settings_update_item_warning": "",
"test_event_icon": "",
"test_event_tooltip": "Speichere die Änderungen, bevor Sie die Einstellungen testen."
}
}

View File

@@ -683,9 +683,15 @@
"WF_Condition_value": "Value",
"WF_Conditions": "Conditions",
"WF_Conditions_logic_rules": "Logic rules",
"WF_Duplicate": "Duplicate Workflow",
"WF_Enabled": "Workflow enabled",
"WF_Export": "Export Workflow",
"WF_Export_Copy": "Copy the below workflow and import it where needed.",
"WF_Import": "Import Workflow",
"WF_Import_Copy": "Paste in the workflow you copied previously.",
"WF_Name": "Workflow name",
"WF_Remove": "Remove Workflow",
"WF_Remove_Copy": "Do you want to remove this workflow?",
"WF_Save": "Save Workflows",
"WF_Trigger": "Trigger",
"WF_Trigger_event_type": "Event type",

8
front/php/templates/language/es_es.json Normal file → Executable file
View File

@@ -759,9 +759,15 @@
"WF_Condition_value": "",
"WF_Conditions": "",
"WF_Conditions_logic_rules": "",
"WF_Duplicate": "",
"WF_Enabled": "",
"WF_Export": "",
"WF_Export_Copy": "",
"WF_Import": "",
"WF_Import_Copy": "",
"WF_Name": "",
"WF_Remove": "",
"WF_Remove_Copy": "",
"WF_Save": "",
"WF_Trigger": "",
"WF_Trigger_event_type": "",
@@ -815,4 +821,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."
}
}

52
front/php/templates/language/fr_fr.json Normal file → Executable file
View File

@@ -22,7 +22,7 @@
"AppEvents_ObjectPlugin": "Plugin lié",
"AppEvents_ObjectPrimaryID": "Identité primaire",
"AppEvents_ObjectSecondaryID": "Identité secondaire",
"AppEvents_ObjectStatus": "État (au moment de l'enregistrement)",
"AppEvents_ObjectStatus": "État enregistré",
"AppEvents_ObjectStatusColumn": "Colonne d'état",
"AppEvents_ObjectType": "Type d'objet",
"AppEvents_Plugin": "Plugin",
@@ -363,8 +363,8 @@
"Maintenance_Title": "Outils de maintenance",
"Maintenance_Tool_DownloadConfig": "Export des paramètres",
"Maintenance_Tool_DownloadConfig_text": "Télécharger une sauvegarde complète de vos paramètres stockés dans le fichier <code>app.conf</code>.",
"Maintenance_Tool_DownloadWorkflows": "",
"Maintenance_Tool_DownloadWorkflows_text": "",
"Maintenance_Tool_DownloadWorkflows": "Export des workflows",
"Maintenance_Tool_DownloadWorkflows_text": "Télécharger une sauvegarde complète de vos workflows enregistrés dans le fichier <code>workflows.json</code>.",
"Maintenance_Tool_ExportCSV": "Export des appareils (csv)",
"Maintenance_Tool_ExportCSV_noti": "Export des appareils (csv)",
"Maintenance_Tool_ExportCSV_noti_text": "Êtes-vous sûr de vouloir générer un fichier CSV?",
@@ -670,26 +670,32 @@
"UI_REFRESH_name": "Rafraîchir automatiquement l'interface graphique",
"VERSION_description": "Valeur de la version ou du timestamp d'aide à vérifier si l'application a été mise à jour.",
"VERSION_name": "Version ou Timestamp",
"WF_Action_Add": "",
"WF_Action_field": "",
"WF_Action_type": "",
"WF_Action_value": "",
"WF_Actions": "",
"WF_Add": "",
"WF_Add_Condition": "",
"WF_Add_Group": "",
"WF_Condition_field": "",
"WF_Condition_operator": "",
"WF_Condition_value": "",
"WF_Conditions": "",
"WF_Conditions_logic_rules": "",
"WF_Enabled": "",
"WF_Name": "",
"WF_Remove": "",
"WF_Save": "",
"WF_Trigger": "",
"WF_Trigger_event_type": "",
"WF_Trigger_type": "",
"WF_Action_Add": "Ajouter une action",
"WF_Action_field": "Champ",
"WF_Action_type": "Type",
"WF_Action_value": "Valeur",
"WF_Actions": "Actions",
"WF_Add": "Ajouter un workflow",
"WF_Add_Condition": "Ajouter une condition",
"WF_Add_Group": "Ajouter un groupe",
"WF_Condition_field": "Champ",
"WF_Condition_operator": "Opérateur",
"WF_Condition_value": "Valeur",
"WF_Conditions": "Conditions",
"WF_Conditions_logic_rules": "Règles logiques",
"WF_Duplicate": "Dupliquer le workflow",
"WF_Enabled": "Workflow activé",
"WF_Export": "Exporter le workflow",
"WF_Export_Copy": "Copier le workflow ci-dessous pour pouvoir l'importer au besoin.",
"WF_Import": "Importer un workflow",
"WF_Import_Copy": "Coller le workflow précédemment copié.",
"WF_Name": "Nom du workflow",
"WF_Remove": "Supprimer le workflow",
"WF_Remove_Copy": "Voulez-vous supprimer ce workflow?",
"WF_Save": "Enregistrer les workflows",
"WF_Trigger": "Déclencheur",
"WF_Trigger_event_type": "Type d'événement",
"WF_Trigger_type": "Type de déclencheur",
"add_icon_event_icon": "fa-square-plus",
"add_icon_event_tooltip": "Ajouter une nouvelle icône",
"add_option_event_icon": "fa-square-plus",

View File

@@ -8,7 +8,7 @@
"About_Design": "Progettato per:",
"About_Exit": "Esci",
"About_Title": "Scanner di sicurezza di rete e framework di notifica",
"AppEvents_AppEventProcessed": "",
"AppEvents_AppEventProcessed": "Elaborato",
"AppEvents_DateTimeCreated": "Loggato",
"AppEvents_Extra": "Extra",
"AppEvents_GUID": "GUID evento applicazione",
@@ -22,7 +22,7 @@
"AppEvents_ObjectPlugin": "Plugin collegato",
"AppEvents_ObjectPrimaryID": "ID primario",
"AppEvents_ObjectSecondaryID": "ID secondario",
"AppEvents_ObjectStatus": "Stato (al momento del log)",
"AppEvents_ObjectStatus": "Stato registrato",
"AppEvents_ObjectStatusColumn": "Colonna di stato",
"AppEvents_ObjectType": "Tipo oggetto",
"AppEvents_Plugin": "Plugin",
@@ -225,7 +225,7 @@
"Device_TableHead_Name": "Nome",
"Device_TableHead_NetworkSite": "Sito di rete",
"Device_TableHead_Owner": "Proprietario",
"Device_TableHead_Parent_MAC": "MAC del nodo principale",
"Device_TableHead_Parent_MAC": "Nodo di rete principale",
"Device_TableHead_Port": "Porta",
"Device_TableHead_PresentLastScan": "Presenza",
"Device_TableHead_RowID": "ID riga",
@@ -363,8 +363,8 @@
"Maintenance_Title": "Strumenti di manutenzione",
"Maintenance_Tool_DownloadConfig": "Impostazioni Esporta",
"Maintenance_Tool_DownloadConfig_text": "Scarica un backup completo della configurazione delle tue Impostazioni memorizzata nel file <code>app.conf</code>.",
"Maintenance_Tool_DownloadWorkflows": "",
"Maintenance_Tool_DownloadWorkflows_text": "",
"Maintenance_Tool_DownloadWorkflows": "Esportazione flussi di lavoro",
"Maintenance_Tool_DownloadWorkflows_text": "Scarica un backup completo dei tuoi flussi di lavoro archiviati nel file <code>workflows.json</code>.",
"Maintenance_Tool_ExportCSV": "Esporta dispositivi (csv)",
"Maintenance_Tool_ExportCSV_noti": "Esporta dispositivi (csv)",
"Maintenance_Tool_ExportCSV_noti_text": "Sei sicuro di voler generare un file CSV?",
@@ -463,7 +463,7 @@
"NETWORK_DEVICE_TYPES_description": "Quali tipi di dispositivo possono essere utilizzati come dispositivi di rete nella vista Rete. Il tipo di dispositivo deve corrispondere esattamente all'impostazione <code>Tipo</code> su un dispositivo specifico nei Dettagli dispositivo. Aggiungilo sul Dispositivo tramite il pulsante <code>+</code>. Non rimuovere i tipi esistenti, aggiungine solo di nuovi.",
"NETWORK_DEVICE_TYPES_name": "Tipi di dispositivi di rete",
"Navigation_About": "Informazioni su",
"Navigation_AppEvents": "",
"Navigation_AppEvents": "Eventi app",
"Navigation_Devices": "Dispositivi",
"Navigation_Donations": "Donazioni",
"Navigation_Events": "Eventi",
@@ -670,26 +670,32 @@
"UI_REFRESH_name": "Aggiorna automaticamente la UI",
"VERSION_description": "Valore di supporto della versione o della marca temporale per verificare se l'app è stata aggiornata.",
"VERSION_name": "Versione o marca temporale",
"WF_Action_Add": "",
"WF_Action_field": "",
"WF_Action_type": "",
"WF_Action_value": "",
"WF_Actions": "",
"WF_Add": "",
"WF_Add_Condition": "",
"WF_Add_Group": "",
"WF_Condition_field": "",
"WF_Condition_operator": "",
"WF_Condition_value": "",
"WF_Conditions": "",
"WF_Conditions_logic_rules": "",
"WF_Enabled": "",
"WF_Name": "",
"WF_Remove": "",
"WF_Save": "",
"WF_Trigger": "",
"WF_Trigger_event_type": "",
"WF_Trigger_type": "",
"WF_Action_Add": "Aggiungi azione",
"WF_Action_field": "Campo",
"WF_Action_type": "Tipo",
"WF_Action_value": "Valore",
"WF_Actions": "Azioni",
"WF_Add": "Aggiungi flusso di lavoro",
"WF_Add_Condition": "Aggiungi condizione",
"WF_Add_Group": "Aggiungi gruppo",
"WF_Condition_field": "Campo",
"WF_Condition_operator": "Operatore",
"WF_Condition_value": "Valore",
"WF_Conditions": "Condizioni",
"WF_Conditions_logic_rules": "Regole logiche",
"WF_Duplicate": "Flusso di lavoro duplicato",
"WF_Enabled": "Flusso di lavoro abilitato",
"WF_Export": "Esporta flusso di lavoro",
"WF_Export_Copy": "Copia il flusso di lavoro sottostante e importalo dove necessario.",
"WF_Import": "Importa flusso di lavoro",
"WF_Import_Copy": "Incolla il flusso di lavoro copiato in precedenza.",
"WF_Name": "Nome flusso di lavoro",
"WF_Remove": "Rimuovi flusso di lavoro",
"WF_Remove_Copy": "Vuoi rimuovere questo flusso di lavoro?",
"WF_Save": "Salva flussi di lavoro",
"WF_Trigger": "Trigger",
"WF_Trigger_event_type": "Tipo evento",
"WF_Trigger_type": "Tipo di trigger",
"add_icon_event_icon": "fa-square-plus",
"add_icon_event_tooltip": "Aggiungi nuova icona",
"add_option_event_icon": "fa-square-plus",

8
front/php/templates/language/nb_no.json Normal file → Executable file
View File

@@ -683,9 +683,15 @@
"WF_Condition_value": "",
"WF_Conditions": "",
"WF_Conditions_logic_rules": "",
"WF_Duplicate": "",
"WF_Enabled": "",
"WF_Export": "",
"WF_Export_Copy": "",
"WF_Import": "",
"WF_Import_Copy": "",
"WF_Name": "",
"WF_Remove": "",
"WF_Remove_Copy": "",
"WF_Save": "",
"WF_Trigger": "",
"WF_Trigger_event_type": "",
@@ -736,4 +742,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."
}
}

8
front/php/templates/language/pl_pl.json Normal file → Executable file
View File

@@ -683,9 +683,15 @@
"WF_Condition_value": "",
"WF_Conditions": "",
"WF_Conditions_logic_rules": "",
"WF_Duplicate": "",
"WF_Enabled": "",
"WF_Export": "",
"WF_Export_Copy": "",
"WF_Import": "",
"WF_Import_Copy": "",
"WF_Name": "",
"WF_Remove": "",
"WF_Remove_Copy": "",
"WF_Save": "",
"WF_Trigger": "",
"WF_Trigger_event_type": "",
@@ -736,4 +742,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."
}
}

8
front/php/templates/language/pt_br.json Normal file → Executable file
View File

@@ -683,9 +683,15 @@
"WF_Condition_value": "",
"WF_Conditions": "",
"WF_Conditions_logic_rules": "",
"WF_Duplicate": "",
"WF_Enabled": "",
"WF_Export": "",
"WF_Export_Copy": "",
"WF_Import": "",
"WF_Import_Copy": "",
"WF_Name": "",
"WF_Remove": "",
"WF_Remove_Copy": "",
"WF_Save": "",
"WF_Trigger": "",
"WF_Trigger_event_type": "",
@@ -736,4 +742,4 @@
"settings_update_item_warning": "",
"test_event_icon": "",
"test_event_tooltip": "Guarde as alterações antes de testar as definições."
}
}

60
front/php/templates/language/ru_ru.json Normal file → Executable file
View File

@@ -8,7 +8,7 @@
"About_Design": "Разработан:",
"About_Exit": "Зарегистрироваться",
"About_Title": "Сетевой сканер и система уведомлений",
"AppEvents_AppEventProcessed": "",
"AppEvents_AppEventProcessed": "Обработанный",
"AppEvents_DateTimeCreated": "Журнал",
"AppEvents_Extra": "Дополнительно",
"AppEvents_GUID": "GUID события приложения",
@@ -22,7 +22,7 @@
"AppEvents_ObjectPlugin": "Связанный плагин",
"AppEvents_ObjectPrimaryID": "Первичный ID",
"AppEvents_ObjectSecondaryID": "Вторичный ID",
"AppEvents_ObjectStatus": "Состояние (во время входа в систему)",
"AppEvents_ObjectStatus": "Зарегистрированный статус",
"AppEvents_ObjectStatusColumn": "Колонка состояния",
"AppEvents_ObjectType": "Тип объекта",
"AppEvents_Plugin": "Плагин",
@@ -70,7 +70,7 @@
"DevDetail_Copy_Device_Tooltip": "Скопируйте данные с устройства из раскрывающегося списка. Все на этой странице будет перезаписано",
"DevDetail_CustomProperties_Title": "Пользовательские свойства",
"DevDetail_CustomProps_reset_info": "Это удалит ваши пользовательские свойства на этом устройстве и вернет их к значению по умолчанию.",
"DevDetail_DisplayFields_Title": "Дисплей",
"DevDetail_DisplayFields_Title": "Отображение",
"DevDetail_EveandAl_AlertAllEvents": "Оповещения о событиях",
"DevDetail_EveandAl_AlertDown": "Оповещение о доступности",
"DevDetail_EveandAl_Archived": "Архив",
@@ -225,7 +225,7 @@
"Device_TableHead_Name": "Имя",
"Device_TableHead_NetworkSite": "Сайт устройства",
"Device_TableHead_Owner": "Владелец",
"Device_TableHead_Parent_MAC": "MAC род. узла",
"Device_TableHead_Parent_MAC": "Родительский узел сети",
"Device_TableHead_Port": "Порт",
"Device_TableHead_PresentLastScan": "Присутствие",
"Device_TableHead_RowID": "ID строки",
@@ -363,8 +363,8 @@
"Maintenance_Title": "Инструменты обслуживания",
"Maintenance_Tool_DownloadConfig": "Экспорт настроек",
"Maintenance_Tool_DownloadConfig_text": "Загрузите полную резервную копию конфигурации настроек, хранящуюся в файле <code>app.conf</code>.",
"Maintenance_Tool_DownloadWorkflows": "",
"Maintenance_Tool_DownloadWorkflows_text": "",
"Maintenance_Tool_DownloadWorkflows": "Экспорт Workflow",
"Maintenance_Tool_DownloadWorkflows_text": "Загрузите полную резервную копию ваших Workflow, хранящихся в файле <code> hairpflows.json </code>.",
"Maintenance_Tool_ExportCSV": "Экспорт устройств (csv)",
"Maintenance_Tool_ExportCSV_noti": "Экспорт устройств (csv)",
"Maintenance_Tool_ExportCSV_noti_text": "Вы уверены, что хотите создать файл CSV?",
@@ -463,7 +463,7 @@
"NETWORK_DEVICE_TYPES_description": "Какие типы устройств разрешено использовать в качестве сетевых устройств в представлении Сеть. Тип устройства должен точно соответствовать настройке <code>Type</code> для конкретного устройства в сведениях об устройстве. Добавьте его на устройство с помощью кнопки <code>+</code>. Не удаляйте существующие типы, а только добавляйте новые.",
"NETWORK_DEVICE_TYPES_name": "Типы сетевых устройств",
"Navigation_About": "О NetAlertX",
"Navigation_AppEvents": "",
"Navigation_AppEvents": "События приложения",
"Navigation_Devices": "Устройства",
"Navigation_Donations": "Пожертвования",
"Navigation_Events": "События",
@@ -670,26 +670,32 @@
"UI_REFRESH_name": "Автоматическое обновление интерфейса",
"VERSION_description": "Вспомогательное значение версии или метки времени, позволяющее проверить, было ли приложение обновлено.",
"VERSION_name": "Версия или временная метка",
"WF_Action_Add": "",
"WF_Action_field": "",
"WF_Action_type": "",
"WF_Action_value": "",
"WF_Actions": "",
"WF_Add": "",
"WF_Add_Condition": "",
"WF_Add_Group": "",
"WF_Condition_field": "",
"WF_Condition_operator": "",
"WF_Condition_value": "",
"WF_Conditions": "",
"WF_Conditions_logic_rules": "",
"WF_Enabled": "",
"WF_Name": "",
"WF_Remove": "",
"WF_Save": "",
"WF_Trigger": "",
"WF_Trigger_event_type": "",
"WF_Trigger_type": "",
"WF_Action_Add": "Добавить действие",
"WF_Action_field": "Поле",
"WF_Action_type": "Тип",
"WF_Action_value": "Значение",
"WF_Actions": "Действия",
"WF_Add": "Добавить Workflow",
"WF_Add_Condition": "Добавить условие",
"WF_Add_Group": "Добавить группу",
"WF_Condition_field": "Поле",
"WF_Condition_operator": "Оператор",
"WF_Condition_value": "Значение",
"WF_Conditions": "Условия",
"WF_Conditions_logic_rules": "Правила логики",
"WF_Duplicate": "Дублировать Workflow",
"WF_Enabled": "Включить Workflow",
"WF_Export": "Экспорт Workflow",
"WF_Export_Copy": "Скопируйте приведенный ниже Workflow и импортируйте его, где это необходимо.",
"WF_Import": "Импорт Workflow",
"WF_Import_Copy": "Вставьте в Workflow, который вы скопировали ранее.",
"WF_Name": "Имя Workflow",
"WF_Remove": "Удалить Workflow",
"WF_Remove_Copy": "Вы хотите удалить этот Workflow?",
"WF_Save": "Сохранить Workflow",
"WF_Trigger": "Триггер",
"WF_Trigger_event_type": "Тип события",
"WF_Trigger_type": "Тип триггера",
"add_icon_event_icon": "fa-square-plus",
"add_icon_event_tooltip": "Добавить новую иконку",
"add_option_event_icon": "fa-square-plus",

View File

@@ -683,9 +683,15 @@
"WF_Condition_value": "",
"WF_Conditions": "",
"WF_Conditions_logic_rules": "",
"WF_Duplicate": "",
"WF_Enabled": "",
"WF_Export": "",
"WF_Export_Copy": "",
"WF_Import": "",
"WF_Import_Copy": "",
"WF_Name": "",
"WF_Remove": "",
"WF_Remove_Copy": "",
"WF_Save": "",
"WF_Trigger": "",
"WF_Trigger_event_type": "",

52
front/php/templates/language/uk_ua.json Normal file → Executable file
View File

@@ -22,7 +22,7 @@
"AppEvents_ObjectPlugin": "Пов’язаний плагін",
"AppEvents_ObjectPrimaryID": "Основний ідентифікатор",
"AppEvents_ObjectSecondaryID": "Вторинний ідентифікатор",
"AppEvents_ObjectStatus": "Статус (під час журналу)",
"AppEvents_ObjectStatus": "Зареєстрований статус",
"AppEvents_ObjectStatusColumn": "Колонка статусу",
"AppEvents_ObjectType": "Тип об'єкта",
"AppEvents_Plugin": "Плагін",
@@ -363,8 +363,8 @@
"Maintenance_Title": "Інструменти обслуговування",
"Maintenance_Tool_DownloadConfig": "Експорт налаштувань",
"Maintenance_Tool_DownloadConfig_text": "Завантажте повну резервну копію конфігурації налаштувань, яка зберігається у файлі <code>app.conf</code>.",
"Maintenance_Tool_DownloadWorkflows": "",
"Maintenance_Tool_DownloadWorkflows_text": "",
"Maintenance_Tool_DownloadWorkflows": "Експорт робочих процесів",
"Maintenance_Tool_DownloadWorkflows_text": "Завантажте повну резервну копію робочих процесів, які зберігаються у файлі <code>workflows.json</code>.",
"Maintenance_Tool_ExportCSV": "Експорт пристроїв (csv)",
"Maintenance_Tool_ExportCSV_noti": "Експорт пристроїв (csv)",
"Maintenance_Tool_ExportCSV_noti_text": "Ви впевнені, що хочете створити файл CSV?",
@@ -670,26 +670,32 @@
"UI_REFRESH_name": "Автоматичне оновлення інтерфейсу користувача",
"VERSION_description": "Допоміжне значення версії або позначки часу, щоб перевірити, чи було оновлено додаток.",
"VERSION_name": "Версія або позначка часу",
"WF_Action_Add": "",
"WF_Action_field": "",
"WF_Action_type": "",
"WF_Action_value": "",
"WF_Actions": "",
"WF_Add": "",
"WF_Add_Condition": "",
"WF_Add_Group": "",
"WF_Condition_field": "",
"WF_Condition_operator": "",
"WF_Condition_value": "",
"WF_Conditions": "",
"WF_Conditions_logic_rules": "",
"WF_Enabled": "",
"WF_Name": "",
"WF_Remove": "",
"WF_Save": "",
"WF_Trigger": "",
"WF_Trigger_event_type": "",
"WF_Trigger_type": "",
"WF_Action_Add": "Додати дію",
"WF_Action_field": "Поле",
"WF_Action_type": "Тип",
"WF_Action_value": "Значення",
"WF_Actions": "Дії",
"WF_Add": "Додати робочий процес",
"WF_Add_Condition": "Додати умову",
"WF_Add_Group": "Додати групу",
"WF_Condition_field": "Поле",
"WF_Condition_operator": "Оператор",
"WF_Condition_value": "Значення",
"WF_Conditions": "Умови",
"WF_Conditions_logic_rules": "Логічні правила",
"WF_Duplicate": "Дубльований робочий процес",
"WF_Enabled": "Робочий процес увімкнено",
"WF_Export": "Робочий процес експорту",
"WF_Export_Copy": "Скопіюйте наведений нижче робочий процес і імпортуйте його, де потрібно.",
"WF_Import": "Робочий процес імпорту",
"WF_Import_Copy": "Вставте робочий процес, який ви скопіювали раніше.",
"WF_Name": "Назва робочого процесу",
"WF_Remove": "Видалити робочий процес",
"WF_Remove_Copy": "Ви хочете видалити цей робочий процес?",
"WF_Save": "Зберегти робочі процеси",
"WF_Trigger": "Тригер",
"WF_Trigger_event_type": "Тип події",
"WF_Trigger_type": "Тип тригера",
"add_icon_event_icon": "фа-квадрат-плюс",
"add_icon_event_tooltip": "додати новий значок",
"add_option_event_icon": "фа-квадрат-плюс",

8
front/php/templates/language/zh_cn.json Normal file → Executable file
View File

@@ -683,9 +683,15 @@
"WF_Condition_value": "",
"WF_Conditions": "",
"WF_Conditions_logic_rules": "",
"WF_Duplicate": "",
"WF_Enabled": "",
"WF_Export": "",
"WF_Export_Copy": "",
"WF_Import": "",
"WF_Import_Copy": "",
"WF_Name": "",
"WF_Remove": "",
"WF_Remove_Copy": "",
"WF_Save": "",
"WF_Trigger": "",
"WF_Trigger_event_type": "",
@@ -736,4 +742,4 @@
"settings_update_item_warning": "更新下面的值。请注意遵循先前的格式。<b>未执行验证。</b>",
"test_event_icon": "",
"test_event_tooltip": "在测试设置之前,请先保存更改。"
}
}

View File

@@ -41,8 +41,8 @@ The OMADA SDN plugin aims at synchronizing data between NetAlertX and a TPLINK O
OMADA SDN limitation fixed by the plugin:
0. OMADA SDN can't use DNS for names and keep using MAC ref: https://community.tp-link.com/en/business/forum/topic/503782
- when you use an OMADA user Role = Administrator, the plugin will attempt to fix OMADA's shortcoming and populat the NAME field from NetAlertX (from DNS/DHCP/...)
![OMADA SDN account page](Oamadaomada_sdn_imp.png)
-
![OMADA SDN account page](omada_account_sample.png)
can not fix some of tplinks OMADA SDN own limitations/bugs:
1. OMADA SDN switches uplinks/downlinks is broken if the default router is not an OMADA native device

View File

@@ -285,16 +285,18 @@ def main():
mylog("verbose", [f"[{pluginName}] login to omada result is: {omada_login}"])
clients_list = callomada(["-t", "myomada", "clients"])
client_list_count = clients_list.count("\n")
mylog(
"verbose",
[f'[{pluginName}] clients found:"{clients_list.count("\n")}"\n{clients_list}'],
[f'[{pluginName}] clients found:"{client_list_count}"\n{clients_list}'],
)
switches_and_aps = callomada(["-t", "myomada", "devices"])
switches_and_aps_count = switches_and_aps.count("\n")
mylog(
"verbose",
[
f'[{pluginName}] omada devices (switches, access points) found:"{switches_and_aps.count("\n")}" \n {switches_and_aps}'
f'[{pluginName}] omada devices (switches, access points) found:"{switches_and_aps_count}" \n {switches_and_aps}'
],
)

View File

@@ -11,19 +11,24 @@
</div>
<div id="buttons" class="bottom-buttons col-sm-12 col-xs-12">
<div class="add-workflow col-sm-12 col-xs-12">
<div class="add-workflow col-sm-4 col-xs-12">
<button type="button" class="btn btn-primary add-workflow-btn col-sm-12 col-xs-12" id="add">
<?= lang('WF_Add');?>
<i class="fa fa-fw fa-plus"></i> <?= lang('WF_Add');?>
</button>
</div>
<div class="save-workflows col-sm-12 col-xs-12">
<button type="button" class="btn btn-primary col-sm-12 col-xs-12" id="save" onclick="saveWorkflows()">
<?= lang('WF_Save');?>
<div class="import-wf col-sm-4 col-xs-12">
<button type="button" class="btn btn-primary col-sm-12 col-xs-12" id="import">
<i class="fa fa-fw fa-file-import"></i> <?= lang('WF_Import');?>
</button>
</div>
<div class="restart-app col-sm-12 col-xs-12">
<div class="restart-app col-sm-4 col-xs-12">
<button type="button" class="btn btn-primary col-sm-12 col-xs-12" id="save" onclick="askRestartBackend()">
<?= lang('Maint_RestartServer');?>
<i class="fa fa-fw fa-arrow-rotate-right"></i> <?= lang('Maint_RestartServer');?>
</button>
</div>
<div class="save-workflows col-xs-12">
<button type="button" class="btn btn-primary bg-green col-sm-12 col-xs-12" id="save" onclick="saveWorkflows()">
<i class="fa fa-fw fa-floppy-disk"></i> <?= lang('WF_Save');?>
</button>
</div>
</div>
@@ -45,6 +50,9 @@ let fieldOptions = [
let triggerTypes = [
"Devices"
];
let triggerEvents = [
"update", "insert", "delete"
];
let wfEnabledOptions = [
"Yes", "No"
@@ -58,6 +66,18 @@ let actionTypes = [
"update_field", "delete_device"
];
let emptyWorkflow = {
"name": "New Workflow",
"trigger": {
"object_type": "Devices",
"event_type": "insert"
},
"conditions": [
],
"actions": [
]
};
// --------------------------------------
// Retrieve and process the data
function getData() {
@@ -108,6 +128,8 @@ function renderWorkflows() {
// Generate UI for a single workflow
function generateWorkflowUI(wf, wfIndex) {
let wfEnabled = (wf?.enabled ?? "No") == "Yes";
let $wfContainer = $("<div>", {
class: "workflow-card panel col-sm-12 col-sx-12",
id: `wf-${wfIndex}-container`
@@ -121,6 +143,11 @@ function generateWorkflowUI(wf, wfIndex) {
}
)
let $wfEnabledIcon = $("<i>", {
class: `alignRight fa-regular ${wfEnabled ? "fa-dot-circle" : "fa-circle" }`
});
let $wfHeaderLink = $("<a>",
{
"class": "pointer ",
@@ -137,7 +164,7 @@ function generateWorkflowUI(wf, wfIndex) {
}
).text(wf.name)
$wfContainer.append($wfHeaderLink.append($wfLinkWrap.append($wfHeaderHeading)));
$wfContainer.append($wfHeaderLink.append($wfLinkWrap.append($wfHeaderHeading.append($wfEnabledIcon))));
// Collapsible panel start
@@ -157,7 +184,7 @@ function generateWorkflowUI(wf, wfIndex) {
`[${wfIndex}].enabled`,
getString("WF_Enabled"),
wfEnabledOptions,
wf?.enabled ?? "No",
wfEnabled ? "Yes" :"No",
`wf-${wfIndex}-enabled`
);
@@ -201,7 +228,7 @@ function generateWorkflowUI(wf, wfIndex) {
let $eventTypeDropdown = createEditableDropdown(
`[${wfIndex}].trigger.event_type`,
getString("WF_Trigger_event_type"),
["update", "create", "delete"],
triggerEvents,
wf.trigger.event_type,
`wf-${wfIndex}-trigger-event-type`
);
@@ -210,9 +237,10 @@ function generateWorkflowUI(wf, wfIndex) {
class: "fa-solid fa-bolt bckg-icon-2-line"
});
$triggerSection.append($triggerIcon);
$triggerSection.append($triggerTypeDropdown);
$triggerSection.append($eventTypeDropdown);
$triggerSection.append($triggerIcon);
$wfCollapsiblePanel.append($triggerSection);
// Conditions
@@ -323,11 +351,13 @@ function generateWorkflowUI(wf, wfIndex) {
$actionRemoveButtonWrap.append($actionRemoveButton);
let $actionIcon = $("<i>", {
class: `fa-solid fa-person-running fa-flip-horizontal bckg-icon-${numberOfLines}-line `
});
class: `fa-solid fa-person-running fa-flip-horizontal bckg-icon-${numberOfLines}-line `
});
$actionEl.prepend($actionIcon)
$actionElWrap.append($actionEl)
$actionElWrap.append($actionIcon)
$actionElWrap.append($actionRemoveButtonWrap)
$actionsContainer.append($actionElWrap);
@@ -350,7 +380,7 @@ function generateWorkflowUI(wf, wfIndex) {
$actionsContainer.append($actionAddButtonWrap)
let $wfRemoveButtonWrap = $("<div>", { class: "button-container col-sm-12 col-sx-12" });
let $wfRemoveButtonWrap = $("<div>", { class: "button-container col-sm-4 col-sx-12" });
let $wfRemoveIcon = $("<i>", {
class: "fa-solid fa-trash"
@@ -362,10 +392,40 @@ function generateWorkflowUI(wf, wfIndex) {
})
.append($wfRemoveIcon) // Add icon
.append(` ${getString("WF_Remove")}`); // Add text
let $wfDuplicateButtonWrap = $("<div>", { class: "button-container col-sm-4 col-sx-12" });
let $wfDuplicateIcon = $("<i>", {
class: "fa-solid fa-copy"
});
let $wfDuplicateButton = $("<div>", {
class: "pointer duplicate-wf green-hover-text",
wfIndex: wfIndex
})
.append($wfDuplicateIcon) // Add icon
.append(` ${getString("WF_Duplicate")}`); // Add text
let $wfExportButtonWrap = $("<div>", { class: "button-container col-sm-4 col-sx-12" });
let $wfExportIcon = $("<i>", {
class: "fa-solid fa-file-export"
});
let $wfExportButton = $("<div>", {
class: "pointer export-wf green-hover-text",
wfIndex: wfIndex
})
.append($wfExportIcon) // Add icon
.append(` ${getString("WF_Export")}`); // Add text
$wfCollapsiblePanel.append($actionsContainer);
$wfCollapsiblePanel.append($wfDuplicateButtonWrap.append($wfDuplicateButton))
$wfCollapsiblePanel.append($wfExportButtonWrap.append($wfExportButton))
$wfCollapsiblePanel.append($wfRemoveButtonWrap.append($wfRemoveButton))
$wfContainer.append($wfCollapsiblePanel)
@@ -377,7 +437,7 @@ function generateWorkflowUI(wf, wfIndex) {
// Render conditions recursively
function renderConditions(wfIndex, parentIndexPath, conditionGroupsIndex, conditions) {
let $conditionList = $("<div>", {
class: "condition-list panel panel-secondary col-sm-12 col-sx-12",
class: "condition-list panel col-sm-12 col-sx-12",
parentIndexPath: parentIndexPath
});
@@ -755,7 +815,62 @@ function addWorkflow(workflows) {
// Function to remove a Workflow
function removeWorkflow(workflows, wfIndex) {
workflows.splice(wfIndex, 1);
showModalWarning ('<?= lang('WF_Remove');?>', '<?= lang('WF_Remove_Copy');?>',
'<?= lang('Gen_Cancel');?>', '<?= lang('Gen_Delete');?>', `executeRemoveWorkflow`, wfIndex);
}
// ---------------------------------------------------
// Function to execute the remove of a Workflow
function executeRemoveWorkflow() {
workflows = getWorkflowsJson()
workflows.splice($('#modal-warning').attr("data-myparam-triggered-by"), 1);
updateWorkflowsJson(workflows)
// Re-render the UI
renderWorkflows();
}
// ---------------------------------------------------
// Function to duplicate a Workflow
function duplicateWorkflow(workflows, wfIndex) {
workflows.push(workflows[wfIndex])
updateWorkflowsJson(workflows)
// Re-render the UI
renderWorkflows();
}
// ---------------------------------------------------
// Function to export a Workflow
function exportWorkflow(workflows, wfIndex) {
// Add new icon as base64 string
showModalInput ('<i class="fa fa-file-export pointer"></i> <?= lang('WF_Export');?>', '<?= lang('WF_Export_Copy');?>',
'<?= lang('Gen_Cancel');?>', '<?= lang('Gen_Okay');?>', null, null, JSON.stringify(workflows[wfIndex], null, 2));
}
// ---------------------------------------------------
// Function to import a Workflow
function importWorkflow(workflows, wfIndex) {
// Add new icon as base64 string
showModalInput ('<i class="fa fa-file-import pointer"></i> <?= lang('WF_Import');?>', '<?= lang('WF_Import_Copy');?>',
'<?= lang('Gen_Cancel');?>', '<?= lang('Gen_Okay');?>', 'importWorkflowExecute', null, "" );
}
function importWorkflowExecute()
{
var json = JSON.parse($('#modal-input-textarea').val());
workflows = getWorkflowsJson()
workflows.push(json);
updateWorkflowsJson(workflows)
@@ -970,17 +1085,7 @@ function updateWorkflowsJson(workflows)
// Get empty workflow JSON
function getEmptyWorkflowJson()
{
return {
"name": "New Workflow",
"trigger": {
"object_type": "Devices",
"event_type": "create"
},
"conditions": [
],
"actions": [
]
}
return emptyWorkflow;
}
// ---------------------------------------------------
@@ -1009,6 +1114,21 @@ $(document).on("click", ".remove-wf", function () {
removeWorkflow(getWorkflowsJson(), wfIndex);
});
$(document).on("click", ".duplicate-wf", function () {
let wfIndex = $(this).attr("wfindex");
duplicateWorkflow(getWorkflowsJson(), wfIndex);
});
$(document).on("click", ".export-wf", function () {
let wfIndex = $(this).attr("wfindex");
exportWorkflow(getWorkflowsJson(), wfIndex);
});
$(document).on("click", ".import-wf", function () {
let wfIndex = $(this).attr("wfindex");
importWorkflow(getWorkflowsJson(), wfIndex);
});
$(document).on("click", ".add-condition", function () {
let wfIndex = $(this).attr("wfindex");
let parentIndexPath = $(this).attr("parentIndexPath");

View File

@@ -35,7 +35,8 @@ nav:
- Security: SECURITY.md
- Advanced guides:
- Remote Networks: REMOTE_NETWORKS.md
- Notifications Guide: NOTIFICATIONS.md
- Notifications Guide: NOTIFICATIONS.md
- Name Resolution: NAME_RESOLUTION.md
- Authelia: AUTHELIA.md
- Performance: PERFORMANCE.md
- Reverse DNS: REVERSE_DNS.md
@@ -57,10 +58,12 @@ nav:
- Icons: ICONS.md
- Network Topology: NETWORK_TREE.md
- Troubleshooting:
- Inspecting Logs: LOGGING.md
- Debugging Tips: DEBUG_TIPS.md
- Debugging Invalid JSON: DEBUG_INVALID_JSON.md
- Debugging Plugins: DEBUG_PLUGINS.md
- Debugging Web UI Port: WEB_UI_PORT_DEBUG.md
- Debugging Workflows: WORKFLOWS_DEBUGGING.md
- Development:
- Plugin and app development:
- Environment Setup: DEV_ENV_SETUP.md

View File

@@ -75,7 +75,6 @@ class DB():
return arr
#-------------------------------------------------------------------------------
def upgradeDB(self):
"""
@@ -542,8 +541,7 @@ class DB():
# Plugin state
sql_Plugins_Objects = """ CREATE TABLE IF NOT EXISTS Plugins_Objects(
"Index" INTEGER,
Plugin TEXT NOT NULL,
ObjectGUID TEXT,
Plugin TEXT NOT NULL,
Object_PrimaryID TEXT NOT NULL,
Object_SecondaryID TEXT NOT NULL,
DateTimeCreated TEXT NOT NULL,
@@ -561,6 +559,7 @@ class DB():
"HelpVal2" TEXT,
"HelpVal3" TEXT,
"HelpVal4" TEXT,
ObjectGUID TEXT,
PRIMARY KEY("Index" AUTOINCREMENT)
); """
self.sql.execute(sql_Plugins_Objects)
@@ -929,4 +928,19 @@ def get_all_devices(db):
return db.read(sql_devices_all)
#-------------------------------------------------------------------------------
def get_array_from_sql_rows(rows):
# Convert result into list of lists
arr = []
for row in rows:
if isinstance(row, sqlite3.Row):
arr.append(list(row)) # Convert row to list
elif isinstance(row, (tuple, list)):
arr.append(list(row)) # Already iterable, just convert to list
else:
arr.append([row]) # Handle single values safely
return arr
#-------------------------------------------------------------------------------

View File

@@ -797,7 +797,7 @@ class plugin_object_class:
def __init__(self, plugin, objDbRow):
self.index = objDbRow[0]
self.pluginPref = objDbRow[1]
self.primaryId = objDbRow[2]
self.primaryId = objDbRow[2]
self.secondaryId = objDbRow[3]
self.created = objDbRow[4] # can be null
self.changed = objDbRow[5] # never null (data coming from plugin)
@@ -819,7 +819,7 @@ class plugin_object_class:
# Check if self.status is valid
if self.status not in ["exists", "watched-changed", "watched-not-changed", "new", "not-processed", "missing-in-last-scan"]:
raise ValueError("Invalid status value for plugin object:", self.status)
raise ValueError(f"Invalid status value for plugin object ({self.pluginPref}|{self.primaryId}|{self.watched1}) invalid status: {self.status} on objDbRow:", objDbRow)
self.idsHash = str(hash(str(self.primaryId) + str(self.secondaryId)))
# self.idsHash = str(self.primaryId) + str(self.secondaryId)

View File

@@ -41,7 +41,7 @@ class Condition:
if self.operator == "equals":
result = str(obj_value) == str(self.value)
elif self.operator == "contains":
result = str(self.value) in str(obj_value)
result = str(self.value).lower() in str(obj_value).lower()
elif self.operator == "regex":
result = bool(re.match(self.value, str(obj_value)))
else:
@@ -57,9 +57,7 @@ class ConditionGroup:
def __init__(self, group_json):
mylog('none', ["[WF] json.dumps(group_json)"])
mylog('none', [json.dumps(group_json)])
mylog('none', [group_json])
mylog('verbose', [f"[WF] ConditionGroup json.dumps(group_json): {json.dumps(group_json)}"])
self.logic = group_json.get("logic", "AND").upper()
self.conditions = []
@@ -78,6 +76,6 @@ class ConditionGroup:
elif self.logic == "OR":
return any(results)
else:
m = f"[WF] Unsupported logic: {self.logic}"
mylog('none', [m])
m = f"[WF] ConditionGroup unsupported logic: {self.logic}"
mylog('verbose', [m])
raise ValueError(m)

View File

@@ -50,20 +50,24 @@ class WorkflowManager:
def process_event(self, event):
"""Process the events. Check if events match a workflow trigger"""
mylog('verbose', [f"[WF] Processing event with GUID {event["GUID"]}"])
evGuid = event["GUID"]
mylog('verbose', [f"[WF] Processing event with GUID {evGuid}"])
# Check if the trigger conditions match
for workflow in self.workflows:
# Ensure workflow is enabled before proceeding
if workflow.get("enabled", "No").lower() == "yes":
wfName = workflow["name"]
mylog('debug', [f"[WF] Checking if '{evGuid}' triggers the workflow '{wfName}'"])
# construct trigger object which also evaluates if the current event triggers it
trigger = Trigger(workflow["trigger"], event, self.db)
if trigger.triggered:
mylog('verbose', [f"[WF] Event with GUID '{event["GUID"]}' triggered the workflow '{workflow["name"]}'"])
mylog('verbose', [f"[WF] Event with GUID '{evGuid}' triggered the workflow '{wfName}'"])
self.execute_workflow(workflow, trigger)
@@ -80,6 +84,8 @@ class WorkflowManager:
def execute_workflow(self, workflow, trigger):
"""Execute the actions in the given workflow if conditions are met."""
wfName = workflow["name"]
# Ensure conditions exist
if not isinstance(workflow.get("conditions"), list):
m = f"[WF] workflow['conditions'] must be a list"
@@ -93,7 +99,7 @@ class WorkflowManager:
if evaluator.evaluate(trigger): # If any group evaluates to True
mylog('none', [f"[WF] Workflow {workflow["name"]} will be executed - conditions were evaluated as TRUE"])
mylog('none', [f"[WF] Workflow {wfName} will be executed - conditions were evaluated as TRUE"])
mylog('debug', [f"[WF] Workflow condition_group: {condition_group}"])
self.execute_actions(workflow["actions"], trigger)

View File

@@ -1,4 +1,5 @@
import sys
import json
# Register NetAlertX directories
INSTALL_PATH="/app"
@@ -7,6 +8,7 @@ sys.path.extend([f"{INSTALL_PATH}/server"])
import conf
from logger import mylog, Logger
from helper import get_setting_value, timeNowTZ
from database import get_array_from_sql_rows
# Make sure log level is initialized correctly
Logger(get_setting_value('LOG_LEVEL'))
@@ -27,7 +29,8 @@ class Trigger:
self.event = event # Store the triggered event context, if provided
self.triggered = self.object_type == event["ObjectType"] and self.event_type == event["AppEventType"]
mylog('verbose', [f"[WF] self.triggered '{self.triggered}'"])
mylog('debug', [f"""[WF] self.triggered '{self.triggered}' for event '{get_array_from_sql_rows(event)} and trigger {json.dumps(triggerJson)}' """])
if self.triggered:
# object type corresponds with the DB table name