Compare commits

...

41 Commits

Author SHA1 Message Date
github-actions[bot]
ac259b1fab [🤖Automation] Update README with sponsors information
Some checks are pending
docker / docker_dev (push) Waiting to run
2024-10-02 11:53:48 +00:00
jokob-sk
14996d6582 MQTT_topic_root
Some checks are pending
docker / docker_dev (push) Waiting to run
2024-10-02 16:10:19 +10:00
jokob-sk
d44744657e MQTT timestamp normalization for HomeAssistant 2024-10-02 15:51:08 +10:00
jokob-sk
615e5e4084 MQTT timestamp normalization for HomeAssistant 2024-10-02 14:36:42 +10:00
jokob-sk
dd948b5e63 Merge pull request #820 from NightMean/main
Some checks are pending
docker / docker_dev (push) Waiting to run
Update MQTT to send model as device name - thanks to @NightMean 🙏
2024-10-02 08:56:39 +10:00
jokob-sk
97a5cb6737 HomeAssistant docs + Delete listed Plugin Obj #813 2024-10-02 08:53:29 +10:00
NightMean
c6fe09d366 Update MQTT to send model as device name
Adds a device name as model for HomeAssistant that shows in Device info.
2024-10-01 22:04:28 +02:00
jokob-sk
040f2792e4 Merge branch 'main' of https://github.com/jokob-sk/NetAlertX
Some checks are pending
docker / docker_dev (push) Waiting to run
2024-10-01 23:09:46 +10:00
jokob-sk
d1d6d7f1ec MQTT docs 2024-10-01 23:09:29 +10:00
github-actions[bot]
33c16c4d00 [🤖Automation] Update README with sponsors information 2024-10-01 11:53:38 +00:00
Hosted Weblate
cc8b57e790 Merge branch 'origin/main' into Weblate.
Some checks are pending
docker / docker_dev (push) Waiting to run
2024-10-01 06:36:47 +02:00
Yannick Torrès
57d8e97b60 Translated using Weblate (French)
Currently translated at 100.0% (699 of 699 strings)

Translation: NetAlertX/core
Translate-URL: https://hosted.weblate.org/projects/pialert/core/fr/
2024-10-01 06:36:47 +02:00
jokob-sk
91ad39e991 Popup display on mobile #772 2024-10-01 14:36:24 +10:00
jokob-sk
15ed621748 Merge branch 'main' of https://github.com/jokob-sk/NetAlertX
Some checks are pending
docker / docker_dev (push) Waiting to run
2024-10-01 08:42:18 +10:00
jokob-sk
50304fd63b 📊 Presence over time updates #816 2024-10-01 08:42:14 +10:00
Yannick Torrès
90689e5c69 Translated using Weblate (French)
Some checks are pending
docker / docker_dev (push) Waiting to run
Currently translated at 98.5% (689 of 699 strings)

Translation: NetAlertX/core
Translate-URL: https://hosted.weblate.org/projects/pialert/core/fr/
2024-09-30 12:16:48 +00:00
gallegonovato
5f4b2f114c Translated using Weblate (Spanish)
Currently translated at 100.0% (699 of 699 strings)

Translation: NetAlertX/core
Translate-URL: https://hosted.weblate.org/projects/pialert/core/es/
2024-09-30 12:16:47 +00:00
github-actions[bot]
e72a87ab43 [🤖Automation] Update README with sponsors information 2024-09-30 11:53:45 +00:00
jokob-sk
044de61ab5 ⬇CSV Import work #808
Some checks are pending
docker / docker_dev (push) Waiting to run
2024-09-30 10:30:09 +10:00
github-actions[bot]
e5d835cfa9 [🤖Automation] Update README with sponsors information
Some checks are pending
docker / docker_dev (push) Waiting to run
2024-09-29 11:53:44 +00:00
jokob-sk
e2d84a1885 MQTT handling diacritics #813
Some checks are pending
docker / docker_dev (push) Waiting to run
2024-09-29 11:52:29 +10:00
jokob-sk
e648acde5c General enhancements 2024-09-29 11:26:06 +10:00
jokob-sk
a17e066f34 🔃Sync enhancements 2024-09-29 11:12:38 +10:00
jokob-sk
0bdc4c4ed1 chore: 🧹Removal of DB backups functionality 2024-09-29 10:00:04 +10:00
jokob-sk
9144fd0c3a Handling checkboxes better #779 2024-09-29 09:19:54 +10:00
jokob-sk
02077d4654 CSV Export - encode quotes #808 2024-09-29 08:18:00 +10:00
github-actions[bot]
e3b2039257 [🤖Automation] Update README with sponsors information
Some checks are pending
docker / docker_dev (push) Waiting to run
2024-09-28 11:53:47 +00:00
jokob-sk
1fa38472e1 📚Docs 2024-09-28 16:58:17 +10:00
jokob-sk
1e197ae749 chore: 🧹 Code Cleanup
Some checks are pending
docker / docker_dev (push) Waiting to run
2024-09-28 11:20:57 +10:00
jokob-sk
7731a01f3c chore: 🧹 Code Cleanup 2024-09-28 10:29:43 +10:00
jokob-sk
3ce08ba97d Merge branch 'main' of https://github.com/jokob-sk/NetAlertX 2024-09-28 10:29:05 +10:00
jokob-sk
c58bbf21b1 chore: 🧹 Code Cleanup 2024-09-28 10:28:05 +10:00
github-actions[bot]
3780e47117 [🤖Automation] Update README with sponsors information
Some checks are pending
docker / docker_dev (push) Waiting to run
2024-09-27 11:53:42 +00:00
jokob-sk
e8f353024f Merge branch 'main' of https://github.com/jokob-sk/NetAlertX 2024-09-27 16:15:36 +10:00
jokob-sk
7308797314 removing cryptography 2024-09-27 16:15:14 +10:00
jokob-sk
6e36f7d7aa Merge pull request #810 from ingoratsdorf/contrib
Some checks are pending
docker / docker_dev (push) Waiting to run
fixes to MQTT publisher
2024-09-27 12:42:47 +10:00
Ingo Ratsdorf
8d3a4500e2 Merge branch 'jokob-sk:main' into contrib 2024-09-27 14:30:58 +12:00
jokob-sk
40d6bdc2b2 Cryptography -> crypto_utils rename #809 2024-09-27 12:21:42 +10:00
Ingo Ratsdorf
b7b2e0bc65 fixes to MQTT publisher
This wasn't working for EMQX due to callback trigger delays it never connected. Also added a reconnect feature and a client id so it looks better in the EMQX connection dashboard. No confirmed to be working with Mosquitto and EMQX
2024-09-27 12:24:46 +12:00
jokob-sk
081d0f3400 Sync 2024-09-27 10:02:24 +10:00
github-actions[bot]
a7f4565954 [🤖Automation] Update README with sponsors information
Some checks are pending
docker / docker_dev (push) Waiting to run
2024-09-26 11:53:36 +00:00
73 changed files with 950 additions and 900 deletions

View File

@@ -43,7 +43,7 @@ RUN phpenmod -v 8.2 sqlite3
RUN apt-get install -y python3-venv RUN apt-get install -y python3-venv
RUN python3 -m venv myenv RUN python3 -m venv myenv
RUN /bin/bash -c "source myenv/bin/activate && update-alternatives --install /usr/bin/python python /usr/bin/python3 10 && pip3 install tplink-omada-client pycryptodome requests paho-mqtt scapy cron-converter pytz json2table dhcp-leases pyunifi speedtest-cli chardet python-nmap dnspython cryptography librouteros " RUN /bin/bash -c "source myenv/bin/activate && update-alternatives --install /usr/bin/python python /usr/bin/python3 10 && pip3 install tplink-omada-client pycryptodome requests paho-mqtt scapy cron-converter pytz json2table dhcp-leases pyunifi speedtest-cli chardet python-nmap dnspython librouteros "
# Create a buildtimestamp.txt to later check if a new version was released # Create a buildtimestamp.txt to later check if a new version was released
RUN date +%s > ${INSTALL_DIR}/front/buildtimestamp.txt RUN date +%s > ${INSTALL_DIR}/front/buildtimestamp.txt

View File

@@ -1,4 +1,4 @@
[![GitHub Committed](https://img.shields.io/github/last-commit/jokob-sk/NetAlertX?color=40ba12&label=Committed&logo=GitHub&logoColor=fff&style=for-the-badge)](https://github.com/jokob-sk/NetAlertX) [![GitHub Committed](https://img.shields.io/github/last-commit/jokob-sk/NetAlertX?color=40ba12&label=Pushed&logo=GitHub&logoColor=fff&style=for-the-badge)](https://github.com/jokob-sk/NetAlertX)
[![Docker Size](https://img.shields.io/docker/image-size/jokobsk/netalertx?label=Size&logo=Docker&color=0aa8d2&logoColor=fff&style=for-the-badge)](https://hub.docker.com/r/jokobsk/netalertx) [![Docker Size](https://img.shields.io/docker/image-size/jokobsk/netalertx?label=Size&logo=Docker&color=0aa8d2&logoColor=fff&style=for-the-badge)](https://hub.docker.com/r/jokobsk/netalertx)
[![Docker Pulls](https://img.shields.io/docker/pulls/jokobsk/netalertx?label=Pulls&logo=docker&color=0aa8d2&logoColor=fff&style=for-the-badge)](https://hub.docker.com/r/jokobsk/netalertx) [![Docker Pulls](https://img.shields.io/docker/pulls/jokobsk/netalertx?label=Pulls&logo=docker&color=0aa8d2&logoColor=fff&style=for-the-badge)](https://hub.docker.com/r/jokobsk/netalertx)
[![GitHub Release](https://img.shields.io/github/v/release/jokob-sk/NetAlertX?color=0aa8d2&logoColor=fff&logo=GitHub&style=for-the-badge)](https://github.com/jokob-sk/NetAlertX/releases) [![GitHub Release](https://img.shields.io/github/v/release/jokob-sk/NetAlertX?color=0aa8d2&logoColor=fff&logo=GitHub&style=for-the-badge)](https://github.com/jokob-sk/NetAlertX/releases)
@@ -93,6 +93,7 @@ Thank you to all the wonderful people who are sponsoring this project.
<!-- SPONSORS-LIST DO NOT MODIFY BELOW --> <!-- SPONSORS-LIST DO NOT MODIFY BELOW -->
| All Sponsors | | All Sponsors |
|---| |---|
| [joel72265](https://github.com/joel72265) |
<!-- SPONSORS-LIST DO NOT MODIFY ABOVE --> <!-- SPONSORS-LIST DO NOT MODIFY ABOVE -->

View File

@@ -1,4 +1,4 @@
## Development environemnt set up ## Development environment set up
>[!NOTE] >[!NOTE]
> Replace `/development` with the path where your code files will be stored. The default container name is `netalertx` so there might be a conflict with your running containers. > Replace `/development` with the path where your code files will be stored. The default container name is `netalertx` so there might be a conflict with your running containers.
@@ -52,13 +52,19 @@ A command to stop, remove the container and the image (replace `netalertx` and `
- `sudo docker container stop netalertx ; sudo docker container rm netalertx ; sudo docker image rm netalertx-netalertx` - `sudo docker container stop netalertx ; sudo docker container rm netalertx ; sudo docker image rm netalertx-netalertx`
### Restart hanging python script ### Restart the server backend
SSH into the container and kill & restart the main script loop Most code changes can be tetsed without rebuilding the container. When working on the python server backend, you only need to restart the server.
1. You can usually restart the backend via Maintenance > Logs > Restart server
![image](/docs/img/DEV_ENV_SETUP/Maintenance_Logs_Restart_server.png)
2. If above doesn't work, SSH into the container and kill & restart the main script loop
- `sudo docker exec -it netalertx /bin/bash` - `sudo docker exec -it netalertx /bin/bash`
- `pkill -f "python /app/server" && python /app/server & ` - `pkill -f "python /app/server" && python /app/server & `
3. If none of the above work, restart the docker image. This is usually the last resort as sometimes the Docker engine becomes unresponsive and the whole engine needs to be restarted.

View File

@@ -6,6 +6,7 @@ NetAlertX comes with MQTT support, allowing you to show all detected devices as
- Please note that discovery takes about ~10s per device. - Please note that discovery takes about ~10s per device.
- Deleting of devices is not handled automatically. Please use [MQTT Explorer](https://mqtt-explorer.com/) to delete devices in the broker (Home Assistant), if needed. - Deleting of devices is not handled automatically. Please use [MQTT Explorer](https://mqtt-explorer.com/) to delete devices in the broker (Home Assistant), if needed.
- For optimization reasons, the devices are not always fully synchronized. You can delete Plugin objects as described in the [MQTT plugin](https://github.com/jokob-sk/NetAlertX/tree/main/front/plugins/_publisher_mqtt#forcing-an-update) docs to force a full synchronization.
## 🧭 Guide ## 🧭 Guide

View File

@@ -27,6 +27,29 @@ If you are running a DNS server, such as **AdGuard**, set up **Private reverse D
5. Click **Apply** to save your settings. 5. Click **Apply** to save your settings.
### Specifying the DNS in the container
You can specify the DNS server in the docker-compose to improve name resolution on your network.
```yaml
services:
netalertx:
container_name: netalertx
image: "jokobsk/netalertx:latest"
restart: unless-stopped
volumes:
- /home/netalertx/config:/app/config
- /home/netalertx/db:/app/db
- /home/netalertx/log:/app/front/log
environment:
- TZ=Europe/Berlin
- PORT=20211
network_mode: host
dns:
- 10.8.0.1
- 10.8.0.17
```
### Using a custom resolv.conf file ### Using a custom resolv.conf file
You can configure a custom **/etc/resolv.conf** file in **docker-compose.yml** and set the nameserver to your LAN DNS server (e.g.: Pi-Hole). See the relevant [resolv.conf man](https://www.man7.org/linux/man-pages/man5/resolv.conf.5.html) entry for details. You can configure a custom **/etc/resolv.conf** file in **docker-compose.yml** and set the nameserver to your LAN DNS server (e.g.: Pi-Hole). See the relevant [resolv.conf man](https://www.man7.org/linux/man-pages/man5/resolv.conf.5.html) entry for details.

View File

@@ -1,108 +1,112 @@
# Subnets configuration # Subnets Configuration
You need to specify the network interface and the network mask. You can also configure multiple subnets and specify VLANS (see exceptions below). You need to specify the network interface and the network mask. You can also configure multiple subnets and specify VLANs (see VLAN exceptions below).
`ARPSCAN` can scan multiple networks if the network allows it. To scan networks directly, the subnets must be accessible from the network where NetAlertX is running. This means NetAlertX needs to have access to the interface attached to that subnet. You can verify this by running the following command in the container:
`sudo arp-scan --interface=eth0 192.168.1.0/24`
In this example, `--interface=eth0 192.168.1.0/24` represents a neighboring subnet. If this command returns no results, the network is not accessible due to your network or firewall restrictions.
If direct scans are not possible, you can use [supplementing plugins](https://github.com/jokob-sk/NetAlertX/blob/main/front/plugins/README.md) that use alternate methods. Protocols used by the `SNMPDSC` or `DHCPLSS` plugins have good support and usually can be used as a workaround.
Alternatively, you can set up separate NetAlertX instances running on the subnets and synchronize the results into one instance with the [`SYNC` plugin](https://github.com/jokob-sk/NetAlertX/tree/main/front/plugins/sync).
> [!TIP] > [!TIP]
> You may need to increase the time between scans `ARPSCAN_RUN_SCHD` and the timeout `ARPSCAN_RUN_TIMEOUT` (and similar setting on related plugins) when adding more subnets. If the timeout setting is exceeded, the scan is cancelled to prevent application hanging from rogue plugins. Check [debugging plugins](/docs/DEBUG_PLUGINS.md) for more tips. > You may need to increase the time between scans `ARPSCAN_RUN_SCHD` and the timeout `ARPSCAN_RUN_TIMEOUT` (and similar settings for related plugins) when adding more subnets. If the timeout setting is exceeded, the scan is canceled to prevent the application from hanging due to rogue plugins.
> Check [debugging plugins](/docs/DEBUG_PLUGINS.md) for more tips.
## Examples ## Example Values
> [!NOTE] > [!NOTE]
> Please use the UI to configure settings as that ensures that the config file is in the correct format. Edit `app.conf` directly only when really necessary. > Please use the UI to configure settings as it ensures the config file is in the correct format. Edit `app.conf` directly only when really necessary.
> ![settings](/docs/img/SUBNETS/subnets-setting-location.png) > ![Settings location](/docs/img/SUBNETS/subnets-setting-location.png)
* Examples for one and two subnets (❗ Note the `['...','...']` format): * **Examples for one and two subnets:**
* One subnet: `SCAN_SUBNETS = ['192.168.1.0/24 --interface=eth0']` * One subnet: `SCAN_SUBNETS = ['192.168.1.0/24 --interface=eth0']`
* Two subnets: `SCAN_SUBNETS = ['192.168.1.0/24 --interface=eth0','192.168.1.0/24 --interface=eth1 -vlan=107']` * Two subnets: `SCAN_SUBNETS = ['192.168.1.0/24 --interface=eth0','192.168.1.0/24 --interface=eth1 -vlan=107']`
If you get timeout messages, decrease the network mask (e.g.: from a `/16` to `/24`) or increase the `TIMEOUT` setting (e.g.: `ARPSCAN_RUN_TIMEOUT` to `300` (a timeout of 5min)) for the plugin and the interval between scans (e.g.: `ARPSCAN_RUN_SCHD` to `*/10 * * * *` (scans every 10 min)). If you get timeout messages, decrease the network mask (e.g.: from `/16` to `/24`) or increase the `TIMEOUT` setting (e.g.: `ARPSCAN_RUN_TIMEOUT` to `300` (5-minute timeout)) for the plugin and the interval between scans (e.g.: `ARPSCAN_RUN_SCHD` to `*/10 * * * *` (scans every 10 minutes)).
---
## Explanation ## Explanation
### Network mask ### Network Mask
**Example value: `192.168.1.0/24`** **Example value:** `192.168.1.0/24`
The arp-scan time itself depends on the number of IP addresses to check. The `arp-scan` time itself depends on the number of IP addresses to check.
> The number of IPs to check depends on the [network mask](https://www.calculator.net/ip-subnet-calculator.html) you set on the `SCAN_SUBNETS` setting. > The number of IPs to check depends on the [network mask](https://www.calculator.net/ip-subnet-calculator.html) you set in the `SCAN_SUBNETS` setting.
> For example, a `/24` mask results in 256 IPs to check, whereas a `/16` mask checks around 65,536. Every IP takes a couple of seconds. This means that with an incorrect configuration, the arp-scan will take hours to complete instead of seconds. > For example, a `/24` mask results in 256 IPs to check, whereas a `/16` mask checks around 65,536 IPs. Each IP takes a couple of seconds, so an incorrect configuration could make `arp-scan` take hours instead of seconds.
Specify the network filter (which **significantly** speeds up the scan process). For example, the filter `192.168.1.0/24` covers IP ranges `192.168.1.0` to `192.168.1.255`. Specify the network filter, which **significantly** speeds up the scan process. For example, the filter `192.168.1.0/24` covers IP ranges from `192.168.1.0` to `192.168.1.255`.
### Network interface (adapter) ### Network Interface (Adapter)
**Example value: `--interface=eth0`** **Example value:** `--interface=eth0`
The adapter will probably be `eth0` or `eth1`. (Check `System info` > `Network Hardware` or run `iwconfig` in the container to find your interface name(s)) The adapter will probably be `eth0` or `eth1`. (Check `System Info` > `Network Hardware` or run `iwconfig` in the container to find your interface name(s)).
![Network hardware](/docs/img/SUBNETS/system_info-network_hardware.png) ![Network hardware](/docs/img/SUBNETS/system_info-network_hardware.png)
> [!TIP] > [!TIP]
> Alterantive to `iwconfig` run `ip -o link show | awk -F': ' '!/lo|vir|docker/ {print $2}'` in your container to find your interface name(s) (e.g.: `eth0`, `eth1`). > As an alternative to `iwconfig`, run `ip -o link show | awk -F': ' '!/lo|vir|docker/ {print $2}'` in your container to find your interface name(s) (e.g.: `eth0`, `eth1`).
### VLANs ### VLANs
**Example value: `-vlan=107`** **Example value:** `-vlan=107`
- Append e.g.: ` -vlan=107` to the interface field (e.g.: `eth0 -vlan=107`) for multiple vlans. More details in this [comment in this issue](https://github.com/jokob-sk/NetAlertX/issues/170#issuecomment-1419902988) - Append `-vlan=107` to the interface field (e.g.: `eth0 -vlan=107`) for multiple VLANs. More details are available in this [comment](https://github.com/jokob-sk/NetAlertX/issues/170#issuecomment-1419902988).
#### VLANs on a Hyper-V Setup
#### VLANs on a Hyper-V setup > Community-sourced content by [mscreations](https://github.com/mscreations) from this [discussion](https://github.com/jokob-sk/NetAlertX/discussions/404).
> Community sourced content by [mscreations](https://github.com/mscreations) from this [discussion](https://github.com/jokob-sk/NetAlertX/discussions/404). **Tested Setup:** Bare Metal → Hyper-V on Win Server 2019 → Ubuntu 22.04 VM → Docker → NetAlertX.
> [!NOTE]
> The setup this was tested on: Bare Metal -> Hyper-V on Win Server 2019 -> Ubuntu 22.04 VM -> Docker -> NetAlertX.
**Approach 1 (may cause issues):** **Approach 1 (may cause issues):**
Configure multiple network adapters in Hyper-V with distinct VLANs connected to each one using Hyper-V's network setup. However, this action can potentially lead to the Docker host's inability to handle network traffic correctly. This might interfere with other applications such as Authentik.
Configure multiple network adapters in Hyper-V with distinct VLANs connected to each one using Hyper-V's network setup. However, this action can potentially lead to the Docker host's inability to handle network traffic correctly. The issue may stem from the creation of routes for network time servers or domain controllers on every interface, thereby preventing proper synchronization of the underlying Ubuntu VM. This interference can affect the performance of other applications such as Authentik. **Approach 2 (working example):**
**Approach 2 (working example)**
Network connections to switches are configured as trunk and allow all VLANs access to the server. Network connections to switches are configured as trunk and allow all VLANs access to the server.
By default Hyper-V only allows untagged packets through to the VM interface and no VLAN tagged packets get through. In order to fix this follow these steps: By default, Hyper-V only allows untagged packets through to the VM interface, blocking VLAN-tagged packets. To fix this, follow these steps:
1) Run the following command in Powershell on the Hyper-V machine: 1. Run the following command in PowerShell on the Hyper-V machine:
```shell ```powershell
Set-VMNetworkAdapterVlan -VMName <Docker VM Name> -Trunk -NativeVlanId 0 -AllowedVlanIdList "<comma separated list of vlans>" Set-VMNetworkAdapterVlan -VMName <Docker VM Name> -Trunk -NativeVlanId 0 -AllowedVlanIdList "<comma separated list of vlans>"
``` ```
(There might be other ways how adjust this.)
2) Within the VM, set up sub-interfaces for each of the VLANs so they can be scanned. On Ubuntu 22.04 Netplan can be used. 2. Within the VM, set up sub-interfaces for each VLAN to enable scanning. On Ubuntu 22.04, Netplan can be used. In /etc/netplan/00-installer-config.yaml, add VLAN definitions:
In /etc/netplan/00-installer-config.yaml, add vlan definitions: ```yaml
``` network:
network: ethernets:
ethernets: eth0:
eth0: dhcp4: yes
dhcp4: yes vlans:
vlans: eth0.2:
eth0.2: id: 2
id: 2 link: eth0
link: eth0 addresses: [ "192.168.2.2/24" ]
addresses: [ "192.168.2.2/24" ] routes:
routes: - to: 192.168.2.0/24
- to: 192.168.2.0/24 via: 192.168.1.1
via: 192.168.1.1 ```
```
3) Run `sudo netplan apply` and the interfaces are then available to scan in NetAlertX. 3. Run `sudo netplan apply` to activate the interfaces for scanning in NetAlertX.
4) In this case, use `192.168.2.0/24 --interface=eth0.2` in NetAlertX
#### VLAN 🔍Example: In this case, use `192.168.2.0/24 --interface=eth0.2` in NetAlertX.
![Vlan configuration example](/docs/img/SUBNETS/subnets_vlan.png) #### VLAN Support & Exceptions
#### Support for VLANS (& exceptions) Please note the accessibility of macvlans when configured on the same computer. This is a general networking behavior, but feel free to clarify via a PR/issue.
Please note the accessibility of the macvlans when they are configured on the same computer. My understanding this is a general networking behavior, but feel free to clarify via a PR/issue.
- NetAlertX does not detect the macvlan container when it is running on the same computer. - NetAlertX does not detect the macvlan container when it is running on the same computer.
- NetAlertX recognizes the macvlan container when it is running on a different computer. - NetAlertX recognizes the macvlan container when it is running on a different computer.

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

View File

@@ -261,7 +261,7 @@
.main-sidebar { .main-sidebar {
padding-top: 50px; padding-top: 50px;
} }
.content-header { .content-header #pageTitle{
display: none; display: none;
} }
} }

View File

@@ -790,7 +790,6 @@ function initializeiCheck () {
// Hide / Show Events // Hide / Show Events
if (event.currentTarget.id == 'chkHideConnectionEvents') { if (event.currentTarget.id == 'chkHideConnectionEvents') {
getDeviceEvents(); getDeviceEvents();
setParameter (parEventsHide, event.currentTarget.checked);
} else { } else {
// Activate save & restore // Activate save & restore
// activateSaveRestoreData(); // activateSaveRestoreData();
@@ -1014,25 +1013,6 @@ function initializeDatatables () {
"info": "<?= lang('Events_Table_info');?>", "info": "<?= lang('Events_Table_info');?>",
} }
}); });
// Save Parameters rows & order when changed
$('#tableSessions').on( 'length.dt', function ( e, settings, len ) {
setParameter (parSessionsRows, len);
// Sync Rows in both datatables
// if ( $('#tableEvents').DataTable().page.len() != len) {
// $('#tableEvents').DataTable().page.len( len ).draw();
// }
} );
$('#tableEvents').on( 'length.dt', function ( e, settings, len ) {
setParameter (parEventsRows, len);
// Sync Rows in both datatables
// if ( $('#tableSessions').DataTable().page.len() != len) {
// $('#tableSessions').DataTable().page.len( len ).draw();
// }
} );
}; };
@@ -1149,10 +1129,6 @@ function initializeCalendar () {
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
function periodChanged () { function periodChanged () {
// Save Parameter Period
period = $('#period').val();
setParameter (parPeriod, period);
// Requery Device data // Requery Device data
getDeviceData(true); getDeviceData(true);
getSessionsPresenceEvents(); getSessionsPresenceEvents();
@@ -1312,7 +1288,7 @@ function getDeviceData (readAllData=false) {
if (deviceData['dev_Favorite'] == 1) {$('#chkFavorite').iCheck('check');} else {$('#chkFavorite').iCheck('uncheck');} if (deviceData['dev_Favorite'] == 1) {$('#chkFavorite').iCheck('check');} else {$('#chkFavorite').iCheck('uncheck');}
$('#txtGroup').val (deviceData['dev_Group']); $('#txtGroup').val (deviceData['dev_Group']);
$('#txtLocation').val (deviceData['dev_Location']); $('#txtLocation').val (deviceData['dev_Location']);
$('#txtComments').val (deviceData['dev_Comments']); $('#txtComments').val (decodeSpecialChars(deviceData['dev_Comments']));
$('#txtNetworkNodeMac').val ( networkParentMacName) ; $('#txtNetworkNodeMac').val ( networkParentMacName) ;
$('#txtNetworkNodeMac').attr ('data-mynodemac', deviceData['dev_Network_Node_MAC_ADDR']); $('#txtNetworkNodeMac').attr ('data-mynodemac', deviceData['dev_Network_Node_MAC_ADDR']);
$('#txtNetworkPort').val (deviceData['dev_Network_Node_port']); $('#txtNetworkPort').val (deviceData['dev_Network_Node_port']);
@@ -1453,7 +1429,7 @@ function setDeviceData (direction='', refreshCallback='') {
+ '&favorite=' + ($('#chkFavorite')[0].checked * 1) + '&favorite=' + ($('#chkFavorite')[0].checked * 1)
+ '&group=' + encodeURIComponent($('#txtGroup').val()) + '&group=' + encodeURIComponent($('#txtGroup').val())
+ '&location=' + encodeURIComponent($('#txtLocation').val()) + '&location=' + encodeURIComponent($('#txtLocation').val())
+ '&comments=' + encodeURIComponent($('#txtComments').val()) + '&comments=' + encodeURIComponent(encodeSpecialChars($('#txtComments').val()))
+ '&networknode=' + $('#txtNetworkNodeMac').attr('data-mynodemac') + '&networknode=' + $('#txtNetworkNodeMac').attr('data-mynodemac')
+ '&networknodeport=' + $('#txtNetworkPort').val() + '&networknodeport=' + $('#txtNetworkPort').val()
+ '&ssid=' + $('#txtSSID').val() + '&ssid=' + $('#txtSSID').val()
@@ -1482,7 +1458,7 @@ function setDeviceData (direction='', refreshCallback='') {
somethingChanged = false; somethingChanged = false;
// refresh API // refresh API
updateApi() updateApi("devices,appevents")
hideSpinner() hideSpinner()
@@ -1702,7 +1678,7 @@ function deleteDevice () {
$('#panDetails :input').attr('disabled', true); $('#panDetails :input').attr('disabled', true);
// refresh API // refresh API
updateApi() updateApi("devices,appevents")
} }
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
@@ -1831,12 +1807,6 @@ function initTable(tableId, mac){
$("#"+tableId).attr("data-mac", mac) $("#"+tableId).attr("data-mac", mac)
// Save Parameters rows & order when changed
$('#'+tableId).on( 'length.dt', function ( e, settings, len ) {
setParameter (parSessionsRows, len);
} );
} }
//----------------------------------------------------------------------------------- //-----------------------------------------------------------------------------------

View File

@@ -15,8 +15,6 @@
<?php <?php
require 'php/templates/header.php'; require 'php/templates/header.php';
require 'php/templates/graph.php';
// check permissions // check permissions
$dbPath = "../db/app.db"; $dbPath = "../db/app.db";
@@ -66,19 +64,37 @@
</div> </div>
<script src="js/graph_online_history.js"></script> <script src="js/graph_online_history.js"></script>
<script> <script>
var pia_js_online_history_time = [<?php pia_graph_devices_data($Pia_Graph_Device_Time); ?>]; $.get('api/table_online_history.json?nocache=' + Date.now(), function(res) {
var pia_js_online_history_ondev = [<?php pia_graph_devices_data($Pia_Graph_Device_Online); ?>]; // Extracting data from the JSON response
var pia_js_online_history_dodev = [<?php pia_graph_devices_data($Pia_Graph_Device_Down); ?>]; var timeStamps = [];
var pia_js_online_history_ardev = [<?php pia_graph_devices_data($Pia_Graph_Device_Arch); ?>]; var onlineCounts = [];
var downCounts = [];
var offlineCounts = [];
var archivedCounts = [];
setTimeout(() => { res.data.forEach(function(entry) {
pia_draw_graph_online_history( var dateObj = new Date(entry.Scan_Date);
pia_js_online_history_time, var formattedTime = dateObj.toLocaleTimeString([], {hour: '2-digit', minute: '2-digit', hour12: false});
pia_js_online_history_ondev,
pia_js_online_history_dodev,
pia_js_online_history_ardev);
}, 500);
timeStamps.push(formattedTime);
onlineCounts.push(entry.Online_Devices);
downCounts.push(entry.Down_Devices);
offlineCounts.push(entry.Offline_Devices);
archivedCounts.push(entry.Archived_Devices);
});
// Call your presenceOverTime function after data is ready
presenceOverTime(
timeStamps,
onlineCounts,
offlineCounts,
archivedCounts,
downCounts
);
}).fail(function() {
// Handle any errors in fetching the data
console.error('Error fetching online history data.');
});
</script> </script>
<!-- datatable ------------------------------------------------------------- --> <!-- datatable ------------------------------------------------------------- -->

View File

@@ -183,43 +183,31 @@
<!-- page script ----------------------------------------------------------- --> <!-- page script ----------------------------------------------------------- -->
<script> <script>
var parPeriod = 'Front_Events_Period'; var parPeriod = 'nax_parPeriod';
var parTableRows = 'Front_Events_Rows'; var parTableRows = 'nax_parTableRows';
var eventsType = 'all'; var eventsType = 'all';
var period = ''; var period = '1 day';
var tableRows = 10; var tableRows = 25;
// Read parameters & Initialize components // Read parameters & Initialize components
main(); main();
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
function main () { function main() {
// get parameter value // Get parameter value from cookies instead of server
$.get('php/server/parameters.php?action=get&defaultValue=1 day&parameter='+ parPeriod, function(data) { period = getCookie(parPeriod) === "" ? "1 day" : getCookie(parPeriod);
var result = JSON.parse(data); $('#period').val(period);
if (result) {
period = result;
$('#period').val(period);
}
// get parameter value tableRows = getCookie(parTableRows) === "" ? 50 : parseInt(getCookie(parTableRows), 10);
$.get('php/server/parameters.php?action=get&defaultValue=50&parameter='+ parTableRows, function(data) {
var result = JSON.parse(data);
result = parseInt(result, 10)
if (Number.isInteger (result) ) {
tableRows = result;
}
// Initialize components // Initialize components
initializeDatatable(); initializeDatatable();
// query data // Query data
getEventsTotals(); getEventsTotals();
getEvents (eventsType); getEvents(eventsType);
});
});
} }
@@ -281,7 +269,7 @@ function initializeDatatable () {
// Save Parameter rows when changed // Save Parameter rows when changed
$('#tableEvents').on( 'length.dt', function ( e, settings, len ) { $('#tableEvents').on( 'length.dt', function ( e, settings, len ) {
setParameter (parTableRows, len); setCookie(parTableRows, len)
} ); } );
}; };
@@ -290,7 +278,8 @@ function initializeDatatable () {
function periodChanged () { function periodChanged () {
// Save Parameter Period // Save Parameter Period
period = $('#period').val(); period = $('#period').val();
setParameter (parPeriod, period);
setCookie(parTableRows, period)
// Requery totals and events // Requery totals and events
getEventsTotals(); getEventsTotals();

View File

@@ -3,11 +3,13 @@
<?php <?php
require dirname(__FILE__).'/php/server/init.php'; require dirname(__FILE__).'/php/server/init.php';
require 'php/templates/security.php'; //------------------------------------------------------------------------------
// check if authenticated
require_once $_SERVER['DOCUMENT_ROOT'] . '/php/templates/security.php';
$CookieSaveLoginName = 'NetAlertX_SaveLogin'; $CookieSaveLoginName = 'NetAlertX_SaveLogin';
if ($Pia_WebProtection != 'true') if ($nax_WebProtection != 'true')
{ {
header('Location: devices.php'); header('Location: devices.php');
$_SESSION["login"] = 1; $_SESSION["login"] = 1;
@@ -24,7 +26,7 @@ if (isset ($_GET["action"]) && $_GET["action"] == 'logout')
} }
// Password without Cookie check -> pass and set initial cookie // Password without Cookie check -> pass and set initial cookie
if (isset ($_POST["loginpassword"]) && $Pia_Password == hash('sha256',$_POST["loginpassword"])) if (isset ($_POST["loginpassword"]) && $nax_Password == hash('sha256',$_POST["loginpassword"]))
{ {
header('Location: devices.php'); header('Location: devices.php');
$_SESSION["login"] = 1; $_SESSION["login"] = 1;
@@ -32,7 +34,7 @@ if (isset ($_POST["loginpassword"]) && $Pia_Password == hash('sha256',$_POST["lo
} }
// active Session or valid cookie (cookie not extends) // active Session or valid cookie (cookie not extends)
if (( isset ($_SESSION["login"]) && ($_SESSION["login"] == 1)) || (isset ($_COOKIE[$CookieSaveLoginName]) && $Pia_Password == $_COOKIE[$CookieSaveLoginName])) if (( isset ($_SESSION["login"]) && ($_SESSION["login"] == 1)) || (isset ($_COOKIE[$CookieSaveLoginName]) && $nax_Password == $_COOKIE[$CookieSaveLoginName]))
{ {
header('Location: devices.php'); header('Location: devices.php');
$_SESSION["login"] = 1; $_SESSION["login"] = 1;
@@ -40,7 +42,7 @@ if (( isset ($_SESSION["login"]) && ($_SESSION["login"] == 1)) || (isset ($_COOK
} }
$login_headline = lang('Login_Toggle_Info_headline'); $login_headline = lang('Login_Toggle_Info_headline');
$login_info = ""; $login_info = lang('Login_Info');
$login_mode = 'danger'; $login_mode = 'danger';
$login_display_mode = 'display: block;'; $login_display_mode = 'display: block;';
$login_icon = 'fa-info'; $login_icon = 'fa-info';
@@ -48,7 +50,7 @@ $login_icon = 'fa-info';
// no active session, cookie not checked // no active session, cookie not checked
if (isset ($_SESSION["login"]) == FALSE || $_SESSION["login"] != 1) if (isset ($_SESSION["login"]) == FALSE || $_SESSION["login"] != 1)
{ {
if ($Pia_Password == '8d969eef6ecad3c29a3a629280e686cf0c3f5d5a86aff3ca12020c923adc6c92') if ($nax_Password == '8d969eef6ecad3c29a3a629280e686cf0c3f5d5a86aff3ca12020c923adc6c92')
{ {
$login_info = lang('Login_Default_PWD'); $login_info = lang('Login_Default_PWD');
$login_mode = 'danger'; $login_mode = 'danger';
@@ -91,6 +93,13 @@ if (isset ($_SESSION["login"]) == FALSE || $_SESSION["login"] != 1)
<link rel="stylesheet" href="lib/AdminLTE/dist/css/AdminLTE.min.css"> <link rel="stylesheet" href="lib/AdminLTE/dist/css/AdminLTE.min.css">
<!-- iCheck --> <!-- iCheck -->
<link rel="stylesheet" href="lib/AdminLTE/plugins/iCheck/square/blue.css"> <link rel="stylesheet" href="lib/AdminLTE/plugins/iCheck/square/blue.css">
<!-- Font Awesome -->
<link rel="stylesheet" href="lib/AdminLTE/bower_components/font-awesome/css/fontawesome.min.css">
<link rel="stylesheet" href="lib/AdminLTE/bower_components/font-awesome/css/solid.css">
<link rel="stylesheet" href="lib/AdminLTE/bower_components/font-awesome/css/brands.css">
<link rel="stylesheet" href="lib/AdminLTE/bower_components/font-awesome/css/v5-font-face.css">
<!-- Favicon -->
<link id="favicon" rel="icon" type="image/x-icon" href="img/NetAlertX_logo.png">
<!-- Dark-Mode Patch --> <!-- Dark-Mode Patch -->
<?php <?php
@@ -140,11 +149,9 @@ if ($ENABLED_DARKMODE === True) {
</div> </div>
<!-- /.login-box-body --> <!-- /.login-box-body -->
<div id="myDIV" class="box-body" style="margin-top: 50px; <?php echo $login_display_mode;?>"> <div id="myDIV" class="box-body" style="margin-top: 50px; <?php echo $login_display_mode;?>">
<div class="alert alert-<?php echo $login_mode;?> alert-dismissible"> <div class="alert alert-<?php echo $login_mode;?> alert-dismissible">
<button type="button" class="close" data-dismiss="alert" aria-hidden="true"><EFBFBD></button> <button type="button" class="close" onclick="Passwordhinfo()" aria-hidden="true">X</button>
<h4><i class="icon fa <?php echo $login_icon;?>"></i><?php echo $login_headline;?></h4> <h4><i class="icon fa <?php echo $login_icon;?>"></i><?php echo $login_headline;?></h4>
<p><?php echo $login_info;?></p> <p><?php echo $login_info;?></p>
</div> </div>

View File

@@ -342,6 +342,8 @@ function getLangCode() {
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// String utilities // String utilities
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// ----------------------------------------------------
function jsonSyntaxHighlight(json) { function jsonSyntaxHighlight(json) {
if (typeof json != 'string') { if (typeof json != 'string') {
json = JSON.stringify(json, undefined, 2); json = JSON.stringify(json, undefined, 2);
@@ -364,6 +366,7 @@ function jsonSyntaxHighlight(json) {
}); });
} }
// ----------------------------------------------------
function isValidBase64(str) { function isValidBase64(str) {
// Base64 characters set // Base64 characters set
var base64CharacterSet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='; var base64CharacterSet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
@@ -373,7 +376,7 @@ function isValidBase64(str) {
return invalidCharacters === ''; return invalidCharacters === '';
} }
// ----------------------------------------------------
function isValidJSON(jsonString) { function isValidJSON(jsonString) {
try { try {
JSON.parse(jsonString); JSON.parse(jsonString);
@@ -383,6 +386,37 @@ function isValidJSON(jsonString) {
} }
} }
// ----------------------------------------------------
// method to sanitize input so that HTML and other things don't break
function encodeSpecialChars(str) {
return str
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;')
.replace(/'/g, '&#039;');
}
// ----------------------------------------------------
function decodeSpecialChars(str) {
return str
.replace(/&amp;/g, '&')
.replace(/&lt;/g, '<')
.replace(/&gt;/g, '>')
.replace(/&quot;/g, '"')
.replace(/&#039;/g, '\'');
}
// ----------------------------------------------------
// base64 conversion of UTF8 chars
function utf8ToBase64(str) {
// Convert the string to a Uint8Array using TextEncoder
const utf8Bytes = new TextEncoder().encode(str);
// Convert the Uint8Array to a base64-encoded string
return btoa(String.fromCharCode(...utf8Bytes));
}
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// General utilities // General utilities
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
@@ -423,29 +457,6 @@ function numberArrayFromString(data)
return data.replace(/\[|\]/g, '').split(',').map(Number); return data.replace(/\[|\]/g, '').split(',').map(Number);
} }
// -----------------------------------------------------------------------------
function setParameter (parameter, value) {
// Retry
$.get('php/server/parameters.php?action=set&parameter=' + parameter +
'&value='+ value,
function(data) {
if (data != "OK") {
// Retry
sleep (200);
$.get('php/server/parameters.php?action=set&parameter=' + parameter +
'&value='+ value,
function(data) {
if (data != "OK") {
// alert (data);
} else {
// alert ("OK. Second attempt");
};
} );
};
} );
}
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
function saveData(functionName, id, value) { function saveData(functionName, id, value) {
$.ajax({ $.ajax({
@@ -995,11 +1006,11 @@ function hideSpinner()
// -------------------------------------------------------- // --------------------------------------------------------
// Calls a backend function to add a front-end event to an execution queue // Calls a backend function to add a front-end event to an execution queue
function updateApi() function updateApi(apiEndpoints)
{ {
// value has to be in format event|param. e.g. run|ARPSCAN // value has to be in format event|param. e.g. run|ARPSCAN
action = `${getGuid()}|update_api|devices,appevents` action = `${getGuid()}|update_api|${apiEndpoints}`
$.ajax({ $.ajax({

View File

@@ -1,13 +1,17 @@
function pia_draw_graph_online_history(pia_js_graph_online_history_time, pia_js_graph_online_history_ondev, pia_js_graph_online_history_dodev, pia_js_graph_online_history_ardev) { function presenceOverTime(
var xValues = pia_js_graph_online_history_time; timeStamp,
onlineCount,
// alert("dev presence") offlineCount,
archivedCount,
downCount
) {
var xValues = timeStamp;
// Data object for online status // Data object for online status
onlineData = { onlineData = {
label: 'Online', label: 'Online',
data: pia_js_graph_online_history_ondev, data: onlineCount,
borderColor: "rgba(0, 166, 89)", borderColor: "#00000",
fill: true, fill: true,
backgroundColor: "rgba(0, 166, 89, .6)", backgroundColor: "rgba(0, 166, 89, .6)",
pointStyle: 'circle', pointStyle: 'circle',
@@ -15,20 +19,29 @@ function pia_draw_graph_online_history(pia_js_graph_online_history_time, pia_js_
pointHoverRadius: 3 pointHoverRadius: 3
}; };
// Data object for down status
downData = {
label: 'Down',
data: downCount,
borderColor: "#00000",
fill: true,
backgroundColor: "#dd4b39",
};
// Data object for offline status // Data object for offline status
offlineData = { offlineData = {
label: 'Offline/Down', label: 'Offline',
data: pia_js_graph_online_history_dodev, data: offlineCount,
borderColor: "rgba(222, 74, 56)", borderColor: "#00000",
fill: true, fill: true,
backgroundColor: "rgba(222, 74, 56, .6)", backgroundColor: "#b2b6be",
}; };
// Data object for archived status // Data object for archived status
archivedData = { archivedData = {
label: 'Archived', label: 'Archived',
data: pia_js_graph_online_history_ardev, data: archivedCount,
borderColor: "rgba(220,220,220)", borderColor: "#00000",
fill: true, fill: true,
backgroundColor: "rgba(220,220,220, .6)", backgroundColor: "rgba(220,220,220, .6)",
}; };
@@ -42,23 +55,27 @@ function pia_draw_graph_online_history(pia_js_graph_online_history_time, pia_js_
// Check if 'online' status should be displayed // Check if 'online' status should be displayed
if(showStats.includes("online")) if(showStats.includes("online"))
{ {
datasets.push(onlineData); // Add onlineData to datasets array datasets.push(onlineData);
}
// Check if 'down' status should be displayed
if(showStats.includes("down"))
{
datasets.push(downData);
} }
// Check if 'offline' status should be displayed // Check if 'offline' status should be displayed
if(showStats.includes("offline")) if(showStats.includes("offline"))
{ {
datasets.push(offlineData); // Add offlineData to datasets array datasets.push(offlineData);
} }
// Check if 'archived' status should be displayed // Check if 'archived' status should be displayed
if(showStats.includes("archived")) if(showStats.includes("archived"))
{ {
datasets.push(archivedData); // Add archivedData to datasets array datasets.push(archivedData);
} }
new Chart("OnlineChart", { new Chart("OnlineChart", {
type: "bar", type: "bar",
scaleIntegersOnly: true, scaleIntegersOnly: true,

View File

@@ -502,33 +502,6 @@ setTimeout(() => {
}); });
}, 1000); }, 1000);
// -----------------------------------------------------------------------------
// handling events on the backend initiated by the front end END
// -----------------------------------------------------------------------------
// ---------------------------------------------------------
// UNUSED?
function getParam(targetId, key, skipCache = false) {
skipCacheQuery = "";
if (skipCache) {
skipCacheQuery = "&skipcache";
}
// get parameter value
$.get(
"php/server/parameters.php?action=get&defaultValue=0&parameter=" +
key +
skipCacheQuery,
function (data) {
var result = data;
result = result.replaceAll('"', "");
document.getElementById(targetId).innerHTML = result.replaceAll('"', "");
}
);
}
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// Show/hide the metadata settings // Show/hide the metadata settings

View File

@@ -39,46 +39,17 @@
// Size and last mod of DB ------------------------------------------------------ // Size and last mod of DB ------------------------------------------------------
$pia_db = str_replace('front', 'db', getcwd()).'/app.db'; $nax_db = str_replace('front', 'db', getcwd()).'/app.db';
$pia_db_size = number_format((filesize($pia_db) / 1000000),2,",",".") . ' MB'; $nax_db_size = number_format((filesize($nax_db) / 1000000),2,",",".") . ' MB';
$pia_db_mod = date ("F d Y H:i:s", filemtime($pia_db)); $nax_db_mod = date ("F d Y H:i:s", filemtime($nax_db));
// Count and Calc Backups -------------------------------------------------------
$Pia_Archive_Path = str_replace('front', 'db', getcwd()).'/';
$Pia_Archive_count = 0;
$Pia_Archive_diskusage = 0;
$files = glob($Pia_Archive_Path."appdb_*.zip");
if ($files){
$Pia_Archive_count = count($files);
}
foreach ($files as $result) {
$Pia_Archive_diskusage = $Pia_Archive_diskusage + filesize($result);
}
$Pia_Archive_diskusage = number_format(($Pia_Archive_diskusage / 1000000),2,",",".") . ' MB';
// Find latest Backup for restore -----------------------------------------------
$latestfiles = glob($Pia_Archive_Path."appdb_*.zip");
natsort($latestfiles);
$latestfiles = array_reverse($latestfiles,False);
$latestbackup = 'none';
$latestbackup_date = 'no backup';
if (count($latestfiles) > 0)
{
$latestbackup = $latestfiles[0];
$latestbackup_date = date ("Y-m-d H:i:s", filemtime($latestbackup));
}
// Table sizes ----------------------------------------------------------------- // Table sizes -----------------------------------------------------------------
$tableSizesHTML = ""; $tableSizesHTML = "";
// Open a connection to the SQLite database // Open a connection to the SQLite database
$db = new SQLite3($pia_db); $db = new SQLite3($nax_db);
// Retrieve the table names from sqlite_master // Retrieve the table names from sqlite_master
$query = "SELECT name FROM sqlite_master WHERE type='table'"; $query = "SELECT name FROM sqlite_master WHERE type='table'";
@@ -133,13 +104,13 @@ $db->close();
<div class="db_info_table_row"> <div class="db_info_table_row">
<div class="db_info_table_cell" style="min-width: 140px"><?= lang('Maintenance_database_path');?></div> <div class="db_info_table_cell" style="min-width: 140px"><?= lang('Maintenance_database_path');?></div>
<div class="db_info_table_cell"> <div class="db_info_table_cell">
<?php echo $pia_db;?> <?php echo $nax_db;?>
</div> </div>
</div> </div>
<div class="db_info_table_row"> <div class="db_info_table_row">
<div class="db_info_table_cell"><?= lang('Maintenance_database_size');?></div> <div class="db_info_table_cell"><?= lang('Maintenance_database_size');?></div>
<div class="db_info_table_cell"> <div class="db_info_table_cell">
<?php echo $pia_db_size;?> <?php echo $nax_db_size;?>
</div> </div>
</div> </div>
<div class="db_info_table_row"> <div class="db_info_table_row">
@@ -151,13 +122,7 @@ $db->close();
<div class="db_info_table_row"> <div class="db_info_table_row">
<div class="db_info_table_cell"><?= lang('Maintenance_database_lastmod');?></div> <div class="db_info_table_cell"><?= lang('Maintenance_database_lastmod');?></div>
<div class="db_info_table_cell"> <div class="db_info_table_cell">
<?php echo $pia_db_mod;?> <?php echo $nax_db_mod;?>
</div>
</div>
<div class="db_info_table_row">
<div class="db_info_table_cell"><?= lang('Maintenance_database_backup');?></div>
<div class="db_info_table_cell">
<?php echo $Pia_Archive_count.' '.lang('Maintenance_database_backup_found').' / '.lang('Maintenance_database_backup_total').': '.$Pia_Archive_diskusage;?>
</div> </div>
</div> </div>
</div> </div>
@@ -255,24 +220,6 @@ $db->close();
</div> </div>
<div class="db_tools_table_cell_b"><?= lang('Maintenance_Tool_ImportPastedCSV_text');?></div> <div class="db_tools_table_cell_b"><?= lang('Maintenance_Tool_ImportPastedCSV_text');?></div>
</div> </div>
<div class="db_info_table_row">
<div class="db_tools_table_cell_a" >
<button type="button" class="btn btn-default pa-btn bg-green dbtools-button" id="btnPiaBackupDBtoArchive" onclick="askPiaBackupDBtoArchive()"><?= lang('Maintenance_Tool_backup');?></button>
</div>
<div class="db_tools_table_cell_b"><?= lang('Maintenance_Tool_backup_text');?></div>
</div>
<div class="db_info_table_row">
<div class="db_tools_table_cell_a" >
<button type="button" class="btn btn-default pa-btn pa-btn-delete bg-red dbtools-button" id="btnPiaRestoreDBfromArchive" onclick="askPiaRestoreDBfromArchive()"><?= lang('Maintenance_Tool_restore');?><br><?php echo $latestbackup_date;?></button>
</div>
<div class="db_tools_table_cell_b"><?= lang('Maintenance_Tool_restore_text');?></div>
</div>
<div class="db_info_table_row">
<div class="db_tools_table_cell_a" >
<button type="button" class="btn btn-default pa-btn pa-btn-delete bg-red dbtools-button" id="btnPiaPurgeDBBackups" onclick="askPiaPurgeDBBackups()"><?= lang('Maintenance_Tool_purgebackup');?></button>
</div>
<div class="db_tools_table_cell_b"><?= lang('Maintenance_Tool_purgebackup_text');?></div>
</div>
</div> </div>
</div> </div>
<!-- ---------------------------Logging-------------------------------------------- --> <!-- ---------------------------Logging-------------------------------------------- -->
@@ -458,51 +405,6 @@ function deleteActHistory()
}); });
} }
// -----------------------------------------------------------
// Backup DB to Archive
function askPiaBackupDBtoArchive () {
// Ask
showModalWarning('<?= lang('Maintenance_Tool_backup_noti');?>', '<?= lang('Maintenance_Tool_backup_noti_text');?>',
'<?= lang('Gen_Cancel');?>', '<?= lang('Gen_Backup');?>', 'PiaBackupDBtoArchive');
}
function PiaBackupDBtoArchive()
{
// Execute
$.get('php/server/devices.php?action=PiaBackupDBtoArchive', function(msg) {
showMessage (msg);
});
}
// -----------------------------------------------------------
// Restore DB from Archive
function askPiaRestoreDBfromArchive () {
// Ask
showModalWarning('<?= lang('Maintenance_Tool_restore_noti');?>', '<?= lang('Maintenance_Tool_restore_noti_text');?>',
'<?= lang('Gen_Cancel');?>', '<?= lang('Gen_Restore');?>', 'PiaRestoreDBfromArchive');
}
function PiaRestoreDBfromArchive()
{
// Execute
$.get('php/server/devices.php?action=PiaRestoreDBfromArchive', function(msg) {
showMessage (msg);
});
}
// -----------------------------------------------------------
// Purge Backups
function askPiaPurgeDBBackups() {
// Ask
showModalWarning('<?= lang('Maintenance_Tool_purgebackup_noti');?>', '<?= lang('Maintenance_Tool_purgebackup_noti_text');?>',
'<?= lang('Gen_Cancel');?>', '<?= lang('Gen_Purge');?>', 'PiaPurgeDBBackups');
}
function PiaPurgeDBBackups()
{
// Execute
$.get('php/server/devices.php?action=PiaPurgeDBBackups', function(msg) {
showMessage (msg);
});
}
// ----------------------------------------------------------- // -----------------------------------------------------------
// Restart Backend Python Server // Restart Backend Python Server
@@ -571,12 +473,15 @@ function askImportPastedCSV() {
function ImportPastedCSV() function ImportPastedCSV()
{ {
var csv = $('#modal-input-textarea').val(); var csv = $('#modal-input-textarea').val();
csvBase64 = btoa(csv)
// Execute csvBase64 = utf8ToBase64(csv);
$.post('php/server/devices.php?action=ImportCSV', { content: csvBase64 }, function(msg) { $.post('php/server/devices.php?action=ImportCSV', { content: csvBase64 }, function(msg) {
showMessage(msg); showMessage(msg);
write_notification(`[Maintenance] Devices imported from pasted content`, 'info'); write_notification(`[Maintenance] Devices imported from pasted content`, 'info');
}); });
} }

View File

@@ -305,7 +305,7 @@ function executeAction(action, whereColumnName, key, targetColumns, newTargetCol
window.onbeforeunload = null; window.onbeforeunload = null;
// update API endpoints to refresh the UI // update API endpoints to refresh the UI
updateApi() updateApi("devices,appevents")
write_notification(`[Multi edit] Executed "${action}" on Columns "${targetColumns}" matching "${key}"`, 'info') write_notification(`[Multi edit] Executed "${action}" on Columns "${targetColumns}" matching "${key}"`, 'info')

View File

@@ -2,6 +2,10 @@
require '../server/init.php'; require '../server/init.php';
//------------------------------------------------------------------------------
// check if authenticated
require_once $_SERVER['DOCUMENT_ROOT'] . '/php/templates/security.php';
// Function to render the log area component // Function to render the log area component
function renderLogArea($params) { function renderLogArea($params) {
$fileName = isset($params['fileName']) ? $params['fileName'] : ''; $fileName = isset($params['fileName']) ? $params['fileName'] : '';

View File

@@ -1,4 +1,9 @@
<?php <?php
//------------------------------------------------------------------------------
// check if authenticated
require_once $_SERVER['DOCUMENT_ROOT'] . '/php/templates/security.php';
function renderInfobox($params) { function renderInfobox($params) {
$onclickEvent = isset($params['onclickEvent']) ? $params['onclickEvent'] : ''; $onclickEvent = isset($params['onclickEvent']) ? $params['onclickEvent'] : '';
$color = isset($params['color']) ? $params['color'] : ''; $color = isset($params['color']) ? $params['color'] : '';

View File

@@ -1,7 +0,0 @@
<?php
// Cache the contents to a cache file
$cached = fopen($cachefile, 'w');
fwrite($cached, ob_get_contents());
fclose($cached);
ob_end_flush(); // Send the output to the browser
?>

View File

@@ -1,15 +0,0 @@
<?php
$url = $_SERVER["SCRIPT_NAME"];
$break = Explode('/', $url);
$file = $break[count($break) - 1];
$cachefile = 'cached-'.substr_replace($file ,"",-4).'.html';
$cachetime = 18000;
// Serve from the cache if it is younger than $cachetime
if (file_exists($cachefile) && time() - $cachetime < filemtime($cachefile)) {
echo "<!-- Cached copy, generated ".date('H:i', filemtime($cachefile))." -->\n";
readfile($cachefile);
exit;
}
ob_start(); // Start the output buffer
?>

View File

@@ -13,6 +13,10 @@
$DBFILE = dirname(__FILE__).'/../../../db/app.db'; $DBFILE = dirname(__FILE__).'/../../../db/app.db';
$DBFILE_LOCKED_FILE = dirname(__FILE__).'/../../../front/log/db_is_locked.log'; $DBFILE_LOCKED_FILE = dirname(__FILE__).'/../../../front/log/db_is_locked.log';
//------------------------------------------------------------------------------
// check if authenticated
require_once $_SERVER['DOCUMENT_ROOT'] . '/php/templates/security.php';
$db_locked = false; $db_locked = false;
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------

View File

@@ -3,16 +3,18 @@
// NetAlertX // NetAlertX
// Open Source Network Guard / WIFI & LAN intrusion detector // Open Source Network Guard / WIFI & LAN intrusion detector
// //
// parameters.php - Front module. Server side. Manage Parameters
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
# Puche 2022+ jokob jokob@duck.com GNU GPLv3 # Puche 2022+ jokob jokob@duck.com GNU GPLv3
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// External files // External files
require dirname(__FILE__).'/init.php'; require dirname(__FILE__).'/init.php';
//------------------------------------------------------------------------------
// check if authenticated
require_once $_SERVER['DOCUMENT_ROOT'] . '/php/templates/security.php';
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// Action selector // Action selector

View File

@@ -11,6 +11,10 @@
// External files // External files
require dirname(__FILE__).'/init.php'; require dirname(__FILE__).'/init.php';
//------------------------------------------------------------------------------
// check if authenticated
require_once $_SERVER['DOCUMENT_ROOT'] . '/php/templates/security.php';
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// Action selector // Action selector
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
@@ -424,41 +428,39 @@ function ExportCSV() {
$func_result = $db->query("SELECT * FROM Devices"); $func_result = $db->query("SELECT * FROM Devices");
// prepare CSV header row // prepare CSV header row
// header array with column names
$columns = getDevicesColumns(); $columns = getDevicesColumns();
// wrap the headers with " (quotes) // wrap the headers with " (quotes)
$resultCSV = '"'.implode('","', $columns).'"'; $resultCSV = '"'.implode('","', $columns).'"'."\n";
//and append a new line
$resultCSV = $resultCSV."\n";
// retrieve the devices from the DB // retrieve the devices from the DB
while ($row = $func_result -> fetchArray (SQLITE3_ASSOC)) { while ($row = $func_result->fetchArray(SQLITE3_ASSOC)) {
// loop through columns and add values to the string // loop through columns and add values to the string
$index = 0; $index = 0;
foreach ($columns as $columnName) { foreach ($columns as $columnName) {
// Escape special chars (e.g.quotes) inside fields by replacing them with html definitions
$fieldValue = encodeSpecialChars($row[$columnName]);
// add quotes around the value to prevent issues with commas in fields // add quotes around the value to prevent issues with commas in fields
$resultCSV = $resultCSV.'"'.$row[$columnName].'"'; $resultCSV .= '"'.$fieldValue.'"';
// detect last loop - skip as no comma needed // detect last loop - skip as no comma needed
if ($index != count($columns) - 1 ) if ($index != count($columns) - 1) {
{ $resultCSV .= ',';
$resultCSV = $resultCSV.',';
} }
$index++; $index++;
} }
//$resultCSV = $resultCSV.implode(",", [$row["dev_MAC"], $row["dev_Name"]]); // add a new line for the next row
$resultCSV = $resultCSV."\n"; $resultCSV .= "\n";
} }
//write the built CSV string //write the built CSV string
echo $resultCSV; echo $resultCSV;
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// Import CSV of devices // Import CSV of devices
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
@@ -474,7 +476,11 @@ function ImportCSV() {
if(isset ($_POST['content']) && !empty ($_POST['content'])) if(isset ($_POST['content']) && !empty ($_POST['content']))
{ {
// Decode the Base64 string // Decode the Base64 string
$data = base64_decode($_POST['content']); // $data = base64_decode($_POST['content']);
$data = base64_decode($_POST['content'], true); // The second parameter ensures safe decoding
// // Ensure the decoded data is treated as UTF-8 text
// $data = mb_convert_encoding($data, 'UTF-8', 'UTF-8');
} else if (file_exists($file)) { // try to get the data form the file } else if (file_exists($file)) { // try to get the data form the file
@@ -486,6 +492,12 @@ function ImportCSV() {
if($data != "") if($data != "")
{ {
// data cleanup - new lines breaking the CSV
$data = preg_replace_callback('/"([^"]*)"/', function($matches) {
// Replace all \n within the quotes with a space
return str_replace("\n", " ", $matches[0]); // Replace with a space
}, $data);
$lines = explode("\n", $data); $lines = explode("\n", $data);
// Get the column headers from the first line of the CSV // Get the column headers from the first line of the CSV

View File

@@ -8,9 +8,12 @@
# Puche 2021 / 2022+ jokob jokob@duck.com GNU GPLv3 # Puche 2021 / 2022+ jokob jokob@duck.com GNU GPLv3
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// External files // External files
require dirname(__FILE__).'/init.php'; require dirname(__FILE__).'/init.php';
//------------------------------------------------------------------------------
// check if authenticated
require_once $_SERVER['DOCUMENT_ROOT'] . '/php/templates/security.php';
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// Action selector // Action selector
@@ -72,7 +75,7 @@ function getEventsTotals() {
$resultJSON = getCache("getEventsTotals".$days); $resultJSON = getCache("getEventsTotals".$days);
} else } else
{ {
// one query to get all numbers, whcih is quicker than multiple queries // one query to get all numbers, which is quicker than multiple queries
$sql = "select $sql = "select
(SELECT Count(*) FROM Events WHERE eve_DateTime >= date('now', '".$periodDateSQL."')) as all_events, (SELECT Count(*) FROM Events WHERE eve_DateTime >= date('now', '".$periodDateSQL."')) as all_events,
(SELECT Count(*) FROM Sessions as sessions WHERE ( ses_DateTimeConnection >= date('now', '".$periodDateSQL."') OR ses_DateTimeDisconnection >= date('now', '".$periodDateSQL."') OR ses_StillConnected = 1 )) as sessions, (SELECT Count(*) FROM Sessions as sessions WHERE ( ses_DateTimeConnection >= date('now', '".$periodDateSQL."') OR ses_DateTimeDisconnection >= date('now', '".$periodDateSQL."') OR ses_StillConnected = 1 )) as sessions,
@@ -334,24 +337,40 @@ function getEventsCalendar() {
$endDate = '"'. $_REQUEST ['end'] .'"'; $endDate = '"'. $_REQUEST ['end'] .'"';
// SQL // SQL
$SQL = 'SELECT ses_MAC, ses_EventTypeConnection, ses_DateTimeConnection, $SQL = 'SELECT SES1.ses_MAC, SES1.ses_EventTypeConnection, SES1.ses_DateTimeConnection,
ses_EventTypeDisconnection, ses_DateTimeDisconnection, ses_IP, ses_AdditionalInfo, ses_StillConnected, SES1.ses_EventTypeDisconnection, SES1.ses_DateTimeDisconnection, SES1.ses_IP,
SES1.ses_AdditionalInfo, SES1.ses_StillConnected,
CASE CASE
WHEN ses_EventTypeConnection = "<missing event>" THEN WHEN SES1.ses_EventTypeConnection = "<missing event>" THEN
IFNULL ((SELECT MAX(ses_DateTimeDisconnection) FROM Sessions AS SES2 WHERE SES2.ses_MAC = SES1.ses_MAC AND SES2.ses_DateTimeDisconnection < SES1.ses_DateTimeDisconnection), DATETIME(ses_DateTimeDisconnection, "-1 hour")) IFNULL (
ELSE ses_DateTimeConnection (SELECT MAX(SES2.ses_DateTimeDisconnection)
END AS ses_DateTimeConnectionCorrected, FROM Sessions AS SES2
WHERE SES2.ses_MAC = SES1.ses_MAC
AND SES2.ses_DateTimeDisconnection < SES1.ses_DateTimeDisconnection
AND SES2.ses_DateTimeDisconnection BETWEEN Date('. $startDate .') AND Date('. $endDate .')
),
DATETIME(SES1.ses_DateTimeDisconnection, "-1 hour")
)
ELSE SES1.ses_DateTimeConnection
END AS ses_DateTimeConnectionCorrected,
CASE CASE
WHEN ses_EventTypeDisconnection = "<missing event>" THEN WHEN SES1.ses_EventTypeDisconnection = "<missing event>" THEN
(SELECT MIN(ses_DateTimeConnection) FROM Sessions AS SES2 WHERE SES2.ses_MAC = SES1.ses_MAC AND SES2.ses_DateTimeConnection > SES1.ses_DateTimeConnection) (SELECT MIN(SES2.ses_DateTimeConnection)
ELSE ses_DateTimeDisconnection FROM Sessions AS SES2
END AS ses_DateTimeDisconnectionCorrected WHERE SES2.ses_MAC = SES1.ses_MAC
AND SES2.ses_DateTimeConnection > SES1.ses_DateTimeConnection
AND SES2.ses_DateTimeConnection BETWEEN Date('. $startDate .') AND Date('. $endDate .')
)
ELSE SES1.ses_DateTimeDisconnection
END AS ses_DateTimeDisconnectionCorrected
FROM Sessions AS SES1
WHERE (SES1.ses_DateTimeConnection BETWEEN Date('. $startDate .') AND Date('. $endDate .'))
OR (SES1.ses_DateTimeDisconnection BETWEEN Date('. $startDate .') AND Date('. $endDate .'))
OR SES1.ses_StillConnected = 1';
FROM Sessions AS SES1
WHERE ( ses_DateTimeConnectionCorrected <= Date('. $endDate .')
AND (ses_DateTimeDisconnectionCorrected >= Date('. $startDate .') OR ses_StillConnected = 1 )) ';
$result = $db->query($SQL); $result = $db->query($SQL);
// arrays of rows // arrays of rows

View File

@@ -15,6 +15,10 @@
// Get init.php // Get init.php
require dirname(__FILE__).'/../server/init.php'; require dirname(__FILE__).'/../server/init.php';
//------------------------------------------------------------------------------
// check if authenticated
require_once $_SERVER['DOCUMENT_ROOT'] . '/php/templates/security.php';
// Perform a test with the PING command // Perform a test with the PING command
$output = shell_exec("curl ipinfo.io"); $output = shell_exec("curl ipinfo.io");

View File

@@ -2,6 +2,10 @@
require 'util.php'; require 'util.php';
//------------------------------------------------------------------------------
// check if authenticated
require_once $_SERVER['DOCUMENT_ROOT'] . '/php/templates/security.php';
$PIA_HOST_IP = $_REQUEST['scan']; $PIA_HOST_IP = $_REQUEST['scan'];
$PIA_SCAN_MODE = $_REQUEST['mode']; $PIA_SCAN_MODE = $_REQUEST['mode'];

View File

@@ -15,6 +15,11 @@
// Get init.php // Get init.php
require dirname(__FILE__).'/../server/init.php'; require dirname(__FILE__).'/../server/init.php';
//------------------------------------------------------------------------------
// check if authenticated
require_once $_SERVER['DOCUMENT_ROOT'] . '/php/templates/security.php';
// Get IP // Get IP
$ip = $_GET['ip']; $ip = $_GET['ip'];

View File

@@ -1,144 +0,0 @@
<?php
//------------------------------------------------------------------------------
// NetAlertX
// Open Source Network Guard / WIFI & LAN intrusion detector
//
// parameters.php - Front module. Server side. Manage Parameters
//------------------------------------------------------------------------------
# Puche 2021 / 2022+ jokob jokob@duck.com GNU GPLv3
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
// External files
require dirname(__FILE__).'/init.php';
//------------------------------------------------------------------------------
// Action selector
//------------------------------------------------------------------------------
// Set maximum execution time to 15 seconds
ini_set ('max_execution_time','15');
$skipCache = FALSE;
$expireMinutes = 5;
$defaultValue = '';
if (isset ($_REQUEST['skipcache'])) {
$skipCache = TRUE;
}
if (isset ($_REQUEST['defaultValue'])) {
$defaultValue = $_REQUEST['defaultValue'];
}
if (isset ($_REQUEST['expireMinutes'])) {
$expireMinutes = $_REQUEST['expireMinutes'];
}
// Action functions
if (isset ($_REQUEST['action']) && !empty ($_REQUEST['action'])) {
$action = $_REQUEST['action'];
switch ($action) {
case 'get': getParameter($skipCache, $defaultValue, $expireMinutes); break;
case 'set': setParameter($expireMinutes); break;
default: logServerConsole ('Action: '. $action); break;
}
}
//------------------------------------------------------------------------------
// Get Parameter Value
//------------------------------------------------------------------------------
function getParameter($skipCache, $defaultValue, $expireMinutes) {
$parameter = $_REQUEST['parameter'];
$value = "";
// get the value from the cache if available
$cachedValue = getCache($parameter);
if($cachedValue != "")
{
$value = $cachedValue;
}
// query the database if no cache entry found or requesting live data (skipping cache)
if($skipCache || $value == "" )
{
global $db;
$sql = 'SELECT par_Value FROM Parameters
WHERE par_ID="'. quotes($parameter) .'"';
$result = $db->query($sql);
$row = $result -> fetchArray (SQLITE3_NUM);
if($row != NULL && count($row) == 1)
{
$value = $row[0];
} else{
$value = $defaultValue;
// Nothing found in the DB, Insert new value
insertNew($parameter, $value);
}
// update cache
setCache($parameter, $value, $expireMinutes);
}
// return value
echo (json_encode ($value));
}
//------------------------------------------------------------------------------
// Set Parameter Value
//------------------------------------------------------------------------------
function setParameter($expireMinutes) {
$parameter = $_REQUEST['parameter'];
$value = $_REQUEST['value'];
global $db;
// Update value
$sql = 'UPDATE Parameters SET par_Value="'. quotes ($value) .'"
WHERE par_ID="'. quotes($parameter) .'"';
$result = $db->query($sql);
if (! $result == TRUE) {
echo "Error updating parameter\n\n$sql \n\n". $db->lastErrorMsg();
return;
}
$changes = $db->changes();
if ($changes == 0) {
// Insert new value
insertNew($parameter, $value);
}
// update cache
setCache($parameter, $value, $expireMinutes);
echo 'OK';
}
function insertNew($parameter, $value)
{
global $db;
// Insert new value
$sql = 'INSERT INTO Parameters (par_ID, par_Value)
VALUES ("'. quotes($parameter) .'",
"'. quotes($value) .'")';
$result = $db->query($sql);
if (! $result == TRUE) {
echo "Error creating parameter\n\n$sql \n\n". $db->lastErrorMsg();
return;
}
}
?>

View File

@@ -1,5 +1,10 @@
<?php <?php
require dirname(__FILE__).'/../server/init.php'; require dirname(__FILE__).'/../server/init.php';
//------------------------------------------------------------------------------
// check if authenticated
require_once $_SERVER['DOCUMENT_ROOT'] . '/php/templates/security.php';
exec('../../../back/speedtest-cli --secure --simple', $output); exec('../../../back/speedtest-cli --secure --simple', $output);
echo '<h4>'. lang('Speedtest_Results') .'</h4>'; echo '<h4>'. lang('Speedtest_Results') .'</h4>';

View File

@@ -15,6 +15,10 @@
// Get init.php // Get init.php
require dirname(__FILE__).'/../server/init.php'; require dirname(__FILE__).'/../server/init.php';
//------------------------------------------------------------------------------
// check if authenticated
require_once $_SERVER['DOCUMENT_ROOT'] . '/php/templates/security.php';
// Get IP // Get IP
$ip = $_GET['ip']; $ip = $_GET['ip'];

View File

@@ -11,6 +11,10 @@
require dirname(__FILE__).'/../templates/timezone.php'; require dirname(__FILE__).'/../templates/timezone.php';
require dirname(__FILE__).'/../templates/skinUI.php'; require dirname(__FILE__).'/../templates/skinUI.php';
//------------------------------------------------------------------------------
// check if authenticated
require_once $_SERVER['DOCUMENT_ROOT'] . '/php/templates/security.php';
$FUNCTION = []; $FUNCTION = [];
$SETTINGS = []; $SETTINGS = [];
$ACTION = ""; $ACTION = "";
@@ -484,7 +488,7 @@ function getDateFromPeriod () {
$days = "3650"; //10 years $days = "3650"; //10 years
break; break;
default: default:
$days = "1"; $days = "1";
} }
$periodDateSQL = "-".$days." day"; $periodDateSQL = "-".$days." day";
@@ -520,6 +524,25 @@ function handleNull ($text, $default = "") {
} }
// -------------------------------------------------------------------------------------------
// Encode special chars
function encodeSpecialChars($str) {
return str_replace(
['&', '<', '>', '"', "'"],
['&amp;', '&lt;', '&gt;', '&quot;', '&#039;'],
$str
);
}
// -------------------------------------------------------------------------------------------
// Decode special chars
function decodeSpecialChars($str) {
return str_replace(
['&amp;', '&lt;', '&gt;', '&quot;', '&#039;'],
['&', '<', '>', '"', "'"],
$str
);
}
// ------------------------------------------------------------------------------------------- // -------------------------------------------------------------------------------------------

View File

@@ -1,13 +0,0 @@
<!-- utils needing a DB connection -->
<?php
require dirname(__FILE__).'/init.php';
// Action functions
if (isset ($_REQUEST['key']))
{
echo lang($_REQUEST['key']);
}
?>

View File

@@ -1,8 +1,11 @@
<?php <?php
require dirname(__FILE__).'/../templates/timezone.php'; require dirname(__FILE__).'/../templates/timezone.php';
//------------------------------------------------------------------------------
// check if authenticated
require_once $_SERVER['DOCUMENT_ROOT'] . '/php/templates/security.php';
// ---------------------------------------------------------------------------------------- // ----------------------------------------------------------------------------------------
// Check if the action parameter is set in the GET request // Check if the action parameter is set in the GET request
if (isset($_GET['action'])) { if (isset($_GET['action'])) {

View File

@@ -1,5 +1,7 @@
<?php <?php
session_start(); if (session_status() == PHP_SESSION_NONE) {
session_start();
}
$isAuthenticated = false; $isAuthenticated = false;
@@ -17,9 +19,9 @@ $config_file = "../../../config/app.conf"; // depends on where this file is call
$config_file_lines = file($config_file); $config_file_lines = file($config_file);
$config_file_lines = array_values(preg_grep('/^SETPWD_password.*=/', $config_file_lines)); $config_file_lines = array_values(preg_grep('/^SETPWD_password.*=/', $config_file_lines));
$password_line = explode("'", $config_file_lines[0]); $password_line = explode("'", $config_file_lines[0]);
$Pia_Password = $password_line[1]; $nax_Password = $password_line[1];
if (isset($_COOKIE[$CookieSaveLoginName]) && $Pia_Password == $_COOKIE[$CookieSaveLoginName]) { if (isset($_COOKIE[$CookieSaveLoginName]) && $nax_Password == $_COOKIE[$CookieSaveLoginName]) {
$isAuthenticated = true; $isAuthenticated = true;
} }

View File

@@ -12,6 +12,12 @@
#---------------------------------------------------------------------------------# #---------------------------------------------------------------------------------#
--> -->
<?php
//------------------------------------------------------------------------------
// check if authenticated
require_once $_SERVER['DOCUMENT_ROOT'] . '/php/templates/security.php';
?>
<!-- Main Footer --> <!-- Main Footer -->
<footer class="main-footer"> <footer class="main-footer">
<!-- Default to the left --> <!-- Default to the left -->

View File

@@ -1,55 +0,0 @@
<?php
global $db;
$Pia_Graph_Device_Time = array();
$Pia_Graph_Device_All = array();
$Pia_Graph_Device_Online = array();
$Pia_Graph_Device_Down = array();
$Pia_Graph_Device_Arch = array();
$statusesToShow = "'online', 'offline', 'archived'";
$statQuery = $db->query("SELECT * FROM Settings WHERE Code_Name = 'UI_PRESENCE'");
while($r = $statQuery->fetchArray(SQLITE3_ASSOC))
{
$statusesToShow = $r['Value'];
}
$results = $db->query('SELECT * FROM Online_History ORDER BY Scan_Date DESC LIMIT 144');
while ($row = $results->fetchArray())
{
$time_raw = explode(' ', $row['Scan_Date']);
$time = explode(':', $time_raw[1]);
array_push($Pia_Graph_Device_Time, $time[0].':'.$time[1]);
// Offline
if(strpos($statusesToShow, 'offline') !== false)
{
array_push($Pia_Graph_Device_Down, $row['Down_Devices']);
}
// All
array_push($Pia_Graph_Device_All, $row['All_Devices']);
// Online
if(strpos($statusesToShow, 'online') !== false)
{
array_push($Pia_Graph_Device_Online, $row['Online_Devices']);
}
// Archived
if(strpos($statusesToShow, 'archived') !== false)
{
array_push($Pia_Graph_Device_Arch, $row['Archived_Devices']);
}
}
function pia_graph_devices_data($Pia_Graph_Array) {
$Pia_Graph_Array_rev = array_reverse($Pia_Graph_Array);
foreach ($Pia_Graph_Array_rev as $result) {
echo "'".$result."'";
echo ",";
}
}

View File

@@ -8,8 +8,10 @@
#--------------------------------------------------------------------------- --> #--------------------------------------------------------------------------- -->
<?php <?php
require dirname(__FILE__).'/../server/init.php'; require dirname(__FILE__).'/../server/init.php';
require dirname(__FILE__).'/security.php'; //------------------------------------------------------------------------------
// check if authenticated
require_once $_SERVER['DOCUMENT_ROOT'] . '/php/templates/security.php';
?> ?>

View File

@@ -351,6 +351,7 @@
"Loading": "", "Loading": "",
"Login_Box": "", "Login_Box": "",
"Login_Default_PWD": "", "Login_Default_PWD": "",
"Login_Info": "",
"Login_Psw-box": "", "Login_Psw-box": "",
"Login_Psw_alert": "", "Login_Psw_alert": "",
"Login_Psw_folder": "", "Login_Psw_folder": "",
@@ -531,6 +532,7 @@
"Plugins_DeleteAll": "", "Plugins_DeleteAll": "",
"Plugins_Filters_Mac": "", "Plugins_Filters_Mac": "",
"Plugins_History": "", "Plugins_History": "",
"Plugins_Obj_DeleteListed": "",
"Plugins_Objects": "", "Plugins_Objects": "",
"Plugins_Out_of": "", "Plugins_Out_of": "",
"Plugins_Unprocessed_Events": "", "Plugins_Unprocessed_Events": "",

View File

@@ -363,6 +363,7 @@
"Loading": "Laden...", "Loading": "Laden...",
"Login_Box": "Passwort eingeben", "Login_Box": "Passwort eingeben",
"Login_Default_PWD": "Standardpasswort \"123456\" noch immer aktiv.", "Login_Default_PWD": "Standardpasswort \"123456\" noch immer aktiv.",
"Login_Info": "",
"Login_Psw-box": "Passwort", "Login_Psw-box": "Passwort",
"Login_Psw_alert": "Sicherheitshinweis!", "Login_Psw_alert": "Sicherheitshinweis!",
"Login_Psw_folder": "im Ordner /app/config", "Login_Psw_folder": "im Ordner /app/config",
@@ -572,6 +573,7 @@
"Plugins_DeleteAll": "Delete all (filters are ignored)", "Plugins_DeleteAll": "Delete all (filters are ignored)",
"Plugins_Filters_Mac": "Mac Filter", "Plugins_Filters_Mac": "Mac Filter",
"Plugins_History": "Events History", "Plugins_History": "Events History",
"Plugins_Obj_DeleteListed": "",
"Plugins_Objects": "Plugin Objects", "Plugins_Objects": "Plugin Objects",
"Plugins_Out_of": "von", "Plugins_Out_of": "von",
"Plugins_Unprocessed_Events": "Unprocessed Events", "Plugins_Unprocessed_Events": "Unprocessed Events",

View File

@@ -351,6 +351,7 @@
"Loading": "Loading...", "Loading": "Loading...",
"Login_Box": "Enter your password", "Login_Box": "Enter your password",
"Login_Default_PWD": "Default password \"123456\" is still active.", "Login_Default_PWD": "Default password \"123456\" is still active.",
"Login_Info": "Passwords are set via the Set Password plugin. Check the <a target=\"_blank\" href=\"https://github.com/jokob-sk/NetAlertX/tree/main/front/plugins/set_password\">SETPWD docs</a> if you have issues logging in.",
"Login_Psw-box": "Password", "Login_Psw-box": "Password",
"Login_Psw_alert": "Password Alert!", "Login_Psw_alert": "Password Alert!",
"Login_Psw_folder": "in the config folder.", "Login_Psw_folder": "in the config folder.",
@@ -531,6 +532,7 @@
"Plugins_DeleteAll": "Delete all (filters are ignored)", "Plugins_DeleteAll": "Delete all (filters are ignored)",
"Plugins_Filters_Mac": "Mac Filter", "Plugins_Filters_Mac": "Mac Filter",
"Plugins_History": "Events History", "Plugins_History": "Events History",
"Plugins_Obj_DeleteListed": "Delete Listed Objects",
"Plugins_Objects": "Plugin Objects", "Plugins_Objects": "Plugin Objects",
"Plugins_Out_of": "out of", "Plugins_Out_of": "out of",
"Plugins_Unprocessed_Events": "Unprocessed Events", "Plugins_Unprocessed_Events": "Unprocessed Events",

View File

@@ -361,6 +361,7 @@
"Loading": "Cargando...", "Loading": "Cargando...",
"Login_Box": "Ingrese su contraseña", "Login_Box": "Ingrese su contraseña",
"Login_Default_PWD": "La contraseña por defecto \"123456\" sigue activa.", "Login_Default_PWD": "La contraseña por defecto \"123456\" sigue activa.",
"Login_Info": "Las contraseñas se establecen a través del plugin Establecer contraseña. Compruebe la <a target=\"_blank\" href=\"https://github.com/jokob-sk/NetAlertX/tree/main/front/plugins/set_password\">documentación SETPWD</a> si tiene problemas para iniciar sesión.",
"Login_Psw-box": "Contraseña", "Login_Psw-box": "Contraseña",
"Login_Psw_alert": "¡Alerta de Contraseña!", "Login_Psw_alert": "¡Alerta de Contraseña!",
"Login_Psw_folder": "en la carpeta config.", "Login_Psw_folder": "en la carpeta config.",
@@ -570,6 +571,7 @@
"Plugins_DeleteAll": "Eliminar todo (se ignoran los filtros)", "Plugins_DeleteAll": "Eliminar todo (se ignoran los filtros)",
"Plugins_Filters_Mac": "Filtro MAC", "Plugins_Filters_Mac": "Filtro MAC",
"Plugins_History": "Historial de eventos", "Plugins_History": "Historial de eventos",
"Plugins_Obj_DeleteListed": "",
"Plugins_Objects": "Objetos del Plugin", "Plugins_Objects": "Objetos del Plugin",
"Plugins_Out_of": "de", "Plugins_Out_of": "de",
"Plugins_Unprocessed_Events": "Eventos sin procesar", "Plugins_Unprocessed_Events": "Eventos sin procesar",

View File

@@ -56,8 +56,8 @@
"BackDevices_Restore_okay": "Restauration exécutée avec succès.", "BackDevices_Restore_okay": "Restauration exécutée avec succès.",
"BackDevices_darkmode_disabled": "Mode sombre désactivé", "BackDevices_darkmode_disabled": "Mode sombre désactivé",
"BackDevices_darkmode_enabled": "Mode sombre activé", "BackDevices_darkmode_enabled": "Mode sombre activé",
"CLEAR_NEW_FLAG_description": "", "CLEAR_NEW_FLAG_description": "Si activé (<code>0</code> est activé), les appareils marqués comme <b>Nouvel appareil</b> seront démarqués si la durée limite (spécifiée en heures) dépasse la durée de la <b>Première Session</b>.",
"CLEAR_NEW_FLAG_name": "", "CLEAR_NEW_FLAG_name": "Supprime le nouveau drapeau",
"DAYS_TO_KEEP_EVENTS_description": "Il s'agit d'un paramètre de maintenance. Il indique le nombre de jours pendant lesquels les entrées d'événements seront conservées. Tous les événements plus anciens seront supprimés périodiquement. S'applique également à l'historique des événements du plugin.", "DAYS_TO_KEEP_EVENTS_description": "Il s'agit d'un paramètre de maintenance. Il indique le nombre de jours pendant lesquels les entrées d'événements seront conservées. Tous les événements plus anciens seront supprimés périodiquement. S'applique également à l'historique des événements du plugin.",
"DAYS_TO_KEEP_EVENTS_name": "Supprimer les événements plus anciens que", "DAYS_TO_KEEP_EVENTS_name": "Supprimer les événements plus anciens que",
"DevDetail_Copy_Device_Title": "<i class=\"fa fa-copy\"></i> Copier les détails de l'appareil", "DevDetail_Copy_Device_Title": "<i class=\"fa fa-copy\"></i> Copier les détails de l'appareil",
@@ -74,7 +74,7 @@
"DevDetail_EveandAl_Skip": "Passer les notifications répétées durant", "DevDetail_EveandAl_Skip": "Passer les notifications répétées durant",
"DevDetail_EveandAl_Title": "<i class=\"fa fa-bolt\"></i> Configuration des événements & Alertes", "DevDetail_EveandAl_Title": "<i class=\"fa fa-bolt\"></i> Configuration des événements & Alertes",
"DevDetail_Events_CheckBox": "Masquer les événements de connexion", "DevDetail_Events_CheckBox": "Masquer les événements de connexion",
"DevDetail_GoToNetworkNode": "Naviguer à la page Réseau pour le noeud sélectionné", "DevDetail_GoToNetworkNode": "Naviguer à la page Réseau pour le nœud sélectionné.",
"DevDetail_Icon": "Icône", "DevDetail_Icon": "Icône",
"DevDetail_Icon_Descr": "Renseigner le nom d'une icône Font Awesome sans le préfixe fa- ou la classe complète; par ex. fa fa-brands fa-apple.", "DevDetail_Icon_Descr": "Renseigner le nom d'une icône Font Awesome sans le préfixe fa- ou la classe complète; par ex. fa fa-brands fa-apple.",
"DevDetail_Loading": "Chargement…", "DevDetail_Loading": "Chargement…",
@@ -98,7 +98,7 @@
"DevDetail_Nmap_Scans": "Scans NMAP manuels", "DevDetail_Nmap_Scans": "Scans NMAP manuels",
"DevDetail_Nmap_Scans_desc": "Vous pouvez lancer des scans NMAP manuels. Vous pouvez aussi programmer des sans réguliers via le plugin Services & Ports (NMAP). Aller dans les <a href='/settings.php' target='_blank'>Paramètres</a> pour plus de details", "DevDetail_Nmap_Scans_desc": "Vous pouvez lancer des scans NMAP manuels. Vous pouvez aussi programmer des sans réguliers via le plugin Services & Ports (NMAP). Aller dans les <a href='/settings.php' target='_blank'>Paramètres</a> pour plus de details",
"DevDetail_Nmap_buttonDefault": "Scan par défaut", "DevDetail_Nmap_buttonDefault": "Scan par défaut",
"DevDetail_Nmap_buttonDefault_text": "Scan par défaut: NMAP scanne les 1 000 premiers ports pour chaque demande de scan de protocole. Cela couvre environ 93% des ports TCP et 49% des ports UDP (environ 5 secondes).", "DevDetail_Nmap_buttonDefault_text": "Scan par défaut : NMAP scanne les 1 000 premiers ports pour chaque demande de scan de protocole. Cela couvre environ 93% des ports TCP et 49% des ports UDP (environ 5 secondes)",
"DevDetail_Nmap_buttonDetail": "Scan détaillé", "DevDetail_Nmap_buttonDetail": "Scan détaillé",
"DevDetail_Nmap_buttonDetail_text": "Scan détaillé: scan par défaut avec la détection de système d'exploitation, la détection de version, l'analyse de script et le traceroute (jusqu'à 30 secondes ou plus)", "DevDetail_Nmap_buttonDetail_text": "Scan détaillé: scan par défaut avec la détection de système d'exploitation, la détection de version, l'analyse de script et le traceroute (jusqu'à 30 secondes ou plus)",
"DevDetail_Nmap_buttonFast": "Scan rapide", "DevDetail_Nmap_buttonFast": "Scan rapide",
@@ -230,7 +230,7 @@
"Device_Title": "Appareils", "Device_Title": "Appareils",
"Donations_Others": "Autres", "Donations_Others": "Autres",
"Donations_Platforms": "Plateformes de sponsoring", "Donations_Platforms": "Plateformes de sponsoring",
"Donations_Text": "Coucou 👋! </br> Merci d'avoir cliqué ici 😅 </br> </br> J'essaie de récolter des donations pour vous faire un meilleur produit. En plus, ça m'aide à éviter le burn-out pour développer cette application plus longtemps. Toute subvention (régulière ou non) me donne envie de poursuivre le développement de cette application.</br> J'aimerais réduire mon activité principale pour me concentrer plus longuement à NetAlertX. Vous auriez plus de fonctionnalités, une application mieux finie et avec moins de bugs.</br> </br> Merci de votre lecture - je vous suis reconnaissant pour votre soutien ❤🙏 </br> </br> Version courte: en me soutenant, vous aurez: </br> </br> <ul><li>Des mises à jour régulières pour protéger vos données personnelles et familiales 🔄</li><li>Moins de bugs 🐛🔫</li><li>Des fonctionnalités plus riches et plus nombreuses </li><li>Je ne me retrouve pas en burn-out 🔥🤯</li><li>Des versions moins à la va-vite 💨</li><li>une meilleure documentation <20></li><li>Un support meilleur et plus réactif en cas de problème 🆘</li></ul> </br> 📧Envoyez-moi un courriel à <a href='mailto:jokob@duck.com?subject=NetAlertX'>jokob@duck.com</a> si vous voulez mebcontacter ou du je peux ajouter une autre plateforme de soutien. </br>", "Donations_Text": "Coucou 👋! </br> Merci d'avoir cliqué ici 😅 </br> </br> J'essaie de récolter des donations pour vous faire un meilleur produit. En plus, ça m'aide à éviter le burn-out pour développer cette application plus longtemps. Toute subvention (régulière ou non) me donne envie de poursuivre le développement de cette application.</br> J'aimerais réduire mon activité principale pour me concentrer plus longuement à NetAlertX. Vous auriez plus de fonctionnalités, une application mieux finie et avec moins de bugs.</br> </br> Merci de votre lecture - je vous suis reconnaissant pour votre soutien ❤🙏 </br> </br> Version courte : en me soutenant, vous aurez : </br> </br> <ul><li>Des mises à jour régulières pour protéger vos données personnelles et familiales 🔄</li><li>Moins de bugs 🐛🔫</li><li>Des fonctionnalités plus riches et plus nombreuses </li><li>Je ne me retrouve pas en burn-out 🔥🤯</li><li>Des versions moins à la va-vite 💨</li><li>une meilleure documentation <20></li><li>Un support meilleur et plus réactif en cas de problème 🆘</li></ul> </br> 📧Envoyez-moi un courriel à <a href='mailto:jokob@duck.com?subject=NetAlertX'>jokob@duck.com</a> si vous voulez me contacter ou du je peux ajouter une autre plateforme de soutien. </br>",
"Donations_Title": "Dons", "Donations_Title": "Dons",
"ENABLE_PLUGINS_description": "Active les fonctionnalités des <a target=\"_blank\" href=\"https://github.com/jokob-sk/NetAlertX/tree/main/front/plugins\">Plugins</a>. Charger des plugins nécessite plus de ressources, il est recommandé de les désactiver sur des systèmes de faible puissance.", "ENABLE_PLUGINS_description": "Active les fonctionnalités des <a target=\"_blank\" href=\"https://github.com/jokob-sk/NetAlertX/tree/main/front/plugins\">Plugins</a>. Charger des plugins nécessite plus de ressources, il est recommandé de les désactiver sur des systèmes de faible puissance.",
"ENABLE_PLUGINS_name": "Activer les Plugins", "ENABLE_PLUGINS_name": "Activer les Plugins",
@@ -276,7 +276,7 @@
"Gen_AreYouSure": "Êtes-vous sûr?", "Gen_AreYouSure": "Êtes-vous sûr?",
"Gen_Backup": "Lancer la sauvegarde", "Gen_Backup": "Lancer la sauvegarde",
"Gen_Cancel": "Annuler", "Gen_Cancel": "Annuler",
"Gen_Change": "", "Gen_Change": "Changement",
"Gen_Copy": "Lancer", "Gen_Copy": "Lancer",
"Gen_DataUpdatedUITakesTime": "OK - cela peut prendre du temps à l'interface pour se mettre à jour si un scan est en cours.", "Gen_DataUpdatedUITakesTime": "OK - cela peut prendre du temps à l'interface pour se mettre à jour si un scan est en cours.",
"Gen_Delete": "Supprimer", "Gen_Delete": "Supprimer",
@@ -295,7 +295,7 @@
"Gen_Save": "Enregistrer", "Gen_Save": "Enregistrer",
"Gen_Saved": "Enregistré", "Gen_Saved": "Enregistré",
"Gen_Search": "Recherche", "Gen_Search": "Recherche",
"Gen_SelectToPreview": "", "Gen_SelectToPreview": "Sélectionnez pour prévisualiser",
"Gen_Selected_Devices": "Appareils sélectionnés:", "Gen_Selected_Devices": "Appareils sélectionnés:",
"Gen_Switch": "Basculer", "Gen_Switch": "Basculer",
"Gen_Upd": "Mise à jour réussie", "Gen_Upd": "Mise à jour réussie",
@@ -307,7 +307,7 @@
"General_display_name": "Général", "General_display_name": "Général",
"General_icon": "<i class=\"fa fa-gears\"></i>", "General_icon": "<i class=\"fa fa-gears\"></i>",
"HRS_TO_KEEP_NEWDEV_description": "Paramétrage de maintenance. S'il est activé (<code>0</code> s'il est désactivé), les appareils marqués comme <b>Nouvel appareil</b> seront supprimés si leur durée depuis la <b>première session</b> est plus ancienne que le nombre d'heures paramétré. Utilisez ce paramétrage si vous voulez supprimer automatiquement les <b>Nouveaux appareils</b> après <code>X</code> heures.", "HRS_TO_KEEP_NEWDEV_description": "Paramétrage de maintenance. S'il est activé (<code>0</code> s'il est désactivé), les appareils marqués comme <b>Nouvel appareil</b> seront supprimés si leur durée depuis la <b>première session</b> est plus ancienne que le nombre d'heures paramétré. Utilisez ce paramétrage si vous voulez supprimer automatiquement les <b>Nouveaux appareils</b> après <code>X</code> heures.",
"HRS_TO_KEEP_NEWDEV_name": "Garder les appareils en Nouveau pendant", "HRS_TO_KEEP_NEWDEV_name": "Supprimer les nouveaux appareils après",
"HelpFAQ_Cat_Detail": "Détails", "HelpFAQ_Cat_Detail": "Détails",
"HelpFAQ_Cat_Detail_300_head": "Que signifie ", "HelpFAQ_Cat_Detail_300_head": "Que signifie ",
"HelpFAQ_Cat_Detail_300_text_a": "signifie que cela représente un équipement réseau (Access Point, Gateway, Firewall, Hyperviseur, Powerline, Switch, WLAN, CPL, adaptateur Ethernet USB, adaptateur Wifi USB, Internet). Les types d'appareils personnalisés peuvent être ajoutés via le paramètre <code>NETWORK_DEVICE_TYPES</code>.", "HelpFAQ_Cat_Detail_300_text_a": "signifie que cela représente un équipement réseau (Access Point, Gateway, Firewall, Hyperviseur, Powerline, Switch, WLAN, CPL, adaptateur Ethernet USB, adaptateur Wifi USB, Internet). Les types d'appareils personnalisés peuvent être ajoutés via le paramètre <code>NETWORK_DEVICE_TYPES</code>.",
@@ -332,7 +332,7 @@
"HelpFAQ_Cat_General_102_head": "Un message m'indique que la base de données est en lecture seule.", "HelpFAQ_Cat_General_102_head": "Un message m'indique que la base de données est en lecture seule.",
"HelpFAQ_Cat_General_102_text": "Vérifiez dans le répertoire de NetAlertX si la base de données (db) possède les bonnes permissions:<br> <span class=\"text-danger help_faq_code\">drwxrwx--- 2 (votre nom d'utilisateur) www-data</span><br> Si la permission n'est pas correcte, vous pouvez la modifier en passant les commandes suivantes dans le terminal:<br> <span class=\"text-danger help_faq_code\">sudo chgrp -R www-data /app/db<br>chmod -R 770 /app/db</span><br>Si la base de données est encore en lecture seule, tentez de la réinstaller ou de restaurer une sauvegarde de la base à partir de la page de maintenance.", "HelpFAQ_Cat_General_102_text": "Vérifiez dans le répertoire de NetAlertX si la base de données (db) possède les bonnes permissions:<br> <span class=\"text-danger help_faq_code\">drwxrwx--- 2 (votre nom d'utilisateur) www-data</span><br> Si la permission n'est pas correcte, vous pouvez la modifier en passant les commandes suivantes dans le terminal:<br> <span class=\"text-danger help_faq_code\">sudo chgrp -R www-data /app/db<br>chmod -R 770 /app/db</span><br>Si la base de données est encore en lecture seule, tentez de la réinstaller ou de restaurer une sauvegarde de la base à partir de la page de maintenance.",
"HelpFAQ_Cat_General_102docker_head": "Erreur de base de données (erreurs AJAX, lecture seule, non trouvé)", "HelpFAQ_Cat_General_102docker_head": "Erreur de base de données (erreurs AJAX, lecture seule, non trouvé)",
"HelpFAQ_Cat_General_102docker_text": "Vérifiez avec attention que vous avez suivi les instructions dans le <a target=\"_blank\" href=\"https://github.com/jokob-sk/NetAlertX/tree/main/dockerfiles\">lisez-moi (readme) Docker (contient les infos les plus récentes)</a>. <br/> <br/> <ul data-sourcepos=\"49:4-52:146\" dir=\"auto\"><li data-sourcepos=\"49:4-49:106\"> Télécharger la <a target=\"_blank\" href=\"https://github.com/jokob-sk/NetAlertX/blob/main/db/app.db\">base de données originelle depuis GitHub</a>.</li><li data-sourcepos=\"50:4-50:195\">Relier le fichier <code>app.db</code> (<g-emoji class=\"g-emoji\" alias=\"warning\" fallback-src=\"https://github.githubassets.com/images/icons/emoji/unicode/26a0.png\">⚠</g-emoji> pas le dossier) au-dessus avec <code>/app/db/app.db</code> (plus de détails dans les <a target=\"_blank\" href=\"https://github.com/jokob-sk/NetAlertX/tree/main/dockerfiles#-examples\">Exemples</a>).</li><li data-sourcepos=\"51:4-51:161\">. Si vous rencontrez des erreurs (erreurs Ajax, impossible d'écrire dans la base, etc.), vérifiez que les permissions sont correctement définies, éventuellement regarder dans les blogs présents dans <code>/app/front/log</code>.</li><li data-sourcepos=\"52:4-52:146\">. Pour résoudre les problèmes de permission, vous pouvez aussi essayer de créer une sauvegarde de la base de données et lancer une restauration via la section <strong>Maintenance &gt; Sauvegarde/Restauration</strong>.</li><li data-sourcepos=\"53:4-53:228\">Si la base de données est en lecture seule, vous pouvez résoudre cela en définissant le propriétaire et le groupe en lançant la commande suivante depuis le système hôte: <code>docker exec netalertx chown -R www-data:www-data /app/db/app.db</code>.</li></ul>", "HelpFAQ_Cat_General_102docker_text": "Vérifiez avec attention que vous avez suivi les instructions dans le <a target=\"_blank\" href=\"https://github.com/jokob-sk/NetAlertX/tree/main/dockerfiles\">lisez-moi (readme) Docker (contient les infos les plus récentes)</a>. <br/> <br/> <ul data-sourcepos=\"49:4-52:146\" dir=\"auto\"><li data-sourcepos=\"49:4-49:106\"> Télécharger la <a target=\"_blank\" href=\"https://github.com/jokob-sk/NetAlertX/blob/main/db/app.db\">base de données originelle depuis GitHub</a>.</li><li data-sourcepos=\"50:4-50:195\">Relier le fichier <code>app.db</code> (<g-emoji class=\"g-emoji\" alias=\"warning\" fallback-src=\"https://github.githubassets.com/images/icons/emoji/unicode/26a0.png\">⚠</g-emoji> pas le dossier) au-dessus avec <code>/app/db/app.db</code> (plus de détails dans les <a target=\"_blank\" href=\"https://github.com/jokob-sk/NetAlertX/tree/main/dockerfiles#-examples\">Exemples</a>).</li><li data-sourcepos=\"51:4-51:161\">. Si vous rencontrez des erreurs (erreurs Ajax, impossible d'écrire dans la base, etc.), vérifiez que les permissions sont correctement définies, éventuellement regarder dans les blogs présents dans <code>/app/front/log</code>.</li><li data-sourcepos=\"52:4-52:146\">. Pour résoudre les problèmes de permission, vous pouvez aussi essayer de créer une sauvegarde de la base de données et lancer une restauration via la section <strong>Maintenance &gt; Sauvegarde/Restauration</strong>.</li><li data-sourcepos=\"53:4-53:228\">Si la base de données est en lecture seule, vous pouvez résoudre cela en définissant le propriétaire et le groupe en lançant la commande suivante depuis le système hôte : <code>docker exec netalertx chown -R www-data:www-data /app/db/app.db</code>.</li></ul>",
"HelpFAQ_Cat_General_103_head": "La page d'authentification n'apparaît pas, même après avoir changé le mot de passe.", "HelpFAQ_Cat_General_103_head": "La page d'authentification n'apparaît pas, même après avoir changé le mot de passe.",
"HelpFAQ_Cat_General_103_text": "En plus du mot de passe, le fichier de configuration doit contenir <span class=\"text-danger help_faq_code\">/app/config/app.conf</span>. De plus, le paramètre <span class=\"text-danger help_faq_code\">PIALERT_WEB_PROTECTION</span> doit être à la valeur<span class=\"text-danger help_faq_code\">True</span>.", "HelpFAQ_Cat_General_103_text": "En plus du mot de passe, le fichier de configuration doit contenir <span class=\"text-danger help_faq_code\">/app/config/app.conf</span>. De plus, le paramètre <span class=\"text-danger help_faq_code\">PIALERT_WEB_PROTECTION</span> doit être à la valeur<span class=\"text-danger help_faq_code\">True</span>.",
"HelpFAQ_Cat_Network_600_head": "A quoi sert cette page?", "HelpFAQ_Cat_Network_600_head": "A quoi sert cette page?",
@@ -351,6 +351,7 @@
"Loading": "Chargement...", "Loading": "Chargement...",
"Login_Box": "Saisir votre mot de passe", "Login_Box": "Saisir votre mot de passe",
"Login_Default_PWD": "Le mot de passe par défaut \"123456\" est encore actif.", "Login_Default_PWD": "Le mot de passe par défaut \"123456\" est encore actif.",
"Login_Info": "Les mots de passe sont définis via le plugin Set Password. Vérifiez la <a target=\"_blank\" href=\"https://github.com/jokob-sk/NetAlertX/tree/main/front/plugins/set_password\">documentation de SETPWD</a> si vous rencontrez des difficultés à vous identifier.",
"Login_Psw-box": "Mot de passe", "Login_Psw-box": "Mot de passe",
"Login_Psw_alert": "Alerte de mot de passe!", "Login_Psw_alert": "Alerte de mot de passe!",
"Login_Psw_folder": "dans le dossier de configuration.", "Login_Psw_folder": "dans le dossier de configuration.",
@@ -374,7 +375,7 @@
"Maintenance_Tool_ExportCSV_text": "Génère un fichier CSV (valeurs séparées par des virgules), contenant la liste des appareils, dont les liens entre nœuds Réseaux et les appareils connectés. Vous pouvez aussi lancer cet export depuis l'URL <code>votre URL de NetAlertX/php/server/devices.php?action=ExportCSV</code> ou en activant le plugin <a href=\"settings.php#CSVBCKP_header\">CSV Backup</a>.", "Maintenance_Tool_ExportCSV_text": "Génère un fichier CSV (valeurs séparées par des virgules), contenant la liste des appareils, dont les liens entre nœuds Réseaux et les appareils connectés. Vous pouvez aussi lancer cet export depuis l'URL <code>votre URL de NetAlertX/php/server/devices.php?action=ExportCSV</code> ou en activant le plugin <a href=\"settings.php#CSVBCKP_header\">CSV Backup</a>.",
"Maintenance_Tool_ImportCSV": "Import CSV", "Maintenance_Tool_ImportCSV": "Import CSV",
"Maintenance_Tool_ImportCSV_noti": "Import CSV", "Maintenance_Tool_ImportCSV_noti": "Import CSV",
"Maintenance_Tool_ImportCSV_noti_text": "Êtes-vous sûr de vouloir importer le fichier CSV? Cela écrasera complètement les appareils de votre base de données.", "Maintenance_Tool_ImportCSV_noti_text": "Êtes-vous sûr de vouloir importer le fichier CSV? Cela <b>écrasera</b> complètement les appareils de votre base de données.",
"Maintenance_Tool_ImportCSV_text": "Avant d'utiliser cette fonctionnalité, il est recommandé de faire une sauvegarde. La fonctionnalité importe un fichier CSV (valeurs séparées par des virgules) contenant la liste des appareils, dont les liens réseau entre les nœuds du réseau et ces appareils. Pour cela, placer un fichier CSV nommé <b>devices.csv</b> dans votre répertoire <b>/config</b>.", "Maintenance_Tool_ImportCSV_text": "Avant d'utiliser cette fonctionnalité, il est recommandé de faire une sauvegarde. La fonctionnalité importe un fichier CSV (valeurs séparées par des virgules) contenant la liste des appareils, dont les liens réseau entre les nœuds du réseau et ces appareils. Pour cela, placer un fichier CSV nommé <b>devices.csv</b> dans votre répertoire <b>/config</b>.",
"Maintenance_Tool_ImportPastedCSV": "Import CSV (coller)", "Maintenance_Tool_ImportPastedCSV": "Import CSV (coller)",
"Maintenance_Tool_ImportPastedCSV_noti_text": "Êtes-vous sûr de vouloir importer les CSV copié? Cela va complètement <b>remplacer</b> les appareils de votre base de données.", "Maintenance_Tool_ImportPastedCSV_noti_text": "Êtes-vous sûr de vouloir importer les CSV copié? Cela va complètement <b>remplacer</b> les appareils de votre base de données.",
@@ -531,6 +532,7 @@
"Plugins_DeleteAll": "Tout supprimer (ne prend pas en compte les filtres)", "Plugins_DeleteAll": "Tout supprimer (ne prend pas en compte les filtres)",
"Plugins_Filters_Mac": "Filtrer par MAC", "Plugins_Filters_Mac": "Filtrer par MAC",
"Plugins_History": "Historique des événements", "Plugins_History": "Historique des événements",
"Plugins_Obj_DeleteListed": "",
"Plugins_Objects": "Objets des plugins", "Plugins_Objects": "Objets des plugins",
"Plugins_Out_of": "sur", "Plugins_Out_of": "sur",
"Plugins_Unprocessed_Events": "Événements non traités", "Plugins_Unprocessed_Events": "Événements non traités",
@@ -560,7 +562,7 @@
"RandomMAC_hover": "Détecté automatiquement - indique si l'appareil dispose d'une adresse MAC générée aléatoirement.", "RandomMAC_hover": "Détecté automatiquement - indique si l'appareil dispose d'une adresse MAC générée aléatoirement.",
"Reports_Sent_Log": "Rapports de log transmis", "Reports_Sent_Log": "Rapports de log transmis",
"SCAN_SUBNETS_description": "La plupart des scanners sur le réseau (scan ARP, NMAP, Nslookup, DIG, Pholud) se base sur le scan d'une partie spécifique des interfaces réseau ou de sous-réseau. Consulter la <a href=\"https://github.com/jokob-sk/NetAlertX/blob/main/docs/SUBNETS.md\" target=\"_blank\">documentation des sous-réseaux</a> pour plus d'aide sur ce paramètre, notamment pour des VLAN, lesquels sont supportés ou sur comment identifier le masque réseau et votre interface réseau. <br/> <br/> Une alternative à ces scanner sur le réseau et d'activer d'autres scanners d'appareils ou des importe, qui ne dépendent pas du fait de laisser NetAlert<sup>X</sup> accéder au réseau (Unifié, baux DHCP, Pi-hole, etc.).<br/><br/> Remarque: la durée du scan en lui-même dépend du nombre d'adresses IP à scanner, renseignez donc soigneusement avec le bon masque réseau et la bonne interface réseau.", "SCAN_SUBNETS_description": "La plupart des scanners sur le réseau (scan ARP, NMAP, Nslookup, DIG, Pholud) se base sur le scan d'une partie spécifique des interfaces réseau ou de sous-réseau. Consulter la <a href=\"https://github.com/jokob-sk/NetAlertX/blob/main/docs/SUBNETS.md\" target=\"_blank\">documentation des sous-réseaux</a> pour plus d'aide sur ce paramètre, notamment pour des VLAN, lesquels sont supportés ou sur comment identifier le masque réseau et votre interface réseau. <br/> <br/> Une alternative à ces scanner sur le réseau et d'activer d'autres scanners d'appareils ou des importe, qui ne dépendent pas du fait de laisser NetAlert<sup>X</sup> accéder au réseau (Unifié, baux DHCP, Pi-hole, etc.).<br/><br/> Remarque: la durée du scan en lui-même dépend du nombre d'adresses IP à scanner, renseignez donc soigneusement avec le bon masque réseau et la bonne interface réseau.",
"SCAN_SUBNETS_name": "", "SCAN_SUBNETS_name": "Réseaux à scanner",
"SYSTEM_TITLE": "Informations système", "SYSTEM_TITLE": "Informations système",
"Setting_Override": "Remplacer la valeur", "Setting_Override": "Remplacer la valeur",
"Setting_Override_Description": "Activer cette option va remplacer la valeur fournie par défaut par une application par la valeur renseignée au-dessus.", "Setting_Override_Description": "Activer cette option va remplacer la valeur fournie par défaut par une application par la valeur renseignée au-dessus.",
@@ -659,8 +661,8 @@
"UI_PRESENCE_name": "Afficher dans le graphique de présence", "UI_PRESENCE_name": "Afficher dans le graphique de présence",
"UI_REFRESH_description": "Renseignez le nombre de secondes après lequel rafraîchir l'interface graphique. Renseignez <code>0</code> pour désactiver.", "UI_REFRESH_description": "Renseignez le nombre de secondes après lequel rafraîchir l'interface graphique. Renseignez <code>0</code> pour désactiver.",
"UI_REFRESH_name": "Rafraîchir automatiquement l'interface graphique", "UI_REFRESH_name": "Rafraîchir automatiquement l'interface graphique",
"VERSION_description": "", "VERSION_description": "Valeur de la version ou du timestamp d'aide à vérifier si l'application a été mise à jour.",
"VERSION_name": "", "VERSION_name": "Version ou Timestamp",
"devices_old": "Rafraichissement...", "devices_old": "Rafraichissement...",
"general_event_description": "L'événement que vous avez lancé peut prendre du temps avant que les tâches de fond ne soit terminées. La durée d'exécution finira une fois que la file d'exécution ci-dessous sera vide (consulter les <a href='/maintenance.php#tab_Logging'>journaux d'erreur</a> si vous rencontrez des erreurs). <br/> <br/> File d'exécution:", "general_event_description": "L'événement que vous avez lancé peut prendre du temps avant que les tâches de fond ne soit terminées. La durée d'exécution finira une fois que la file d'exécution ci-dessous sera vide (consulter les <a href='/maintenance.php#tab_Logging'>journaux d'erreur</a> si vous rencontrez des erreurs). <br/> <br/> File d'exécution:",
"general_event_title": "Lancement d'un événement sur mesure", "general_event_title": "Lancement d'un événement sur mesure",

View File

@@ -351,6 +351,7 @@
"Loading": "Caricamento...", "Loading": "Caricamento...",
"Login_Box": "Inserisci la tua password", "Login_Box": "Inserisci la tua password",
"Login_Default_PWD": "La password predefinita \"123456\" è ancora attiva.", "Login_Default_PWD": "La password predefinita \"123456\" è ancora attiva.",
"Login_Info": "",
"Login_Psw-box": "Password", "Login_Psw-box": "Password",
"Login_Psw_alert": "Avviso password!", "Login_Psw_alert": "Avviso password!",
"Login_Psw_folder": "nella cartella di configurazione.", "Login_Psw_folder": "nella cartella di configurazione.",
@@ -531,6 +532,7 @@
"Plugins_DeleteAll": "Elimina tutti (i filtri vengono ignorati)", "Plugins_DeleteAll": "Elimina tutti (i filtri vengono ignorati)",
"Plugins_Filters_Mac": "Filtro MAC", "Plugins_Filters_Mac": "Filtro MAC",
"Plugins_History": "Storico eventi", "Plugins_History": "Storico eventi",
"Plugins_Obj_DeleteListed": "",
"Plugins_Objects": "Oggetti plugin", "Plugins_Objects": "Oggetti plugin",
"Plugins_Out_of": "fuori da", "Plugins_Out_of": "fuori da",
"Plugins_Unprocessed_Events": "Eventi non processati", "Plugins_Unprocessed_Events": "Eventi non processati",

View File

@@ -28,8 +28,6 @@ switch($result){
if (isset($pia_lang_selected) == FALSE or (strlen($pia_lang_selected) == 0)) {$pia_lang_selected = $defaultLang;} if (isset($pia_lang_selected) == FALSE or (strlen($pia_lang_selected) == 0)) {$pia_lang_selected = $defaultLang;}
require dirname(__FILE__).'/../skinUI.php';
$result = $db->query("SELECT * FROM Plugins_Language_Strings"); $result = $db->query("SELECT * FROM Plugins_Language_Strings");
$strings = array(); $strings = array();
while ($row = $result->fetchArray(SQLITE3_ASSOC)) { while ($row = $result->fetchArray(SQLITE3_ASSOC)) {

View File

@@ -351,6 +351,7 @@
"Loading": "Laster...", "Loading": "Laster...",
"Login_Box": "Skriv inn passordet ditt", "Login_Box": "Skriv inn passordet ditt",
"Login_Default_PWD": "Standard passordet \"123456\" er fortsatt aktivt.", "Login_Default_PWD": "Standard passordet \"123456\" er fortsatt aktivt.",
"Login_Info": "",
"Login_Psw-box": "Passord", "Login_Psw-box": "Passord",
"Login_Psw_alert": "Passord varsling!", "Login_Psw_alert": "Passord varsling!",
"Login_Psw_folder": "i config-mappen.", "Login_Psw_folder": "i config-mappen.",
@@ -531,6 +532,7 @@
"Plugins_DeleteAll": "Slett alle (filtre blir ignorert)", "Plugins_DeleteAll": "Slett alle (filtre blir ignorert)",
"Plugins_Filters_Mac": "Mac filter", "Plugins_Filters_Mac": "Mac filter",
"Plugins_History": "Hendelses historikk", "Plugins_History": "Hendelses historikk",
"Plugins_Obj_DeleteListed": "",
"Plugins_Objects": "Plugin-objekter", "Plugins_Objects": "Plugin-objekter",
"Plugins_Out_of": "ut av", "Plugins_Out_of": "ut av",
"Plugins_Unprocessed_Events": "Uprosesserte hendelser", "Plugins_Unprocessed_Events": "Uprosesserte hendelser",

View File

@@ -351,6 +351,7 @@
"Loading": "Wczytywanie...", "Loading": "Wczytywanie...",
"Login_Box": "Wprowadź hasło", "Login_Box": "Wprowadź hasło",
"Login_Default_PWD": "Podstawowe hasło \"123456\" jest aktywne.", "Login_Default_PWD": "Podstawowe hasło \"123456\" jest aktywne.",
"Login_Info": "",
"Login_Psw-box": "Hasło", "Login_Psw-box": "Hasło",
"Login_Psw_alert": "Alert HASŁA!", "Login_Psw_alert": "Alert HASŁA!",
"Login_Psw_folder": "w folderze konfiguracji.", "Login_Psw_folder": "w folderze konfiguracji.",
@@ -531,6 +532,7 @@
"Plugins_DeleteAll": "Usuń wszystkie (filtry są ignorowane)", "Plugins_DeleteAll": "Usuń wszystkie (filtry są ignorowane)",
"Plugins_Filters_Mac": "Filtr MAC", "Plugins_Filters_Mac": "Filtr MAC",
"Plugins_History": "Historia Wydarzeń", "Plugins_History": "Historia Wydarzeń",
"Plugins_Obj_DeleteListed": "",
"Plugins_Objects": "Obiekty Wtyczek", "Plugins_Objects": "Obiekty Wtyczek",
"Plugins_Out_of": "brakujące", "Plugins_Out_of": "brakujące",
"Plugins_Unprocessed_Events": "Nieprzeprocesowane Wydarzenia", "Plugins_Unprocessed_Events": "Nieprzeprocesowane Wydarzenia",

View File

@@ -351,6 +351,7 @@
"Loading": "", "Loading": "",
"Login_Box": "", "Login_Box": "",
"Login_Default_PWD": "", "Login_Default_PWD": "",
"Login_Info": "",
"Login_Psw-box": "", "Login_Psw-box": "",
"Login_Psw_alert": "", "Login_Psw_alert": "",
"Login_Psw_folder": "", "Login_Psw_folder": "",
@@ -531,6 +532,7 @@
"Plugins_DeleteAll": "", "Plugins_DeleteAll": "",
"Plugins_Filters_Mac": "", "Plugins_Filters_Mac": "",
"Plugins_History": "", "Plugins_History": "",
"Plugins_Obj_DeleteListed": "",
"Plugins_Objects": "", "Plugins_Objects": "",
"Plugins_Out_of": "", "Plugins_Out_of": "",
"Plugins_Unprocessed_Events": "", "Plugins_Unprocessed_Events": "",

View File

@@ -351,6 +351,7 @@
"Loading": "Загрузка...", "Loading": "Загрузка...",
"Login_Box": "Введите пароль", "Login_Box": "Введите пароль",
"Login_Default_PWD": "Пароль по умолчанию «123456» все еще активен.", "Login_Default_PWD": "Пароль по умолчанию «123456» все еще активен.",
"Login_Info": "",
"Login_Psw-box": "Пароль", "Login_Psw-box": "Пароль",
"Login_Psw_alert": "Предупреждение о пароле!", "Login_Psw_alert": "Предупреждение о пароле!",
"Login_Psw_folder": "в папке конфигурации.", "Login_Psw_folder": "в папке конфигурации.",
@@ -531,6 +532,7 @@
"Plugins_DeleteAll": "Удалить все (фильтры игнорируются)", "Plugins_DeleteAll": "Удалить все (фильтры игнорируются)",
"Plugins_Filters_Mac": "Фильтр MAC-адреса", "Plugins_Filters_Mac": "Фильтр MAC-адреса",
"Plugins_History": "История событий", "Plugins_History": "История событий",
"Plugins_Obj_DeleteListed": "",
"Plugins_Objects": "Объекты плагина", "Plugins_Objects": "Объекты плагина",
"Plugins_Out_of": "из", "Plugins_Out_of": "из",
"Plugins_Unprocessed_Events": "Необработанные события", "Plugins_Unprocessed_Events": "Необработанные события",

View File

@@ -351,6 +351,7 @@
"Loading": "Yükleniyor...", "Loading": "Yükleniyor...",
"Login_Box": "Şifrenizi giriniz", "Login_Box": "Şifrenizi giriniz",
"Login_Default_PWD": "Varsayılan şifre \"123456\" hâlâ aktif.", "Login_Default_PWD": "Varsayılan şifre \"123456\" hâlâ aktif.",
"Login_Info": "",
"Login_Psw-box": "Şİfre", "Login_Psw-box": "Şİfre",
"Login_Psw_alert": "", "Login_Psw_alert": "",
"Login_Psw_folder": "", "Login_Psw_folder": "",
@@ -531,6 +532,7 @@
"Plugins_DeleteAll": "", "Plugins_DeleteAll": "",
"Plugins_Filters_Mac": "", "Plugins_Filters_Mac": "",
"Plugins_History": "", "Plugins_History": "",
"Plugins_Obj_DeleteListed": "",
"Plugins_Objects": "", "Plugins_Objects": "",
"Plugins_Out_of": "", "Plugins_Out_of": "",
"Plugins_Unprocessed_Events": "", "Plugins_Unprocessed_Events": "",

View File

@@ -351,6 +351,7 @@
"Loading": "加载中...", "Loading": "加载中...",
"Login_Box": "输入密码", "Login_Box": "输入密码",
"Login_Default_PWD": "默认密码“123456”仍然有效。", "Login_Default_PWD": "默认密码“123456”仍然有效。",
"Login_Info": "",
"Login_Psw-box": "密码", "Login_Psw-box": "密码",
"Login_Psw_alert": "密码警报!", "Login_Psw_alert": "密码警报!",
"Login_Psw_folder": "在配置文件夹中。", "Login_Psw_folder": "在配置文件夹中。",
@@ -531,6 +532,7 @@
"Plugins_DeleteAll": "全部删除(忽略过滤器)", "Plugins_DeleteAll": "全部删除(忽略过滤器)",
"Plugins_Filters_Mac": "Mac 过滤器", "Plugins_Filters_Mac": "Mac 过滤器",
"Plugins_History": "事件历史", "Plugins_History": "事件历史",
"Plugins_Obj_DeleteListed": "",
"Plugins_Objects": "插件对象", "Plugins_Objects": "插件对象",
"Plugins_Out_of": "", "Plugins_Out_of": "",
"Plugins_Unprocessed_Events": "未处理的事件", "Plugins_Unprocessed_Events": "未处理的事件",

View File

@@ -1,4 +1,10 @@
<?php require 'php/templates/notification.php'; ?> <?php
require 'php/templates/notification.php';
//------------------------------------------------------------------------------
// check if authenticated
require_once $_SERVER['DOCUMENT_ROOT'] . '/php/templates/security.php';
?>
<script> <script>

View File

@@ -1,67 +1,75 @@
<?php <?php
$url = 'http://' . $_SERVER['SERVER_NAME'] . $_SERVER['REQUEST_URI']; // Constants
$isLogonPage = FALSE; define('CONFIG_PATH', $_SERVER['DOCUMENT_ROOT'] . "/../config/app.conf");
define('COOKIE_SAVE_LOGIN_NAME', "NetAlertX_SaveLogin");
$CookieSaveLoginName = "NetAlertX_SaveLogin"; // Utility Functions
function getConfigLine($pattern, $config_lines) {
$matches = preg_grep($pattern, $config_lines);
if (strpos($url,'index.php') !== false) { return !empty($matches) ? explode("=", array_values($matches)[0]) : null;
$isLogonPage = TRUE;
} }
session_start(); function getConfigValue($pattern, $config_lines, $delimiter = "'") {
$line = preg_grep($pattern, $config_lines);
return !empty($line) ? explode($delimiter, array_values($line)[0])[1] : '';
}
if(array_search('action', $_REQUEST) != FALSE) function redirect($url) {
{ header("Location: $url");
if ($_REQUEST['action'] == 'logout') { exit();
}
// Initialization
$protocol = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? 'https://' : 'http://';
$url = $protocol . $_SERVER['SERVER_NAME'] . $_SERVER['REQUEST_URI'];
$isLogonPage = strpos($url, 'index.php') !== false;
$authHeader = apache_request_headers()['Authorization'] ?? '';
$sessionLogin = isset($_SESSION['login']) ? $_SESSION['login'] : 0;
// Start session if not already started
if (session_status() == PHP_SESSION_NONE) {
session_start();
}
// Handle logout
if (!empty($_REQUEST['action']) && $_REQUEST['action'] == 'logout') {
session_destroy(); session_destroy();
setcookie($CookieSaveLoginName, "", time() - 3600); setcookie(COOKIE_SAVE_LOGIN_NAME, "", time() - 3600);
header('Location: index.php'); redirect('index.php');
}
} }
// ################################################## // Load configuration
// ## Login Processing start if (!file_exists(CONFIG_PATH)) {
// ################################################## die("Configuration file not found.");
$config_file = "../config/app.conf"; }
$config_file_lines = file($config_file); $configLines = file(CONFIG_PATH);
$CookieSaveLoginName = "NetAlertX_SaveLogin";
// ################################### // Handle web protection and password
// ## SETPWD_enable_password FALSE $nax_WebProtection = strtolower(trim(getConfigLine('/^SETPWD_enable_password.*=/', $configLines)[1] ?? 'false'));
// ################################### $nax_Password = getConfigValue('/^SETPWD_password.*=/', $configLines);
$api_token = getConfigValue('/^SYNC_api_token.*=/', $configLines, "'");
$config_file_lines_bypass = array_values(preg_grep('/^SETPWD_enable_password.*=/', $config_file_lines)); $expectedToken = 'Bearer ' . $api_token;
$protection_line = explode("=", $config_file_lines_bypass[0]);
$Pia_WebProtection = strtolower(trim($protection_line[1]));
// ################################### // Authentication Handling
// ## SETPWD_enable_password TRUE if ($nax_WebProtection == 'true') {
// ################################### if ($authHeader === $expectedToken) {
$_SESSION['login'] = 1; // User authenticated with bearer token
$config_file_lines = array_values(preg_grep('/^SETPWD_password.*=/', $config_file_lines)); } elseif (!empty($authHeader)) {
$password_line = explode("'", $config_file_lines[0]); echo "[Security] Incorrect Bearer Token";
$Pia_Password = $password_line[1];
// active Session or valid cookie (cookie not extends)
if($Pia_WebProtection == 'true')
{
if(isset ($_SESSION["login"]) == FALSE )
{
$_SESSION["login"] = 0;
} }
if ( ($_SESSION["login"] == 1) || $isLogonPage || (( isset($_COOKIE[$CookieSaveLoginName]) && $Pia_Password == $_COOKIE[$CookieSaveLoginName ]))) // Safely check if the session login exists before checking its value
{ $isLoggedIn = isset($_SESSION['login']) && $_SESSION['login'] == 1;
//Logged in or stay on this page if we are on the index.php already
} else // Determine if the user should be redirected
{ if ($isLoggedIn || $isLogonPage || (isset($_COOKIE[COOKIE_SAVE_LOGIN_NAME]) && $nax_Password == $_COOKIE[COOKIE_SAVE_LOGIN_NAME])) {
// we need to redirect // Logged in or stay on this page if we are on the index.php already
header('Location: index.php'); } else {
// We need to redirect
redirect('/index.php');
} }
} }
?> ?>

View File

@@ -12,10 +12,7 @@ if( isset($_COOKIE['UI_dark_mode']))
$ENABLED_DARKMODE = False; $ENABLED_DARKMODE = False;
} }
foreach (glob("/app/db/setting_skin*") as $filename) { $pia_skin_selected = 'skin-blue';
$pia_skin_selected = str_replace('setting_','',basename($filename));
}
if (isset($pia_skin_selected) == FALSE or (strlen($pia_skin_selected) == 0)) {$pia_skin_selected = 'skin-blue';}
// ################################### // ###################################
// ## GUI settings processing end // ## GUI settings processing end

View File

@@ -12,6 +12,7 @@
#---------------------------------------------------------------------------------# #---------------------------------------------------------------------------------#
$filename = "/app/.VERSION"; $filename = "/app/.VERSION";
if(file_exists($filename)) { if(file_exists($filename)) {
$fileContents = file_get_contents($filename); $fileContents = file_get_contents($filename);
if(trim($fileContents) === 'Dev') { if(trim($fileContents) === 'Dev') {
@@ -23,4 +24,5 @@ if(file_exists($filename)) {
else { else {
echo date('H:i:s') . " - N/A"; echo date('H:i:s') . " - N/A";
} }
?> ?>

Binary file not shown.

After

Width:  |  Height:  |  Size: 142 KiB

View File

@@ -7,6 +7,24 @@
- Go to settings and fill in relevant details. There are 2 types of "devices" generated and sent to the broker. A generic overview device that contains online/down/archived device stats and then the actual devices detected by the application. - Go to settings and fill in relevant details. There are 2 types of "devices" generated and sent to the broker. A generic overview device that contains online/down/archived device stats and then the actual devices detected by the application.
## Forcing an update
In order to speed up the processing, device configs are only pushed to the broker if a change occurs. The plugin compares the previous data with the current device state, and the following fields are checked:
- icon
- device name
- mac
You can force an update of all devices by deleting plugin objects of the MQTT plugin. For example, navigate to:
`Device -> Plugins -> MQTT -> Delete all`
Filters will be ignored, and this will delete all objects associated with the plugin. The next time the MQTT plugin is processed, all data is re-sent to the broker.
![image](./Deleting_MQTT_Plugin_Objects.png)
This is not the case for the online/offline state of the device, which is always updated absed on the scan result and if it changed from the previous value.
# Sample Payloads # Sample Payloads

View File

@@ -5,6 +5,15 @@
"enabled": true, "enabled": true,
"data_source": "script", "data_source": "script",
"show_ui": true, "show_ui": true,
"data_filters": [
{
"compare_column": "Watched_Value4",
"compare_operator": "==",
"compare_field_id": "txtMacFilter",
"compare_js_template": "'{value}'.toString()",
"compare_use_quotes": true
}
],
"localized": ["display_name", "description", "icon"], "localized": ["display_name", "description", "icon"],
"display_name": [ "display_name": [
{ {
@@ -647,7 +656,7 @@
"description": [ "description": [
{ {
"language_code": "en_us", "language_code": "en_us",
"string": "The root path of the stats overview sensor. Inserted into the <code>system-sensors/sensor/{DEVICE_ID}/state</code> topic." "string": "The root path of the stats overview sensor. Inserted into the <code>{MQTT_topic_root}/sensor/{MQTT_DEVICE_ID}/state</code> topic."
} }
] ]
}, },
@@ -703,6 +712,30 @@
} }
] ]
}, },
{
"function": "topic_root",
"type": {
"dataType": "string",
"elements": [
{ "elementType": "input", "elementOptions": [], "transformers": [] }
]
},
"default_value": "system-sensors",
"options": [],
"localized": ["name", "description"],
"name": [
{
"language_code": "en_us",
"string": "MQTT topic root"
}
],
"description": [
{
"language_code": "en_us",
"string": "The topic root of the devices sensors. Inserted into the <code>{MQTT_topic_root}/sensor/{DEVICE_ID}/state</code> topic."
}
]
},
{ {
"function": "DEVICES_SQL", "function": "DEVICES_SQL",
"type": { "type": {

View File

@@ -9,6 +9,7 @@ import sys
from datetime import datetime from datetime import datetime
import time import time
import re import re
import unicodedata
import paho.mqtt.client as mqtt import paho.mqtt.client as mqtt
# from paho.mqtt import client as mqtt_client # from paho.mqtt import client as mqtt_client
# from paho.mqtt import CallbackAPIVersion as mqtt_CallbackAPIVersion # from paho.mqtt import CallbackAPIVersion as mqtt_CallbackAPIVersion
@@ -26,7 +27,7 @@ from const import apiPath, confFileName
from plugin_utils import getPluginObject from plugin_utils import getPluginObject
from plugin_helper import Plugin_Objects from plugin_helper import Plugin_Objects
from logger import mylog, append_line_to_file from logger import mylog, append_line_to_file
from helper import timeNowTZ, get_setting_value, bytes_to_string, sanitize_string from helper import timeNowTZ, get_setting_value, bytes_to_string, sanitize_string, normalize_string
from notification import Notification_obj from notification import Notification_obj
from database import DB, get_device_stats from database import DB, get_device_stats
from pytz import timezone from pytz import timezone
@@ -37,7 +38,6 @@ conf.tz = timezone(get_setting_value('TIMEZONE'))
CUR_PATH = str(pathlib.Path(__file__).parent.resolve()) CUR_PATH = str(pathlib.Path(__file__).parent.resolve())
RESULT_FILE = os.path.join(CUR_PATH, 'last_result.log') RESULT_FILE = os.path.join(CUR_PATH, 'last_result.log')
# Initialize the Plugin obj output file # Initialize the Plugin obj output file
plugin_objects = Plugin_Objects(RESULT_FILE) plugin_objects = Plugin_Objects(RESULT_FILE)
# Create an MD5 hash object # Create an MD5 hash object
@@ -46,10 +46,10 @@ md5_hash = hashlib.md5()
pluginName = 'MQTT' pluginName = 'MQTT'
# globals # globals
mqtt_sensors = [] mqtt_sensors = []
mqtt_connected_to_broker = False mqtt_connected_to_broker = False
mqtt_client = None # mqtt client mqtt_client = None # mqtt client
topic_root = get_setting_value('MQTT_topic_root')
def main(): def main():
@@ -86,111 +86,144 @@ def check_config():
# Sensor configs are tracking which sensors in NetAlertX exist and if a config has changed # Sensor configs are tracking which sensors in NetAlertX exist and if a config has changed
class sensor_config: class sensor_config:
def __init__(self, deviceId, deviceName, sensorType, sensorName, icon, mac): def __init__(self, deviceId, deviceName, sensorType, sensorName, icon, mac):
"""
Initialize the sensor_config object with provided parameters. Sets up sensor configuration
and generates necessary MQTT topics and messages based on the sensor type.
"""
# Assign initial attributes
self.deviceId = deviceId self.deviceId = deviceId
self.deviceName = deviceName self.deviceName = deviceName
self.sensorType = sensorType self.sensorType = sensorType
self.sensorName = sensorName self.sensorName = sensorName
self.icon = icon self.icon = icon
self.mac = mac self.mac = mac
self.model = deviceName
self.hash = ''
self.state_topic = '' self.state_topic = ''
self.json_attr_topic = '' self.json_attr_topic = ''
self.topic = '' self.topic = ''
self.message = '' self.message = {} # Initialize message as an empty dictionary
self.unique_id = '' self.unique_id = ''
# binary sensor only sensor # Call helper functions to initialize the message, generate a hash, and handle plugin object
if self.sensorType == 'binary_sensor' or self.sensorType == 'sensor': self.initialize_message()
self.generate_hash()
self.handle_plugin_object()
def initialize_message(self):
"""
Initialize the MQTT message payload based on the sensor type. This method handles sensors of types:
- 'timestamp'
- 'binary_sensor'
- 'sensor'
- 'device_tracker'
"""
# Ensure self.message is initialized as a dictionary if not already done
if not isinstance(self.message, dict):
self.message = {}
# Handle sensors with a 'timestamp' device class
if self.sensorName in ['last_connection', 'first_connection']:
self.message.update({
"device_class": "timestamp"
})
# Handle 'binary_sensor' or 'sensor' types
if self.sensorType in ['binary_sensor', 'sensor']:
self.topic = f'homeassistant/{self.sensorType}/{self.deviceId}/{self.sensorName}/config' self.topic = f'homeassistant/{self.sensorType}/{self.deviceId}/{self.sensorName}/config'
self.state_topic = f'system-sensors/{self.sensorType}/{self.deviceId}/state' self.state_topic = f'{topic_root}/{self.sensorType}/{self.deviceId}/state'
self.unique_id = self.deviceId+'_sensor_'+self.sensorName self.unique_id = f'{self.deviceId}_sensor_{self.sensorName}'
self.message = { # Update the message dictionary, expanding it without overwriting
"name" : self.sensorName, self.message.update({
"state_topic" : self.state_topic, "name": self.sensorName,
"value_template" : "{{value_json."+self.sensorName+"}}", "state_topic": self.state_topic,
"unique_id" : self.unique_id, "value_template": f"{{{{value_json.{self.sensorName}}}}}",
"device": "unique_id": self.unique_id,
{ "device": {
"identifiers" : [self.deviceId+"_sensor"], "identifiers": [f"{self.deviceId}_sensor"],
"manufacturer" : "NetAlertX", "manufacturer": "NetAlertX",
"name" : self.deviceName "name": self.deviceName
}, },
"icon": f'mdi:{self.icon}' "icon": f'mdi:{self.icon}'
} })
# Handle 'device_tracker' sensor type
elif self.sensorType == 'device_tracker': elif self.sensorType == 'device_tracker':
self.topic = f'homeassistant/device_tracker/{self.deviceId}/config' self.topic = f'homeassistant/device_tracker/{self.deviceId}/config'
self.state_topic = f'system-sensors/device_tracker/{self.deviceId}/state' self.state_topic = f'{topic_root}/device_tracker/{self.deviceId}/state'
self.json_attr_topic = f'system-sensors/device_tracker/{self.deviceId}/attributes' self.json_attr_topic = f'{topic_root}/device_tracker/{self.deviceId}/attributes'
self.unique_id = f'{self.deviceId}_{self.sensorType}_{self.sensorName}' self.unique_id = f'{self.deviceId}_{self.sensorType}_{self.sensorName}'
payload_home = 'home' # Construct the message dictionary for device_tracker
payload_away = 'away' self.message = {
"state_topic": self.state_topic,
"json_attributes_topic": self.json_attr_topic,
"name": self.sensorName,
"payload_home": 'home',
"payload_not_home": 'away',
"unique_id": self.unique_id,
"icon": f'mdi:{self.icon}',
"device": {
"identifiers": [f"{self.deviceId}_sensor", self.unique_id],
"manufacturer": "NetAlertX",
"model": self.model or "Unknown", # Use model if available, else set to 'Unknown'
"name": self.deviceName
}
}
self.message = { def generate_hash(self):
"state_topic": self.state_topic, """
"json_attributes_topic": self.json_attr_topic, Generate an MD5 hash based on the combined string of deviceId, deviceName, sensorType, sensorName, and icon.
"name": self.sensorName, This hash will uniquely identify the sensor configuration.
"payload_home": payload_home, """
"payload_not_home": payload_away, # Concatenate all relevant attributes into a single string
"unique_id" : self.unique_id, input_string = f"{self.deviceId}{self.deviceName}{self.sensorType}{self.sensorName}{self.icon}"
"icon": f'mdi:{self.icon}', md5_hash = hashlib.md5() # Initialize the MD5 hash object
"device": md5_hash.update(input_string.encode('utf-8')) # Update hash with input string
{ self.hash = md5_hash.hexdigest() # Store the hex representation of the hash
"identifiers" : [self.deviceId+"_sensor", self.unique_id],
"manufacturer" : "NetAlertX",
"name" : self.deviceName
},
}
# Define your input string def handle_plugin_object(self):
input_string = str(self.deviceId) + str(self.deviceName) + str(self.sensorType) + str(self.sensorName) + str(self.icon) """
Fetch the plugin object from the system based on the generated hash. If the object exists, it logs that the sensor is
already known. If not, it marks the sensor as new and logs relevant information.
"""
# Retrieve the plugin object based on the sensor's hash
plugObj = getPluginObject({"Plugin": "MQTT", "Watched_Value3": self.hash})
# Hash the input string and convert the hash to a string # Check if the plugin object is new
# Update the hash object with the bytes of the input string if not plugObj:
md5_hash.update(input_string.encode('utf-8'))
# Get the hexadecimal representation of the MD5 hash
md5_hash_hex = md5_hash.hexdigest()
hash_value = str(md5_hash_hex)
self.hash = hash_value
plugObj = getPluginObject({"Plugin":"MQTT", "Watched_Value3":hash_value})
# mylog('verbose', [f"[{pluginName}] Previous plugin object entry: {json.dumps(plugObj)}"])
if plugObj == {}:
self.isNew = True self.isNew = True
mylog('verbose', [f"[{pluginName}] New sensor entry name : {self.deviceName}"]) mylog('verbose', [f"[{pluginName}] New sensor entry (name|mac|hash) : ({self.deviceName}|{self.mac}|{self.hash}"])
mylog('verbose', [f"[{pluginName}] New sensor entry mac : {self.mac}"])
mylog('verbose', [f"[{pluginName}] New sensor entry hash_value : {hash_value}"])
else: else:
device_name = plugObj.get("Watched_Value1", "Unknown") device_name = plugObj.get("Watched_Value1", "Unknown")
mylog('verbose', [f"[{pluginName}] Existing, skip Device Name : {device_name}"]) mylog('verbose', [f"[{pluginName}] Existing, skip Device Name: {device_name}"])
self.isNew = False self.isNew = False
# Store the sensor configuration in global plugin_objects
self.store_plugin_object()
# Log sensor def store_plugin_object(self):
"""
Store the sensor configuration in the global plugin_objects, which tracks sensors based on a unique combination
of attributes including deviceId, sensorName, hash, and MAC.
"""
global plugin_objects global plugin_objects
if mac == '': # Add the sensor to the global plugin_objects
mac = "N/A"
plugin_objects.add_object( plugin_objects.add_object(
primaryId = deviceId, primaryId=self.deviceId,
secondaryId = sensorName, secondaryId=self.sensorName,
watched1 = deviceName, watched1=self.deviceName,
watched2 = sensorType, watched2=self.sensorType,
watched3 = hash_value, watched3=self.hash,
watched4 = mac, watched4=self.mac,
extra = input_string, extra=f"{self.deviceId}{self.deviceName}{self.sensorType}{self.sensorName}{self.icon}",
foreignKey = mac foreignKey=self.mac
) )
#------------------------------------------------------------------------------- #-------------------------------------------------------------------------------
def publish_mqtt(mqtt_client, topic, message): def publish_mqtt(mqtt_client, topic, message):
@@ -273,29 +306,54 @@ def create_sensor(mqtt_client, deviceId, deviceName, sensorType, sensorName, ico
#------------------------------------------------------------------------------- #-------------------------------------------------------------------------------
def mqtt_create_client(): def mqtt_create_client():
# attempt reconnections on failure, ref https://www.emqx.com/en/blog/how-to-use-mqtt-in-python
FIRST_RECONNECT_DELAY = 1
RECONNECT_RATE = 2
MAX_RECONNECT_COUNT = 12
MAX_RECONNECT_DELAY = 60
mytransport = 'tcp' # or 'websockets' mytransport = 'tcp' # or 'websockets'
def on_disconnect(mqtt_client, userdata, reason_code): def on_disconnect(mqtt_client, userdata, rc):
global mqtt_connected_to_broker global mqtt_connected_to_broker
# REF: If we wanted a auto reconnect, a good source is here: https://www.emqx.com/en/blog/how-to-use-mqtt-in-python mylog('verbose', [f"[{pluginName}] Connection terminated, reason_code: {rc}"])
mqtt_connected_to_broker = False reconnect_count, reconnect_delay = 0, FIRST_RECONNECT_DELAY
mylog('debug', [f"[{pluginName}] Connection terminated, reason_code: {reason_code}"]) while reconnect_count < MAX_RECONNECT_COUNT:
mylog('verbose', [f"[{pluginName}] Reconnecting in {reconnect_delay} seconds..."])
time.sleep(reconnect_delay)
def on_connect(mqtt_client, userdata, flags, reason_code, properties): try:
mqtt_client.reconnect()
mqtt_connected_to_broker = True # Signal connection
mylog('verbose', [f"[{pluginName}] Reconnected successfully"])
return
except Exception as err:
mylog('verbose', [f"[{pluginName}] {err} Reconnect failed. Retrying..."])
pass
reconnect_delay *= RECONNECT_RATE
reconnect_delay = min(reconnect_delay, MAX_RECONNECT_DELAY)
reconnect_count += 1
mqtt_connected_to_broker = False
def on_connect(mqtt_client, userdata, flags, rc, properties):
global mqtt_connected_to_broker global mqtt_connected_to_broker
# REF: Good docu on reason codes: https://www.emqx.com/en/blog/mqtt5-new-features-reason-code-and-ack # REF: Good docu on reason codes: https://www.emqx.com/en/blog/mqtt5-new-features-reason-code-and-ack
if reason_code == 0: if rc == 0:
mylog('verbose', [f"[{pluginName}] Connected to broker"]) mylog('verbose', [f"[{pluginName}] Connected to broker"])
mqtt_connected_to_broker = True # Signal connection mqtt_connected_to_broker = True # Signal connection
else: else:
mylog('verbose', [f"[{pluginName}] Connection failed, reason_code: {reason_code}"]) mylog('verbose', [f"[{pluginName}] Connection failed, reason_code: {rc}"])
mqtt_connected_to_broker = False mqtt_connected_to_broker = False
global mqtt_client global mqtt_client
global mqtt_connected_to_broker
# Paho will be soon not supporting V1 anymore, so this really should not be a user choice to start with # Paho will be soon not supporting V1 anymore, so this really should not be a user choice to start with
# This code now uses V2 by default # This code now uses V2 by default
@@ -306,10 +364,13 @@ def mqtt_create_client():
else: else:
version = mqtt.MQTTv5 version = mqtt.MQTTv5
# we now hardcode the client id into here.
# TODO: Add config ffor client id
mqtt_client = mqtt.Client( mqtt_client = mqtt.Client(
client_id='netalertx',
callback_api_version = mqtt.CallbackAPIVersion.VERSION2, callback_api_version = mqtt.CallbackAPIVersion.VERSION2,
transport=mytransport, transport=mytransport,
protocol=mqtt.MQTTv5) protocol=version)
mqtt_client.on_connect = on_connect mqtt_client.on_connect = on_connect
mqtt_client.on_disconnect = on_disconnect mqtt_client.on_disconnect = on_disconnect
@@ -317,7 +378,15 @@ def mqtt_create_client():
mqtt_client.tls_set() mqtt_client.tls_set()
mqtt_client.username_pw_set(username = get_setting_value('MQTT_USER'), password = get_setting_value('MQTT_PASSWORD')) mqtt_client.username_pw_set(username = get_setting_value('MQTT_USER'), password = get_setting_value('MQTT_PASSWORD'))
mqtt_client.connect(host = get_setting_value('MQTT_BROKER'), port = get_setting_value('MQTT_PORT')) err_code = mqtt_client.connect(host = get_setting_value('MQTT_BROKER'), port = get_setting_value('MQTT_PORT'))
if (err_code == mqtt.MQTT_ERR_SUCCESS):
# We (prematurely) set the connection state to connected
# the callback may be delayed
mqtt_connected_to_broker = True
# the client connects but connect callbacks will be called async and there may be a waiting time
# Mosquitto works straight away
# EMQX has a delay and does not update in loop below, so we cannot rely on it, we wait 1 sec
time.sleep(1)
mqtt_client.loop_start() mqtt_client.loop_start()
return mqtt_client return mqtt_client
@@ -346,7 +415,7 @@ def mqtt_start(db):
row = get_device_stats(db) row = get_device_stats(db)
# Publish (wrap into {} and remove last ',' from above) # Publish (wrap into {} and remove last ',' from above)
publish_mqtt(mqtt_client, f"system-sensors/sensor/{deviceId}/state", publish_mqtt(mqtt_client, f"{topic_root}/sensor/{deviceId}/state",
{ {
"online": row[0], "online": row[0],
"down": row[1], "down": row[1],
@@ -373,12 +442,15 @@ def mqtt_start(db):
for device in devices: for device in devices:
# debug statement # # debug statement START 🔻
# if 'Moto' in device["dev_Name"]: # if 'Moto' not in device["dev_Name"]:
# continue
# # debug statement END 🔺
# Create devices in Home Assistant - send config messages # Create devices in Home Assistant - send config messages
deviceId = 'mac_' + device["dev_MAC"].replace(" ", "").replace(":", "_").lower() deviceId = 'mac_' + device["dev_MAC"].replace(" ", "").replace(":", "_").lower()
devDisplayName = re.sub('[^a-zA-Z0-9-_\\s]', '', device["dev_Name"]) # Normalize the string and remove unwanted characters
devDisplayName = re.sub('[^a-zA-Z0-9-_\\s]', '', normalize_string(device["dev_Name"]))
sensorConfig = create_sensor(mqtt_client, deviceId, devDisplayName, 'sensor', 'last_ip', 'ip-network', device["dev_MAC"]) sensorConfig = create_sensor(mqtt_client, deviceId, devDisplayName, 'sensor', 'last_ip', 'ip-network', device["dev_MAC"])
sensorConfig = create_sensor(mqtt_client, deviceId, devDisplayName, 'sensor', 'mac_address', 'folder-key-network', device["dev_MAC"]) sensorConfig = create_sensor(mqtt_client, deviceId, devDisplayName, 'sensor', 'mac_address', 'folder-key-network', device["dev_MAC"])
@@ -387,14 +459,15 @@ def mqtt_start(db):
sensorConfig = create_sensor(mqtt_client, deviceId, devDisplayName, 'sensor', 'first_connection', 'calendar-start', device["dev_MAC"]) sensorConfig = create_sensor(mqtt_client, deviceId, devDisplayName, 'sensor', 'first_connection', 'calendar-start', device["dev_MAC"])
sensorConfig = create_sensor(mqtt_client, deviceId, devDisplayName, 'sensor', 'last_connection', 'calendar-end', device["dev_MAC"]) sensorConfig = create_sensor(mqtt_client, deviceId, devDisplayName, 'sensor', 'last_connection', 'calendar-end', device["dev_MAC"])
devJson = { devJson = {
"last_ip": device["dev_LastIP"], "last_ip": device["dev_LastIP"],
"is_new": str(device["dev_NewDevice"]), "is_new": str(device["dev_NewDevice"]),
"vendor": sanitize_string(device["dev_Vendor"]), "vendor": sanitize_string(device["dev_Vendor"]),
"mac_address": str(device["dev_MAC"]), "mac_address": str(device["dev_MAC"]),
"last_connection": str(device["dev_LastConnection"]), "model": devDisplayName,
"first_connection": str(device["dev_FirstConnection"]) "last_connection": prepTimeStamp(str(device["dev_LastConnection"])),
} "first_connection": prepTimeStamp(str(device["dev_FirstConnection"])) }
# bulk update device sensors in home assistant # bulk update device sensors in home assistant
publish_mqtt(mqtt_client, sensorConfig.state_topic, devJson) publish_mqtt(mqtt_client, sensorConfig.state_topic, devJson)
@@ -444,8 +517,24 @@ def to_binary_sensor(input):
result = "ON" result = "ON"
return result return result
# -------------------------------------
# Convert to format that is interpretable by Home Assistant
def prepTimeStamp(datetime_str):
try:
# Attempt to parse the input string to ensure it's a valid datetime
parsed_datetime = datetime.fromisoformat(datetime_str)
# If the parsed datetime is naive (i.e., does not contain timezone info), add UTC timezone
if parsed_datetime.tzinfo is None:
parsed_datetime = parsed_datetime.replace(tzinfo=conf.tz)
except ValueError:
mylog('verbose', [f"[{pluginName}] Timestamp conversion failed of string '{datetime_str}'"])
# Use the current time if the input format is invalid
parsed_datetime = timeNowTZ() # Assuming this function returns the current time with timezone
# Convert to the required format with 'T' between date and time and ensure the timezone is included
return parsed_datetime.isoformat() # This will include the timezone offset
# -------------INIT--------------------- # -------------INIT---------------------
if __name__ == '__main__': if __name__ == '__main__':

View File

@@ -19,7 +19,7 @@ from plugin_utils import get_plugins_configs, decode_and_rename_files
from logger import mylog from logger import mylog
from const import pluginsPath, fullDbPath from const import pluginsPath, fullDbPath
from helper import timeNowTZ, get_setting_value from helper import timeNowTZ, get_setting_value
from cryptography import encrypt_data from crypto_utils import encrypt_data
from notification import write_notification from notification import write_notification
import conf import conf
from pytz import timezone from pytz import timezone
@@ -151,10 +151,10 @@ def main():
write_notification(message, 'info', timeNowTZ()) write_notification(message, 'info', timeNowTZ())
# Process any received data for the Device DB table # Process any received data for the Device DB table (ONLY JSON)
# Create the file path # Create the file path
# Decode files, rename them, and get the list of files # Get all "last_result" files from the sync folder, decode, rename them, and get the list of files
files_to_process = decode_and_rename_files(file_dir, file_prefix) files_to_process = decode_and_rename_files(file_dir, file_prefix)
if len(files_to_process) > 0: if len(files_to_process) > 0:
@@ -193,6 +193,16 @@ def main():
unique_mac_addresses.add(device['dev_MAC']) unique_mac_addresses.add(device['dev_MAC'])
device_data.append(device) device_data.append(device)
# Rename the file to "processed_" + current name
new_file_name = f"processed_{file_name}"
new_file_path = os.path.join(file_dir, new_file_name)
# Overwrite if the new file already exists
if os.path.exists(new_file_path):
os.remove(new_file_path)
os.rename(file_path, new_file_path)
if len(device_data) > 0: if len(device_data) > 0:
# Retrieve existing dev_MAC values from the Devices table # Retrieve existing dev_MAC values from the Devices table
placeholders = ', '.join('?' for _ in unique_mac_addresses) placeholders = ', '.join('?' for _ in unique_mac_addresses)
@@ -244,6 +254,9 @@ def main():
mylog('verbose', [message]) mylog('verbose', [message])
write_notification(message, 'info', timeNowTZ()) write_notification(message, 'info', timeNowTZ())
# Commit and close the connection # Commit and close the connection
conn.commit() conn.commit()
conn.close() conn.close()
@@ -292,7 +305,7 @@ def get_data(api_token, node_url):
api_endpoint = f"{node_url}/plugins/sync/hub.php" api_endpoint = f"{node_url}/plugins/sync/hub.php"
response = requests.get(api_endpoint, headers=headers) response = requests.get(api_endpoint, headers=headers)
# mylog('verbose', [f'[{pluginName}] response: "{response}"']) # mylog('verbose', [f'[{pluginName}] response: "{response.text}"'])
if response.status_code == 200: if response.status_code == 200:
try: try:

View File

@@ -253,8 +253,8 @@
] ]
}, },
"maxLength": 50, "maxLength": 50,
"default_value": ["online", "offline", "archived"], "default_value": ["online", "down", "offline", "archived"],
"options": ["online", "offline", "archived"], "options": ["online", "down", "offline", "archived"],
"localized": [], "localized": [],
"name": [ "name": [
{ {

View File

@@ -246,9 +246,6 @@ function getData(){
generateTabs() generateTabs()
// hide spinning icon
hideSpinner()
}); });
}); });
}); });
@@ -259,6 +256,8 @@ function getData(){
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
function generateTabs() function generateTabs()
{ {
showSpinner()
activetab = 'active' activetab = 'active'
// clear previous headers data // clear previous headers data
@@ -413,6 +412,8 @@ function generateTabs()
</table> </table>
<div class="plugin-obj-purge"> <div class="plugin-obj-purge">
<button class="btn btn-primary" onclick="purgeAll('${prefix}', 'Plugins_Objects' )"><?= lang('Plugins_DeleteAll');?></button> <button class="btn btn-primary" onclick="purgeAll('${prefix}', 'Plugins_Objects' )"><?= lang('Plugins_DeleteAll');?></button>
<button class="btn btn-danger " onclick="deleteListed('${prefix}', 'Plugins_Objects' )"><?= lang('Plugins_Obj_DeleteListed');?></button>
</div> </div>
</div> </div>
<div id="eventsTarget_${prefix}" class="tab-pane"> <div id="eventsTarget_${prefix}" class="tab-pane">
@@ -492,6 +493,9 @@ function initTabs() {
} }
} }
}); });
// hide spinning icon
hideSpinner()
} }
@@ -564,15 +568,18 @@ function purgeAllExecute() {
} }
// -------------------------------------------------------- // --------------------------------------------------------
function purgeVisible() { function deleteListed(plugPrefix, dbTable) {
idArr = $(`#${plugPrefix} table[data-my-dbtable="${dbTable}"] tr[data-my-index]`).map(function(){return $(this).attr("data-my-index");}).get(); idArr = $(`#${plugPrefix} table[data-my-dbtable="${dbTable}"] tr[data-my-index]`).map(function(){return $(this).attr("data-my-index");}).get();
console.log(idArr);
$.ajax({ $.ajax({
method: "POST", method: "POST",
url: "php/server/dbHelper.php", url: "php/server/dbHelper.php",
data: { action: "delete", dbtable: dbTable, columnName: 'Index', id:idArr.toString() }, data: { action: "delete", dbtable: dbTable, columnName: 'Index', id:idArr.toString() },
success: function(data, textStatus) { success: function(data, textStatus) {
updateApi("plugins_objects")
showModalOk ('Result', data ); showModalOk ('Result', data );
} }
}) })

View File

@@ -14,7 +14,6 @@
<?php <?php
require 'php/templates/header.php'; require 'php/templates/header.php';
require 'php/templates/graph.php';
?> ?>
<!-- Page ------------------------------------------------------------------ --> <!-- Page ------------------------------------------------------------------ -->
@@ -128,19 +127,37 @@
<script src="js/graph_online_history.js"></script> <script src="js/graph_online_history.js"></script>
<script> <script>
var pia_js_online_history_time = [<?php pia_graph_devices_data($Pia_Graph_Device_Time); ?>]; $.get('api/table_online_history.json?nocache=' + Date.now(), function(res) {
var pia_js_online_history_ondev = [<?php pia_graph_devices_data($Pia_Graph_Device_Online); ?>]; // Extracting data from the JSON response
var pia_js_online_history_dodev = [<?php pia_graph_devices_data($Pia_Graph_Device_Down); ?>]; var timeStamps = [];
var pia_js_online_history_ardev = [<?php pia_graph_devices_data($Pia_Graph_Device_Arch); ?>]; var onlineCounts = [];
var downCounts = [];
var offlineCounts = [];
var archivedCounts = [];
setTimeout(() => { res.data.forEach(function(entry) {
pia_draw_graph_online_history( var dateObj = new Date(entry.Scan_Date);
pia_js_online_history_time, var formattedTime = dateObj.toLocaleTimeString([], {hour: '2-digit', minute: '2-digit', hour12: false});
pia_js_online_history_ondev,
pia_js_online_history_dodev,
pia_js_online_history_ardev);
}, 500);
timeStamps.push(formattedTime);
onlineCounts.push(entry.Online_Devices);
downCounts.push(entry.Down_Devices);
offlineCounts.push(entry.Offline_Devices);
archivedCounts.push(entry.Archived_Devices);
});
// Call your presenceOverTime function after data is ready
presenceOverTime(
timeStamps,
onlineCounts,
offlineCounts,
archivedCounts,
downCounts
);
}).fail(function() {
// Handle any errors in fetching the data
console.error('Error fetching online history data.');
});
</script> </script>
<!-- /.row --> <!-- /.row -->
@@ -239,7 +256,7 @@ function initializeCalendar () {
center : 'title', center : 'title',
right : 'timelineYear,timelineMonth,timelineWeek,timelineDay' right : 'timelineYear,timelineMonth,timelineWeek,timelineDay'
}, },
defaultView : 'timelineMonth', defaultView : 'timelineWeek',
height : 'auto', height : 'auto',
firstDay : 1, firstDay : 1,
allDaySlot : false, allDaySlot : false,
@@ -389,6 +406,33 @@ function getDevicesPresence (status) {
default: tableTitle = '<?= lang('Presence_Shortcut_Devices');?>'; color = 'gray'; break; default: tableTitle = '<?= lang('Presence_Shortcut_Devices');?>'; color = 'gray'; break;
} }
period = "7 days"
// Calculate startDate and endDate based on the period
let startDate = "";
let endDate = new Date().toISOString().slice(0, 10); // Today's date in ISO format (YYYY-MM-DD)
// Calculate startDate based on period
switch (period) {
case "7 days":
startDate = new Date();
startDate.setDate(startDate.getDate() - 7); // Subtract 7 days
startDate = startDate.toISOString().slice(0, 10); // Convert to ISO format
break;
case "1 month":
startDate = new Date();
startDate.setMonth(startDate.getMonth() - 1); // Subtract 1 month
startDate = startDate.toISOString().slice(0, 10); // Convert to ISO format
break;
case "1 year":
startDate = new Date();
startDate.setFullYear(startDate.getFullYear() - 1); // Subtract 1 year
startDate = startDate.toISOString().slice(0, 10); // Convert to ISO format
break;
default:
console.error("Invalid period selected");
}
// Set title and color // Set title and color
$('#tableDevicesTitle')[0].className = 'box-title text-'+ color; $('#tableDevicesTitle')[0].className = 'box-title text-'+ color;
$('#tableDevicesBox')[0].className = 'box box-'+ color; $('#tableDevicesBox')[0].className = 'box box-'+ color;
@@ -399,7 +443,7 @@ function getDevicesPresence (status) {
$('#calendar').fullCalendar ('refetchResources'); $('#calendar').fullCalendar ('refetchResources');
$('#calendar').fullCalendar('removeEventSources'); $('#calendar').fullCalendar('removeEventSources');
$('#calendar').fullCalendar('addEventSource', { url: 'php/server/events.php?action=getEventsCalendar' }); $('#calendar').fullCalendar('addEventSource', { url: `php/server/events.php?period=${period}&start=${startDate}&end=${endDate}&action=getEventsCalendar` });
}; };
</script> </script>

View File

@@ -713,11 +713,16 @@ $settingsJSON_DB = json_encode($settings, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX
settingsArray.push([prefix, setCodeName, dataType, value]); settingsArray.push([prefix, setCodeName, dataType, value]);
} else if (dataType === 'boolean') { } else if (inputType === 'checkbox') {
value = $(`#${setCodeName}`).is(':checked') ? 1 : 0; value = $(`#${setCodeName}`).is(':checked') ? 1 : 0;
value = applyTransformers(value, transformers);
if(dataType === "boolean")
{
value = value == 1 ? "True" : "False";
}
value = applyTransformers(value, transformers);
settingsArray.push([prefix, setCodeName, dataType, value]); settingsArray.push([prefix, setCodeName, dataType, value]);
} else if (dataType === "array" ) { } else if (dataType === "array" ) {
@@ -753,10 +758,11 @@ $settingsJSON_DB = json_encode($settings, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX
} else { } else {
console.error(`[saveSettings] Couldn't determnine how to handle (setCodeName|dataType|inputType):(${setCodeName}|${dataType}|${inputType})`); console.error(`[saveSettings] Couldn't determine how to handle (setCodeName|dataType|inputType):(${setCodeName}|${dataType}|${inputType})`);
value = $('#' + setCodeName).val(); value = $('#' + setCodeName).val();
value = applyTransformers(value, transformers); value = applyTransformers(value, transformers);
console.error(`[saveSettings] Saving value "${value}"`);
settingsArray.push([prefix, setCodeName, dataType, value]); settingsArray.push([prefix, setCodeName, dataType, value]);
} }
}); });
@@ -789,11 +795,13 @@ $settingsJSON_DB = json_encode($settings, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX
clearCache() clearCache()
} else{ } else{
// something went wrong // something went wrong
// write_notification(data, 'interrupt') write_notification("[Important] DO NOT REFERSH the page. Open the browser DEV console (F12). Please take a screenshot of it. Submit it (with the nginx and php error logs) as a new issue here: https://github.com/jokob-sk/NetAlertX/issues", 'interrupt')
write_notification("Please screenshot the next popup (or check Monitoring > Notifications), dev console (F12) and submit it as a new issue here: https://github.com/jokob-sk/NetAlertX/issues", 'interrupt')
console.log("🔽");
console.log(settingsArray); console.log(settingsArray);
console.log(JSON.stringify(settingsArray)); console.log(JSON.stringify(settingsArray));
write_notification(JSON.stringify(settingsArray), 'interrupt') console.log(data);
console.log("🔼");
} }
} }
}); });

View File

@@ -30,5 +30,5 @@ source myenv/bin/activate
update-alternatives --install /usr/bin/python python /usr/bin/python3 10 update-alternatives --install /usr/bin/python python /usr/bin/python3 10
# install packages thru pip3 # install packages thru pip3
pip3 install netifaces tplink-omada-client pycryptodome requests paho-mqtt scapy cron-converter pytz json2table dhcp-leases pyunifi speedtest-cli chardet python-nmap dnspython librouteros cryptography pip3 install netifaces tplink-omada-client pycryptodome requests paho-mqtt scapy cron-converter pytz json2table dhcp-leases pyunifi speedtest-cli chardet python-nmap dnspython librouteros

View File

@@ -3,7 +3,7 @@ import json
# Register NetAlertX modules # Register NetAlertX modules
import conf import conf
from const import (apiPath, sql_appevents, sql_devices_all, sql_events_pending_alert, sql_settings, sql_plugins_events, sql_plugins_history, sql_plugins_objects,sql_language_strings, sql_notifications_all) from const import (apiPath, sql_appevents, sql_devices_all, sql_events_pending_alert, sql_settings, sql_plugins_events, sql_plugins_history, sql_plugins_objects,sql_language_strings, sql_notifications_all, sql_online_history)
from logger import mylog from logger import mylog
from helper import write_file from helper import write_file
@@ -32,6 +32,7 @@ def update_api(db, all_plugins, isNotification = False, updateOnlyDataSources =
["plugins_objects", sql_plugins_objects], ["plugins_objects", sql_plugins_objects],
["plugins_language_strings", sql_language_strings], ["plugins_language_strings", sql_language_strings],
["notifications", sql_notifications_all], ["notifications", sql_notifications_all],
["online_history", sql_online_history],
["custom_endpoint", conf.API_CUSTOM_SQL], ["custom_endpoint", conf.API_CUSTOM_SQL],
] ]

View File

@@ -38,6 +38,7 @@ sql_settings = "SELECT * FROM Settings"
sql_plugins_objects = "SELECT * FROM Plugins_Objects" sql_plugins_objects = "SELECT * FROM Plugins_Objects"
sql_language_strings = "SELECT * FROM Plugins_Language_Strings" sql_language_strings = "SELECT * FROM Plugins_Language_Strings"
sql_notifications_all = "SELECT * FROM Notifications" sql_notifications_all = "SELECT * FROM Notifications"
sql_online_history = "SELECT * FROM Online_History"
sql_plugins_events = "SELECT * FROM Plugins_Events" sql_plugins_events = "SELECT * FROM Plugins_Events"
sql_plugins_history = "SELECT * FROM Plugins_History ORDER BY DateTimeChanged DESC" sql_plugins_history = "SELECT * FROM Plugins_History ORDER BY DateTimeChanged DESC"
sql_new_devices = """SELECT * FROM ( sql_new_devices = """SELECT * FROM (

View File

@@ -1,45 +1,9 @@
# from cryptography.fernet import Fernet
from Crypto.Cipher import AES from Crypto.Cipher import AES
from Crypto.Util.Padding import pad, unpad from Crypto.Util.Padding import pad, unpad
import base64 import base64
import hashlib import hashlib
# FERET - Requires C compiler-------------------------------------------------------------------------
# def prepare_key(encryption_key):
# if(len(encryption_key) < 32):
# encryption_key = (int((32 / len(encryption_key)))+1 )*encryption_key
# key_bytearray = bytearray(encryption_key[:32], 'ASCII')
# return base64.urlsafe_b64encode(key_bytearray)
# def encrypt_data(data, encryption_key):
# fernet = Fernet(prepare_key(encryption_key))
# # then use the Fernet class instance
# # to encrypt the string string must
# # be encoded to byte string before encryption
# encrypted_data = fernet.encrypt(data.encode())
# return encrypted_data
# def decrypt_data(data, encryption_key):
# fernet = Fernet(prepare_key(encryption_key))
# # decrypt the encrypted string with the
# # Fernet instance of the key,
# # that was used for encrypting the string
# # encoded byte string is returned by decrypt method,
# # so decode it to string with decode methods
# decrypted_data = fernet.decrypt(data).decode()
# return decrypted_data
# SIMPLE CRYPT - requeres C compiler ------------------------------------------------------------------------- # SIMPLE CRYPT - requeres C compiler -------------------------------------------------------------------------
# def prepare_key(encryption_key): # def prepare_key(encryption_key):

View File

@@ -115,6 +115,18 @@ class DB():
); );
""") """)
# Offline_Devices column
Offline_Devices_missing = self.sql.execute ("""
SELECT COUNT(*) AS CNTREC FROM pragma_table_info('Online_History') WHERE name='Offline_Devices'
""").fetchone()[0] == 0
if Offline_Devices_missing :
mylog('verbose', ["[upgradeDB] Adding Offline_Devices to the Online_History table"])
self.sql.execute("""
ALTER TABLE "Online_History" ADD "Offline_Devices" INTEGER
""")
# ------------------------------------------------------------------------- # -------------------------------------------------------------------------
# Alter Devices table # Alter Devices table
# ------------------------------------------------------------------------- # -------------------------------------------------------------------------
@@ -278,8 +290,8 @@ class DB():
Plugin, Plugin,
Object_PrimaryID, Object_PrimaryID,
Object_SecondaryID, Object_SecondaryID,
DateTimeCreated, DateTimeCreated,
DateTimeChanged, DateTimeChanged,
Watched_Value1, Watched_Value1,
Watched_Value2, Watched_Value2,
Watched_Value3, Watched_Value3,
@@ -293,8 +305,8 @@ class DB():
'NMAP' AS Plugin, 'NMAP' AS Plugin,
MAC AS Object_PrimaryID, MAC AS Object_PrimaryID,
Port AS Object_SecondaryID, Port AS Object_SecondaryID,
Time AS DateTimeCreated, Time AS DateTimeCreated,
DATETIME('now') AS DateTimeChanged, DATETIME('now') AS DateTimeChanged,
State AS Watched_Value1, State AS Watched_Value1,
Service AS Watched_Value2, Service AS Watched_Value2,
'' AS Watched_Value3, '' AS Watched_Value3,
@@ -644,22 +656,3 @@ def get_all_devices(db):
#------------------------------------------------------------------------------- #-------------------------------------------------------------------------------
#-------------------------------------------------------------------------------
def insertOnlineHistory(db):
sql = db.sql #TO-DO
startTime = timeNowTZ()
# Add to History
History_All = db.read("SELECT * FROM Devices")
History_All_Devices = len(History_All)
History_Archived = db.read("SELECT * FROM Devices WHERE dev_Archived = 1")
History_Archived_Devices = len(History_Archived)
History_Online = db.read("SELECT * FROM Devices WHERE dev_PresentLastScan = 1")
History_Online_Devices = len(History_Online)
History_Offline_Devices = History_All_Devices - History_Archived_Devices - History_Online_Devices
sql.execute ("INSERT INTO Online_History (Scan_Date, Online_Devices, Down_Devices, All_Devices, Archived_Devices) "+
"VALUES ( ?, ?, ?, ?, ?)", (startTime, History_Online_Devices, History_Offline_Devices, History_All_Devices, History_Archived_Devices ) )
db.commitDB()

View File

@@ -3,9 +3,9 @@
import io import io
import sys import sys
import datetime import datetime
# from datetime import strptime
import os import os
import re import re
import unicodedata
import subprocess import subprocess
import pytz import pytz
from pytz import timezone from pytz import timezone
@@ -812,6 +812,14 @@ def sanitize_SQL_input(val):
return '' return ''
return val.replace("'", "_") return val.replace("'", "_")
#-------------------------------------------------------------------------------
# Function to normalize the string and remove diacritics
def normalize_string(text):
# Normalize the text to 'NFD' to separate base characters and diacritics
normalized_text = unicodedata.normalize('NFD', text)
# Filter out diacritics and unwanted characters
return ''.join(c for c in normalized_text if unicodedata.category(c) != 'Mn')
#------------------------------------------------------------------------------- #-------------------------------------------------------------------------------
def generate_mac_links (html, deviceUrl): def generate_mac_links (html, deviceUrl):

View File

@@ -2,7 +2,7 @@
import conf import conf
from database import insertOnlineHistory
from device import create_new_devices, print_scan_stats, save_scanned_devices, update_devices_data_from_scan from device import create_new_devices, print_scan_stats, save_scanned_devices, update_devices_data_from_scan
from helper import timeNowTZ from helper import timeNowTZ
from logger import mylog from logger import mylog
@@ -233,3 +233,44 @@ def insert_events (db):
WHERE dev_MAC = cur_MAC WHERE dev_MAC = cur_MAC
AND dev_LastIP <> cur_IP """ ) AND dev_LastIP <> cur_IP """ )
mylog('debug','[Events] - Events end') mylog('debug','[Events] - Events end')
#-------------------------------------------------------------------------------
def insertOnlineHistory(db):
sql = db.sql # TO-DO: Implement sql object
scanTimestamp = timeNowTZ()
# Query to fetch all relevant device counts in one go
query = """
SELECT
COUNT(*) AS allDevics,
SUM(CASE WHEN dev_Archived = 1 THEN 1 ELSE 0 END) AS archivedDevices,
SUM(CASE WHEN dev_PresentLastScan = 1 THEN 1 ELSE 0 END) AS onlineDevices,
SUM(CASE WHEN dev_PresentLastScan = 0 AND dev_AlertDeviceDown = 1 THEN 1 ELSE 0 END) AS downDevices
FROM Devices
"""
deviceCounts = db.read(query)[0] # Assuming db.read returns a list of rows, take the first (and only) row
allDevics = deviceCounts['allDevics']
archivedDevices = deviceCounts['archivedDevices']
onlineDevices = deviceCounts['onlineDevices']
downDevices = deviceCounts['downDevices']
offlineDevices = allDevics - archivedDevices - onlineDevices
# Prepare the insert query using parameterized inputs
insert_query = """
INSERT INTO Online_History (Scan_Date, Online_Devices, Down_Devices, All_Devices, Archived_Devices, Offline_Devices)
VALUES (?, ?, ?, ?, ?, ?)
"""
mylog('debug', f'[Presence graph] Sql query: {insert_query} with values: {scanTimestamp}, {onlineDevices}, {downDevices}, {allDevics}, {archivedDevices}, {offlineDevices}')
# Insert the gathered data into the history table
sql.execute(insert_query, (scanTimestamp, onlineDevices, downDevices, allDevics, archivedDevices, offlineDevices))
db.commitDB()

View File

@@ -129,7 +129,7 @@ def run_plugin_scripts(db, all_plugins, runType, pluginsState = plugins_state())
if shouldRun: if shouldRun:
# Header # Header
updateState(f"Plugins: {prefix}") updateState(f"Plugin: {prefix}")
print_plugin_info(plugin, ['display_name']) print_plugin_info(plugin, ['display_name'])
mylog('debug', ['[Plugins] CMD: ', get_plugin_setting_obj(plugin, "CMD")["value"]]) mylog('debug', ['[Plugins] CMD: ', get_plugin_setting_obj(plugin, "CMD")["value"]])
@@ -226,13 +226,15 @@ def execute_plugin(db, all_plugins, plugin, pluginsState = plugins_state() ):
file_dir = os.path.join(pluginsPath, plugin["code_name"]) file_dir = os.path.join(pluginsPath, plugin["code_name"])
file_prefix = 'last_result' file_prefix = 'last_result'
# Decode files, rename them, and get the list of files # Decode files, rename them, and get the list of files, this will return all files starting with the prefix, even if they are not encoded
files_to_process = decode_and_rename_files(file_dir, file_prefix) files_to_process = decode_and_rename_files(file_dir, file_prefix)
for filename in files_to_process: for filename in files_to_process:
full_path = os.path.join(file_dir, filename) full_path = os.path.join(file_dir, filename)
mylog('debug', [f'[Plugins] Processing file "{full_path}"'])
# Open the decrypted file and process its contents # Open the decrypted file and process its contents
with open(full_path, 'r') as f: with open(full_path, 'r') as f:
newLines = f.read().split('\n') newLines = f.read().split('\n')

View File

@@ -5,7 +5,7 @@ import conf
from logger import mylog from logger import mylog
from const import pluginsPath, logPath, apiPath from const import pluginsPath, logPath, apiPath
from helper import timeNowTZ, updateState, get_file_content, write_file, get_setting, get_setting_value from helper import timeNowTZ, updateState, get_file_content, write_file, get_setting, get_setting_value
from cryptography import decrypt_data from crypto_utils import decrypt_data
module_name = 'Plugin utils' module_name = 'Plugin utils'