mirror of
https://github.com/jokob-sk/NetAlertX.git
synced 2026-04-06 18:21:46 -07:00
Compare commits
97 Commits
v24.9.12
...
ac259b1fab
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ac259b1fab | ||
|
|
14996d6582 | ||
|
|
d44744657e | ||
|
|
615e5e4084 | ||
|
|
dd948b5e63 | ||
|
|
97a5cb6737 | ||
|
|
c6fe09d366 | ||
|
|
040f2792e4 | ||
|
|
d1d6d7f1ec | ||
|
|
33c16c4d00 | ||
|
|
cc8b57e790 | ||
|
|
57d8e97b60 | ||
|
|
91ad39e991 | ||
|
|
15ed621748 | ||
|
|
50304fd63b | ||
|
|
90689e5c69 | ||
|
|
5f4b2f114c | ||
|
|
e72a87ab43 | ||
|
|
044de61ab5 | ||
|
|
e5d835cfa9 | ||
|
|
e2d84a1885 | ||
|
|
e648acde5c | ||
|
|
a17e066f34 | ||
|
|
0bdc4c4ed1 | ||
|
|
9144fd0c3a | ||
|
|
02077d4654 | ||
|
|
e3b2039257 | ||
|
|
1fa38472e1 | ||
|
|
1e197ae749 | ||
|
|
7731a01f3c | ||
|
|
3ce08ba97d | ||
|
|
c58bbf21b1 | ||
|
|
3780e47117 | ||
|
|
e8f353024f | ||
|
|
7308797314 | ||
|
|
6e36f7d7aa | ||
|
|
8d3a4500e2 | ||
|
|
40d6bdc2b2 | ||
|
|
b7b2e0bc65 | ||
|
|
081d0f3400 | ||
|
|
a7f4565954 | ||
|
|
15a7779d6e | ||
|
|
2784f2ebeb | ||
|
|
d46046beea | ||
|
|
6233f4d646 | ||
|
|
31411e0a14 | ||
|
|
8d824af3bd | ||
|
|
f05f0d625a | ||
|
|
2fec3b6607 | ||
|
|
f285a28887 | ||
|
|
11cb47fada | ||
|
|
d8b413b5e7 | ||
|
|
656bba7ff7 | ||
|
|
a2cf8c1167 | ||
|
|
737cb07403 | ||
|
|
3febbc21cb | ||
|
|
7e14fae29c | ||
|
|
a16fe4561b | ||
|
|
f2afe9d681 | ||
|
|
f8c0a5a1ef | ||
|
|
631e992411 | ||
|
|
feafaff218 | ||
|
|
f6a06842cc | ||
|
|
0cc3ede86c | ||
|
|
aa277136c6 | ||
|
|
82ccb0c0b6 | ||
|
|
30750a9449 | ||
|
|
5278af48c5 | ||
|
|
77f19c3575 | ||
|
|
10df7363d6 | ||
|
|
06e49f7adb | ||
|
|
9fcbd9d64e | ||
|
|
c6888a79fd | ||
|
|
ef458903b7 | ||
|
|
b544734209 | ||
|
|
815810dc7a | ||
|
|
552d79eee8 | ||
|
|
2f70e2e8d8 | ||
|
|
4a20b66c92 | ||
|
|
36cec0ab38 | ||
|
|
6bde0f9084 | ||
|
|
f64ef5b881 | ||
|
|
1895f68233 | ||
|
|
d2fe53bc81 | ||
|
|
e9e45c34ae | ||
|
|
064a51acee | ||
|
|
7340ce6da2 | ||
|
|
703885308a | ||
|
|
71856b49a4 | ||
|
|
86c7d26107 | ||
|
|
d858f4f9d0 | ||
|
|
aefe470d31 | ||
|
|
99fb60c1b5 | ||
|
|
ec37e4d71b | ||
|
|
e240821d6c | ||
|
|
632e441dda | ||
|
|
24f7935891 |
@@ -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
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
[](https://github.com/jokob-sk/NetAlertX)
|
[](https://github.com/jokob-sk/NetAlertX)
|
||||||
[](https://hub.docker.com/r/jokobsk/netalertx)
|
[](https://hub.docker.com/r/jokobsk/netalertx)
|
||||||
[](https://hub.docker.com/r/jokobsk/netalertx)
|
[](https://hub.docker.com/r/jokobsk/netalertx)
|
||||||
[](https://github.com/jokob-sk/NetAlertX/releases)
|
[](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 -->
|
||||||
|
|
||||||
|
|||||||
@@ -41,7 +41,8 @@ docker run -d --rm --network=host \
|
|||||||
| `PORT` |Port of the web interface | `20211` |
|
| `PORT` |Port of the web interface | `20211` |
|
||||||
| `LISTEN_ADDR` |Set the specific IP Address for the listener address for the nginx webserver (web interface). This could be useful when using multiple subnets to hide the web interface from all untrusted networks. | `0.0.0.0` |
|
| `LISTEN_ADDR` |Set the specific IP Address for the listener address for the nginx webserver (web interface). This could be useful when using multiple subnets to hide the web interface from all untrusted networks. | `0.0.0.0` |
|
||||||
|`TZ` |Time zone to display stats correctly. Find your time zone [here](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones) | `Europe/Berlin` |
|
|`TZ` |Time zone to display stats correctly. Find your time zone [here](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones) | `Europe/Berlin` |
|
||||||
|`ALWAYS_FRESH_INSTALL` | Setting to `true` will delete the content of the `/db` & `/config` folders. For testing purposes. Can be coupled with [watchtower](https://github.com/containrrr/watchtower) to have an always freshly installed `netalertx`/`-dev` image. | `N/A` |
|
|`APP_CONF_OVERRIDE` | JSON override for settings, e.g. `{"SCAN_SUBNETS":"['192.168.1.0/24 --interface=eth1']","UI_dark_mode":"True"}` (Experimental 🧪) | `N/A` |
|
||||||
|
|`ALWAYS_FRESH_INSTALL` | If `true` will delete the content of the `/db` & `/config` folders. For testing purposes. Can be coupled with [watchtower](https://github.com/containrrr/watchtower) to have an always freshly installed `netalertx`/`netalertx-dev` image. | `N/A` |
|
||||||
|
|
||||||
### Docker paths
|
### Docker paths
|
||||||
|
|
||||||
|
|||||||
275
docs/AUTHELIA.md
Executable file
275
docs/AUTHELIA.md
Executable file
@@ -0,0 +1,275 @@
|
|||||||
|
(DRAFT) Authelia support
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
theme: dark
|
||||||
|
|
||||||
|
default_2fa_method: "totp"
|
||||||
|
|
||||||
|
server:
|
||||||
|
address: 0.0.0.0:9091
|
||||||
|
endpoints:
|
||||||
|
enable_expvars: false
|
||||||
|
enable_pprof: false
|
||||||
|
authz:
|
||||||
|
forward-auth:
|
||||||
|
implementation: 'ForwardAuth'
|
||||||
|
authn_strategies:
|
||||||
|
- name: 'HeaderAuthorization'
|
||||||
|
schemes:
|
||||||
|
- 'Basic'
|
||||||
|
- name: 'CookieSession'
|
||||||
|
ext-authz:
|
||||||
|
implementation: 'ExtAuthz'
|
||||||
|
authn_strategies:
|
||||||
|
- name: 'HeaderAuthorization'
|
||||||
|
schemes:
|
||||||
|
- 'Basic'
|
||||||
|
- name: 'CookieSession'
|
||||||
|
auth-request:
|
||||||
|
implementation: 'AuthRequest'
|
||||||
|
authn_strategies:
|
||||||
|
- name: 'HeaderAuthRequestProxyAuthorization'
|
||||||
|
schemes:
|
||||||
|
- 'Basic'
|
||||||
|
- name: 'CookieSession'
|
||||||
|
legacy:
|
||||||
|
implementation: 'Legacy'
|
||||||
|
authn_strategies:
|
||||||
|
- name: 'HeaderLegacy'
|
||||||
|
- name: 'CookieSession'
|
||||||
|
disable_healthcheck: false
|
||||||
|
tls:
|
||||||
|
key: ""
|
||||||
|
certificate: ""
|
||||||
|
client_certificates: []
|
||||||
|
headers:
|
||||||
|
csp_template: ""
|
||||||
|
|
||||||
|
log:
|
||||||
|
## Level of verbosity for logs: info, debug, trace.
|
||||||
|
level: info
|
||||||
|
|
||||||
|
###############################################################
|
||||||
|
# The most important section
|
||||||
|
###############################################################
|
||||||
|
access_control:
|
||||||
|
## Default policy can either be 'bypass', 'one_factor', 'two_factor' or 'deny'.
|
||||||
|
default_policy: deny
|
||||||
|
networks:
|
||||||
|
- name: internal
|
||||||
|
networks:
|
||||||
|
- '192.168.0.0/18'
|
||||||
|
- '10.10.10.0/8' # Zerotier
|
||||||
|
- name: private
|
||||||
|
networks:
|
||||||
|
- '172.16.0.0/12'
|
||||||
|
rules:
|
||||||
|
- networks:
|
||||||
|
- private
|
||||||
|
domain:
|
||||||
|
- '*'
|
||||||
|
policy: bypass
|
||||||
|
- networks:
|
||||||
|
- internal
|
||||||
|
domain:
|
||||||
|
- '*'
|
||||||
|
policy: bypass
|
||||||
|
- domain:
|
||||||
|
# exclude itself from auth, should not happen as we use Traefik middleware on a case-by-case screnario
|
||||||
|
- 'auth.MYDOMAIN1.TLD'
|
||||||
|
- 'authelia.MYDOMAIN1.TLD'
|
||||||
|
- 'auth.MYDOMAIN2.TLD'
|
||||||
|
- 'authelia.MYDOMAIN2.TLD'
|
||||||
|
policy: bypass
|
||||||
|
- domain:
|
||||||
|
#All subdomains match
|
||||||
|
- 'MYDOMAIN1.TLD'
|
||||||
|
- '*.MYDOMAIN1.TLD'
|
||||||
|
policy: two_factor
|
||||||
|
- domain:
|
||||||
|
# This will not work yet as Authelio does not support multi-domain authentication
|
||||||
|
- 'MYDOMAIN2.TLD'
|
||||||
|
- '*.MYDOMAIN2.TLD'
|
||||||
|
policy: two_factor
|
||||||
|
|
||||||
|
|
||||||
|
############################################################
|
||||||
|
identity_validation:
|
||||||
|
reset_password:
|
||||||
|
jwt_secret: "[REDACTED]"
|
||||||
|
|
||||||
|
identity_providers:
|
||||||
|
oidc:
|
||||||
|
enable_client_debug_messages: true
|
||||||
|
enforce_pkce: public_clients_only
|
||||||
|
hmac_secret: [REDACTED]
|
||||||
|
lifespans:
|
||||||
|
authorize_code: 1m
|
||||||
|
id_token: 1h
|
||||||
|
refresh_token: 90m
|
||||||
|
access_token: 1h
|
||||||
|
cors:
|
||||||
|
endpoints:
|
||||||
|
- authorization
|
||||||
|
- token
|
||||||
|
- revocation
|
||||||
|
- introspection
|
||||||
|
- userinfo
|
||||||
|
allowed_origins:
|
||||||
|
- "*"
|
||||||
|
allowed_origins_from_client_redirect_uris: false
|
||||||
|
jwks:
|
||||||
|
- key: [REDACTED]
|
||||||
|
certificate_chain:
|
||||||
|
clients:
|
||||||
|
- client_id: portainer
|
||||||
|
client_name: Portainer
|
||||||
|
# generate secret with "authelia crypto hash generate pbkdf2 --random --random.length 32 --random.charset alphanumeric"
|
||||||
|
# Random Password: [REDACTED]
|
||||||
|
# Digest: [REDACTED]
|
||||||
|
client_secret: [REDACTED]
|
||||||
|
token_endpoint_auth_method: 'client_secret_post'
|
||||||
|
public: false
|
||||||
|
authorization_policy: two_factor
|
||||||
|
consent_mode: pre-configured #explicit
|
||||||
|
pre_configured_consent_duration: '6M' #Must be re-authorised every 6 Months
|
||||||
|
scopes:
|
||||||
|
- openid
|
||||||
|
#- groups #Currently not supported in Authelia V
|
||||||
|
- email
|
||||||
|
- profile
|
||||||
|
redirect_uris:
|
||||||
|
- https://portainer.MYDOMAIN1.LTD
|
||||||
|
userinfo_signed_response_alg: none
|
||||||
|
|
||||||
|
- client_id: openproject
|
||||||
|
client_name: OpenProject
|
||||||
|
# generate secret with "authelia crypto hash generate pbkdf2 --random --random.length 32 --random.charset alphanumeric"
|
||||||
|
# Random Password: [REDACTED]
|
||||||
|
# Digest: [REDACTED]
|
||||||
|
client_secret: [REDACTED]
|
||||||
|
token_endpoint_auth_method: 'client_secret_basic'
|
||||||
|
public: false
|
||||||
|
authorization_policy: two_factor
|
||||||
|
consent_mode: pre-configured #explicit
|
||||||
|
pre_configured_consent_duration: '6M' #Must be re-authorised every 6 Months
|
||||||
|
scopes:
|
||||||
|
- openid
|
||||||
|
#- groups #Currently not supported in Authelia V
|
||||||
|
- email
|
||||||
|
- profile
|
||||||
|
redirect_uris:
|
||||||
|
- https://op.MYDOMAIN.TLD
|
||||||
|
#grant_types:
|
||||||
|
# - refresh_token
|
||||||
|
# - authorization_code
|
||||||
|
#response_types:
|
||||||
|
# - code
|
||||||
|
#response_modes:
|
||||||
|
# - form_post
|
||||||
|
# - query
|
||||||
|
# - fragment
|
||||||
|
userinfo_signed_response_alg: none
|
||||||
|
##################################################################
|
||||||
|
|
||||||
|
|
||||||
|
telemetry:
|
||||||
|
metrics:
|
||||||
|
enabled: false
|
||||||
|
address: tcp://0.0.0.0:9959
|
||||||
|
|
||||||
|
totp:
|
||||||
|
disable: false
|
||||||
|
issuer: authelia.com
|
||||||
|
algorithm: sha1
|
||||||
|
digits: 6
|
||||||
|
period: 30 ## The period in seconds a one-time password is valid for.
|
||||||
|
skew: 1
|
||||||
|
secret_size: 32
|
||||||
|
|
||||||
|
webauthn:
|
||||||
|
disable: false
|
||||||
|
timeout: 60s ## Adjust the interaction timeout for Webauthn dialogues.
|
||||||
|
display_name: Authelia
|
||||||
|
attestation_conveyance_preference: indirect
|
||||||
|
user_verification: preferred
|
||||||
|
|
||||||
|
ntp:
|
||||||
|
address: "pool.ntp.org"
|
||||||
|
version: 4
|
||||||
|
max_desync: 5s
|
||||||
|
disable_startup_check: false
|
||||||
|
disable_failure: false
|
||||||
|
|
||||||
|
authentication_backend:
|
||||||
|
password_reset:
|
||||||
|
disable: false
|
||||||
|
custom_url: ""
|
||||||
|
refresh_interval: 5m
|
||||||
|
file:
|
||||||
|
path: /config/users_database.yml
|
||||||
|
watch: true
|
||||||
|
password:
|
||||||
|
algorithm: argon2
|
||||||
|
argon2:
|
||||||
|
variant: argon2id
|
||||||
|
iterations: 3
|
||||||
|
memory: 65536
|
||||||
|
parallelism: 4
|
||||||
|
key_length: 32
|
||||||
|
salt_length: 16
|
||||||
|
|
||||||
|
password_policy:
|
||||||
|
standard:
|
||||||
|
enabled: false
|
||||||
|
min_length: 8
|
||||||
|
max_length: 0
|
||||||
|
require_uppercase: true
|
||||||
|
require_lowercase: true
|
||||||
|
require_number: true
|
||||||
|
require_special: true
|
||||||
|
## zxcvbn is a well known and used password strength algorithm. It does not have tunable settings.
|
||||||
|
zxcvbn:
|
||||||
|
enabled: false
|
||||||
|
min_score: 3
|
||||||
|
|
||||||
|
regulation:
|
||||||
|
max_retries: 3
|
||||||
|
find_time: 2m
|
||||||
|
ban_time: 5m
|
||||||
|
|
||||||
|
session:
|
||||||
|
name: authelia_session
|
||||||
|
secret: [REDACTED]
|
||||||
|
expiration: 60m
|
||||||
|
inactivity: 15m
|
||||||
|
cookies:
|
||||||
|
- domain: 'MYDOMAIN1.LTD'
|
||||||
|
authelia_url: 'https://auth.MYDOMAIN1.LTD'
|
||||||
|
name: 'authelia_session'
|
||||||
|
default_redirection_url: 'https://MYDOMAIN1.LTD'
|
||||||
|
- domain: 'MYDOMAIN2.LTD'
|
||||||
|
authelia_url: 'https://auth.MYDOMAIN2.LTD'
|
||||||
|
name: 'authelia_session_other'
|
||||||
|
default_redirection_url: 'https://MYDOMAIN2.LTD'
|
||||||
|
|
||||||
|
storage:
|
||||||
|
encryption_key: [REDACTED]
|
||||||
|
local:
|
||||||
|
path: /config/db.sqlite3
|
||||||
|
|
||||||
|
notifier:
|
||||||
|
disable_startup_check: true
|
||||||
|
smtp:
|
||||||
|
address: MYOTHERDOMAIN.LTD:465
|
||||||
|
timeout: 5s
|
||||||
|
username: "USER@DOMAIN"
|
||||||
|
password: "[REDACTED]"
|
||||||
|
sender: "Authelia <postmaster@MYOTHERDOMAIN.LTD>"
|
||||||
|
identifier: NAME@MYOTHERDOMAIN.LTD
|
||||||
|
subject: "[Authelia] {title}"
|
||||||
|
startup_check_address: postmaster@MYOTHERDOMAIN.LTD
|
||||||
|
|
||||||
|
```
|
||||||
@@ -8,7 +8,7 @@ There are 3 artifacts that can be used to backup the application:
|
|||||||
| File | Description | Limitations |
|
| File | Description | Limitations |
|
||||||
|-----------------------|-------------------------------|-------------------------------|
|
|-----------------------|-------------------------------|-------------------------------|
|
||||||
| `/db/app.db` | Database file(s) | The database file might be in an uncommitted state or corrupted |
|
| `/db/app.db` | Database file(s) | The database file might be in an uncommitted state or corrupted |
|
||||||
| `/config/app.conf` | Configuration file | Doesn't contain settings from the Maintenance section |
|
| `/config/app.conf` | Configuration file | Can be overridden with the [`APP_CONF_OVERRIDE` env variable](https://github.com/jokob-sk/NetAlertX/tree/main/dockerfiles#docker-environment-variables). |
|
||||||
| `/config/devices.csv` | CSV file containing device information | Doesn't contain historical data |
|
| `/config/devices.csv` | CSV file containing device information | Doesn't contain historical data |
|
||||||
|
|
||||||
## Data and backup storage
|
## Data and backup storage
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -518,6 +518,60 @@ Required attributes are:
|
|||||||
| (optional) `"override_value"` | Used to determine a user-defined override for the setting. Useful for template-based plugins, where you can choose to leave the current value or override it with the value defined in the setting. (Work in progress) |
|
| (optional) `"override_value"` | Used to determine a user-defined override for the setting. Useful for template-based plugins, where you can choose to leave the current value or override it with the value defined in the setting. (Work in progress) |
|
||||||
| (optional) `"events"` | Used to trigger the plugin. Usually used on the `RUN` setting. Not fully tested in all scenarios. Will show a play button next to the setting. After clicking, an event is generated for the backend in the `Parameters` database table to process the front-end event on the next run. |
|
| (optional) `"events"` | Used to trigger the plugin. Usually used on the `RUN` setting. Not fully tested in all scenarios. Will show a play button next to the setting. After clicking, an event is generated for the backend in the `Parameters` database table to process the front-end event on the next run. |
|
||||||
|
|
||||||
|
### UI Component Types Documentation
|
||||||
|
|
||||||
|
This section outlines the structure and types of UI components, primarily used to build HTML forms or interactive elements dynamically. Each UI component has a `"type"` which defines its structure, behavior, and rendering options.
|
||||||
|
|
||||||
|
#### UI Component JSON Structure
|
||||||
|
The UI component is defined as a JSON object containing a list of `elements`. Each element specifies how it should behave, with properties like `elementType`, `elementOptions`, and any associated `transformers` to modify the data. The example below demonstrates how a component with two elements (`span` and `select`) is structured:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"function": "dev_Icon",
|
||||||
|
"type": {
|
||||||
|
"dataType": "string",
|
||||||
|
"elements": [
|
||||||
|
{
|
||||||
|
"elementType": "span",
|
||||||
|
"elementOptions": [
|
||||||
|
{ "cssClasses": "input-group-addon iconPreview" },
|
||||||
|
{ "getStringKey": "Gen_SelectToPreview" },
|
||||||
|
{ "customId": "NEWDEV_dev_Icon_preview" }
|
||||||
|
],
|
||||||
|
"transformers": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"elementType": "select",
|
||||||
|
"elementHasInputValue": 1,
|
||||||
|
"elementOptions": [
|
||||||
|
{ "cssClasses": "col-xs-12" },
|
||||||
|
{
|
||||||
|
"onChange": "updateIconPreview(this)"
|
||||||
|
},
|
||||||
|
{ "customParams": "NEWDEV_dev_Icon,NEWDEV_dev_Icon_preview" }
|
||||||
|
],
|
||||||
|
"transformers": []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
### Rendering Logic
|
||||||
|
|
||||||
|
The code snippet provided demonstrates how the elements are iterated over to generate their corresponding HTML. Depending on the `elementType`, different HTML tags (like `<select>`, `<input>`, `<textarea>`, `<button>`, etc.) are created with the respective attributes such as `onChange`, `my-data-type`, and `class` based on the provided `elementOptions`. Events can also be attached to elements like buttons or select inputs.
|
||||||
|
|
||||||
|
### Key Element Types
|
||||||
|
|
||||||
|
- **`select`**: Renders a dropdown list. Additional options like `isMultiSelect` and event handlers (e.g., `onChange`) can be attached.
|
||||||
|
- **`input`**: Handles various types of input fields, including checkboxes, text, and others, with customizable attributes like `readOnly`, `placeholder`, etc.
|
||||||
|
- **`button`**: Generates clickable buttons with custom event handlers (`onClick`), icons, or labels.
|
||||||
|
- **`textarea`**: Creates a multi-line input box for text input.
|
||||||
|
- **`span`**: Used for inline text or content with customizable classes and data attributes.
|
||||||
|
|
||||||
|
Each element may also have associated events (e.g., running a scan or triggering a notification) defined under `Events`.
|
||||||
|
|
||||||
|
|
||||||
##### Supported settings `function` values
|
##### Supported settings `function` values
|
||||||
|
|
||||||
|
|||||||
@@ -63,6 +63,7 @@ There is also an in-app Help / FAQ section that should be answering frequently a
|
|||||||
|
|
||||||
- [Version history (legacy)](/docs/VERSIONS_HISTORY.md)
|
- [Version history (legacy)](/docs/VERSIONS_HISTORY.md)
|
||||||
- [Reverse proxy (Nginx, Apache, SWAG)](/docs/REVERSE_PROXY.md)
|
- [Reverse proxy (Nginx, Apache, SWAG)](/docs/REVERSE_PROXY.md)
|
||||||
|
- [Setting up Authelia](/docs/AUTHELIA.md) (DRAFT)
|
||||||
|
|
||||||
#### 👩💻For Developers👨💻
|
#### 👩💻For Developers👨💻
|
||||||
|
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
150
docs/SUBNETS.md
150
docs/SUBNETS.md
@@ -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).
|
||||||
|
|
||||||
> [!TIP]
|
`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:
|
||||||
> 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.
|
|
||||||
|
|
||||||
## Examples
|
`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]
|
||||||
|
> 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.
|
||||||
|
|
||||||
|
## 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.
|
||||||
> 
|
> 
|
||||||
|
|
||||||
* 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)).
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
> [!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
|
||||||
|
|
||||||
|
> 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.
|
||||||
|
|
||||||
|
**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.
|
||||||
|
|
||||||
|
**Approach 2 (working example):**
|
||||||
|
|
||||||
|
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, blocking VLAN-tagged packets. To fix this, follow these steps:
|
||||||
|
|
||||||
|
1. Run the following command in PowerShell on the Hyper-V machine:
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
Set-VMNetworkAdapterVlan -VMName <Docker VM Name> -Trunk -NativeVlanId 0 -AllowedVlanIdList "<comma separated list of vlans>"
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
#### VLANs on a Hyper-V setup
|
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:
|
||||||
|
|
||||||
> Community sourced content by [mscreations](https://github.com/mscreations) from this [discussion](https://github.com/jokob-sk/NetAlertX/discussions/404).
|
```yaml
|
||||||
|
|
||||||
> [!NOTE]
|
network:
|
||||||
> The setup this was tested on: Bare Metal -> Hyper-V on Win Server 2019 -> Ubuntu 22.04 VM -> Docker -> NetAlertX.
|
ethernets:
|
||||||
|
eth0:
|
||||||
|
dhcp4: yes
|
||||||
|
vlans:
|
||||||
|
eth0.2:
|
||||||
|
id: 2
|
||||||
|
link: eth0
|
||||||
|
addresses: [ "192.168.2.2/24" ]
|
||||||
|
routes:
|
||||||
|
- to: 192.168.2.0/24
|
||||||
|
via: 192.168.1.1
|
||||||
|
```
|
||||||
|
|
||||||
**Approach 1 (may cause issues):**
|
3. Run `sudo netplan apply` to activate the interfaces for scanning in NetAlertX.
|
||||||
|
|
||||||
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.
|
In this case, use `192.168.2.0/24 --interface=eth0.2` in NetAlertX.
|
||||||
|
|
||||||
**Approach 2 (working example)**
|
#### VLAN Support & Exceptions
|
||||||
|
|
||||||
Network connections to switches are configured as trunk and allow all VLANs access to the server.
|
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.
|
||||||
|
|
||||||
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:
|
|
||||||
|
|
||||||
1) Run the following command in Powershell on the Hyper-V machine:
|
|
||||||
|
|
||||||
```shell
|
|
||||||
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.
|
|
||||||
|
|
||||||
In /etc/netplan/00-installer-config.yaml, add vlan definitions:
|
|
||||||
|
|
||||||
```
|
|
||||||
network:
|
|
||||||
ethernets:
|
|
||||||
eth0:
|
|
||||||
dhcp4: yes
|
|
||||||
vlans:
|
|
||||||
eth0.2:
|
|
||||||
id: 2
|
|
||||||
link: eth0
|
|
||||||
addresses: [ "192.168.2.2/24" ]
|
|
||||||
routes:
|
|
||||||
- to: 192.168.2.0/24
|
|
||||||
via: 192.168.1.1
|
|
||||||
```
|
|
||||||
|
|
||||||
3) Run `sudo netplan apply` and the interfaces are then available to scan in NetAlertX.
|
|
||||||
4) In this case, use `192.168.2.0/24 --interface=eth0.2` in NetAlertX
|
|
||||||
|
|
||||||
#### VLAN 🔍Example:
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
#### Support for VLANS (& exceptions)
|
|
||||||
|
|
||||||
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.
|
||||||
|
|
||||||
|
|||||||
BIN
docs/img/DEV_ENV_SETUP/Maintenance_Logs_Restart_server.png
Executable file
BIN
docs/img/DEV_ENV_SETUP/Maintenance_Logs_Restart_server.png
Executable file
Binary file not shown.
|
After Width: | Height: | Size: 59 KiB |
@@ -17,7 +17,7 @@ showSpinner()
|
|||||||
$(document).ready(function() {
|
$(document).ready(function() {
|
||||||
|
|
||||||
// Load JSON data from the provided URL
|
// Load JSON data from the provided URL
|
||||||
$.getJSON('/api/table_appevents.json', function(data) {
|
$.getJSON('api/table_appevents.json', function(data) {
|
||||||
// Process the JSON data and generate UI dynamically
|
// Process the JSON data and generate UI dynamically
|
||||||
processData(data)
|
processData(data)
|
||||||
|
|
||||||
|
|||||||
@@ -261,7 +261,7 @@
|
|||||||
.main-sidebar {
|
.main-sidebar {
|
||||||
padding-top: 50px;
|
padding-top: 50px;
|
||||||
}
|
}
|
||||||
.content-header {
|
.content-header #pageTitle{
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1097,6 +1097,11 @@ input[readonly] {
|
|||||||
display: inline-block;
|
display: inline-block;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#settingsPage .form-control
|
||||||
|
{
|
||||||
|
min-height: 42px;
|
||||||
|
}
|
||||||
|
|
||||||
#settingsPage .select2-selection
|
#settingsPage .select2-selection
|
||||||
{
|
{
|
||||||
background-color: rgb(96, 96, 96);
|
background-color: rgb(96, 96, 96);
|
||||||
@@ -1112,11 +1117,41 @@ input[readonly] {
|
|||||||
display: inline-grid;
|
display: inline-grid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Basic style for the div elements */
|
||||||
|
#settingsPage .setting_overriden_by_env {
|
||||||
|
position: relative;
|
||||||
|
/* width: 300px;
|
||||||
|
height: 200px; */
|
||||||
|
background-color: #f3f3f3;
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
margin: 20px;
|
||||||
|
z-index: 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Style for the overlay */
|
||||||
|
#settingsPage .setting_overriden_by_env::after {
|
||||||
|
content: "Overridden with ENV variable";
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background: rgba(0, 0, 0, 0.6); /* semi-transparent black overlay */
|
||||||
|
color: white;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 700;
|
||||||
|
z-index: 11;
|
||||||
|
}
|
||||||
|
|
||||||
/* ----------------------------------------------------------------- */
|
/* ----------------------------------------------------------------- */
|
||||||
/* Devices page */
|
/* Devices page */
|
||||||
/* ----------------------------------------------------------------- */
|
/* ----------------------------------------------------------------- */
|
||||||
|
|
||||||
#txtIconFA {
|
.iconPreview {
|
||||||
min-width: 40px;
|
min-width: 40px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -100,7 +100,7 @@
|
|||||||
<div class="col-lg-12 col-sm-12 col-xs-12">
|
<div class="col-lg-12 col-sm-12 col-xs-12">
|
||||||
<!-- <div class="box-transparent"> -->
|
<!-- <div class="box-transparent"> -->
|
||||||
<div id="navDevice" class="nav-tabs-custom">
|
<div id="navDevice" class="nav-tabs-custom">
|
||||||
<ul class="nav nav-tabs" style="fon t-size:16px;">
|
<ul class="nav nav-tabs" style="font-size:16px;">
|
||||||
<li> <a id="tabDetails" href="#panDetails" data-toggle="tab"> <?= lang('DevDetail_Tab_Details');?> </a></li>
|
<li> <a id="tabDetails" href="#panDetails" data-toggle="tab"> <?= lang('DevDetail_Tab_Details');?> </a></li>
|
||||||
<li> <a id="tabTools" href="#panTools" data-toggle="tab"> <?= lang('DevDetail_Tab_Tools');?> </a></li>
|
<li> <a id="tabTools" href="#panTools" data-toggle="tab"> <?= lang('DevDetail_Tab_Tools');?> </a></li>
|
||||||
<li> <a id="tabSessions" href="#panSessions" data-toggle="tab"> <?= lang('DevDetail_Tab_Sessions');?> </a></li>
|
<li> <a id="tabSessions" href="#panSessions" data-toggle="tab"> <?= lang('DevDetail_Tab_Sessions');?> </a></li>
|
||||||
@@ -197,8 +197,8 @@
|
|||||||
</label>
|
</label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<span class="input-group-addon" id="txtIconFA"></span>
|
<span class="input-group-addon iconPreview" id="txtIconPreview" my-customid="txtIconPreview"></span>
|
||||||
<input class="form-control" id="txtIcon" type="text" value="--" readonly>
|
<input class="form-control" id="txtIcon" my-customid="txtIcon" my-customparams="txtIcon,txtIconPreview" type="text" value="--" readonly>
|
||||||
<span class="input-group-addon" title='<?= lang('DevDetail_button_AddIcon_Tooltip');?>'><i class="fa fa-square-plus pointer" onclick="askAddIcon();"></i></span>
|
<span class="input-group-addon" title='<?= lang('DevDetail_button_AddIcon_Tooltip');?>'><i class="fa fa-square-plus pointer" onclick="askAddIcon();"></i></span>
|
||||||
<span class="input-group-addon" title='<?= lang('DevDetail_button_OverwriteIcons_Tooltip');?>'><i class="fa fa-copy pointer" onclick="askOverwriteIconType();"></i></span>
|
<span class="input-group-addon" title='<?= lang('DevDetail_button_OverwriteIcons_Tooltip');?>'><i class="fa fa-copy pointer" onclick="askOverwriteIconType();"></i></span>
|
||||||
<div class="input-group-btn">
|
<div class="input-group-btn">
|
||||||
@@ -755,7 +755,7 @@ function main () {
|
|||||||
|
|
||||||
// Show device icon as it changes
|
// Show device icon as it changes
|
||||||
$('#txtIcon').on('change input', function() {
|
$('#txtIcon').on('change input', function() {
|
||||||
updateIconPreview('#txtIcon')
|
updateIconPreview(this)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
@@ -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();
|
|
||||||
// }
|
|
||||||
} );
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@@ -1136,7 +1116,7 @@ function initializeCalendar () {
|
|||||||
showSpinner()
|
showSpinner()
|
||||||
} else {
|
} else {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
updateIconPreview('#txtIcon')
|
updateIconPreview($('#txtIcon'))
|
||||||
}, 500);
|
}, 500);
|
||||||
|
|
||||||
hideSpinner()
|
hideSpinner()
|
||||||
@@ -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']);
|
||||||
@@ -1445,15 +1421,15 @@ function setDeviceData (direction='', refreshCallback='') {
|
|||||||
|
|
||||||
// update data to server
|
// update data to server
|
||||||
$.get('php/server/devices.php?action=setDeviceData&mac='+ mac
|
$.get('php/server/devices.php?action=setDeviceData&mac='+ mac
|
||||||
+ '&name=' + encodeURIComponent($('#txtName').val())
|
+ '&name=' + encodeURIComponent($('#txtName').val().replace(/'/g, ""))
|
||||||
+ '&owner=' + encodeURIComponent($('#txtOwner').val())
|
+ '&owner=' + encodeURIComponent($('#txtOwner').val().replace(/'/g, ""))
|
||||||
+ '&type=' + $('#txtDeviceType').val()
|
+ '&type=' + $('#txtDeviceType').val()
|
||||||
+ '&vendor=' + encodeURIComponent($('#txtVendor').val())
|
+ '&vendor=' + encodeURIComponent($('#txtVendor').val().replace(/'/g, ""))
|
||||||
+ '&icon=' + encodeURIComponent($('#txtIcon').val())
|
+ '&icon=' + encodeURIComponent($('#txtIcon').val())
|
||||||
+ '&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()
|
||||||
|
|
||||||
@@ -1667,7 +1643,7 @@ function addAsBase64 () {
|
|||||||
|
|
||||||
$('#txtIcon').val(iconHtmlBase64);
|
$('#txtIcon').val(iconHtmlBase64);
|
||||||
|
|
||||||
updateIconPreview('#txtIcon')
|
updateIconPreview($('#txtIcon'))
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -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);
|
|
||||||
|
|
||||||
} );
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//-----------------------------------------------------------------------------------
|
//-----------------------------------------------------------------------------------
|
||||||
|
|||||||
@@ -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 = [];
|
||||||
setTimeout(() => {
|
var offlineCounts = [];
|
||||||
pia_draw_graph_online_history(
|
var archivedCounts = [];
|
||||||
pia_js_online_history_time,
|
|
||||||
pia_js_online_history_ondev,
|
|
||||||
pia_js_online_history_dodev,
|
|
||||||
pia_js_online_history_ardev);
|
|
||||||
}, 500);
|
|
||||||
|
|
||||||
|
res.data.forEach(function(entry) {
|
||||||
|
var dateObj = new Date(entry.Scan_Date);
|
||||||
|
var formattedTime = dateObj.toLocaleTimeString([], {hour: '2-digit', minute: '2-digit', hour12: false});
|
||||||
|
|
||||||
|
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 ------------------------------------------------------------- -->
|
||||||
|
|||||||
@@ -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¶meter='+ 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¶meter='+ 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();
|
||||||
|
|||||||
@@ -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';
|
||||||
@@ -78,7 +80,7 @@ if (isset ($_SESSION["login"]) == FALSE || $_SESSION["login"] != 1)
|
|||||||
<meta http-equiv="Pragma" content="no-cache" />
|
<meta http-equiv="Pragma" content="no-cache" />
|
||||||
<meta http-equiv="Expires" content="0" />
|
<meta http-equiv="Expires" content="0" />
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
<title>Net Alert X | Log in</title>
|
<title>NetAlert X | Log in</title>
|
||||||
<!-- Tell the browser to be responsive to screen width -->
|
<!-- Tell the browser to be responsive to screen width -->
|
||||||
<meta content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" name="viewport">
|
<meta content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" name="viewport">
|
||||||
<!-- Bootstrap 3.3.7 -->
|
<!-- Bootstrap 3.3.7 -->
|
||||||
@@ -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
|
||||||
@@ -104,7 +113,7 @@ if ($ENABLED_DARKMODE === True) {
|
|||||||
<body class="hold-transition login-page">
|
<body class="hold-transition login-page">
|
||||||
<div class="login-box login-custom">
|
<div class="login-box login-custom">
|
||||||
<div class="login-logo">
|
<div class="login-logo">
|
||||||
<a href="/index2.php">Net <b>Alert</b><sup>x</sup></a>
|
<a href="/index2.php">Net<b>Alert</b><sup>x</sup></a>
|
||||||
</div>
|
</div>
|
||||||
<!-- /.login-logo -->
|
<!-- /.login-logo -->
|
||||||
<div class="login-box-body">
|
<div class="login-box-body">
|
||||||
@@ -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>
|
||||||
|
|||||||
@@ -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, '&')
|
||||||
|
.replace(/</g, '<')
|
||||||
|
.replace(/>/g, '>')
|
||||||
|
.replace(/"/g, '"')
|
||||||
|
.replace(/'/g, ''');
|
||||||
|
}
|
||||||
|
// ----------------------------------------------------
|
||||||
|
function decodeSpecialChars(str) {
|
||||||
|
return str
|
||||||
|
.replace(/&/g, '&')
|
||||||
|
.replace(/</g, '<')
|
||||||
|
.replace(/>/g, '>')
|
||||||
|
.replace(/"/g, '"')
|
||||||
|
.replace(/'/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¶meter=' + parameter +
|
|
||||||
'&value='+ value,
|
|
||||||
function(data) {
|
|
||||||
if (data != "OK") {
|
|
||||||
// Retry
|
|
||||||
sleep (200);
|
|
||||||
$.get('php/server/parameters.php?action=set¶meter=' + 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({
|
||||||
@@ -630,17 +641,11 @@ function debugTimer () {
|
|||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
function secondsSincePageLoad() {
|
function secondsSincePageLoad() {
|
||||||
// Get the current time
|
// Get the current time since the page was loaded
|
||||||
var currentTime = Date.now();
|
var timeSincePageLoad = performance.now();
|
||||||
|
|
||||||
// Get the time when the page was loaded
|
|
||||||
var pageLoadTime = performance.timeOrigin;
|
|
||||||
|
|
||||||
// Calculate the difference in milliseconds
|
|
||||||
var timeDifference = currentTime - pageLoadTime;
|
|
||||||
|
|
||||||
// Convert milliseconds to seconds
|
// Convert milliseconds to seconds
|
||||||
var secondsAgo = Math.floor(timeDifference / 1000);
|
var secondsAgo = Math.floor(timeSincePageLoad / 1000);
|
||||||
|
|
||||||
return secondsAgo;
|
return secondsAgo;
|
||||||
}
|
}
|
||||||
@@ -1001,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({
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -295,6 +295,7 @@ function checkNotification() {
|
|||||||
console.log(response);
|
console.log(response);
|
||||||
// After marking the notification as read, check for the next one
|
// After marking the notification as read, check for the next one
|
||||||
checkNotification();
|
checkNotification();
|
||||||
|
hideSpinner();
|
||||||
},
|
},
|
||||||
error: function(xhr, status, error) {
|
error: function(xhr, status, error) {
|
||||||
console.error("Error marking notification as read:", status, error);
|
console.error("Error marking notification as read:", status, error);
|
||||||
|
|||||||
@@ -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¶meter=" +
|
|
||||||
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
|
||||||
@@ -667,7 +640,11 @@ const handleElementOptions = (codeName, elementOptions, transformers, val) => {
|
|||||||
let valRes = val;
|
let valRes = val;
|
||||||
let sourceIds = [];
|
let sourceIds = [];
|
||||||
let getStringKey = "";
|
let getStringKey = "";
|
||||||
let onClick = "alert('Not implemented');";
|
let onClick = "console.log('onClick - Not implemented');";
|
||||||
|
let onChange = "console.log('onChange - Not implemented');";
|
||||||
|
let customParams = "";
|
||||||
|
let customId = "";
|
||||||
|
|
||||||
|
|
||||||
elementOptions.forEach((option) => {
|
elementOptions.forEach((option) => {
|
||||||
if (option.prefillValue) {
|
if (option.prefillValue) {
|
||||||
@@ -711,6 +688,15 @@ const handleElementOptions = (codeName, elementOptions, transformers, val) => {
|
|||||||
if (option.onClick) {
|
if (option.onClick) {
|
||||||
onClick = option.onClick;
|
onClick = option.onClick;
|
||||||
}
|
}
|
||||||
|
if (option.onChange) {
|
||||||
|
onChange = option.onChange;
|
||||||
|
}
|
||||||
|
if (option.customParams) {
|
||||||
|
customParams = option.customParams;
|
||||||
|
}
|
||||||
|
if (option.customId) {
|
||||||
|
customId = option.customId;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (transformers.includes("sha256")) {
|
if (transformers.includes("sha256")) {
|
||||||
@@ -731,6 +717,9 @@ const handleElementOptions = (codeName, elementOptions, transformers, val) => {
|
|||||||
valRes,
|
valRes,
|
||||||
getStringKey,
|
getStringKey,
|
||||||
onClick,
|
onClick,
|
||||||
|
onChange,
|
||||||
|
customParams,
|
||||||
|
customId
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -68,23 +68,66 @@ function initDeviceSelectors(devicesListAll_JSON) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// ----------------------------------------------
|
||||||
// Updates the icon preview
|
// Updates the icon preview
|
||||||
function updateIconPreview (inputId) {
|
function updateIconPreview(elem) {
|
||||||
// update icon
|
// Retrieve and parse custom parameters from the element
|
||||||
iconInput = $(inputId)
|
let params = $(elem).attr("my-customparams")?.split(',').map(param => param.trim());
|
||||||
|
|
||||||
value = iconInput.val()
|
// console.log(params);
|
||||||
|
|
||||||
iconInput.on('change input', function() {
|
if (params && params.length >= 2) {
|
||||||
$('#txtIconFA').html(atob(value))
|
var inputElementID = params[0];
|
||||||
});
|
var targetElementID = params[1];
|
||||||
|
} else {
|
||||||
|
console.error("Invalid parameters passed to updateIconPreview function");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
$('#txtIconFA').html(atob(value))
|
// Get the input element using the inputElementID
|
||||||
|
let iconInput = $("#" + inputElementID);
|
||||||
|
|
||||||
|
if (iconInput.length === 0) {
|
||||||
|
console.error("Icon input element not found");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the initial value and update the target element
|
||||||
|
let value = iconInput.val();
|
||||||
|
if (!value) {
|
||||||
|
console.error("Input value is empty or not defined");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!targetElementID) {
|
||||||
|
targetElementID = "txtIcon";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the target element exists, if not find an element with matching custom attribute
|
||||||
|
let targetElement = $('#' + targetElementID);
|
||||||
|
if (targetElement.length === 0) {
|
||||||
|
// Look for an element with my-custom-id attribute equal to targetElementID
|
||||||
|
targetElement = $('[my-customid="' + targetElementID + '"]');
|
||||||
|
if (targetElement.length === 0) {
|
||||||
|
console.error("Neither target element with ID nor element with custom attribute found");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the target element with decoded base64 value
|
||||||
|
targetElement.html(atob(value));
|
||||||
|
|
||||||
|
// Add event listener to update the icon on input change
|
||||||
|
iconInput.on('change input', function () {
|
||||||
|
let newValue = $(this).val();
|
||||||
|
$('#' + targetElementID).html(atob(newValue));
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
// Generic function to copy text to clipboard
|
// Generic function to copy text to clipboard
|
||||||
function copyToClipboard(buttonElement) {
|
function copyToClipboard(buttonElement) {
|
||||||
|
|||||||
@@ -1034,7 +1034,7 @@ var mouse = $.widget("ui.mouse", {
|
|||||||
return this.mouseDelayMet;
|
return this.mouseDelayMet;
|
||||||
},
|
},
|
||||||
|
|
||||||
// These are placeholder methods, to be overriden by extending plugin
|
// These are placeholder methods, to be overridden by extending plugin
|
||||||
_mouseStart: function(/* event */) {},
|
_mouseStart: function(/* event */) {},
|
||||||
_mouseDrag: function(/* event */) {},
|
_mouseDrag: function(/* event */) {},
|
||||||
_mouseStop: function(/* event */) {},
|
_mouseStop: function(/* event */) {},
|
||||||
|
|||||||
@@ -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,15 +122,9 @@ $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>
|
</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>
|
||||||
<!-- /.box-body -->
|
<!-- /.box-body -->
|
||||||
@@ -254,25 +219,7 @@ $db->close();
|
|||||||
<button type="button" class="btn btn-default pa-btn pa-btn-delete bg-red dbtools-button" id="btnImportPastedCSV" onclick="askImportPastedCSV()"><?= lang('Maintenance_Tool_ImportPastedCSV');?></button>
|
<button type="button" class="btn btn-default pa-btn pa-btn-delete bg-red dbtools-button" id="btnImportPastedCSV" onclick="askImportPastedCSV()"><?= lang('Maintenance_Tool_ImportPastedCSV');?></button>
|
||||||
</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');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -101,19 +101,20 @@
|
|||||||
for (let j = i * elementsPerColumn; j < Math.min((i + 1) * elementsPerColumn, columns.length); j++) {
|
for (let j = i * elementsPerColumn; j < Math.min((i + 1) * elementsPerColumn, columns.length); j++) {
|
||||||
|
|
||||||
const setTypeObject = JSON.parse(columns[j].Type.replace(/'/g, '"'));
|
const setTypeObject = JSON.parse(columns[j].Type.replace(/'/g, '"'));
|
||||||
// console.log(setTypeObject); 🔽
|
|
||||||
// const lastElementObj = setTypeObject.elements[setTypeObject.elements.length - 1]
|
|
||||||
|
|
||||||
// get the element with the input value(s)
|
// get the element with the input value(s)
|
||||||
let elementsWithInputValue = setTypeObject.elements.filter(element => element.elementHasInputValue === 1);
|
let elements = setTypeObject.elements.filter(element => element.elementHasInputValue === 1);
|
||||||
|
|
||||||
// if none found, take last
|
// if none found, take last
|
||||||
if(elementsWithInputValue.length == 0)
|
if(elements.length == 0)
|
||||||
{
|
{
|
||||||
elementsWithInputValue = setTypeObject.elements[setTypeObject.elements.length - 1]
|
elementWithInputValue = setTypeObject.elements[setTypeObject.elements.length - 1]
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
elementWithInputValue = elements[0]
|
||||||
}
|
}
|
||||||
|
|
||||||
const { elementType, elementOptions = [], transformers = [] } = elementsWithInputValue;
|
const { elementType, elementOptions = [], transformers = [] } = elementWithInputValue;
|
||||||
const {
|
const {
|
||||||
inputType,
|
inputType,
|
||||||
readOnly,
|
readOnly,
|
||||||
@@ -127,26 +128,28 @@
|
|||||||
editable,
|
editable,
|
||||||
valRes,
|
valRes,
|
||||||
getStringKey,
|
getStringKey,
|
||||||
onClick
|
onClick,
|
||||||
|
onChange,
|
||||||
|
customParams,
|
||||||
|
customId
|
||||||
} = handleElementOptions('none', elementOptions, transformers, val = "");
|
} = handleElementOptions('none', elementOptions, transformers, val = "");
|
||||||
|
|
||||||
// console.log(setTypeObject);
|
|
||||||
// console.log(inputType);
|
|
||||||
|
|
||||||
// render based on element type
|
// render based on element type
|
||||||
if (elementsWithInputValue.elementType === 'select') {
|
if (elementType === 'select') {
|
||||||
|
|
||||||
targetLocation = columns[j].Code_Name + "_generateSetOptions"
|
targetLocation = columns[j].Code_Name + "_generateSetOptions"
|
||||||
|
|
||||||
generateOptionsOrSetOptions(columns[j].Code_Name, [], targetLocation, generateOptions)
|
generateOptionsOrSetOptions(columns[j].Code_Name, [], targetLocation, generateOptions)
|
||||||
|
|
||||||
// Handle Icons as tehy need a preview
|
console.log(columns[j].Code_Name)
|
||||||
|
// Handle Icons as they need a preview
|
||||||
if(columns[j].Code_Name == 'NEWDEV_dev_Icon')
|
if(columns[j].Code_Name == 'NEWDEV_dev_Icon')
|
||||||
{
|
{
|
||||||
input = `
|
input = `
|
||||||
<span class="input-group-addon" id="txtIconFA"></span>
|
<span class="input-group-addon iconPreview" my-customid="NEWDEV_dev_Icon_preview"></span>
|
||||||
<select class="form-control"
|
<select class="form-control"
|
||||||
onChange="updateIconPreview('#NEWDEV_dev_Icon')"
|
onChange="updateIconPreview(this)"
|
||||||
|
my-customparams="NEWDEV_dev_Icon,NEWDEV_dev_Icon_preview"
|
||||||
id="${columns[j].Code_Name}"
|
id="${columns[j].Code_Name}"
|
||||||
data-my-column="${columns[j].Code_Name}"
|
data-my-column="${columns[j].Code_Name}"
|
||||||
data-my-targetColumns="${columns[j].Code_Name.replace('NEWDEV_','')}" >
|
data-my-targetColumns="${columns[j].Code_Name.replace('NEWDEV_','')}" >
|
||||||
@@ -164,7 +167,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
} else if (elementsWithInputValue.elementType === 'input'){
|
} else if (elementType === 'input'){
|
||||||
|
|
||||||
// Add classes specifically for checkboxes
|
// Add classes specifically for checkboxes
|
||||||
inputType === 'checkbox' ? inputClass = 'checkbox' : inputClass = 'form-control';
|
inputType === 'checkbox' ? inputClass = 'checkbox' : inputClass = 'form-control';
|
||||||
@@ -172,6 +175,7 @@
|
|||||||
|
|
||||||
input = `<input class="${inputClass}"
|
input = `<input class="${inputClass}"
|
||||||
id="${columns[j].Code_Name}"
|
id="${columns[j].Code_Name}"
|
||||||
|
my-customid="${columns[j].Code_Name}"
|
||||||
data-my-column="${columns[j].Code_Name}"
|
data-my-column="${columns[j].Code_Name}"
|
||||||
data-my-targetColumns="${columns[j].Code_Name.replace('NEWDEV_','')}"
|
data-my-targetColumns="${columns[j].Code_Name.replace('NEWDEV_','')}"
|
||||||
type="${inputType}">`
|
type="${inputType}">`
|
||||||
@@ -301,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')
|
||||||
|
|
||||||
|
|||||||
@@ -783,7 +783,7 @@
|
|||||||
setCache(key, target.replaceAll(":","_")+'_id') // _id is added so it doesn't conflict with AdminLTE tab behavior
|
setCache(key, target.replaceAll(":","_")+'_id') // _id is added so it doesn't conflict with AdminLTE tab behavior
|
||||||
}
|
}
|
||||||
|
|
||||||
// get the tab id from the cookie (already overriden by the target)
|
// get the tab id from the cookie (already overridden by the target)
|
||||||
if(!emptyArr.includes(getCache(key)))
|
if(!emptyArr.includes(getCache(key)))
|
||||||
{
|
{
|
||||||
selectedTab = getCache(key);
|
selectedTab = getCache(key);
|
||||||
|
|||||||
@@ -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'] : '';
|
||||||
|
|||||||
@@ -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'] : '';
|
||||||
|
|||||||
@@ -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
|
|
||||||
?>
|
|
||||||
@@ -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
|
|
||||||
?>
|
|
||||||
@@ -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;
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
|
||||||
WHEN ses_EventTypeConnection = "<missing event>" THEN
|
CASE
|
||||||
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"))
|
WHEN SES1.ses_EventTypeConnection = "<missing event>" THEN
|
||||||
ELSE ses_DateTimeConnection
|
IFNULL (
|
||||||
END AS ses_DateTimeConnectionCorrected,
|
(SELECT MAX(SES2.ses_DateTimeDisconnection)
|
||||||
|
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
|
||||||
|
|||||||
@@ -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");
|
||||||
|
|
||||||
|
|||||||
@@ -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'];
|
||||||
|
|
||||||
|
|||||||
@@ -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'];
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
?>
|
|
||||||
@@ -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>';
|
||||||
|
|||||||
@@ -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'];
|
||||||
|
|
||||||
|
|||||||
@@ -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(
|
||||||
|
['&', '<', '>', '"', "'"],
|
||||||
|
['&', '<', '>', '"', '''],
|
||||||
|
$str
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------------------------
|
||||||
|
// Decode special chars
|
||||||
|
function decodeSpecialChars($str) {
|
||||||
|
return str_replace(
|
||||||
|
['&', '<', '>', '"', '''],
|
||||||
|
['&', '<', '>', '"', "'"],
|
||||||
|
$str
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// -------------------------------------------------------------------------------------------
|
// -------------------------------------------------------------------------------------------
|
||||||
|
|||||||
@@ -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']);
|
|
||||||
}
|
|
||||||
|
|
||||||
?>
|
|
||||||
@@ -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'])) {
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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 -->
|
||||||
@@ -53,8 +59,8 @@
|
|||||||
<link rel="stylesheet" href="lib/AdminLTE/bower_components/select2/dist/css/select2.min.css">
|
<link rel="stylesheet" href="lib/AdminLTE/bower_components/select2/dist/css/select2.min.css">
|
||||||
|
|
||||||
<!-- NetAlertX -->
|
<!-- NetAlertX -->
|
||||||
<script src="js/handle_version.js"></script>
|
<script defer src="js/handle_version.js"></script>
|
||||||
<script defer src="js/ui_components.js?v=<?php include 'php/templates/version.php'; ?>"></script>
|
<script src="js/ui_components.js?v=<?php include 'php/templates/version.php'; ?>"></script>
|
||||||
|
|
||||||
|
|
||||||
<!-- Select2 JavaScript -->
|
<!-- Select2 JavaScript -->
|
||||||
|
|||||||
@@ -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 ",";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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';
|
||||||
|
|
||||||
?>
|
?>
|
||||||
|
|
||||||
|
|||||||
@@ -56,6 +56,8 @@
|
|||||||
"BackDevices_Restore_okay": "",
|
"BackDevices_Restore_okay": "",
|
||||||
"BackDevices_darkmode_disabled": "",
|
"BackDevices_darkmode_disabled": "",
|
||||||
"BackDevices_darkmode_enabled": "",
|
"BackDevices_darkmode_enabled": "",
|
||||||
|
"CLEAR_NEW_FLAG_description": "",
|
||||||
|
"CLEAR_NEW_FLAG_name": "",
|
||||||
"DAYS_TO_KEEP_EVENTS_description": "",
|
"DAYS_TO_KEEP_EVENTS_description": "",
|
||||||
"DAYS_TO_KEEP_EVENTS_name": "",
|
"DAYS_TO_KEEP_EVENTS_name": "",
|
||||||
"DevDetail_Copy_Device_Title": "",
|
"DevDetail_Copy_Device_Title": "",
|
||||||
@@ -293,6 +295,7 @@
|
|||||||
"Gen_Save": "",
|
"Gen_Save": "",
|
||||||
"Gen_Saved": "",
|
"Gen_Saved": "",
|
||||||
"Gen_Search": "",
|
"Gen_Search": "",
|
||||||
|
"Gen_SelectToPreview": "",
|
||||||
"Gen_Selected_Devices": "",
|
"Gen_Selected_Devices": "",
|
||||||
"Gen_Switch": "",
|
"Gen_Switch": "",
|
||||||
"Gen_Upd": "",
|
"Gen_Upd": "",
|
||||||
@@ -348,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": "",
|
||||||
@@ -528,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": "",
|
||||||
|
|||||||
@@ -68,6 +68,8 @@
|
|||||||
"BackDevices_Restore_okay": "Die Wiederherstellung wurde erfolgreich ausgeführt.",
|
"BackDevices_Restore_okay": "Die Wiederherstellung wurde erfolgreich ausgeführt.",
|
||||||
"BackDevices_darkmode_disabled": "Heller Modus aktiviert.",
|
"BackDevices_darkmode_disabled": "Heller Modus aktiviert.",
|
||||||
"BackDevices_darkmode_enabled": "Dunkler Modus aktiviert.",
|
"BackDevices_darkmode_enabled": "Dunkler Modus aktiviert.",
|
||||||
|
"CLEAR_NEW_FLAG_description": "",
|
||||||
|
"CLEAR_NEW_FLAG_name": "",
|
||||||
"DAYS_TO_KEEP_EVENTS_description": "Dies ist eine Wartungseinstellung. Spezifiziert wie viele Tage Events gespeichert bleiben. Alle älteren Events werden periodisch gelöscht. Wird auch auf die Plugins History angewendet.",
|
"DAYS_TO_KEEP_EVENTS_description": "Dies ist eine Wartungseinstellung. Spezifiziert wie viele Tage Events gespeichert bleiben. Alle älteren Events werden periodisch gelöscht. Wird auch auf die Plugins History angewendet.",
|
||||||
"DAYS_TO_KEEP_EVENTS_name": "Lösche Events älter als",
|
"DAYS_TO_KEEP_EVENTS_name": "Lösche Events älter als",
|
||||||
"DevDetail_Copy_Device_Title": "<i class=\"fa fa-copy\"></i> Details von Gerät kopieren",
|
"DevDetail_Copy_Device_Title": "<i class=\"fa fa-copy\"></i> Details von Gerät kopieren",
|
||||||
@@ -305,6 +307,7 @@
|
|||||||
"Gen_Save": "Speichern",
|
"Gen_Save": "Speichern",
|
||||||
"Gen_Saved": "Gespeichert",
|
"Gen_Saved": "Gespeichert",
|
||||||
"Gen_Search": "Suchen",
|
"Gen_Search": "Suchen",
|
||||||
|
"Gen_SelectToPreview": "",
|
||||||
"Gen_Selected_Devices": "Ausgewählte Geräte:",
|
"Gen_Selected_Devices": "Ausgewählte Geräte:",
|
||||||
"Gen_Switch": "Umschalten",
|
"Gen_Switch": "Umschalten",
|
||||||
"Gen_Upd": "Aktualisierung erfolgreich",
|
"Gen_Upd": "Aktualisierung erfolgreich",
|
||||||
@@ -360,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",
|
||||||
@@ -569,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",
|
||||||
|
|||||||
@@ -56,6 +56,8 @@
|
|||||||
"BackDevices_Restore_okay": "Restore executed successfully.",
|
"BackDevices_Restore_okay": "Restore executed successfully.",
|
||||||
"BackDevices_darkmode_disabled": "Darkmode Disabled",
|
"BackDevices_darkmode_disabled": "Darkmode Disabled",
|
||||||
"BackDevices_darkmode_enabled": "Darkmode Enabled",
|
"BackDevices_darkmode_enabled": "Darkmode Enabled",
|
||||||
|
"CLEAR_NEW_FLAG_description": "If enabled (<code>0</code> is disabled), devices flagged as <b>New Device</b> will be unflagged if the time limit (specified in hours) exceeds their <b>First Session</b> time.",
|
||||||
|
"CLEAR_NEW_FLAG_name": "Clear new flag",
|
||||||
"DAYS_TO_KEEP_EVENTS_description": "This is a maintenance setting. This specifies the number of days worth of event entries that will be kept. All older events will be deleted periodically. Also applies on Plugin Events History.",
|
"DAYS_TO_KEEP_EVENTS_description": "This is a maintenance setting. This specifies the number of days worth of event entries that will be kept. All older events will be deleted periodically. Also applies on Plugin Events History.",
|
||||||
"DAYS_TO_KEEP_EVENTS_name": "Delete events older than",
|
"DAYS_TO_KEEP_EVENTS_name": "Delete events older than",
|
||||||
"DevDetail_Copy_Device_Title": "<i class=\"fa fa-copy\"></i> Copy details from device",
|
"DevDetail_Copy_Device_Title": "<i class=\"fa fa-copy\"></i> Copy details from device",
|
||||||
@@ -293,6 +295,7 @@
|
|||||||
"Gen_Save": "Save",
|
"Gen_Save": "Save",
|
||||||
"Gen_Saved": "Saved",
|
"Gen_Saved": "Saved",
|
||||||
"Gen_Search": "Search",
|
"Gen_Search": "Search",
|
||||||
|
"Gen_SelectToPreview": "Select to preview",
|
||||||
"Gen_Selected_Devices": "Selected Devices:",
|
"Gen_Selected_Devices": "Selected Devices:",
|
||||||
"Gen_Switch": "Switch",
|
"Gen_Switch": "Switch",
|
||||||
"Gen_Upd": "Updated successfully",
|
"Gen_Upd": "Updated successfully",
|
||||||
@@ -303,8 +306,8 @@
|
|||||||
"Gen_Work_In_Progress": "Work in progress, good time to feedback on https://github.com/jokob-sk/NetAlertX/issues",
|
"Gen_Work_In_Progress": "Work in progress, good time to feedback on https://github.com/jokob-sk/NetAlertX/issues",
|
||||||
"General_display_name": "General",
|
"General_display_name": "General",
|
||||||
"General_icon": "<i class=\"fa fa-gears\"></i>",
|
"General_icon": "<i class=\"fa fa-gears\"></i>",
|
||||||
"HRS_TO_KEEP_NEWDEV_description": "This is a maintenance setting. If enabled (<code>0</code> is disabled), devices marked as <b>New Device</b> will be deleted if their <b>First Session</b> time was older than the specified hours in this setting. Use this setting if you want to auto-delete <b>New Devices</b> after <code>X</code> hours.",
|
"HRS_TO_KEEP_NEWDEV_description": "This is a maintenance setting <b>DELETING devices</b>. If enabled (<code>0</code> is disabled), devices marked as <b>New Device</b> will be deleted if their <b>First Session</b> time was older than the specified hours in this setting. Use this setting if you want to auto-delete <b>New Devices</b> after <code>X</code> hours.",
|
||||||
"HRS_TO_KEEP_NEWDEV_name": "Keep new devices for",
|
"HRS_TO_KEEP_NEWDEV_name": "Delete new devices after",
|
||||||
"HelpFAQ_Cat_Detail": "Details",
|
"HelpFAQ_Cat_Detail": "Details",
|
||||||
"HelpFAQ_Cat_Detail_300_head": "What means ",
|
"HelpFAQ_Cat_Detail_300_head": "What means ",
|
||||||
"HelpFAQ_Cat_Detail_300_text_a": "means a network device (a device of the type AP, Gateway, Firewall, Hypervisor, Powerline, Switch, WLAN, PLC, Router,USB LAN Adapter, USB WIFI Adapter, or Internet). Custom types can be added via the <code>NETWORK_DEVICE_TYPES</code> setting.",
|
"HelpFAQ_Cat_Detail_300_text_a": "means a network device (a device of the type AP, Gateway, Firewall, Hypervisor, Powerline, Switch, WLAN, PLC, Router,USB LAN Adapter, USB WIFI Adapter, or Internet). Custom types can be added via the <code>NETWORK_DEVICE_TYPES</code> setting.",
|
||||||
@@ -348,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.",
|
||||||
@@ -528,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",
|
||||||
|
|||||||
@@ -66,6 +66,8 @@
|
|||||||
"BackDevices_Restore_okay": "Restauración ejecutado con éxito.",
|
"BackDevices_Restore_okay": "Restauración ejecutado con éxito.",
|
||||||
"BackDevices_darkmode_disabled": "Darkmode Desactivado",
|
"BackDevices_darkmode_disabled": "Darkmode Desactivado",
|
||||||
"BackDevices_darkmode_enabled": "Darkmode Activado",
|
"BackDevices_darkmode_enabled": "Darkmode Activado",
|
||||||
|
"CLEAR_NEW_FLAG_description": "Si está habilitado (<code>0</code> está desactivado), los dispositivos marcados como <b>Nuevo dispositivo</b> se desmarcarán si el límite de tiempo (especificado en horas) excede su tiempo de <b>primera sesión</b>.",
|
||||||
|
"CLEAR_NEW_FLAG_name": "Eliminar la nueva bandera",
|
||||||
"DAYS_TO_KEEP_EVENTS_description": "Esta es una configuración de mantenimiento. Esto especifica el número de días de entradas de eventos que se guardarán. Todos los eventos anteriores se eliminarán periódicamente.",
|
"DAYS_TO_KEEP_EVENTS_description": "Esta es una configuración de mantenimiento. Esto especifica el número de días de entradas de eventos que se guardarán. Todos los eventos anteriores se eliminarán periódicamente.",
|
||||||
"DAYS_TO_KEEP_EVENTS_name": "Eliminar eventos anteriores a",
|
"DAYS_TO_KEEP_EVENTS_name": "Eliminar eventos anteriores a",
|
||||||
"DevDetail_Copy_Device_Title": "<i class=\"fa fa-copy\"></i> Copiar detalles del dispositivo",
|
"DevDetail_Copy_Device_Title": "<i class=\"fa fa-copy\"></i> Copiar detalles del dispositivo",
|
||||||
@@ -284,7 +286,7 @@
|
|||||||
"Gen_AreYouSure": "¿Estás seguro?",
|
"Gen_AreYouSure": "¿Estás seguro?",
|
||||||
"Gen_Backup": "Ejecutar copia de seguridad",
|
"Gen_Backup": "Ejecutar copia de seguridad",
|
||||||
"Gen_Cancel": "Cancelar",
|
"Gen_Cancel": "Cancelar",
|
||||||
"Gen_Change": "",
|
"Gen_Change": "Cambiar",
|
||||||
"Gen_Copy": "Ejecutar",
|
"Gen_Copy": "Ejecutar",
|
||||||
"Gen_DataUpdatedUITakesTime": "Correcto - La interfaz puede tardar en actualizarse si se está ejecutando un escaneo.",
|
"Gen_DataUpdatedUITakesTime": "Correcto - La interfaz puede tardar en actualizarse si se está ejecutando un escaneo.",
|
||||||
"Gen_Delete": "Eliminar",
|
"Gen_Delete": "Eliminar",
|
||||||
@@ -303,6 +305,7 @@
|
|||||||
"Gen_Save": "Guardar",
|
"Gen_Save": "Guardar",
|
||||||
"Gen_Saved": "Guardado",
|
"Gen_Saved": "Guardado",
|
||||||
"Gen_Search": "Buscar",
|
"Gen_Search": "Buscar",
|
||||||
|
"Gen_SelectToPreview": "Seleccionar para previsualizar",
|
||||||
"Gen_Selected_Devices": "Dispositivos seleccionados:",
|
"Gen_Selected_Devices": "Dispositivos seleccionados:",
|
||||||
"Gen_Switch": "Cambiar",
|
"Gen_Switch": "Cambiar",
|
||||||
"Gen_Upd": "Actualizado correctamente",
|
"Gen_Upd": "Actualizado correctamente",
|
||||||
@@ -313,8 +316,8 @@
|
|||||||
"Gen_Work_In_Progress": "Trabajo en curso, un buen momento para hacer comentarios en https://github.com/jokob-sk/NetAlertX/issues",
|
"Gen_Work_In_Progress": "Trabajo en curso, un buen momento para hacer comentarios en https://github.com/jokob-sk/NetAlertX/issues",
|
||||||
"General_display_name": "General",
|
"General_display_name": "General",
|
||||||
"General_icon": "<i class=\"fa fa-gears\"></i>",
|
"General_icon": "<i class=\"fa fa-gears\"></i>",
|
||||||
"HRS_TO_KEEP_NEWDEV_description": "Esta es una configuración de mantenimiento. Si está habilitado (<code>0</code> está deshabilitado), los dispositivos marcados como <b>Nuevo dispositivo</b> se eliminarán si su <b>Primera sesión</b> el tiempo era anterior a las horas especificadas en esta configuración. Utilice esta configuración si desea eliminar automáticamente <b>Nuevos dispositivos</b> después de <code>X</code> horas.",
|
"HRS_TO_KEEP_NEWDEV_description": "Se trata de una configuración de mantenimiento <b>BORRAR dispositivos</b>. Si está activado (<code>0</code> está desactivado), los dispositivos marcados como <b>Nuevo dispositivo</b> se eliminarán si su fecha de <b>primera sesión</b> es anterior a las horas especificadas en este ajuste. Use este ajuste si desea eliminar automáticamente <b>Nuevos dispositivos</b> después de <code>X</code> horas.",
|
||||||
"HRS_TO_KEEP_NEWDEV_name": "Guardar nuevos dispositivos para",
|
"HRS_TO_KEEP_NEWDEV_name": "Eliminar nuevos dispositivos después",
|
||||||
"HelpFAQ_Cat_Detail": "Detalles",
|
"HelpFAQ_Cat_Detail": "Detalles",
|
||||||
"HelpFAQ_Cat_Detail_300_head": "¿Qué significa? ",
|
"HelpFAQ_Cat_Detail_300_head": "¿Qué significa? ",
|
||||||
"HelpFAQ_Cat_Detail_300_text_a": "significa un dispositivo de red (un dispositivo del tipo AP, Gateway, Firewall, Hypervisor, Powerline, Switch, WLAN, PLC, Router,Adaptador LAN USB, Adaptador WIFI USB o Internet). Los tipos personalizados pueden añadirse mediante el ajuste <code>NETWORK_DEVICE_TYPES</code>.",
|
"HelpFAQ_Cat_Detail_300_text_a": "significa un dispositivo de red (un dispositivo del tipo AP, Gateway, Firewall, Hypervisor, Powerline, Switch, WLAN, PLC, Router,Adaptador LAN USB, Adaptador WIFI USB o Internet). Los tipos personalizados pueden añadirse mediante el ajuste <code>NETWORK_DEVICE_TYPES</code>.",
|
||||||
@@ -358,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.",
|
||||||
@@ -567,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",
|
||||||
@@ -724,8 +729,8 @@
|
|||||||
"UI_PRESENCE_name": "Mostrar en el gráfico de presencia",
|
"UI_PRESENCE_name": "Mostrar en el gráfico de presencia",
|
||||||
"UI_REFRESH_description": "Ingrese el número de segundos después de los cuales se recarga la interfaz de usuario. Ajustado a <code> 0 </code> para desactivar.",
|
"UI_REFRESH_description": "Ingrese el número de segundos después de los cuales se recarga la interfaz de usuario. Ajustado a <code> 0 </code> para desactivar.",
|
||||||
"UI_REFRESH_name": "Actualización automática de la interfaz de usuario",
|
"UI_REFRESH_name": "Actualización automática de la interfaz de usuario",
|
||||||
"VERSION_description": "",
|
"VERSION_description": "Valor de ayuda de versión o marca de tiempo para comprobar si la aplicación se ha actualizado.",
|
||||||
"VERSION_name": "",
|
"VERSION_name": "Versión o marca de tiempo",
|
||||||
"WEBHOOK_PAYLOAD_description": "El formato de datos de carga de Webhook para el atributo <code>body</code> > <code>attachments</code> > <code>text</code> en el json de carga. Vea un ejemplo de la carga <a target=\"_blank\" href=\"https://github.com/jokob-sk/NetAlertX/blob/main/front/report_templates/webhook_json_sample.json\">aquí</a>. (por ejemplo: para discord use <code>text</code>)",
|
"WEBHOOK_PAYLOAD_description": "El formato de datos de carga de Webhook para el atributo <code>body</code> > <code>attachments</code> > <code>text</code> en el json de carga. Vea un ejemplo de la carga <a target=\"_blank\" href=\"https://github.com/jokob-sk/NetAlertX/blob/main/front/report_templates/webhook_json_sample.json\">aquí</a>. (por ejemplo: para discord use <code>text</code>)",
|
||||||
"WEBHOOK_PAYLOAD_name": "Tipo de carga",
|
"WEBHOOK_PAYLOAD_name": "Tipo de carga",
|
||||||
"WEBHOOK_REQUEST_METHOD_description": "El método de solicitud HTTP que se utilizará para la llamada de webhook.",
|
"WEBHOOK_REQUEST_METHOD_description": "El método de solicitud HTTP que se utilizará para la llamada de webhook.",
|
||||||
|
|||||||
@@ -56,6 +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": "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": "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",
|
||||||
@@ -72,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…",
|
||||||
@@ -96,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",
|
||||||
@@ -228,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",
|
||||||
@@ -274,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",
|
||||||
@@ -293,6 +295,7 @@
|
|||||||
"Gen_Save": "Enregistrer",
|
"Gen_Save": "Enregistrer",
|
||||||
"Gen_Saved": "Enregistré",
|
"Gen_Saved": "Enregistré",
|
||||||
"Gen_Search": "Recherche",
|
"Gen_Search": "Recherche",
|
||||||
|
"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",
|
||||||
@@ -304,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>.",
|
||||||
@@ -329,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 > 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 > 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 ?",
|
||||||
@@ -348,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.",
|
||||||
@@ -371,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.",
|
||||||
@@ -528,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",
|
||||||
@@ -557,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.",
|
||||||
@@ -656,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",
|
||||||
|
|||||||
@@ -56,6 +56,8 @@
|
|||||||
"BackDevices_Restore_okay": "Ripristino eseguito correttamente.",
|
"BackDevices_Restore_okay": "Ripristino eseguito correttamente.",
|
||||||
"BackDevices_darkmode_disabled": "Modalità scura disabilitata",
|
"BackDevices_darkmode_disabled": "Modalità scura disabilitata",
|
||||||
"BackDevices_darkmode_enabled": "Modalità scura abilitata",
|
"BackDevices_darkmode_enabled": "Modalità scura abilitata",
|
||||||
|
"CLEAR_NEW_FLAG_description": "",
|
||||||
|
"CLEAR_NEW_FLAG_name": "",
|
||||||
"DAYS_TO_KEEP_EVENTS_description": "Questa è un'impostazione di manutenzione. Specifica il numero di giorni delle voci degli eventi che verranno conservati. Tutti gli eventi più vecchi verranno eliminati periodicamente. Si applica anche alla cronologia degli eventi del plugin (Plugin Events History).",
|
"DAYS_TO_KEEP_EVENTS_description": "Questa è un'impostazione di manutenzione. Specifica il numero di giorni delle voci degli eventi che verranno conservati. Tutti gli eventi più vecchi verranno eliminati periodicamente. Si applica anche alla cronologia degli eventi del plugin (Plugin Events History).",
|
||||||
"DAYS_TO_KEEP_EVENTS_name": "Elimina eventi più vecchi di",
|
"DAYS_TO_KEEP_EVENTS_name": "Elimina eventi più vecchi di",
|
||||||
"DevDetail_Copy_Device_Title": "<i class=\"fa fa-copy\"></i> Copia dettagli dal dispositivo",
|
"DevDetail_Copy_Device_Title": "<i class=\"fa fa-copy\"></i> Copia dettagli dal dispositivo",
|
||||||
@@ -293,6 +295,7 @@
|
|||||||
"Gen_Save": "Salva",
|
"Gen_Save": "Salva",
|
||||||
"Gen_Saved": "Salvato",
|
"Gen_Saved": "Salvato",
|
||||||
"Gen_Search": "Cerca",
|
"Gen_Search": "Cerca",
|
||||||
|
"Gen_SelectToPreview": "",
|
||||||
"Gen_Selected_Devices": "Dispositivi selezionati:",
|
"Gen_Selected_Devices": "Dispositivi selezionati:",
|
||||||
"Gen_Switch": "Cambia",
|
"Gen_Switch": "Cambia",
|
||||||
"Gen_Upd": "Aggiornato correttamente",
|
"Gen_Upd": "Aggiornato correttamente",
|
||||||
@@ -348,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.",
|
||||||
@@ -528,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",
|
||||||
|
|||||||
@@ -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)) {
|
||||||
|
|||||||
@@ -56,6 +56,8 @@
|
|||||||
"BackDevices_Restore_okay": "Gjenoppretting utført.",
|
"BackDevices_Restore_okay": "Gjenoppretting utført.",
|
||||||
"BackDevices_darkmode_disabled": "Mørk modus Deaktivert",
|
"BackDevices_darkmode_disabled": "Mørk modus Deaktivert",
|
||||||
"BackDevices_darkmode_enabled": "Mørk modus Aktivert",
|
"BackDevices_darkmode_enabled": "Mørk modus Aktivert",
|
||||||
|
"CLEAR_NEW_FLAG_description": "",
|
||||||
|
"CLEAR_NEW_FLAG_name": "",
|
||||||
"DAYS_TO_KEEP_EVENTS_description": "Dette er en vedlikeholdsinnstilling. Dette spesifiserer antall dager verdt med hendelsesoppføringer som vil beholdes. Alle eldre hendelser vil bli slettet med jevne mellomrom. Gjelder også for plugin-hendelseshistorikk.",
|
"DAYS_TO_KEEP_EVENTS_description": "Dette er en vedlikeholdsinnstilling. Dette spesifiserer antall dager verdt med hendelsesoppføringer som vil beholdes. Alle eldre hendelser vil bli slettet med jevne mellomrom. Gjelder også for plugin-hendelseshistorikk.",
|
||||||
"DAYS_TO_KEEP_EVENTS_name": "Slett hendelser eldre enn",
|
"DAYS_TO_KEEP_EVENTS_name": "Slett hendelser eldre enn",
|
||||||
"DevDetail_Copy_Device_Title": "<i class=\"fa fa-copy\"></i> Kopier detaljer fra enhet",
|
"DevDetail_Copy_Device_Title": "<i class=\"fa fa-copy\"></i> Kopier detaljer fra enhet",
|
||||||
@@ -293,6 +295,7 @@
|
|||||||
"Gen_Save": "Lagre",
|
"Gen_Save": "Lagre",
|
||||||
"Gen_Saved": "Lagret",
|
"Gen_Saved": "Lagret",
|
||||||
"Gen_Search": "Søk",
|
"Gen_Search": "Søk",
|
||||||
|
"Gen_SelectToPreview": "",
|
||||||
"Gen_Selected_Devices": "Valgte Enheter:",
|
"Gen_Selected_Devices": "Valgte Enheter:",
|
||||||
"Gen_Switch": "Bytt",
|
"Gen_Switch": "Bytt",
|
||||||
"Gen_Upd": "Oppdatering vellykket",
|
"Gen_Upd": "Oppdatering vellykket",
|
||||||
@@ -348,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.",
|
||||||
@@ -528,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",
|
||||||
|
|||||||
@@ -56,6 +56,8 @@
|
|||||||
"BackDevices_Restore_okay": "Przywracanie wykonane z sukcesem.",
|
"BackDevices_Restore_okay": "Przywracanie wykonane z sukcesem.",
|
||||||
"BackDevices_darkmode_disabled": "Tryb ciemny Wyłączony",
|
"BackDevices_darkmode_disabled": "Tryb ciemny Wyłączony",
|
||||||
"BackDevices_darkmode_enabled": "Tryb ciemny Włączony",
|
"BackDevices_darkmode_enabled": "Tryb ciemny Włączony",
|
||||||
|
"CLEAR_NEW_FLAG_description": "",
|
||||||
|
"CLEAR_NEW_FLAG_name": "",
|
||||||
"DAYS_TO_KEEP_EVENTS_description": "To jest ustawienie konserwacji. Określa ile dni mają być utrzymywane wpisy wydarzeń. Wszystkie starsze wpisy wydarzeń zostaną usunięte okresowo. Dotyczy także Historii Wydarzeń Pluginów.",
|
"DAYS_TO_KEEP_EVENTS_description": "To jest ustawienie konserwacji. Określa ile dni mają być utrzymywane wpisy wydarzeń. Wszystkie starsze wpisy wydarzeń zostaną usunięte okresowo. Dotyczy także Historii Wydarzeń Pluginów.",
|
||||||
"DAYS_TO_KEEP_EVENTS_name": "Usuń wydarzenia starsze niż",
|
"DAYS_TO_KEEP_EVENTS_name": "Usuń wydarzenia starsze niż",
|
||||||
"DevDetail_Copy_Device_Title": "<i class=\"fa fa-copy\"></i>Kopiuj opis z urządzenia",
|
"DevDetail_Copy_Device_Title": "<i class=\"fa fa-copy\"></i>Kopiuj opis z urządzenia",
|
||||||
@@ -293,6 +295,7 @@
|
|||||||
"Gen_Save": "Zapisz",
|
"Gen_Save": "Zapisz",
|
||||||
"Gen_Saved": "Zapisano",
|
"Gen_Saved": "Zapisano",
|
||||||
"Gen_Search": "Szukaj",
|
"Gen_Search": "Szukaj",
|
||||||
|
"Gen_SelectToPreview": "",
|
||||||
"Gen_Selected_Devices": "Wybierz Urządzenia:",
|
"Gen_Selected_Devices": "Wybierz Urządzenia:",
|
||||||
"Gen_Switch": "Switch",
|
"Gen_Switch": "Switch",
|
||||||
"Gen_Upd": "Zaktualizowane poprawnie",
|
"Gen_Upd": "Zaktualizowane poprawnie",
|
||||||
@@ -348,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.",
|
||||||
@@ -528,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",
|
||||||
|
|||||||
@@ -56,6 +56,8 @@
|
|||||||
"BackDevices_Restore_okay": "Restauração executada com sucesso.",
|
"BackDevices_Restore_okay": "Restauração executada com sucesso.",
|
||||||
"BackDevices_darkmode_disabled": "Modo Noturno Desabilitado",
|
"BackDevices_darkmode_disabled": "Modo Noturno Desabilitado",
|
||||||
"BackDevices_darkmode_enabled": "Modo Noturno Habilitado",
|
"BackDevices_darkmode_enabled": "Modo Noturno Habilitado",
|
||||||
|
"CLEAR_NEW_FLAG_description": "",
|
||||||
|
"CLEAR_NEW_FLAG_name": "",
|
||||||
"DAYS_TO_KEEP_EVENTS_description": "Esta é uma definição de manutenção. Especifica o número de dias de entradas de eventos que serão mantidas. Todos os eventos mais antigos serão eliminados periodicamente. Também se aplica ao Histórico de eventos do plug-in.",
|
"DAYS_TO_KEEP_EVENTS_description": "Esta é uma definição de manutenção. Especifica o número de dias de entradas de eventos que serão mantidas. Todos os eventos mais antigos serão eliminados periodicamente. Também se aplica ao Histórico de eventos do plug-in.",
|
||||||
"DAYS_TO_KEEP_EVENTS_name": "Excluir eventos mais antigos que",
|
"DAYS_TO_KEEP_EVENTS_name": "Excluir eventos mais antigos que",
|
||||||
"DevDetail_Copy_Device_Title": "<i class=\"fa fa-copy\"></i> Copiar detalhes do dispositivo",
|
"DevDetail_Copy_Device_Title": "<i class=\"fa fa-copy\"></i> Copiar detalhes do dispositivo",
|
||||||
@@ -293,6 +295,7 @@
|
|||||||
"Gen_Save": "Salvar",
|
"Gen_Save": "Salvar",
|
||||||
"Gen_Saved": "Salvo",
|
"Gen_Saved": "Salvo",
|
||||||
"Gen_Search": "Procurar",
|
"Gen_Search": "Procurar",
|
||||||
|
"Gen_SelectToPreview": "",
|
||||||
"Gen_Selected_Devices": "Dispositivos selecionados:",
|
"Gen_Selected_Devices": "Dispositivos selecionados:",
|
||||||
"Gen_Switch": "Trocar",
|
"Gen_Switch": "Trocar",
|
||||||
"Gen_Upd": "Atualizado com sucesso",
|
"Gen_Upd": "Atualizado com sucesso",
|
||||||
@@ -348,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": "",
|
||||||
@@ -528,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": "",
|
||||||
|
|||||||
@@ -56,6 +56,8 @@
|
|||||||
"BackDevices_Restore_okay": "Восстановление выполнено успешно.",
|
"BackDevices_Restore_okay": "Восстановление выполнено успешно.",
|
||||||
"BackDevices_darkmode_disabled": "Темный режим отключен",
|
"BackDevices_darkmode_disabled": "Темный режим отключен",
|
||||||
"BackDevices_darkmode_enabled": "Темный режим включен",
|
"BackDevices_darkmode_enabled": "Темный режим включен",
|
||||||
|
"CLEAR_NEW_FLAG_description": "",
|
||||||
|
"CLEAR_NEW_FLAG_name": "",
|
||||||
"DAYS_TO_KEEP_EVENTS_description": "Это настройка обслуживания. Здесь указывается количество дней, в течение которых будут храниться записи о событиях. Все старые события будут периодически удаляться. Также применимо к истории событий плагина.",
|
"DAYS_TO_KEEP_EVENTS_description": "Это настройка обслуживания. Здесь указывается количество дней, в течение которых будут храниться записи о событиях. Все старые события будут периодически удаляться. Также применимо к истории событий плагина.",
|
||||||
"DAYS_TO_KEEP_EVENTS_name": "Удалить события старше",
|
"DAYS_TO_KEEP_EVENTS_name": "Удалить события старше",
|
||||||
"DevDetail_Copy_Device_Title": "<i class=\"fa fa-copy\"></i> Скопировать данные с устройства",
|
"DevDetail_Copy_Device_Title": "<i class=\"fa fa-copy\"></i> Скопировать данные с устройства",
|
||||||
@@ -293,6 +295,7 @@
|
|||||||
"Gen_Save": "Сохранить",
|
"Gen_Save": "Сохранить",
|
||||||
"Gen_Saved": "Сохранено",
|
"Gen_Saved": "Сохранено",
|
||||||
"Gen_Search": "Поиск",
|
"Gen_Search": "Поиск",
|
||||||
|
"Gen_SelectToPreview": "",
|
||||||
"Gen_Selected_Devices": "Выбранные устройства:",
|
"Gen_Selected_Devices": "Выбранные устройства:",
|
||||||
"Gen_Switch": "Переключить",
|
"Gen_Switch": "Переключить",
|
||||||
"Gen_Upd": "Успешное обновление",
|
"Gen_Upd": "Успешное обновление",
|
||||||
@@ -348,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": "в папке конфигурации.",
|
||||||
@@ -528,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": "Необработанные события",
|
||||||
|
|||||||
@@ -56,6 +56,8 @@
|
|||||||
"BackDevices_Restore_okay": "",
|
"BackDevices_Restore_okay": "",
|
||||||
"BackDevices_darkmode_disabled": "",
|
"BackDevices_darkmode_disabled": "",
|
||||||
"BackDevices_darkmode_enabled": "",
|
"BackDevices_darkmode_enabled": "",
|
||||||
|
"CLEAR_NEW_FLAG_description": "",
|
||||||
|
"CLEAR_NEW_FLAG_name": "",
|
||||||
"DAYS_TO_KEEP_EVENTS_description": "",
|
"DAYS_TO_KEEP_EVENTS_description": "",
|
||||||
"DAYS_TO_KEEP_EVENTS_name": "",
|
"DAYS_TO_KEEP_EVENTS_name": "",
|
||||||
"DevDetail_Copy_Device_Title": "",
|
"DevDetail_Copy_Device_Title": "",
|
||||||
@@ -293,6 +295,7 @@
|
|||||||
"Gen_Save": "Kaydet",
|
"Gen_Save": "Kaydet",
|
||||||
"Gen_Saved": "Kaydedildi",
|
"Gen_Saved": "Kaydedildi",
|
||||||
"Gen_Search": "",
|
"Gen_Search": "",
|
||||||
|
"Gen_SelectToPreview": "",
|
||||||
"Gen_Selected_Devices": "Seçilmiş Cihazlar:",
|
"Gen_Selected_Devices": "Seçilmiş Cihazlar:",
|
||||||
"Gen_Switch": "",
|
"Gen_Switch": "",
|
||||||
"Gen_Upd": "Başarılı bir şekilde güncellendi",
|
"Gen_Upd": "Başarılı bir şekilde güncellendi",
|
||||||
@@ -348,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": "",
|
||||||
@@ -528,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": "",
|
||||||
|
|||||||
@@ -56,6 +56,8 @@
|
|||||||
"BackDevices_Restore_okay": "已成功恢复。",
|
"BackDevices_Restore_okay": "已成功恢复。",
|
||||||
"BackDevices_darkmode_disabled": "暗黑模式已禁用",
|
"BackDevices_darkmode_disabled": "暗黑模式已禁用",
|
||||||
"BackDevices_darkmode_enabled": "已启用暗黑模式",
|
"BackDevices_darkmode_enabled": "已启用暗黑模式",
|
||||||
|
"CLEAR_NEW_FLAG_description": "",
|
||||||
|
"CLEAR_NEW_FLAG_name": "",
|
||||||
"DAYS_TO_KEEP_EVENTS_description": "这是维护设置。它指定将保留的事件条目的天数。所有较旧的事件将被定期删除。也适用于插件事件历史记录。",
|
"DAYS_TO_KEEP_EVENTS_description": "这是维护设置。它指定将保留的事件条目的天数。所有较旧的事件将被定期删除。也适用于插件事件历史记录。",
|
||||||
"DAYS_TO_KEEP_EVENTS_name": "删除早于",
|
"DAYS_TO_KEEP_EVENTS_name": "删除早于",
|
||||||
"DevDetail_Copy_Device_Title": "<i class=\"fa fa-copy\"></i> 从设备复制详细信息",
|
"DevDetail_Copy_Device_Title": "<i class=\"fa fa-copy\"></i> 从设备复制详细信息",
|
||||||
@@ -293,6 +295,7 @@
|
|||||||
"Gen_Save": "保存",
|
"Gen_Save": "保存",
|
||||||
"Gen_Saved": "已保存",
|
"Gen_Saved": "已保存",
|
||||||
"Gen_Search": "搜索",
|
"Gen_Search": "搜索",
|
||||||
|
"Gen_SelectToPreview": "",
|
||||||
"Gen_Selected_Devices": "选定的设备:",
|
"Gen_Selected_Devices": "选定的设备:",
|
||||||
"Gen_Switch": "交换",
|
"Gen_Switch": "交换",
|
||||||
"Gen_Upd": "已成功更新",
|
"Gen_Upd": "已成功更新",
|
||||||
@@ -348,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": "在配置文件夹中。",
|
||||||
@@ -528,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": "未处理的事件",
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
||||||
|
return !empty($matches) ? explode("=", array_values($matches)[0]) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getConfigValue($pattern, $config_lines, $delimiter = "'") {
|
||||||
|
$line = preg_grep($pattern, $config_lines);
|
||||||
|
return !empty($line) ? explode($delimiter, array_values($line)[0])[1] : '';
|
||||||
|
}
|
||||||
|
|
||||||
if (strpos($url,'index.php') !== false) {
|
function redirect($url) {
|
||||||
$isLogonPage = TRUE;
|
header("Location: $url");
|
||||||
}
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
session_start();
|
// 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;
|
||||||
|
|
||||||
if(array_search('action', $_REQUEST) != FALSE)
|
// Start session if not already started
|
||||||
{
|
if (session_status() == PHP_SESSION_NONE) {
|
||||||
if ($_REQUEST['action'] == 'logout') {
|
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
|
||||||
|
} elseif (!empty($authHeader)) {
|
||||||
|
echo "[Security] Incorrect Bearer Token";
|
||||||
|
}
|
||||||
|
|
||||||
$config_file_lines = array_values(preg_grep('/^SETPWD_password.*=/', $config_file_lines));
|
// Safely check if the session login exists before checking its value
|
||||||
$password_line = explode("'", $config_file_lines[0]);
|
$isLoggedIn = isset($_SESSION['login']) && $_SESSION['login'] == 1;
|
||||||
$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 ])))
|
|
||||||
{
|
|
||||||
//Logged in or stay on this page if we are on the index.php already
|
|
||||||
|
|
||||||
} else
|
|
||||||
{
|
|
||||||
// we need to redirect
|
|
||||||
header('Location: index.php');
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// Determine if the user should be redirected
|
||||||
|
if ($isLoggedIn || $isLogonPage || (isset($_COOKIE[COOKIE_SAVE_LOGIN_NAME]) && $nax_Password == $_COOKIE[COOKIE_SAVE_LOGIN_NAME])) {
|
||||||
|
// Logged in or stay on this page if we are on the index.php already
|
||||||
|
} else {
|
||||||
|
// We need to redirect
|
||||||
|
redirect('/index.php');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
?>
|
?>
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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') {
|
||||||
@@ -22,5 +23,6 @@ if(file_exists($filename)) {
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
echo date('H:i:s') . " - N/A";
|
echo date('H:i:s') . " - N/A";
|
||||||
}
|
}
|
||||||
|
|
||||||
?>
|
?>
|
||||||
|
|||||||
BIN
front/plugins/_publisher_mqtt/Deleting_MQTT_Plugin_Objects.png
Executable file
BIN
front/plugins/_publisher_mqtt/Deleting_MQTT_Plugin_Objects.png
Executable file
Binary file not shown.
|
After Width: | Height: | Size: 142 KiB |
@@ -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.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
|||||||
@@ -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": {
|
||||||
|
|||||||
@@ -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,28 +442,32 @@ 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"])
|
||||||
sensorConfig = create_sensor(mqtt_client, deviceId, devDisplayName, 'sensor', 'is_new', 'bell-alert-outline', device["dev_MAC"])
|
sensorConfig = create_sensor(mqtt_client, deviceId, devDisplayName, 'sensor', 'is_new', 'bell-alert-outline', device["dev_MAC"])
|
||||||
sensorConfig = create_sensor(mqtt_client, deviceId, devDisplayName, 'sensor', 'vendor', 'cog', device["dev_MAC"])
|
sensorConfig = create_sensor(mqtt_client, deviceId, devDisplayName, 'sensor', 'vendor', 'cog', 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', '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__':
|
||||||
|
|||||||
@@ -44,12 +44,13 @@ def main():
|
|||||||
HRS_TO_KEEP_NEWDEV = int(values.hourstokeepnewdevice.split('=')[1])
|
HRS_TO_KEEP_NEWDEV = int(values.hourstokeepnewdevice.split('=')[1])
|
||||||
DAYS_TO_KEEP_EVENTS = int(values.daystokeepevents.split('=')[1])
|
DAYS_TO_KEEP_EVENTS = int(values.daystokeepevents.split('=')[1])
|
||||||
PHOLUS_DAYS_DATA = get_setting_value("PHOLUS_DAYS_DATA")
|
PHOLUS_DAYS_DATA = get_setting_value("PHOLUS_DAYS_DATA")
|
||||||
|
CLEAR_NEW_FLAG = get_setting_value("CLEAR_NEW_FLAG")
|
||||||
|
|
||||||
mylog('verbose', [f'[{pluginName}] In script'])
|
mylog('verbose', [f'[{pluginName}] In script'])
|
||||||
|
|
||||||
|
|
||||||
# Execute cleanup/upkeep
|
# Execute cleanup/upkeep
|
||||||
cleanup_database(fullDbPath, DAYS_TO_KEEP_EVENTS, PHOLUS_DAYS_DATA, HRS_TO_KEEP_NEWDEV, PLUGINS_KEEP_HIST)
|
cleanup_database(fullDbPath, DAYS_TO_KEEP_EVENTS, PHOLUS_DAYS_DATA, HRS_TO_KEEP_NEWDEV, PLUGINS_KEEP_HIST, CLEAR_NEW_FLAG)
|
||||||
|
|
||||||
mylog('verbose', [f'[{pluginName}] Cleanup complete'])
|
mylog('verbose', [f'[{pluginName}] Cleanup complete'])
|
||||||
|
|
||||||
@@ -58,7 +59,7 @@ def main():
|
|||||||
#===============================================================================
|
#===============================================================================
|
||||||
# Cleanup / upkeep database
|
# Cleanup / upkeep database
|
||||||
#===============================================================================
|
#===============================================================================
|
||||||
def cleanup_database (dbPath, DAYS_TO_KEEP_EVENTS, PHOLUS_DAYS_DATA, HRS_TO_KEEP_NEWDEV, PLUGINS_KEEP_HIST):
|
def cleanup_database (dbPath, DAYS_TO_KEEP_EVENTS, PHOLUS_DAYS_DATA, HRS_TO_KEEP_NEWDEV, PLUGINS_KEEP_HIST, CLEAR_NEW_FLAG):
|
||||||
"""
|
"""
|
||||||
Cleaning out old records from the tables that don't need to keep all data.
|
Cleaning out old records from the tables that don't need to keep all data.
|
||||||
"""
|
"""
|
||||||
@@ -151,6 +152,17 @@ def cleanup_database (dbPath, DAYS_TO_KEEP_EVENTS, PHOLUS_DAYS_DATA, HRS_TO_KEEP
|
|||||||
mylog('verbose', [f'[{pluginName}] Query: {query} '])
|
mylog('verbose', [f'[{pluginName}] Query: {query} '])
|
||||||
cursor.execute (query)
|
cursor.execute (query)
|
||||||
|
|
||||||
|
# -----------------------------------------------------
|
||||||
|
# Clear New Flag
|
||||||
|
if CLEAR_NEW_FLAG != 0:
|
||||||
|
mylog('verbose', [f'[{pluginName}] Devices: Clear "New Device" flag for all devices older than {str(CLEAR_NEW_FLAG)} hours (CLEAR_NEW_FLAG setting)'])
|
||||||
|
query = f"""UPDATE Devices SET dev_NewDevice = 0 WHERE dev_NewDevice = 1 AND date(dev_FirstConnection, '+{str(CLEAR_NEW_FLAG)} hour') < date('now')"""
|
||||||
|
# select * from Devices where dev_NewDevice = 1 AND date(dev_FirstConnection, '+3 hour' ) < date('now')
|
||||||
|
mylog('verbose', [f'[{pluginName}] Query: {query} '])
|
||||||
|
cursor.execute(query)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# -----------------------------------------------------
|
# -----------------------------------------------------
|
||||||
# Cleanup Pholus_Scan
|
# Cleanup Pholus_Scan
|
||||||
if PHOLUS_DAYS_DATA != "" and PHOLUS_DAYS_DATA != 0:
|
if PHOLUS_DAYS_DATA != "" and PHOLUS_DAYS_DATA != 0:
|
||||||
|
|||||||
@@ -198,7 +198,7 @@
|
|||||||
"elementOptions": [
|
"elementOptions": [
|
||||||
{ "sourceSuffixes": ["_in"] },
|
{ "sourceSuffixes": ["_in"] },
|
||||||
{ "separator": "" },
|
{ "separator": "" },
|
||||||
{ "cssClasses": "col-sm-2" },
|
{ "cssClasses": "col-xs-12" },
|
||||||
{ "onClick": "addList(this, false)" },
|
{ "onClick": "addList(this, false)" },
|
||||||
{ "getStringKey": "Gen_Add" }
|
{ "getStringKey": "Gen_Add" }
|
||||||
],
|
],
|
||||||
@@ -219,7 +219,7 @@
|
|||||||
"elementOptions": [
|
"elementOptions": [
|
||||||
{ "sourceSuffixes": [] },
|
{ "sourceSuffixes": [] },
|
||||||
{ "separator": "" },
|
{ "separator": "" },
|
||||||
{ "cssClasses": "col-sm-3" },
|
{ "cssClasses": "col-xs-6" },
|
||||||
{ "onClick": "removeFromList(this)" },
|
{ "onClick": "removeFromList(this)" },
|
||||||
{ "getStringKey": "Gen_Remove_Last" }
|
{ "getStringKey": "Gen_Remove_Last" }
|
||||||
],
|
],
|
||||||
@@ -230,7 +230,7 @@
|
|||||||
"elementOptions": [
|
"elementOptions": [
|
||||||
{ "sourceSuffixes": [] },
|
{ "sourceSuffixes": [] },
|
||||||
{ "separator": "" },
|
{ "separator": "" },
|
||||||
{ "cssClasses": "col-sm-3" },
|
{ "cssClasses": "col-xs-6" },
|
||||||
{ "onClick": "removeAllOptions(this)" },
|
{ "onClick": "removeAllOptions(this)" },
|
||||||
{ "getStringKey": "Gen_Remove_All" }
|
{ "getStringKey": "Gen_Remove_All" }
|
||||||
],
|
],
|
||||||
@@ -261,7 +261,7 @@
|
|||||||
"description": [
|
"description": [
|
||||||
{
|
{
|
||||||
"language_code": "en_us",
|
"language_code": "en_us",
|
||||||
"string": "All the newly discovered device names are clened up by applying the following REGEX expression in this order. All the below are replaced by a blank string."
|
"string": "All the newly discovered device names are cleaned up by applying the following REGEX expression in this order. All the below are replaced by a blank string."
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@@ -977,7 +977,27 @@
|
|||||||
"type": {
|
"type": {
|
||||||
"dataType": "string",
|
"dataType": "string",
|
||||||
"elements": [
|
"elements": [
|
||||||
{ "elementType": "select", "elementOptions": [], "transformers": [] }
|
{
|
||||||
|
"elementType": "span",
|
||||||
|
"elementOptions": [
|
||||||
|
{ "cssClasses": "input-group-addon iconPreview" },
|
||||||
|
{ "getStringKey": "Gen_SelectToPreview" },
|
||||||
|
{ "customId": "NEWDEV_dev_Icon_preview" }
|
||||||
|
],
|
||||||
|
"transformers": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"elementType": "select",
|
||||||
|
"elementHasInputValue": 1,
|
||||||
|
"elementOptions": [
|
||||||
|
{ "cssClasses": "col-xs-12" },
|
||||||
|
{
|
||||||
|
"onChange": "updateIconPreview(this)"
|
||||||
|
},
|
||||||
|
{ "customParams": "NEWDEV_dev_Icon,NEWDEV_dev_Icon_preview" }
|
||||||
|
],
|
||||||
|
"transformers": []
|
||||||
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"default_value": "",
|
"default_value": "",
|
||||||
|
|||||||
@@ -1057,7 +1057,7 @@ def main():
|
|||||||
elif values.rdns_scanning:
|
elif values.rdns_scanning:
|
||||||
file_print_pr("[DEBUG] Timestamp 45: ", timeNow())
|
file_print_pr("[DEBUG] Timestamp 45: ", timeNow())
|
||||||
dns_query=None
|
dns_query=None
|
||||||
ipn = ipaddress.ip_network(values.rdns_scanning)
|
ipn = ipaddress.ip_network(values.rdns_scanning, strict=False)
|
||||||
for ip in ipn.hosts():
|
for ip in ipn.hosts():
|
||||||
the_query = ip.reverse_pointer
|
the_query = ip.reverse_pointer
|
||||||
if not dns_query:
|
if not dns_query:
|
||||||
|
|||||||
@@ -1,41 +1,63 @@
|
|||||||
## Overview
|
## Overview
|
||||||
|
|
||||||
Synchronization plugin to synchronize multiple app instances. The Plugin can sychronize 2 types of data:
|
The synchronization plugin is designed to synchronize data across multiple instances of the app. It supports the following data synchronization modes:
|
||||||
|
|
||||||
1. 💻 Devices: The plugin sends an encrypted `table_devices.json` file to synchronize the whole Devices DB table.
|
1. **💻 Devices**: Sends an encrypted `table_devices.json` file to synchronize the entire Devices database table.
|
||||||
1. 🔌 Plugin data: The plugin sends encrypted `last_result.log` files for individual plugins.
|
2. **🔌 Plugin Data**: Sends encrypted `last_result.log` files for individual plugins.
|
||||||
|
|
||||||
> [!TIP]
|
> **Note:** `[n]` indicates a setting specified for the node instance, and `[n,h]` indicates a setting used on both the node and the hub instances.
|
||||||
> `[n]` indicates a setting that is usually specified for the node instance. `[n,h]` indicates a setting used both, on the node and on the hub instance.
|
|
||||||
|
|
||||||
### Synchronizing 💻 Devices data or 🔌 Plugins data
|
### Synchronization Modes
|
||||||
|
|
||||||
Most of the setups will probably only use 💻 Devices synchronization. 🔌 Plugins data will be probably used in only special use cases.
|
The plugin operates in three different modes based on the configuration settings:
|
||||||
|
|
||||||
#### [n] Node (Source) Settings
|
1. **Mode 1: PUSH (NODE)** - Sends data from the node to the hub.
|
||||||
|
- This mode is activated if `SYNC_hub_url` is set and either `SYNC_devices` or `SYNC_plugins` is enabled.
|
||||||
|
- **Actions**:
|
||||||
|
- Sends `table_devices.json` to the hub if `SYNC_devices` is enabled.
|
||||||
|
- Sends individual plugin `last_result.log` files to the hub if `SYNC_plugins` is enabled.
|
||||||
|
|
||||||
- When to run [n,h] `SYNC_RUN`
|
2. **Mode 2: PULL (HUB)** - Retrieves data from nodes to the hub.
|
||||||
- Schedule [n,h] `SYNC_RUN_SCHD`
|
- This mode is activated if `SYNC_nodes` is set.
|
||||||
- API token [n,h] `SYNC_api_token`
|
- **Actions**:
|
||||||
- Encryption Key [n,h] `SYNC_encryption_key`
|
- Retrieves data from configured nodes using the API and saves it locally for further processing.
|
||||||
- Node name [n] `SYNC_node_name`
|
|
||||||
- Hub URL [n] `SYNC_hub_url`
|
|
||||||
- Sync Devices [n] `SYNC_devices` or Sync Plugins [n] `SYNC_plugins` (or both)
|
|
||||||
|
|
||||||
#### [h] Hub (Target) Settings
|
3. **Mode 3: RECEIVE (HUB)** - Processes received data on the hub.
|
||||||
|
- Activated when data is received in Mode 2 and is ready to be processed.
|
||||||
|
- **Actions**:
|
||||||
|
- Decodes received data files, processes them, and updates the Devices table accordingly.
|
||||||
|
|
||||||
- When to run [n,h] `SYNC_RUN`
|
### Settings
|
||||||
- Schedule [n,h] `SYNC_RUN_SCHD`
|
|
||||||
- API token [n,h] `SYNC_api_token`
|
|
||||||
- Encryption Key [n,h] `SYNC_encryption_key`
|
|
||||||
|
|
||||||
|
#### Node (Source) Settings `[n]`
|
||||||
|
|
||||||
|
- **When to Run** `[n,h]`: `SYNC_RUN`
|
||||||
|
- **Schedule** `[n,h]`: `SYNC_RUN_SCHD`
|
||||||
|
- **API Token** `[n,h]`: `SYNC_api_token`
|
||||||
|
- **Encryption Key** `[n,h]`: `SYNC_encryption_key`
|
||||||
|
- **Node Name** `[n]`: `SYNC_node_name`
|
||||||
|
- **Hub URL** `[n]`: `SYNC_hub_url`
|
||||||
|
- **Sync Devices** `[n]`: `SYNC_devices`
|
||||||
|
- **Sync Plugins** `[n]`: `SYNC_plugins`
|
||||||
|
|
||||||
|
#### Hub (Target) Settings `[h]`
|
||||||
|
|
||||||
|
- **When to Run** `[n,h]`: `SYNC_RUN`
|
||||||
|
- **Schedule** `[n,h]`: `SYNC_RUN_SCHD`
|
||||||
|
- **API Token** `[n,h]`: `SYNC_api_token`
|
||||||
|
- **Encryption Key** `[n,h]`: `SYNC_encryption_key`
|
||||||
|
- **Nodes to Pull From** `[h]`: `SYNC_nodes`
|
||||||
|
|
||||||
### Usage
|
### Usage
|
||||||
|
|
||||||
- Head to **Settings** > **Sync Hub** to adjust the default values.
|
1. **Adjust Settings**:
|
||||||
|
- Navigate to **Settings** > **Sync Hub** to modify default settings.
|
||||||
|
2. **Data Flow**:
|
||||||
|
- Nodes send or receive data based on the specified modes, either pushing data to the hub or pulling from nodes.
|
||||||
|
|
||||||
### Notes
|
### Notes
|
||||||
|
|
||||||
- If a MAC address already exists on the hub, the device will be skipped in the data coming from this SYNC plugin.
|
- Existing devices on the hub will not be updated by the data received from this SYNC plugin if their MAC addresses are already present.
|
||||||
|
- It is recommended to use Device synchronization primarily. Plugin data synchronization is more suitable for specific use cases.
|
||||||
|
|
||||||

|

|
||||||
|
|||||||
@@ -159,6 +159,81 @@
|
|||||||
"string": "Encryption key used to encrypt the data before sending and for decryption on the hub. The key needs to be the same on the hub and on the nodes."
|
"string": "Encryption key used to encrypt the data before sending and for decryption on the hub. The key needs to be the same on the hub and on the nodes."
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
},{
|
||||||
|
"function": "nodes",
|
||||||
|
"type": {
|
||||||
|
"dataType": "array",
|
||||||
|
"elements": [
|
||||||
|
{
|
||||||
|
"elementType": "input",
|
||||||
|
"elementOptions": [
|
||||||
|
{ "placeholder": "Enter full url" },
|
||||||
|
{ "suffix": "_in" },
|
||||||
|
{ "cssClasses": "col-sm-10" },
|
||||||
|
{ "prefillValue": "null" }
|
||||||
|
],
|
||||||
|
"transformers": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"elementType": "button",
|
||||||
|
"elementOptions": [
|
||||||
|
{ "sourceSuffixes": ["_in"] },
|
||||||
|
{ "separator": "" },
|
||||||
|
{ "cssClasses": "col-xs-12" },
|
||||||
|
{ "onClick": "addList(this, false)" },
|
||||||
|
{ "getStringKey": "Gen_Add" }
|
||||||
|
],
|
||||||
|
"transformers": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"elementType": "select",
|
||||||
|
"elementHasInputValue": 1,
|
||||||
|
"elementOptions": [
|
||||||
|
{ "multiple": "true" },
|
||||||
|
{ "readonly": "true" },
|
||||||
|
{ "editable": "true" }
|
||||||
|
],
|
||||||
|
"transformers": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"elementType": "button",
|
||||||
|
"elementOptions": [
|
||||||
|
{ "sourceSuffixes": [] },
|
||||||
|
{ "separator": "" },
|
||||||
|
{ "cssClasses": "col-xs-6" },
|
||||||
|
{ "onClick": "removeAllOptions(this)" },
|
||||||
|
{ "getStringKey": "Gen_Remove_All" }
|
||||||
|
],
|
||||||
|
"transformers": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"elementType": "button",
|
||||||
|
"elementOptions": [
|
||||||
|
{ "sourceSuffixes": [] },
|
||||||
|
{ "separator": "" },
|
||||||
|
{ "cssClasses": "col-xs-6" },
|
||||||
|
{ "onClick": "removeFromList(this)" },
|
||||||
|
{ "getStringKey": "Gen_Remove_Last" }
|
||||||
|
],
|
||||||
|
"transformers": []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"default_value": [],
|
||||||
|
"options": [],
|
||||||
|
"localized": ["name", "description"],
|
||||||
|
"name": [
|
||||||
|
{
|
||||||
|
"language_code": "en_us",
|
||||||
|
"string": "Nodes [h]"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": [
|
||||||
|
{
|
||||||
|
"language_code": "en_us",
|
||||||
|
"string": "If specified, the hub will pull Devices data from the listed nodes."
|
||||||
|
}
|
||||||
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"function": "hub_url",
|
"function": "hub_url",
|
||||||
|
|||||||
@@ -3,8 +3,11 @@
|
|||||||
// External files
|
// External files
|
||||||
require '/app/front/php/server/init.php';
|
require '/app/front/php/server/init.php';
|
||||||
|
|
||||||
|
$method = $_SERVER['REQUEST_METHOD'];
|
||||||
|
|
||||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
// ----------------------------------------------
|
||||||
|
// Method to check authorization
|
||||||
|
function checkAuthorization($method) {
|
||||||
// Retrieve the authorization header
|
// Retrieve the authorization header
|
||||||
$headers = apache_request_headers();
|
$headers = apache_request_headers();
|
||||||
$auth_header = $headers['Authorization'] ?? '';
|
$auth_header = $headers['Authorization'] ?? '';
|
||||||
@@ -14,16 +17,56 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
|||||||
if ($auth_header !== $expected_token) {
|
if ($auth_header !== $expected_token) {
|
||||||
http_response_code(403);
|
http_response_code(403);
|
||||||
echo 'Forbidden';
|
echo 'Forbidden';
|
||||||
write_notification("[Plugin: SYNC] Incoming data: Incorrect API Token", "alert");
|
write_notification("[Plugin: SYNC] Incoming data: Incorrect API Token (".$method.")", "alert");
|
||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------
|
||||||
|
// Function to return JSON response
|
||||||
|
function jsonResponse($status, $data = '', $message = '') {
|
||||||
|
http_response_code($status);
|
||||||
|
header('Content-Type: application/json');
|
||||||
|
echo json_encode([
|
||||||
|
'node_name' => getSettingValue('SYNC_node_name'),
|
||||||
|
'status' => $status,
|
||||||
|
'message' => $message,
|
||||||
|
'data_base64' => $data,
|
||||||
|
'timestamp' => date('Y-m-d H:i:s')
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------
|
||||||
|
// MAIN
|
||||||
|
// ----------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
// requesting data (this is a NODE)
|
||||||
|
if ($method === 'GET') {
|
||||||
|
checkAuthorization($method);
|
||||||
|
|
||||||
|
$file_path = "/app/front/api/table_devices.json";
|
||||||
|
|
||||||
|
$data = file_get_contents($file_path);
|
||||||
|
|
||||||
|
// Prepare the data to return as a JSON response
|
||||||
|
$response_data = base64_encode($data);
|
||||||
|
|
||||||
|
// Return JSON response
|
||||||
|
jsonResponse(200, $response_data, 'OK');
|
||||||
|
|
||||||
|
write_notification("[Plugin: SYNC] Data sent", "info");
|
||||||
|
|
||||||
|
}
|
||||||
|
// receiving data (this is a HUB)
|
||||||
|
else if ($method === 'POST') {
|
||||||
|
checkAuthorization($method);
|
||||||
|
|
||||||
// Retrieve and decode the data from the POST request
|
// Retrieve and decode the data from the POST request
|
||||||
$data = $_POST['data'] ?? '';
|
$data = $_POST['data'] ?? '';
|
||||||
$plugin_folder = $_POST['plugin_folder'] ?? '';
|
$plugin_folder = $_POST['plugin_folder'] ?? '';
|
||||||
$node_name = $_POST['node_name'] ?? '';
|
$node_name = $_POST['node_name'] ?? '';
|
||||||
|
|
||||||
|
|
||||||
$storage_path = "/app/front/plugins/{$plugin_folder}";
|
$storage_path = "/app/front/plugins/{$plugin_folder}";
|
||||||
|
|
||||||
// Create the storage directory if it doesn't exist
|
// Create the storage directory if it doesn't exist
|
||||||
@@ -43,12 +86,12 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
|||||||
|
|
||||||
$file_path = "{$storage_path}/last_result.encoded.{$node_name}.{$file_count}.log";
|
$file_path = "{$storage_path}/last_result.encoded.{$node_name}.{$file_count}.log";
|
||||||
|
|
||||||
|
|
||||||
// Save the decoded data to the file
|
// Save the decoded data to the file
|
||||||
file_put_contents($file_path, $data);
|
file_put_contents($file_path, $data);
|
||||||
http_response_code(200);
|
http_response_code(200);
|
||||||
echo 'Data received and stored successfully';
|
echo 'Data received and stored successfully';
|
||||||
write_notification("[Plugin: SYNC] Data received ({$plugin_folder})", "info");
|
write_notification("[Plugin: SYNC] Data received ({$plugin_folder})", "info");
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
http_response_code(405);
|
http_response_code(405);
|
||||||
echo 'Method Not Allowed';
|
echo 'Method Not Allowed';
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import hashlib
|
|||||||
import requests
|
import requests
|
||||||
import json
|
import json
|
||||||
import sqlite3
|
import sqlite3
|
||||||
|
import base64
|
||||||
|
|
||||||
|
|
||||||
# Define the installation path and extend the system path for plugin imports
|
# Define the installation path and extend the system path for plugin imports
|
||||||
@@ -18,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
|
||||||
@@ -46,24 +47,66 @@ def main():
|
|||||||
hub_url = get_setting_value('SYNC_hub_url')
|
hub_url = get_setting_value('SYNC_hub_url')
|
||||||
node_name = get_setting_value('SYNC_node_name')
|
node_name = get_setting_value('SYNC_node_name')
|
||||||
send_devices = get_setting_value('SYNC_devices')
|
send_devices = get_setting_value('SYNC_devices')
|
||||||
|
pull_nodes = get_setting_value('SYNC_nodes')
|
||||||
|
|
||||||
|
# variables to determine operation mode
|
||||||
|
is_hub = False
|
||||||
|
is_node = False
|
||||||
|
|
||||||
|
# Check if api_token set
|
||||||
|
if not api_token:
|
||||||
|
mylog('verbose', [f'[{pluginName}] ⚠ ERROR api_token not defined - quitting.'])
|
||||||
|
return -1
|
||||||
|
|
||||||
# Get all plugin configurations
|
# check if this is a hub or a node
|
||||||
all_plugins = get_plugins_configs()
|
if len(hub_url) > 0 and (send_devices or plugins_to_sync):
|
||||||
|
is_node = True
|
||||||
|
mylog('verbose', [f'[{pluginName}] Mode 1: PUSH (NODE) - This is a NODE as SYNC_hub_url, SYNC_devices or SYNC_plugins are set'])
|
||||||
|
if len(pull_nodes) > 0:
|
||||||
|
is_hub = True
|
||||||
|
mylog('verbose', [f'[{pluginName}] Mode 2: PULL (HUB) - This is a HUB as SYNC_nodes is set'])
|
||||||
|
|
||||||
mylog('verbose', [f'[{pluginName}] plugins_to_sync {plugins_to_sync}'])
|
# Mode 1: PUSH/SEND (NODE)
|
||||||
|
if is_node:
|
||||||
|
# PUSHING/SENDING Plugins
|
||||||
|
|
||||||
|
# Get all plugin configurations
|
||||||
|
all_plugins = get_plugins_configs()
|
||||||
|
|
||||||
# Plugins processing
|
mylog('verbose', [f'[{pluginName}] plugins_to_sync {plugins_to_sync}'])
|
||||||
index = 0
|
|
||||||
for plugin in all_plugins:
|
for plugin in all_plugins:
|
||||||
pref = plugin["unique_prefix"]
|
pref = plugin["unique_prefix"]
|
||||||
|
|
||||||
if pref in plugins_to_sync:
|
index = 0
|
||||||
index += 1
|
if pref in plugins_to_sync:
|
||||||
mylog('verbose', [f'[{pluginName}] synching "{pref}" ({index}/{len(plugins_to_sync)})'])
|
index += 1
|
||||||
|
mylog('verbose', [f'[{pluginName}] synching "{pref}" ({index}/{len(plugins_to_sync)})'])
|
||||||
|
|
||||||
# Construct the file path for the plugin's last_result.log file
|
# Construct the file path for the plugin's last_result.log file
|
||||||
plugin_folder = plugin["code_name"]
|
plugin_folder = plugin["code_name"]
|
||||||
file_path = f"{INSTALL_PATH}/front/plugins/{plugin_folder}/last_result.log"
|
file_path = f"{INSTALL_PATH}/front/plugins/{plugin_folder}/last_result.log"
|
||||||
|
|
||||||
|
if os.path.exists(file_path):
|
||||||
|
# Read the content of the log file
|
||||||
|
with open(file_path, 'r') as f:
|
||||||
|
file_content = f.read()
|
||||||
|
|
||||||
|
mylog('verbose', [f'[{pluginName}] Sending file_content: "{file_content}"'])
|
||||||
|
|
||||||
|
# encrypt and send data to the hub
|
||||||
|
send_data(api_token, file_content, encryption_key, plugin_folder, node_name, pref, hub_url)
|
||||||
|
|
||||||
|
else:
|
||||||
|
mylog('verbose', [f'[{pluginName}] {plugin_folder}/last_result.log not found'])
|
||||||
|
|
||||||
|
|
||||||
|
# PUSHING/SENDING devices
|
||||||
|
if send_devices:
|
||||||
|
|
||||||
|
file_path = f"{INSTALL_PATH}/front/api/table_devices.json"
|
||||||
|
plugin_folder = 'sync'
|
||||||
|
pref = 'SYNC'
|
||||||
|
|
||||||
if os.path.exists(file_path):
|
if os.path.exists(file_path):
|
||||||
# Read the content of the log file
|
# Read the content of the log file
|
||||||
@@ -71,131 +114,160 @@ def main():
|
|||||||
file_content = f.read()
|
file_content = f.read()
|
||||||
|
|
||||||
mylog('verbose', [f'[{pluginName}] Sending file_content: "{file_content}"'])
|
mylog('verbose', [f'[{pluginName}] Sending file_content: "{file_content}"'])
|
||||||
|
|
||||||
# encrypt and send data to the hub
|
|
||||||
send_data(api_token, file_content, encryption_key, plugin_folder, node_name, pref, hub_url)
|
send_data(api_token, file_content, encryption_key, plugin_folder, node_name, pref, hub_url)
|
||||||
|
else:
|
||||||
|
mylog('verbose', [f'[{pluginName}] SYNC_hub_url not defined, skipping posting "Devices" data'])
|
||||||
|
else:
|
||||||
|
mylog('verbose', [f'[{pluginName}] SYNC_hub_url not defined, skipping posting "Plugins" and "Devices" data'])
|
||||||
|
|
||||||
else:
|
# Mode 2: PULL/GET (HUB)
|
||||||
mylog('verbose', [f'[{pluginName}] {plugin_folder}/last_result.log not found'])
|
|
||||||
|
# PULLING DEVICES
|
||||||
# Devices procesing
|
|
||||||
if send_devices:
|
|
||||||
|
|
||||||
file_path = f"{INSTALL_PATH}/front/api/table_devices.json"
|
|
||||||
plugin_folder = 'sync'
|
|
||||||
pref = 'SYNC'
|
|
||||||
|
|
||||||
if os.path.exists(file_path):
|
|
||||||
# Read the content of the log file
|
|
||||||
with open(file_path, 'r') as f:
|
|
||||||
file_content = f.read()
|
|
||||||
|
|
||||||
mylog('verbose', [f'[{pluginName}] Sending file_content: "{file_content}"'])
|
|
||||||
send_data(api_token, file_content, encryption_key, plugin_folder, node_name, pref, hub_url)
|
|
||||||
|
|
||||||
# process any received data for the Device DB table
|
|
||||||
# Create the file path
|
|
||||||
file_dir = os.path.join(pluginsPath, 'sync')
|
file_dir = os.path.join(pluginsPath, 'sync')
|
||||||
file_prefix = 'last_result'
|
file_prefix = 'last_result'
|
||||||
|
|
||||||
# Decode files, rename them, and get the list of files
|
|
||||||
files_to_process = decode_and_rename_files(file_dir, file_prefix)
|
|
||||||
|
|
||||||
# Connect to the App database
|
|
||||||
conn = sqlite3.connect(fullDbPath)
|
|
||||||
cursor = conn.cursor()
|
|
||||||
|
|
||||||
# Collect all unique dev_MAC values from the JSON files
|
|
||||||
unique_mac_addresses = set()
|
|
||||||
device_data = []
|
|
||||||
|
|
||||||
mylog('verbose', [f'[{pluginName}] Devices files to process: "{files_to_process}"'])
|
|
||||||
|
|
||||||
for file_name in files_to_process:
|
|
||||||
|
|
||||||
# only process received .log files, skipping the one logging the progress of this plugin
|
|
||||||
if file_name != 'last_result.log':
|
|
||||||
mylog('verbose', [f'[{pluginName}] Processing: "{file_name}"'])
|
|
||||||
|
|
||||||
# Store e.g. Node_1 from last_result.encoded.Node_1.1.log
|
|
||||||
tmp_SyncHubNodeName = ''
|
|
||||||
if len(file_name.split('.')) > 3:
|
|
||||||
tmp_SyncHubNodeName = file_name.split('.')[2]
|
|
||||||
|
|
||||||
|
|
||||||
file_path = f"{INSTALL_PATH}/front/plugins/sync/{file_name}"
|
|
||||||
|
|
||||||
with open(file_path, 'r') as f:
|
|
||||||
data = json.load(f)
|
|
||||||
for device in data['data']:
|
|
||||||
if device['dev_MAC'] not in unique_mac_addresses:
|
|
||||||
device['dev_SyncHubNodeName'] = tmp_SyncHubNodeName
|
|
||||||
unique_mac_addresses.add(device['dev_MAC'])
|
|
||||||
device_data.append(device)
|
|
||||||
|
|
||||||
|
|
||||||
|
# pull data from nodes if specified
|
||||||
|
if is_hub:
|
||||||
|
for node_url in pull_nodes:
|
||||||
|
response_json = get_data(api_token, node_url)
|
||||||
|
|
||||||
|
# Extract node_name and base64 data
|
||||||
|
node_name = response_json.get('node_name', 'unknown_node')
|
||||||
|
data_base64 = response_json.get('data_base64', '')
|
||||||
|
|
||||||
if len(device_data) > 0:
|
# Decode base64 data
|
||||||
# Retrieve existing dev_MAC values from the Devices table
|
decoded_data = base64.b64decode(data_base64)
|
||||||
placeholders = ', '.join('?' for _ in unique_mac_addresses)
|
|
||||||
cursor.execute(f'SELECT dev_MAC FROM Devices WHERE dev_MAC IN ({placeholders})', tuple(unique_mac_addresses))
|
# Create log file name using node name
|
||||||
existing_mac_addresses = set(row[0] for row in cursor.fetchall())
|
log_file_name = f'{file_prefix}.{node_name}.log'
|
||||||
|
|
||||||
|
# Write decoded data to log file
|
||||||
|
with open(os.path.join(file_dir, log_file_name), 'wb') as log_file:
|
||||||
|
log_file.write(decoded_data)
|
||||||
|
|
||||||
|
message = f'[{pluginName}] Device data from node "{node_name}" written to {log_file_name}'
|
||||||
|
mylog('verbose', [message])
|
||||||
|
write_notification(message, 'info', timeNowTZ())
|
||||||
|
|
||||||
|
|
||||||
# insert devices into the lats_result.log to manage state
|
# Process any received data for the Device DB table (ONLY JSON)
|
||||||
for device in device_data:
|
# Create the file path
|
||||||
if device['dev_PresentLastScan'] == 1:
|
|
||||||
plugin_objects.add_object(
|
|
||||||
primaryId = device['dev_MAC'],
|
|
||||||
secondaryId = device['dev_LastIP'],
|
|
||||||
watched1 = device['dev_Name'],
|
|
||||||
watched2 = device['dev_Vendor'],
|
|
||||||
watched3 = device['dev_SyncHubNodeName'],
|
|
||||||
watched4 = device['dev_GUID'],
|
|
||||||
extra = '',
|
|
||||||
foreignKey = device['dev_GUID'])
|
|
||||||
|
|
||||||
# Filter out existing devices
|
# Get all "last_result" files from the sync folder, decode, rename them, and get the list of files
|
||||||
new_devices = [device for device in device_data if device['dev_MAC'] not in existing_mac_addresses]
|
files_to_process = decode_and_rename_files(file_dir, file_prefix)
|
||||||
|
|
||||||
|
if len(files_to_process) > 0:
|
||||||
|
|
||||||
|
mylog('verbose', [f'[{pluginName}] Mode 3: RECEIVE (HUB) - This is a HUB as received data found'])
|
||||||
|
|
||||||
# Remove 'rowid' key if it exists
|
# Connect to the App database
|
||||||
for device in new_devices:
|
conn = sqlite3.connect(fullDbPath)
|
||||||
device.pop('rowid', None)
|
cursor = conn.cursor()
|
||||||
|
|
||||||
mylog('verbose', [f'[{pluginName}] All devices: "{len(device_data)}"'])
|
# Collect all unique dev_MAC values from the JSON files
|
||||||
mylog('verbose', [f'[{pluginName}] New devices: "{len(new_devices)}"'])
|
unique_mac_addresses = set()
|
||||||
|
device_data = []
|
||||||
|
|
||||||
# Prepare the insert statement
|
mylog('verbose', [f'[{pluginName}] Devices files to process: "{files_to_process}"'])
|
||||||
if new_devices:
|
|
||||||
|
|
||||||
columns = ', '.join(k for k in new_devices[0].keys() if k != 'rowid')
|
for file_name in files_to_process:
|
||||||
placeholders = ', '.join('?' for k in new_devices[0] if k != 'rowid')
|
|
||||||
sql = f'INSERT INTO Devices ({columns}) VALUES ({placeholders})'
|
|
||||||
|
|
||||||
# Extract values for the new devices
|
# only process received .log files, skipping the one logging the progress of this plugin
|
||||||
values = [tuple(device.values()) for device in new_devices]
|
if file_name != 'last_result.log':
|
||||||
|
mylog('verbose', [f'[{pluginName}] Processing: "{file_name}"'])
|
||||||
|
|
||||||
mylog('verbose', [f'[{pluginName}] Inserting Devices SQL : "{sql}"'])
|
# Store e.g. Node_1 from last_result.encoded.Node_1.1.log
|
||||||
mylog('verbose', [f'[{pluginName}] Inserting Devices VALUES: "{values}"'])
|
tmp_SyncHubNodeName = ''
|
||||||
|
if len(file_name.split('.')) > 3:
|
||||||
|
tmp_SyncHubNodeName = file_name.split('.')[2]
|
||||||
|
|
||||||
# Use executemany for batch insertion
|
|
||||||
cursor.executemany(sql, values)
|
|
||||||
|
|
||||||
message = f'[{pluginName}] Inserted "{len(new_devices)}" new devices'
|
file_path = f"{INSTALL_PATH}/front/plugins/sync/{file_name}"
|
||||||
|
|
||||||
|
with open(file_path, 'r') as f:
|
||||||
|
data = json.load(f)
|
||||||
|
for device in data['data']:
|
||||||
|
if device['dev_MAC'] not in unique_mac_addresses:
|
||||||
|
device['dev_SyncHubNodeName'] = tmp_SyncHubNodeName
|
||||||
|
unique_mac_addresses.add(device['dev_MAC'])
|
||||||
|
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)
|
||||||
|
|
||||||
mylog('verbose', [message])
|
# Overwrite if the new file already exists
|
||||||
write_notification(message, 'info', timeNowTZ())
|
if os.path.exists(new_file_path):
|
||||||
|
os.remove(new_file_path)
|
||||||
|
|
||||||
# Commit and close the connection
|
os.rename(file_path, new_file_path)
|
||||||
conn.commit()
|
|
||||||
conn.close()
|
|
||||||
|
|
||||||
# log result
|
if len(device_data) > 0:
|
||||||
plugin_objects.write_result_file()
|
# Retrieve existing dev_MAC values from the Devices table
|
||||||
|
placeholders = ', '.join('?' for _ in unique_mac_addresses)
|
||||||
|
cursor.execute(f'SELECT dev_MAC FROM Devices WHERE dev_MAC IN ({placeholders})', tuple(unique_mac_addresses))
|
||||||
|
existing_mac_addresses = set(row[0] for row in cursor.fetchall())
|
||||||
|
|
||||||
|
|
||||||
|
# insert devices into the lats_result.log to manage state
|
||||||
|
for device in device_data:
|
||||||
|
if device['dev_PresentLastScan'] == 1:
|
||||||
|
plugin_objects.add_object(
|
||||||
|
primaryId = device['dev_MAC'],
|
||||||
|
secondaryId = device['dev_LastIP'],
|
||||||
|
watched1 = device['dev_Name'],
|
||||||
|
watched2 = device['dev_Vendor'],
|
||||||
|
watched3 = device['dev_SyncHubNodeName'],
|
||||||
|
watched4 = device['dev_GUID'],
|
||||||
|
extra = '',
|
||||||
|
foreignKey = device['dev_GUID'])
|
||||||
|
|
||||||
|
# Filter out existing devices
|
||||||
|
new_devices = [device for device in device_data if device['dev_MAC'] not in existing_mac_addresses]
|
||||||
|
|
||||||
|
# Remove 'rowid' key if it exists
|
||||||
|
for device in new_devices:
|
||||||
|
device.pop('rowid', None)
|
||||||
|
|
||||||
|
mylog('verbose', [f'[{pluginName}] All devices: "{len(device_data)}"'])
|
||||||
|
mylog('verbose', [f'[{pluginName}] New devices: "{len(new_devices)}"'])
|
||||||
|
|
||||||
|
# Prepare the insert statement
|
||||||
|
if new_devices:
|
||||||
|
|
||||||
|
columns = ', '.join(k for k in new_devices[0].keys() if k != 'rowid')
|
||||||
|
placeholders = ', '.join('?' for k in new_devices[0] if k != 'rowid')
|
||||||
|
sql = f'INSERT INTO Devices ({columns}) VALUES ({placeholders})'
|
||||||
|
|
||||||
|
# Extract values for the new devices
|
||||||
|
values = [tuple(device.values()) for device in new_devices]
|
||||||
|
|
||||||
|
mylog('verbose', [f'[{pluginName}] Inserting Devices SQL : "{sql}"'])
|
||||||
|
mylog('verbose', [f'[{pluginName}] Inserting Devices VALUES: "{values}"'])
|
||||||
|
|
||||||
|
# Use executemany for batch insertion
|
||||||
|
cursor.executemany(sql, values)
|
||||||
|
|
||||||
|
message = f'[{pluginName}] Inserted "{len(new_devices)}" new devices'
|
||||||
|
|
||||||
|
mylog('verbose', [message])
|
||||||
|
write_notification(message, 'info', timeNowTZ())
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# Commit and close the connection
|
||||||
|
conn.commit()
|
||||||
|
conn.close()
|
||||||
|
|
||||||
|
# log result
|
||||||
|
plugin_objects.write_result_file()
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
# send data to the HUB
|
||||||
def send_data(api_token, file_content, encryption_key, plugin_folder, node_name, pref, hub_url):
|
def send_data(api_token, file_content, encryption_key, plugin_folder, node_name, pref, hub_url):
|
||||||
# Encrypt the log data using the encryption_key
|
# Encrypt the log data using the encryption_key
|
||||||
encrypted_data = encrypt_data(file_content, encryption_key)
|
encrypted_data = encrypt_data(file_content, encryption_key)
|
||||||
@@ -223,6 +295,36 @@ def send_data(api_token, file_content, encryption_key, plugin_folder, node_name,
|
|||||||
message = f'[{pluginName}] Failed to send data for "{plugin_folder}" (Status code: {response.status_code})'
|
message = f'[{pluginName}] Failed to send data for "{plugin_folder}" (Status code: {response.status_code})'
|
||||||
mylog('verbose', [message])
|
mylog('verbose', [message])
|
||||||
write_notification(message, 'alert', timeNowTZ())
|
write_notification(message, 'alert', timeNowTZ())
|
||||||
|
|
||||||
|
# get data from the nodes to the HUB
|
||||||
|
def get_data(api_token, node_url):
|
||||||
|
mylog('verbose', [f'[{pluginName}] Getting data from node: "{node_url}"'])
|
||||||
|
|
||||||
|
# Set the authorization header with the API token
|
||||||
|
headers = {'Authorization': f'Bearer {api_token}'}
|
||||||
|
api_endpoint = f"{node_url}/plugins/sync/hub.php"
|
||||||
|
response = requests.get(api_endpoint, headers=headers)
|
||||||
|
|
||||||
|
# mylog('verbose', [f'[{pluginName}] response: "{response.text}"'])
|
||||||
|
|
||||||
|
if response.status_code == 200:
|
||||||
|
try:
|
||||||
|
# Parse JSON response
|
||||||
|
response_json = response.json()
|
||||||
|
|
||||||
|
return response_json
|
||||||
|
|
||||||
|
except json.JSONDecodeError:
|
||||||
|
message = f'[{pluginName}] Failed to parse JSON response from "{node_url}"'
|
||||||
|
mylog('verbose', [message])
|
||||||
|
write_notification(message, 'alert', timeNowTZ())
|
||||||
|
return ""
|
||||||
|
|
||||||
|
else:
|
||||||
|
message = f'[{pluginName}] Failed to send data for "{node_url}" (Status code: {response.status_code})'
|
||||||
|
mylog('verbose', [message])
|
||||||
|
write_notification(message, 'alert', timeNowTZ())
|
||||||
|
return ""
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -163,16 +163,15 @@
|
|||||||
},
|
},
|
||||||
"maxLength": 50,
|
"maxLength": 50,
|
||||||
"default_value": [
|
"default_value": [
|
||||||
"PGkgY2xhc3M9J2ZhIGZhLXdpZmknPjwvaT4=",
|
|
||||||
"PGkgY2xhc3M9ImZhIGZhLWNvbXB1dGVyIj48L2k+",
|
"PGkgY2xhc3M9ImZhIGZhLWNvbXB1dGVyIj48L2k+",
|
||||||
"PGkgY2xhc3M9ImZhIGZhLWV0aGVybmV0Ij48L2k+",
|
"PGkgY2xhc3M9ImZhIGZhLWV0aGVybmV0Ij48L2k+",
|
||||||
"PGkgY2xhc3M9ImZhIGZhLWdhbWVwYWQiPjwvaT4",
|
"PGkgY2xhc3M9ImZhIGZhLWdhbWVwYWQiPjwvaT4=",
|
||||||
"PGkgY2xhc3M9ImZhIGZhLWdsb2JlIj48L2k+",
|
"PGkgY2xhc3M9ImZhIGZhLWdsb2JlIj48L2k+",
|
||||||
"PGkgY2xhc3M9ImZhIGZhLWxhcHRvcCI+PC9pPg==",
|
"PGkgY2xhc3M9ImZhIGZhLWxhcHRvcCI+PC9pPg==",
|
||||||
"PGkgY2xhc3M9ImZhIGZhLWxpZ2h0YnVsYiI+PC9pPg==",
|
"PGkgY2xhc3M9ImZhIGZhLWxpZ2h0YnVsYiI+PC9pPg==",
|
||||||
"PGkgY2xhc3M9ImZhIGZhLXNoaWVsZCI+PC9pPg==",
|
"PGkgY2xhc3M9ImZhIGZhLXNoaWVsZCI+PC9pPg==",
|
||||||
"PGkgY2xhc3M9ImZhIGZhLXdpZmkiPjwvaT4",
|
"PGkgY2xhc3M9ImZhIGZhLXdpZmkiPjwvaT4=",
|
||||||
"PGkgY2xhc3M9J2ZhIGZhLWdhbWVwYWQnPjwvaT4"
|
"PGkgY2xhc3M9ImZhIGZhLWdhbWVwYWQiPjwvaT4="
|
||||||
],
|
],
|
||||||
"options": [],
|
"options": [],
|
||||||
"localized": [],
|
"localized": [],
|
||||||
@@ -254,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": [
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -25,28 +25,12 @@
|
|||||||
{
|
{
|
||||||
"language_code": "en_us",
|
"language_code": "en_us",
|
||||||
"string": "<i class=\"fa-solid fa-binoculars\"></i>"
|
"string": "<i class=\"fa-solid fa-binoculars\"></i>"
|
||||||
},
|
|
||||||
{
|
|
||||||
"language_code": "es_es",
|
|
||||||
"string": "<i class=\"fa-solid fa-binoculars\"></i>"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"language_code": "de_de",
|
|
||||||
"string": "<i class=\"fa-solid fa-binoculars\"></i>"
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"description": [
|
"description": [
|
||||||
{
|
{
|
||||||
"language_code": "en_us",
|
"language_code": "en_us",
|
||||||
"string": "This plugin is to import undiscoverable devices from a file."
|
"string": "This plugin is to import undiscoverable devices from a file. Only ASCII characters are supported."
|
||||||
},
|
|
||||||
{
|
|
||||||
"language_code": "es_es",
|
|
||||||
"string": "Este complemento es para importar dispositivos no detectables desde un archivo."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"language_code": "de_de",
|
|
||||||
"string": "Ein Plugin zum Importieren von nicht erkennbaren Geräten aus einer Datei."
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"params": [
|
"params": [
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ def main():
|
|||||||
fake_mac = string_to_mac_hash(fake_dev)
|
fake_mac = string_to_mac_hash(fake_dev)
|
||||||
|
|
||||||
plugin_objects.add_object(
|
plugin_objects.add_object(
|
||||||
primaryId=fake_dev, # MAC (Device Name)
|
primaryId=fake_mac, # MAC (Device Name)
|
||||||
secondaryId="0.0.0.0", # IP Address (always 0.0.0.0)
|
secondaryId="0.0.0.0", # IP Address (always 0.0.0.0)
|
||||||
watched1=fake_dev, # Device Name
|
watched1=fake_dev, # Device Name
|
||||||
watched2="",
|
watched2="",
|
||||||
|
|||||||
@@ -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,16 +568,19 @@ 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) {
|
||||||
showModalOk ('Result', data );
|
updateApi("plugins_objects")
|
||||||
|
showModalOk ('Result', data );
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -99,7 +99,7 @@
|
|||||||
// Function to update the displayed data and timestamp based on the selected format and index
|
// Function to update the displayed data and timestamp based on the selected format and index
|
||||||
function updateData(format, index) {
|
function updateData(format, index) {
|
||||||
// Fetch data from the API endpoint
|
// Fetch data from the API endpoint
|
||||||
fetch('/api/table_notifications.json?nocache=' + Date.now())
|
fetch('api/table_notifications.json?nocache=' + Date.now())
|
||||||
.then(response => response.json())
|
.then(response => response.json())
|
||||||
.then(data => {
|
.then(data => {
|
||||||
if (index < 0) {
|
if (index < 0) {
|
||||||
@@ -163,7 +163,7 @@
|
|||||||
const urlParams = new URLSearchParams(window.location.search);
|
const urlParams = new URLSearchParams(window.location.search);
|
||||||
if (urlParams.has('guid')) {
|
if (urlParams.has('guid')) {
|
||||||
const guid = urlParams.get('guid');
|
const guid = urlParams.get('guid');
|
||||||
fetch('/api/table_notifications.json')
|
fetch('api/table_notifications.json')
|
||||||
.then(response => response.json())
|
.then(response => response.json())
|
||||||
.then(data => {
|
.then(data => {
|
||||||
const index = findIndexByGUID(data.data, guid);
|
const index = findIndexByGUID(data.data, guid);
|
||||||
|
|||||||
@@ -378,6 +378,7 @@ $settingsJSON_DB = json_encode($settings, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX
|
|||||||
|
|
||||||
const valIn = set['Value'];
|
const valIn = set['Value'];
|
||||||
const codeName = set['Code_Name'];
|
const codeName = set['Code_Name'];
|
||||||
|
const overriddenByEnv = set['OverriddenByEnv'] == 1;
|
||||||
const setType = set['Type'];
|
const setType = set['Type'];
|
||||||
const isMetadata = codeName.includes('__metadata');
|
const isMetadata = codeName.includes('__metadata');
|
||||||
// is this isn't a metadata entry, get corresponding metadata object from the dummy setting
|
// is this isn't a metadata entry, get corresponding metadata object from the dummy setting
|
||||||
@@ -416,11 +417,11 @@ $settingsJSON_DB = json_encode($settings, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX
|
|||||||
<div class="table_cell setting_description">
|
<div class="table_cell setting_description">
|
||||||
${getString(codeName + '_description', set['Description'])}
|
${getString(codeName + '_description', set['Description'])}
|
||||||
</div>
|
</div>
|
||||||
<div class="table_cell setting_input input-group col-sm-12">
|
<div class="table_cell input-group setting_input ${overriddenByEnv ? "setting_overriden_by_env" : ""} input-group col-sm-12">
|
||||||
`;
|
`;
|
||||||
|
|
||||||
// OVERRIDE
|
// OVERRIDE
|
||||||
// surface settings override functionality if the setting is a template that can be overriden with user defined values
|
// surface settings override functionality if the setting is a template that can be overridden with user defined values
|
||||||
// if the setting is a json of the correct structure, handle like a template setting
|
// if the setting is a json of the correct structure, handle like a template setting
|
||||||
|
|
||||||
let overrideHtml = "";
|
let overrideHtml = "";
|
||||||
@@ -428,7 +429,7 @@ $settingsJSON_DB = json_encode($settings, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX
|
|||||||
//pre-check if this is a json object that needs value extraction
|
//pre-check if this is a json object that needs value extraction
|
||||||
|
|
||||||
let overridable = false; // indicates if the setting is overridable
|
let overridable = false; // indicates if the setting is overridable
|
||||||
let override = false; // If the setting is set to be overriden by the user or by default
|
let override = false; // If the setting is set to be overridden by the user or by default
|
||||||
let readonly = ""; // helper variable to make text input readonly
|
let readonly = ""; // helper variable to make text input readonly
|
||||||
let disabled = ""; // helper variable to make checkbox input readonly
|
let disabled = ""; // helper variable to make checkbox input readonly
|
||||||
|
|
||||||
@@ -485,7 +486,10 @@ $settingsJSON_DB = json_encode($settings, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX
|
|||||||
editable,
|
editable,
|
||||||
valRes,
|
valRes,
|
||||||
getStringKey,
|
getStringKey,
|
||||||
onClick
|
onClick,
|
||||||
|
onChange,
|
||||||
|
customParams,
|
||||||
|
customId
|
||||||
} = handleElementOptions(codeName, elementOptions, transformers, valIn);
|
} = handleElementOptions(codeName, elementOptions, transformers, valIn);
|
||||||
|
|
||||||
// override
|
// override
|
||||||
@@ -498,7 +502,15 @@ $settingsJSON_DB = json_encode($settings, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX
|
|||||||
let addCss = isOrdeable ? "select2 select2-hidden-accessible" : "";
|
let addCss = isOrdeable ? "select2 select2-hidden-accessible" : "";
|
||||||
|
|
||||||
|
|
||||||
inputHtml += `<select onChange="settingsChanged()" my-data-type="${dataType}" my-editable="${editable}" class="form-control ${addCss}" name="${codeName}" id="${codeName}" ${multi}>
|
inputHtml += `<select onChange="settingsChanged();${onChange}"
|
||||||
|
my-data-type="${dataType}"
|
||||||
|
my-editable="${editable}"
|
||||||
|
class="form-control ${addCss}"
|
||||||
|
name="${codeName}"
|
||||||
|
id="${codeName}"
|
||||||
|
my-customparams="${customParams}"
|
||||||
|
my-customid="${customId}"
|
||||||
|
${multi}>
|
||||||
<option value="" id="${codeName + "_temp_"}"></option>
|
<option value="" id="${codeName + "_temp_"}"></option>
|
||||||
</select>`;
|
</select>`;
|
||||||
|
|
||||||
@@ -513,8 +525,10 @@ $settingsJSON_DB = json_encode($settings, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX
|
|||||||
inputHtml += `
|
inputHtml += `
|
||||||
<input
|
<input
|
||||||
class="${inputClass} ${cssClasses}"
|
class="${inputClass} ${cssClasses}"
|
||||||
onChange="settingsChanged()"
|
onChange="settingsChanged();${onChange}"
|
||||||
my-data-type="${dataType}"
|
my-data-type="${dataType}"
|
||||||
|
my-customparams="${customParams}"
|
||||||
|
my-customid="${customId}"
|
||||||
id="${codeName}${suffix}"
|
id="${codeName}${suffix}"
|
||||||
type="${inputType}"
|
type="${inputType}"
|
||||||
value="${val}"
|
value="${val}"
|
||||||
@@ -529,6 +543,8 @@ $settingsJSON_DB = json_encode($settings, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX
|
|||||||
inputHtml += `
|
inputHtml += `
|
||||||
<button
|
<button
|
||||||
class="btn btn-primary ${cssClasses}"
|
class="btn btn-primary ${cssClasses}"
|
||||||
|
my-customparams="${customParams}"
|
||||||
|
my-customid="${customId}"
|
||||||
my-input-from="${sourceIds}"
|
my-input-from="${sourceIds}"
|
||||||
my-input-to="${codeName}"
|
my-input-to="${codeName}"
|
||||||
onclick="${onClick}">
|
onclick="${onClick}">
|
||||||
@@ -539,12 +555,25 @@ $settingsJSON_DB = json_encode($settings, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX
|
|||||||
inputHtml += `
|
inputHtml += `
|
||||||
<textarea
|
<textarea
|
||||||
class="form-control input"
|
class="form-control input"
|
||||||
|
my-customparams="${customParams}"
|
||||||
|
my-customid="${customId}"
|
||||||
my-data-type="${dataType}"
|
my-data-type="${dataType}"
|
||||||
id="${codeName}"
|
id="${codeName}"
|
||||||
${readOnly}>
|
${readOnly}>
|
||||||
${val}
|
${val}
|
||||||
</textarea>`;
|
</textarea>`;
|
||||||
break;
|
break;
|
||||||
|
case 'span':
|
||||||
|
inputHtml += `
|
||||||
|
<span
|
||||||
|
class="${cssClasses}"
|
||||||
|
my-data-type="${dataType}"
|
||||||
|
my-customparams="${customParams}"
|
||||||
|
my-customid="${customId}"
|
||||||
|
>
|
||||||
|
${getString(getStringKey)}
|
||||||
|
</span>`;
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
console.warn(`🟥Unknown element type: ${elementType}`);
|
console.warn(`🟥Unknown element type: ${elementType}`);
|
||||||
@@ -640,12 +669,21 @@ $settingsJSON_DB = json_encode($settings, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX
|
|||||||
// console.log(setTypeObject);
|
// console.log(setTypeObject);
|
||||||
|
|
||||||
const dataType = setTypeObject.dataType;
|
const dataType = setTypeObject.dataType;
|
||||||
// const lastElementObj = setTypeObject.elements[setTypeObject.elements.length - 1]; //🔽
|
|
||||||
|
|
||||||
// get the element with the input value(s)
|
// get the element with the input value(s)
|
||||||
const elementsWithInputValue = setTypeObject.elements.filter(element => element.elementHasInputValue === 1);
|
let elements = setTypeObject.elements.filter(element => element.elementHasInputValue === 1);
|
||||||
|
|
||||||
const { elementType, elementOptions = [], transformers = [] } = elementsWithInputValue;
|
// if none found, take last
|
||||||
|
if(elements.length == 0)
|
||||||
|
{
|
||||||
|
elementWithInputValue = setTypeObject.elements[setTypeObject.elements.length - 1]
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
elementWithInputValue = elements[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
const { elementType, elementOptions = [], transformers = [] } = elementWithInputValue;
|
||||||
const {
|
const {
|
||||||
inputType,
|
inputType,
|
||||||
readOnly,
|
readOnly,
|
||||||
@@ -659,21 +697,31 @@ $settingsJSON_DB = json_encode($settings, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX
|
|||||||
editable,
|
editable,
|
||||||
valRes,
|
valRes,
|
||||||
getStringKey,
|
getStringKey,
|
||||||
onClick
|
onClick,
|
||||||
|
onChange,
|
||||||
|
customParams,
|
||||||
|
customId
|
||||||
} = handleElementOptions('none', elementOptions, transformers, val = "");
|
} = handleElementOptions('none', elementOptions, transformers, val = "");
|
||||||
|
|
||||||
let value;
|
let value;
|
||||||
|
|
||||||
if (dataType === "string" ||
|
if (dataType === "string" ||
|
||||||
(dataType === "integer" && (inputType === "number" || inputType === "text"))) {
|
(dataType === "integer" && (inputType === "number" || inputType === "text"))) {
|
||||||
|
|
||||||
value = $('#' + setCodeName).val();
|
value = $('#' + setCodeName).val();
|
||||||
value = applyTransformers(value, transformers);
|
value = applyTransformers(value, transformers);
|
||||||
|
|
||||||
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;
|
||||||
|
|
||||||
|
if(dataType === "boolean")
|
||||||
|
{
|
||||||
|
value = value == 1 ? "True" : "False";
|
||||||
|
}
|
||||||
|
|
||||||
value = applyTransformers(value, transformers);
|
value = applyTransformers(value, transformers);
|
||||||
settingsArray.push([prefix, setCodeName, dataType, value]);
|
settingsArray.push([prefix, setCodeName, dataType, value]);
|
||||||
|
|
||||||
@@ -710,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]);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -745,12 +794,14 @@ $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("🔼");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -813,7 +864,8 @@ $settingsJSON_DB = json_encode($settings, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX
|
|||||||
|
|
||||||
// Reload page if outdated information might be displayed
|
// Reload page if outdated information might be displayed
|
||||||
if (secondsSincePageLoad() > 10) {
|
if (secondsSincePageLoad() > 10) {
|
||||||
clearCache();
|
console.log("App outdated, reloading...");
|
||||||
|
clearCache();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
console.log("App not initialized, checking again in 1s...");
|
console.log("App not initialized, checking again in 1s...");
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ require 'php/templates/header.php';
|
|||||||
<script>
|
<script>
|
||||||
function fetchData(callback) {
|
function fetchData(callback) {
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: '/api/user_notifications.json?nocache=' + Date.now(),
|
url: 'api/user_notifications.json?nocache=' + Date.now(),
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
dataType: 'json',
|
dataType: 'json',
|
||||||
success: function(response) {
|
success: function(response) {
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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],
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|||||||
@@ -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 (
|
||||||
|
|||||||
@@ -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):
|
||||||
@@ -101,7 +101,7 @@ class DB():
|
|||||||
mylog('none','[upgradeDB] Table is incompatible, Dropping the Online_History table')
|
mylog('none','[upgradeDB] Table is incompatible, Dropping the Online_History table')
|
||||||
self.sql.execute("DROP TABLE Online_History;")
|
self.sql.execute("DROP TABLE Online_History;")
|
||||||
onlineHistoryAvailable = False
|
onlineHistoryAvailable = False
|
||||||
|
|
||||||
if onlineHistoryAvailable == False :
|
if onlineHistoryAvailable == False :
|
||||||
self.sql.execute("""
|
self.sql.execute("""
|
||||||
CREATE TABLE "Online_History" (
|
CREATE TABLE "Online_History" (
|
||||||
@@ -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
|
||||||
# -------------------------------------------------------------------------
|
# -------------------------------------------------------------------------
|
||||||
@@ -213,15 +225,16 @@ class DB():
|
|||||||
self.sql.execute(""" DROP TABLE IF EXISTS Settings;""")
|
self.sql.execute(""" DROP TABLE IF EXISTS Settings;""")
|
||||||
self.sql.execute("""
|
self.sql.execute("""
|
||||||
CREATE TABLE "Settings" (
|
CREATE TABLE "Settings" (
|
||||||
"Code_Name" TEXT,
|
"Code_Name" TEXT,
|
||||||
"Display_Name" TEXT,
|
"Display_Name" TEXT,
|
||||||
"Description" TEXT,
|
"Description" TEXT,
|
||||||
"Type" TEXT,
|
"Type" TEXT,
|
||||||
"Options" TEXT,
|
"Options" TEXT,
|
||||||
"RegEx" TEXT,
|
"RegEx" TEXT,
|
||||||
"Value" TEXT,
|
"Group" TEXT,
|
||||||
"Group" TEXT,
|
"Value" TEXT,
|
||||||
"Events" TEXT
|
"Events" TEXT,
|
||||||
|
"OverriddenByEnv" INTEGER
|
||||||
);
|
);
|
||||||
""")
|
""")
|
||||||
|
|
||||||
@@ -277,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,
|
||||||
@@ -292,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,
|
||||||
@@ -643,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()
|
|
||||||
@@ -4,7 +4,7 @@ import subprocess
|
|||||||
import conf
|
import conf
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
from helper import timeNowTZ, get_setting, get_setting_value, list_to_where, resolve_device_name_dig, resolve_device_name_pholus, get_device_name_nbtlookup, get_device_name_nslookup, check_IP_format
|
from helper import timeNowTZ, get_setting, get_setting_value, list_to_where, resolve_device_name_dig, resolve_device_name_pholus, get_device_name_nbtlookup, get_device_name_nslookup, check_IP_format, sanitize_SQL_input
|
||||||
from logger import mylog, print_log
|
from logger import mylog, print_log
|
||||||
from const import vendorsPath, vendorsPathNewest, sql_generateGuid
|
from const import vendorsPath, vendorsPathNewest, sql_generateGuid
|
||||||
|
|
||||||
@@ -192,12 +192,12 @@ def create_new_devices (db):
|
|||||||
{get_setting_value('NEWDEV_dev_NewDevice')},
|
{get_setting_value('NEWDEV_dev_NewDevice')},
|
||||||
{get_setting_value('NEWDEV_dev_SkipRepeated')},
|
{get_setting_value('NEWDEV_dev_SkipRepeated')},
|
||||||
{get_setting_value('NEWDEV_dev_ScanCycle')},
|
{get_setting_value('NEWDEV_dev_ScanCycle')},
|
||||||
'{get_setting_value('NEWDEV_dev_Owner')}',
|
'{sanitize_SQL_input(get_setting_value('NEWDEV_dev_Owner'))}',
|
||||||
{get_setting_value('NEWDEV_dev_Favorite')},
|
{get_setting_value('NEWDEV_dev_Favorite')},
|
||||||
'{get_setting_value('NEWDEV_dev_Group')}',
|
'{sanitize_SQL_input(get_setting_value('NEWDEV_dev_Group'))}',
|
||||||
'{get_setting_value('NEWDEV_dev_Comments')}',
|
'{sanitize_SQL_input(get_setting_value('NEWDEV_dev_Comments'))}',
|
||||||
{get_setting_value('NEWDEV_dev_LogEvents')},
|
{get_setting_value('NEWDEV_dev_LogEvents')},
|
||||||
'{get_setting_value('NEWDEV_dev_Location')}'"""
|
'{sanitize_SQL_input(get_setting_value('NEWDEV_dev_Location'))}'"""
|
||||||
|
|
||||||
# Fetch data from CurrentScan
|
# Fetch data from CurrentScan
|
||||||
current_scan_data = sql.execute("SELECT cur_MAC, cur_Name, cur_Vendor, cur_IP, cur_SyncHubNodeName, cur_NetworkNodeMAC, cur_PORT, cur_NetworkSite, cur_SSID, cur_Type FROM CurrentScan").fetchall()
|
current_scan_data = sql.execute("SELECT cur_MAC, cur_Name, cur_Vendor, cur_IP, cur_SyncHubNodeName, cur_NetworkNodeMAC, cur_PORT, cur_NetworkSite, cur_SSID, cur_Type FROM CurrentScan").fetchall()
|
||||||
@@ -210,6 +210,7 @@ def create_new_devices (db):
|
|||||||
cur_Type = cur_Type.strip() if cur_Type else get_setting_value("NEWDEV_dev_DeviceType")
|
cur_Type = cur_Type.strip() if cur_Type else get_setting_value("NEWDEV_dev_DeviceType")
|
||||||
cur_NetworkNodeMAC = cur_NetworkNodeMAC.strip() if cur_NetworkNodeMAC else ''
|
cur_NetworkNodeMAC = cur_NetworkNodeMAC.strip() if cur_NetworkNodeMAC else ''
|
||||||
cur_NetworkNodeMAC = cur_NetworkNodeMAC if cur_NetworkNodeMAC and cur_MAC != "Internet" else (get_setting_value("NEWDEV_dev_Network_Node_MAC_ADDR") if cur_MAC != "Internet" else "null")
|
cur_NetworkNodeMAC = cur_NetworkNodeMAC if cur_NetworkNodeMAC and cur_MAC != "Internet" else (get_setting_value("NEWDEV_dev_Network_Node_MAC_ADDR") if cur_MAC != "Internet" else "null")
|
||||||
|
cur_SyncHubNodeName = cur_SyncHubNodeName if cur_SyncHubNodeName and cur_SyncHubNodeName != "null" else (get_setting_value("SYNC_node_name"))
|
||||||
|
|
||||||
# Preparing the individual insert statement
|
# Preparing the individual insert statement
|
||||||
sqlQuery = f"""INSERT OR IGNORE INTO Devices
|
sqlQuery = f"""INSERT OR IGNORE INTO Devices
|
||||||
@@ -231,19 +232,19 @@ def create_new_devices (db):
|
|||||||
)
|
)
|
||||||
VALUES
|
VALUES
|
||||||
(
|
(
|
||||||
'{cur_MAC}',
|
'{sanitize_SQL_input(cur_MAC)}',
|
||||||
'{cur_Name}',
|
'{sanitize_SQL_input(cur_Name)}',
|
||||||
'{cur_Vendor}',
|
'{sanitize_SQL_input(cur_Vendor)}',
|
||||||
'{cur_IP}',
|
'{sanitize_SQL_input(cur_IP)}',
|
||||||
?,
|
?,
|
||||||
?,
|
?,
|
||||||
'{cur_SyncHubNodeName}',
|
'{sanitize_SQL_input(cur_SyncHubNodeName)}',
|
||||||
{sql_generateGuid},
|
{sql_generateGuid},
|
||||||
'{cur_NetworkNodeMAC}',
|
'{sanitize_SQL_input(cur_NetworkNodeMAC)}',
|
||||||
'{cur_PORT}',
|
'{sanitize_SQL_input(cur_PORT)}',
|
||||||
'{cur_NetworkSite}',
|
'{sanitize_SQL_input(cur_NetworkSite)}',
|
||||||
'{cur_SSID}',
|
'{sanitize_SQL_input(cur_SSID)}',
|
||||||
'{cur_Type}',
|
'{sanitize_SQL_input(cur_Type)}',
|
||||||
{newDevDefaults}
|
{newDevDefaults}
|
||||||
)"""
|
)"""
|
||||||
|
|
||||||
@@ -637,8 +638,8 @@ icons = {
|
|||||||
def guess_icon(vendor, mac, ip, name, default):
|
def guess_icon(vendor, mac, ip, name, default):
|
||||||
result = default
|
result = default
|
||||||
mac = mac.upper()
|
mac = mac.upper()
|
||||||
vendor = vendor.lower()
|
vendor = vendor.lower() if vendor else "unknown"
|
||||||
name = name.lower()
|
name = name.lower() if name else "(unknown)"
|
||||||
|
|
||||||
# Guess icon based on vendor
|
# Guess icon based on vendor
|
||||||
if any(brand in vendor for brand in {"samsung", "motorola"}):
|
if any(brand in vendor for brand in {"samsung", "motorola"}):
|
||||||
@@ -693,8 +694,8 @@ def guess_icon(vendor, mac, ip, name, default):
|
|||||||
def guess_type(vendor, mac, ip, name, default):
|
def guess_type(vendor, mac, ip, name, default):
|
||||||
result = default
|
result = default
|
||||||
mac = mac.upper()
|
mac = mac.upper()
|
||||||
vendor = vendor.lower()
|
vendor = vendor.lower() if vendor else "unknown"
|
||||||
name = name.lower()
|
name = name.lower() if name else "(unknown)"
|
||||||
|
|
||||||
# Guess icon based on vendor
|
# Guess icon based on vendor
|
||||||
if any(brand in vendor for brand in {"samsung", "motorola"}):
|
if any(brand in vendor for brand in {"samsung", "motorola"}):
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -319,7 +319,7 @@ def get_setting_value(key):
|
|||||||
set_type = 'Error: Not handled'
|
set_type = 'Error: Not handled'
|
||||||
set_value = 'Error: Not handled'
|
set_value = 'Error: Not handled'
|
||||||
|
|
||||||
set_value = setting["Value"] # Setting value (Value (upper case) = user overriden default_value)
|
set_value = setting["Value"] # Setting value (Value (upper case) = user overridden default_value)
|
||||||
set_type = setting["Type"] # Setting type # lower case "type" - default json value vs uppper-case "Type" (= from user defined settings)
|
set_type = setting["Type"] # Setting type # lower case "type" - default json value vs uppper-case "Type" (= from user defined settings)
|
||||||
|
|
||||||
value = setting_value_to_python_type(set_type, set_value)
|
value = setting_value_to_python_type(set_type, set_value)
|
||||||
@@ -806,6 +806,21 @@ def sanitize_string(input):
|
|||||||
return input
|
return input
|
||||||
|
|
||||||
|
|
||||||
|
#-------------------------------------------------------------------------------
|
||||||
|
def sanitize_SQL_input(val):
|
||||||
|
if val is None:
|
||||||
|
return ''
|
||||||
|
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):
|
||||||
|
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ from notification import write_notification
|
|||||||
|
|
||||||
#-------------------------------------------------------------------------------
|
#-------------------------------------------------------------------------------
|
||||||
# managing application settings, ensuring SQL safety for user input, and updating internal configuration lists
|
# managing application settings, ensuring SQL safety for user input, and updating internal configuration lists
|
||||||
def ccd(key, default, config_dir, name, inputtype, options, group, events=None, desc="", regex="", setJsonMetadata=None, overrideTemplate=None, forceDefault=False):
|
def ccd(key, default, config_dir, name, inputtype, options, group, events=None, desc="", regex="", setJsonMetadata=None, overrideTemplate=None, forceDefault=False, overriddenByEnv=0):
|
||||||
if events is None:
|
if events is None:
|
||||||
events = []
|
events = []
|
||||||
if setJsonMetadata is None:
|
if setJsonMetadata is None:
|
||||||
@@ -50,8 +50,8 @@ def ccd(key, default, config_dir, name, inputtype, options, group, events=None,
|
|||||||
result = result.replace('\'', "{s-quote}")
|
result = result.replace('\'', "{s-quote}")
|
||||||
|
|
||||||
# Create the tuples
|
# Create the tuples
|
||||||
sql_safe_tuple = (key, name, desc, str(inputtype), options, regex, str(result), group, str(events))
|
sql_safe_tuple = (key, name, desc, str(inputtype), options, regex, str(result), group, str(events), overriddenByEnv)
|
||||||
settings_tuple = (key, name, desc, inputtype, options, regex, result, group, str(events))
|
settings_tuple = (key, name, desc, inputtype, options, regex, result, group, str(events), overriddenByEnv)
|
||||||
|
|
||||||
# Update or append the tuples in the lists
|
# Update or append the tuples in the lists
|
||||||
conf.mySettingsSQLsafe = update_or_append(conf.mySettingsSQLsafe, sql_safe_tuple, key)
|
conf.mySettingsSQLsafe = update_or_append(conf.mySettingsSQLsafe, sql_safe_tuple, key)
|
||||||
@@ -59,7 +59,7 @@ def ccd(key, default, config_dir, name, inputtype, options, group, events=None,
|
|||||||
|
|
||||||
# Save metadata in dummy setting if not a metadata key
|
# Save metadata in dummy setting if not a metadata key
|
||||||
if '__metadata' not in key:
|
if '__metadata' not in key:
|
||||||
metadata_tuple = (f'{key}__metadata', "metadata name", "metadata desc", '{"dataType":"json", "elements": [{"elementType" : "textarea", "elementOptions" : [{"readonly": "true"}] ,"transformers": []}]}', '[]', "", json.dumps(setJsonMetadata), group, '[]')
|
metadata_tuple = (f'{key}__metadata', "metadata name", "metadata desc", '{"dataType":"json", "elements": [{"elementType" : "textarea", "elementOptions" : [{"readonly": "true"}] ,"transformers": []}]}', '[]', "", json.dumps(setJsonMetadata), group, '[]', overriddenByEnv)
|
||||||
conf.mySettingsSQLsafe = update_or_append(conf.mySettingsSQLsafe, metadata_tuple, f'{key}__metadata')
|
conf.mySettingsSQLsafe = update_or_append(conf.mySettingsSQLsafe, metadata_tuple, f'{key}__metadata')
|
||||||
conf.mySettings = update_or_append(conf.mySettings, metadata_tuple, f'{key}__metadata')
|
conf.mySettings = update_or_append(conf.mySettings, metadata_tuple, f'{key}__metadata')
|
||||||
|
|
||||||
@@ -145,6 +145,7 @@ def importConfigs (db, all_plugins):
|
|||||||
conf.REPORT_DASHBOARD_URL = ccd('REPORT_DASHBOARD_URL', 'http://netalertx/' , c_d, 'NetAlertX URL', '{"dataType":"string", "elements": [{"elementType" : "input", "elementOptions" : [] ,"transformers": []}]}', '[]', 'General')
|
conf.REPORT_DASHBOARD_URL = ccd('REPORT_DASHBOARD_URL', 'http://netalertx/' , c_d, 'NetAlertX URL', '{"dataType":"string", "elements": [{"elementType" : "input", "elementOptions" : [] ,"transformers": []}]}', '[]', 'General')
|
||||||
conf.DAYS_TO_KEEP_EVENTS = ccd('DAYS_TO_KEEP_EVENTS', 90 , c_d, 'Delete events days', '{"dataType":"integer", "elements": [{"elementType" : "input", "elementOptions" : [{"type": "number"}] ,"transformers": []}]}', '[]', 'General')
|
conf.DAYS_TO_KEEP_EVENTS = ccd('DAYS_TO_KEEP_EVENTS', 90 , c_d, 'Delete events days', '{"dataType":"integer", "elements": [{"elementType" : "input", "elementOptions" : [{"type": "number"}] ,"transformers": []}]}', '[]', 'General')
|
||||||
conf.HRS_TO_KEEP_NEWDEV = ccd('HRS_TO_KEEP_NEWDEV', 0 , c_d, 'Keep new devices for', '{"dataType":"integer", "elements": [{"elementType" : "input", "elementOptions" : [{"type": "number"}] ,"transformers": []}]}', "[]", 'General')
|
conf.HRS_TO_KEEP_NEWDEV = ccd('HRS_TO_KEEP_NEWDEV', 0 , c_d, 'Keep new devices for', '{"dataType":"integer", "elements": [{"elementType" : "input", "elementOptions" : [{"type": "number"}] ,"transformers": []}]}', "[]", 'General')
|
||||||
|
conf.CLEAR_NEW_FLAG = ccd('CLEAR_NEW_FLAG', 0 , c_d, 'Clear new flag', '{"dataType":"integer", "elements": [{"elementType" : "input", "elementOptions" : [{"type": "number"}] ,"transformers": []}]}', "[]", 'General')
|
||||||
conf.API_CUSTOM_SQL = ccd('API_CUSTOM_SQL', 'SELECT * FROM Devices WHERE dev_PresentLastScan = 0' , c_d, 'Custom endpoint', '{"dataType":"string", "elements": [{"elementType" : "input", "elementOptions" : [] ,"transformers": []}]}', '[]', 'General')
|
conf.API_CUSTOM_SQL = ccd('API_CUSTOM_SQL', 'SELECT * FROM Devices WHERE dev_PresentLastScan = 0' , c_d, 'Custom endpoint', '{"dataType":"string", "elements": [{"elementType" : "input", "elementOptions" : [] ,"transformers": []}]}', '[]', 'General')
|
||||||
conf.VERSION = ccd('VERSION', '' , c_d, 'Version', '{"dataType":"string", "elements": [{"elementType" : "input", "elementOptions" : [{ "readonly": "true" }] ,"transformers": []}]}', '', 'General')
|
conf.VERSION = ccd('VERSION', '' , c_d, 'Version', '{"dataType":"string", "elements": [{"elementType" : "input", "elementOptions" : [{ "readonly": "true" }] ,"transformers": []}]}', '', 'General')
|
||||||
conf.NETWORK_DEVICE_TYPES = ccd('NETWORK_DEVICE_TYPES', ['AP', 'Gateway', 'Firewall', 'Hypervisor', 'Powerline', 'Switch', 'WLAN', 'PLC', 'Router','USB LAN Adapter', 'USB WIFI Adapter', 'Internet'] , c_d, 'Network device types', '{"dataType":"array","elements":[{"elementType":"input","elementOptions":[{"placeholder":"Enter value"},{"suffix":"_in"},{"cssClasses":"col-sm-10"},{"prefillValue":"null"}],"transformers":[]},{"elementType":"button","elementOptions":[{"sourceSuffixes":["_in"]},{"separator":""},{"cssClasses":"col-xs-12"},{"onClick":"addList(this,false)"},{"getStringKey":"Gen_Add"}],"transformers":[]},{"elementType":"select", "elementHasInputValue":1,"elementOptions":[{"multiple":"true"},{"readonly":"true"},{"editable":"true"}],"transformers":[]},{"elementType":"button","elementOptions":[{"sourceSuffixes":[]},{"separator":""},{"cssClasses":"col-xs-6"},{"onClick":"removeAllOptions(this)"},{"getStringKey":"Gen_Remove_All"}],"transformers":[]},{"elementType":"button","elementOptions":[{"sourceSuffixes":[]},{"separator":""},{"cssClasses":"col-xs-6"},{"onClick":"removeFromList(this)"},{"getStringKey":"Gen_Remove_Last"}],"transformers":[]}]}', '[]', 'General')
|
conf.NETWORK_DEVICE_TYPES = ccd('NETWORK_DEVICE_TYPES', ['AP', 'Gateway', 'Firewall', 'Hypervisor', 'Powerline', 'Switch', 'WLAN', 'PLC', 'Router','USB LAN Adapter', 'USB WIFI Adapter', 'Internet'] , c_d, 'Network device types', '{"dataType":"array","elements":[{"elementType":"input","elementOptions":[{"placeholder":"Enter value"},{"suffix":"_in"},{"cssClasses":"col-sm-10"},{"prefillValue":"null"}],"transformers":[]},{"elementType":"button","elementOptions":[{"sourceSuffixes":["_in"]},{"separator":""},{"cssClasses":"col-xs-12"},{"onClick":"addList(this,false)"},{"getStringKey":"Gen_Add"}],"transformers":[]},{"elementType":"select", "elementHasInputValue":1,"elementOptions":[{"multiple":"true"},{"readonly":"true"},{"editable":"true"}],"transformers":[]},{"elementType":"button","elementOptions":[{"sourceSuffixes":[]},{"separator":""},{"cssClasses":"col-xs-6"},{"onClick":"removeAllOptions(this)"},{"getStringKey":"Gen_Remove_All"}],"transformers":[]},{"elementType":"button","elementOptions":[{"sourceSuffixes":[]},{"separator":""},{"cssClasses":"col-xs-6"},{"onClick":"removeFromList(this)"},{"getStringKey":"Gen_Remove_Last"}],"transformers":[]}]}', '[]', 'General')
|
||||||
@@ -297,7 +298,7 @@ def importConfigs (db, all_plugins):
|
|||||||
# -----------------
|
# -----------------
|
||||||
# Plugins END
|
# Plugins END
|
||||||
|
|
||||||
# TODO check app_conf_override.json
|
# HANDLE APP_CONF_OVERRIDE via app_conf_override.json
|
||||||
# Assuming fullConfFolder is defined elsewhere
|
# Assuming fullConfFolder is defined elsewhere
|
||||||
app_conf_override_path = fullConfFolder + '/app_conf_override.json'
|
app_conf_override_path = fullConfFolder + '/app_conf_override.json'
|
||||||
|
|
||||||
@@ -314,12 +315,12 @@ def importConfigs (db, all_plugins):
|
|||||||
# Log the value being passed
|
# Log the value being passed
|
||||||
# ccd(key, default, config_dir, name, inputtype, options, group, events=None, desc="", regex="", setJsonMetadata=None, overrideTemplate=None, forceDefault=False)
|
# ccd(key, default, config_dir, name, inputtype, options, group, events=None, desc="", regex="", setJsonMetadata=None, overrideTemplate=None, forceDefault=False)
|
||||||
mylog('debug', [f"[Config] Setting override {setting_name} with value: {value}"])
|
mylog('debug', [f"[Config] Setting override {setting_name} with value: {value}"])
|
||||||
ccd(setting_name, value, c_d, '_KEEP_', '_KEEP_', '_KEEP_', '_KEEP_', None, "_KEEP_", "", None, None, True)
|
ccd(setting_name, value, c_d, '_KEEP_', '_KEEP_', '_KEEP_', '_KEEP_', None, "_KEEP_", "", None, None, True, 1)
|
||||||
else:
|
else:
|
||||||
# Convert to string and log
|
# Convert to string and log
|
||||||
# ccd(key, default, config_dir, name, inputtype, options, group, events=None, desc="", regex="", setJsonMetadata=None, overrideTemplate=None, forceDefault=False)
|
# ccd(key, default, config_dir, name, inputtype, options, group, events=None, desc="", regex="", setJsonMetadata=None, overrideTemplate=None, forceDefault=False)
|
||||||
mylog('debug', [f"[Config] Setting override {setting_name} with value: {str(value)}"])
|
mylog('debug', [f"[Config] Setting override {setting_name} with value: {str(value)}"])
|
||||||
ccd(setting_name, str(value), c_d, '_KEEP_', '_KEEP_', '_KEEP_', '_KEEP_', None, "_KEEP_", "", None, None, True)
|
ccd(setting_name, str(value), c_d, '_KEEP_', '_KEEP_', '_KEEP_', '_KEEP_', None, "_KEEP_", "", None, None, True, 1)
|
||||||
|
|
||||||
except json.JSONDecodeError:
|
except json.JSONDecodeError:
|
||||||
mylog('none', [f"[Config] [ERROR] Setting override decoding JSON from {app_conf_override_path}"])
|
mylog('none', [f"[Config] [ERROR] Setting override decoding JSON from {app_conf_override_path}"])
|
||||||
@@ -342,13 +343,14 @@ def importConfigs (db, all_plugins):
|
|||||||
# ccd(key, default, config_dir, name, inputtype, options, group, events=None, desc="", regex="", setJsonMetadata=None, overrideTemplate=None, forceDefault=False)
|
# ccd(key, default, config_dir, name, inputtype, options, group, events=None, desc="", regex="", setJsonMetadata=None, overrideTemplate=None, forceDefault=False)
|
||||||
ccd('VERSION', buildTimestamp , c_d, '_KEEP_', '_KEEP_', '_KEEP_', '_KEEP_', None, "_KEEP_", "", None, None, True)
|
ccd('VERSION', buildTimestamp , c_d, '_KEEP_', '_KEEP_', '_KEEP_', '_KEEP_', None, "_KEEP_", "", None, None, True)
|
||||||
|
|
||||||
write_notification(f'[Upgrade] : App upgraded 🚀 Please clear the cache: <ol> <li>Clear the browser cache (shift + browser refresh button)</li> <li> Clear app cache with the 🔄 (reload) button in the header</li></ol> Check out new features and what has changed in the <a href="https://github.com/jokob-sk/NetAlertX/releases" target="_blank">📓 release notes</a>.', 'interrupt', timeNowTZ())
|
write_notification(f'[Upgrade] : App upgraded 🚀 Please clear the cache: <ol> <li>Click OK below</li> <li>Clear the browser cache (shift + browser refresh button)</li> <li> Clear app cache with the 🔄 (reload) button in the header</li><li>Go to Settings and click Save</li> </ol> Check out new features and what has changed in the <a href="https://github.com/jokob-sk/NetAlertX/releases" target="_blank">📓 release notes</a>.', 'interrupt', timeNowTZ())
|
||||||
|
|
||||||
|
|
||||||
# Insert settings into the DB
|
# Insert settings into the DB
|
||||||
sql.execute ("DELETE FROM Settings")
|
sql.execute ("DELETE FROM Settings")
|
||||||
|
# mylog('debug', [f"[Config] conf.mySettingsSQLsafe : '{conf.mySettingsSQLsafe}'"])
|
||||||
sql.executemany ("""INSERT INTO Settings ("Code_Name", "Display_Name", "Description", "Type", "Options",
|
sql.executemany ("""INSERT INTO Settings ("Code_Name", "Display_Name", "Description", "Type", "Options",
|
||||||
"RegEx", "Value", "Group", "Events" ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)""", conf.mySettingsSQLsafe)
|
"RegEx", "Value", "Group", "Events", "OverriddenByEnv" ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)""", conf.mySettingsSQLsafe)
|
||||||
|
|
||||||
db.commitDB()
|
db.commitDB()
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -232,4 +232,45 @@ def insert_events (db):
|
|||||||
FROM Devices, CurrentScan
|
FROM Devices, CurrentScan
|
||||||
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()
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -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,12 +226,14 @@ 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:
|
||||||
@@ -248,7 +250,11 @@ def execute_plugin(db, all_plugins, plugin, pluginsState = plugins_state() ):
|
|||||||
|
|
||||||
for line in newLines:
|
for line in newLines:
|
||||||
columns = line.split("|")
|
columns = line.split("|")
|
||||||
# There have to be 9 or 13 columns
|
# There have to be 9 or 13 columns
|
||||||
|
if len(columns) not in [9, 13]:
|
||||||
|
mylog('none', [f'[Plugins] Wrong number of input values, must be 9 or 13, got {len(columns)} from: {line}'])
|
||||||
|
continue # Skip lines with incorrect number of columns
|
||||||
|
|
||||||
# Common part of the SQL parameters
|
# Common part of the SQL parameters
|
||||||
base_params = [
|
base_params = [
|
||||||
0, # "Index" placeholder
|
0, # "Index" placeholder
|
||||||
@@ -284,8 +290,6 @@ def execute_plugin(db, all_plugins, plugin, pluginsState = plugins_state() ):
|
|||||||
'null', # "HelpVal3"
|
'null', # "HelpVal3"
|
||||||
'null' # "HelpVal4"
|
'null' # "HelpVal4"
|
||||||
])
|
])
|
||||||
else:
|
|
||||||
mylog('none', [f'[Plugins] Wrong number of input values, must be 9 or 13, got {len(columns)} from: {line} '])
|
|
||||||
|
|
||||||
# Create a tuple containing values to be inserted into the database.
|
# Create a tuple containing values to be inserted into the database.
|
||||||
# Each value corresponds to a column in the table in the order of the columns.
|
# Each value corresponds to a column in the table in the order of the columns.
|
||||||
|
|||||||
@@ -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'
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user