mirror of
https://github.com/jokob-sk/NetAlertX.git
synced 2026-04-07 02:31:27 -07:00
Compare commits
79 Commits
19aaa92fa3
...
v24.9.26
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 | ||
|
|
dcc43d1f3c | ||
|
|
8f35bf36ff | ||
|
|
1548168eba | ||
|
|
2e35bac6ec | ||
|
|
ba348fc4c2 | ||
|
|
d3337e75a9 | ||
|
|
9e0bc043b0 | ||
|
|
29fdd0b115 | ||
|
|
48e92a186e | ||
|
|
1dcb66e972 | ||
|
|
fa0d6d312d | ||
|
|
a19fe342e7 | ||
|
|
c4fc68cac8 | ||
|
|
3a050c31a7 | ||
|
|
2cd406a390 | ||
|
|
b086417686 | ||
|
|
dbecbfc85f | ||
|
|
3f9e4c4425 | ||
|
|
4fd1869bde | ||
|
|
78025a376c | ||
|
|
615fd08f5b | ||
|
|
4839211fe1 | ||
|
|
ae1673c1c3 |
28
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
28
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
@@ -9,20 +9,6 @@ body:
|
|||||||
options:
|
options:
|
||||||
- label: I have searched the existing open and closed issues
|
- label: I have searched the existing open and closed issues
|
||||||
required: true
|
required: true
|
||||||
- type: checkboxes
|
|
||||||
attributes:
|
|
||||||
label: Am I willing to test this? 🧪
|
|
||||||
description: I rely on the community to test unreleased features. If you are requesting a feature, please be willing to test it within 48h of test request. Otherwise, the feature might be pulled from the code base.
|
|
||||||
options:
|
|
||||||
- label: I will do my best to test this feature on the `netlertx-dev` image when requested within 48h and report bugs to help deliver a great user experience for everyone and not to break existing installations.
|
|
||||||
required: true
|
|
||||||
- type: checkboxes
|
|
||||||
attributes:
|
|
||||||
label: Can I help implement this? 👩💻👨💻
|
|
||||||
description: The maintainer will provide guidance and help. The implementer will read the PR guidelines https://github.com/jokob-sk/NetAlertX/tree/main/docs#-pull-requests-prs
|
|
||||||
options:
|
|
||||||
- label: "Yes"
|
|
||||||
- label: "No"
|
|
||||||
- type: textarea
|
- type: textarea
|
||||||
attributes:
|
attributes:
|
||||||
label: Is your feature request related to a problem? Please describe
|
label: Is your feature request related to a problem? Please describe
|
||||||
@@ -50,3 +36,17 @@ body:
|
|||||||
Tip: You can attach images or log files by clicking this area to highlight it and then dragging files in.
|
Tip: You can attach images or log files by clicking this area to highlight it and then dragging files in.
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
|
- type: checkboxes
|
||||||
|
attributes:
|
||||||
|
label: Am I willing to test this? 🧪
|
||||||
|
description: I rely on the community to test unreleased features. If you are requesting a feature, please be willing to test it within 48h of test request. Otherwise, the feature might be pulled from the code base.
|
||||||
|
options:
|
||||||
|
- label: I will do my best to test this feature on the `netlertx-dev` image when requested within 48h and report bugs to help deliver a great user experience for everyone and not to break existing installations.
|
||||||
|
required: true
|
||||||
|
- type: checkboxes
|
||||||
|
attributes:
|
||||||
|
label: Can I help implement this? 👩💻👨💻
|
||||||
|
description: The maintainer will provide guidance and help. The implementer will read the PR guidelines https://github.com/jokob-sk/NetAlertX/tree/main/docs#-pull-requests-prs
|
||||||
|
options:
|
||||||
|
- label: "Yes"
|
||||||
|
- label: "No"
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
FROM alpine:3.20 as builder
|
FROM alpine:3.20 AS builder
|
||||||
|
|
||||||
ARG INSTALL_DIR=/app
|
ARG INSTALL_DIR=/app
|
||||||
|
|
||||||
ENV PYTHONUNBUFFERED 1
|
ENV PYTHONUNBUFFERED=1
|
||||||
|
|
||||||
# Install build dependencies
|
# Install build dependencies
|
||||||
RUN apk add --no-cache bash python3 python3-dev gcc musl-dev libffi-dev openssl-dev \
|
RUN apk add --no-cache bash python3 python3-dev gcc musl-dev libffi-dev openssl-dev \
|
||||||
@@ -21,7 +21,7 @@ RUN pip install netifaces tplink-omada-client pycryptodome requests paho-mqtt sc
|
|||||||
&& bash -c "find ${INSTALL_DIR} -type f \( -name '*.sh' -o -name '*.py' -o -name 'speedtest-cli' \) -exec chmod 750 {} \;"
|
&& bash -c "find ${INSTALL_DIR} -type f \( -name '*.sh' -o -name '*.py' -o -name 'speedtest-cli' \) -exec chmod 750 {} \;"
|
||||||
|
|
||||||
# second stage
|
# second stage
|
||||||
FROM alpine:3.20 as runner
|
FROM alpine:3.20 AS runner
|
||||||
|
|
||||||
ARG INSTALL_DIR=/app
|
ARG INSTALL_DIR=/app
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
FROM debian:bookworm-slim
|
FROM debian:bookworm-slim
|
||||||
|
|
||||||
# default UID and GID
|
# default UID and GID
|
||||||
ENV USER=pi USER_ID=1000 USER_GID=1000 PORT=20211
|
ENV USER=pi USER_ID=1000 USER_GID=1000 PORT=20211
|
||||||
#TZ=Europe/London
|
#TZ=Europe/London
|
||||||
|
|
||||||
# Todo, figure out why using a workdir instead of full paths don't work
|
# Todo, figure out why using a workdir instead of full paths don't work
|
||||||
|
|||||||
@@ -64,7 +64,9 @@ services:
|
|||||||
# DELETE END anyone trying to use this file: comment out / delete ABOVE lines, they are only for development purposes
|
# DELETE END anyone trying to use this file: comment out / delete ABOVE lines, they are only for development purposes
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
environment:
|
environment:
|
||||||
|
# - APP_CONF_OVERRIDE={"SCAN_SUBNETS":"['192.168.1.0/24 --interface=eth1']","UI_dark_mode":"True"}
|
||||||
- TZ=${TZ}
|
- TZ=${TZ}
|
||||||
- PORT=${PORT}
|
- PORT=${PORT}
|
||||||
# ❗ DANGER ZONE BELOW - Setting ALWAYS_FRESH_INSTALL=true will delete the content of the /db & /config folders
|
# ❗ DANGER ZONE BELOW - Setting ALWAYS_FRESH_INSTALL=true will delete the content of the /db & /config folders
|
||||||
- ALWAYS_FRESH_INSTALL=${ALWAYS_FRESH_INSTALL}
|
- ALWAYS_FRESH_INSTALL=${ALWAYS_FRESH_INSTALL}
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -41,6 +41,16 @@ if [ "$ALWAYS_FRESH_INSTALL" = true ]; then
|
|||||||
rm -rf "$INSTALL_DIR_OLD/db/"*
|
rm -rf "$INSTALL_DIR_OLD/db/"*
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# OVERRIDE settings: Handling APP_CONF_OVERRIDE
|
||||||
|
# Check if APP_CONF_OVERRIDE is set
|
||||||
|
if [ -z "$APP_CONF_OVERRIDE" ]; then
|
||||||
|
echo "APP_CONF_OVERRIDE is not set. Skipping config file creation."
|
||||||
|
else
|
||||||
|
# Save the APP_CONF_OVERRIDE env variable as a JSON file
|
||||||
|
echo "$APP_CONF_OVERRIDE" > "${INSTALL_DIR}/config/app_conf_override.json"
|
||||||
|
echo "Config file saved to ${INSTALL_DIR}/config/app_conf_override.json"
|
||||||
|
fi
|
||||||
|
|
||||||
# 🔻 FOR BACKWARD COMPATIBILITY - REMOVE AFTER 12/12/2024
|
# 🔻 FOR BACKWARD COMPATIBILITY - REMOVE AFTER 12/12/2024
|
||||||
|
|
||||||
# Check if pialert.db exists, then create a symbolic link to app.db
|
# Check if pialert.db exists, then create a symbolic link to app.db
|
||||||
|
|||||||
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
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
| Pholus_Scan | Scan results of the Pholus python network penetration script. | ![Screen8][screen8] |
|
| Pholus_Scan | Scan results of the Pholus python network penetration script. | ![Screen8][screen8] |
|
||||||
| Plugins_Events | For capturing events exposed by a plugin via the `last_result.log` file. If unique then saved into the `Plugins_Objects` table. Entries are deleted once processed and stored in the `Plugins_History` and/or `Plugins_Objects` tables. | ![Screen10][screen10] |
|
| Plugins_Events | For capturing events exposed by a plugin via the `last_result.log` file. If unique then saved into the `Plugins_Objects` table. Entries are deleted once processed and stored in the `Plugins_History` and/or `Plugins_Objects` tables. | ![Screen10][screen10] |
|
||||||
| Plugins_History | History of all entries from the `Plugins_Events` table | ![Screen11][screen11] |
|
| Plugins_History | History of all entries from the `Plugins_Events` table | ![Screen11][screen11] |
|
||||||
| Plugins_Language_Strings | Language strings colelcted from the plugin `config.json` files used for string resolution in the frontend. | ![Screen12][screen12] |
|
| Plugins_Language_Strings | Language strings collected from the plugin `config.json` files used for string resolution in the frontend. | ![Screen12][screen12] |
|
||||||
| Plugins_Objects | Unique objects detected by individual plugins. | ![Screen13][screen13] |
|
| Plugins_Objects | Unique objects detected by individual plugins. | ![Screen13][screen13] |
|
||||||
| Sessions | Used to display sessions in the charts | ![Screen15][screen15] |
|
| Sessions | Used to display sessions in the charts | ![Screen15][screen15] |
|
||||||
| Settings | Database representation of the sum of all settings from `app.conf` and plugins coming from `config.json` files. | ![Screen16][screen16] |
|
| Settings | Database representation of the sum of all settings from `app.conf` and plugins coming from `config.json` files. | ![Screen16][screen16] |
|
||||||
|
|||||||
@@ -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👨💻
|
||||||
|
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ You need to specify the network interface and the network mask. You can also con
|
|||||||
* 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)).
|
||||||
|
|
||||||
## Explanation
|
## Explanation
|
||||||
|
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|
||||||
|
|||||||
@@ -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">
|
||||||
@@ -693,7 +693,6 @@ if ($ENABLED_DARKMODE === True) {
|
|||||||
|
|
||||||
var pos = -1;
|
var pos = -1;
|
||||||
var parPeriod = 'Front_Details_Period';
|
var parPeriod = 'Front_Details_Period';
|
||||||
var parTab = 'Front_Details_Tab';
|
|
||||||
var parSessionsRows = 'Front_Details_Sessions_Rows';
|
var parSessionsRows = 'Front_Details_Sessions_Rows';
|
||||||
var parEventsRows = 'Front_Details_Events_Rows';
|
var parEventsRows = 'Front_Details_Events_Rows';
|
||||||
var parEventsHide = 'Front_Details_Events_Hide';
|
var parEventsHide = 'Front_Details_Events_Hide';
|
||||||
@@ -736,7 +735,7 @@ function main () {
|
|||||||
$('#chkHideConnectionEvents')[0].checked = eval(eventsHide == 'true');
|
$('#chkHideConnectionEvents')[0].checked = eval(eventsHide == 'true');
|
||||||
|
|
||||||
// Initialize components with parameters
|
// Initialize components with parameters
|
||||||
initializeTabs();
|
initializeTabsNew();
|
||||||
initializeiCheck();
|
initializeiCheck();
|
||||||
initializeCombos();
|
initializeCombos();
|
||||||
initializeDatatables();
|
initializeDatatables();
|
||||||
@@ -756,24 +755,13 @@ 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)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
|
||||||
function initializeTabs () {
|
|
||||||
// Activate panel
|
|
||||||
$('.nav-tabs a[id='+ tab +']').tab('show');
|
|
||||||
|
|
||||||
// When changed save new current tab
|
|
||||||
$('a[data-toggle="tab"]').on('shown.bs.tab', function (e) {
|
|
||||||
setParameter (parTab, $(e.target).attr('id'));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
function initializeiCheck () {
|
function initializeiCheck () {
|
||||||
// Blue
|
// Blue
|
||||||
@@ -1148,7 +1136,7 @@ function initializeCalendar () {
|
|||||||
showSpinner()
|
showSpinner()
|
||||||
} else {
|
} else {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
updateIconPreview('#txtIcon')
|
updateIconPreview($('#txtIcon'))
|
||||||
}, 500);
|
}, 500);
|
||||||
|
|
||||||
hideSpinner()
|
hideSpinner()
|
||||||
@@ -1457,10 +1445,10 @@ 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())
|
||||||
@@ -1679,7 +1667,7 @@ function addAsBase64 () {
|
|||||||
|
|
||||||
$('#txtIcon').val(iconHtmlBase64);
|
$('#txtIcon').val(iconHtmlBase64);
|
||||||
|
|
||||||
updateIconPreview('#txtIcon')
|
updateIconPreview($('#txtIcon'))
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -137,10 +137,8 @@
|
|||||||
<!-- page script ----------------------------------------------------------- -->
|
<!-- page script ----------------------------------------------------------- -->
|
||||||
<script>
|
<script>
|
||||||
var deviceStatus = 'all';
|
var deviceStatus = 'all';
|
||||||
var parTableRows = 'Front_Devices_Rows';
|
|
||||||
var parTableOrder = 'Front_Devices_Order';
|
|
||||||
var tableRows = getCookie ("nax_parTableRows") == "" ? 10 : getCookie ("nax_parTableRows") ;
|
var tableRows = getCookie ("nax_parTableRows") == "" ? 10 : getCookie ("nax_parTableRows") ;
|
||||||
var tableOrder = getCookie ("nax_parTableOrder") == "" ? [[3,'desc'], [0,'asc']] : JSON.parse(getCookie ("nax_parTableOrder")) ;
|
var tableOrder = getCookie ("nax_parTableOrder") == "" ? [[3,'desc'], [0,'asc']] : JSON.parse(getCookie ("nax_parTableOrder")) ;
|
||||||
|
|
||||||
var tableColumnHide = [];
|
var tableColumnHide = [];
|
||||||
var tableColumnOrder = [];
|
var tableColumnOrder = [];
|
||||||
@@ -404,6 +402,11 @@ function initializeDatatable (status) {
|
|||||||
|
|
||||||
$.get('api/table_devices.json?nocache=' + Date.now(), function(result) {
|
$.get('api/table_devices.json?nocache=' + Date.now(), function(result) {
|
||||||
|
|
||||||
|
// refresh devices cache
|
||||||
|
devicesListAll_JSON = result["data"]
|
||||||
|
devicesListAll_JSON_str = JSON.stringify(devicesListAll_JSON)
|
||||||
|
setCache('devicesListAll_JSON', devicesListAll_JSON_str)
|
||||||
|
|
||||||
// query data
|
// query data
|
||||||
getDevicesTotals(result.data);
|
getDevicesTotals(result.data);
|
||||||
|
|
||||||
@@ -451,10 +454,6 @@ function initializeDatatable (status) {
|
|||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
// TODO displayed columns
|
|
||||||
|
|
||||||
|
|
||||||
// Check if the DataTable already exists
|
// Check if the DataTable already exists
|
||||||
if ($.fn.dataTable.isDataTable('#tableDevices')) {
|
if ($.fn.dataTable.isDataTable('#tableDevices')) {
|
||||||
// The DataTable exists, so destroy it
|
// The DataTable exists, so destroy it
|
||||||
@@ -462,12 +461,12 @@ function initializeDatatable (status) {
|
|||||||
table.clear().destroy();
|
table.clear().destroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
var table=
|
var table =
|
||||||
$('#tableDevices').DataTable({
|
$('#tableDevices').DataTable({
|
||||||
'data' : dataArray["data"],
|
'data' : dataArray["data"],
|
||||||
'paging' : true,
|
'paging' : true,
|
||||||
'lengthChange' : true,
|
'lengthChange' : true,
|
||||||
'lengthMenu' : [[10, 25, 50, 100, 500, -1], [10, 25, 50, 100, 500, getString('Device_Tablelenght_all')]],
|
'lengthMenu' : [[10, 25, 50, 100, 500, 100000], [10, 25, 50, 100, 500, getString('Device_Tablelenght_all')]],
|
||||||
'searching' : true,
|
'searching' : true,
|
||||||
|
|
||||||
'ordering' : true,
|
'ordering' : true,
|
||||||
@@ -639,8 +638,6 @@ function initializeDatatable (status) {
|
|||||||
// Check if any row is selected
|
// Check if any row is selected
|
||||||
var anyRowSelected = $('#tableDevices tr.selected').length > 0;
|
var anyRowSelected = $('#tableDevices tr.selected').length > 0;
|
||||||
|
|
||||||
console.log(anyRowSelected);
|
|
||||||
|
|
||||||
// Toggle visibility of element with ID 'multiEdit'
|
// Toggle visibility of element with ID 'multiEdit'
|
||||||
$('#multiEdit').toggle(anyRowSelected);
|
$('#multiEdit').toggle(anyRowSelected);
|
||||||
}, 200);
|
}, 200);
|
||||||
|
|||||||
@@ -78,7 +78,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 -->
|
||||||
@@ -104,7 +104,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">
|
||||||
|
|||||||
@@ -212,7 +212,8 @@ function cacheStrings() {
|
|||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
|
|
||||||
// Create a promise for each language
|
// Create a promise for each language
|
||||||
const languagePromises = allLanguages.map((language_code) => {
|
languagesToLoad = ['en_us', getLangCode()]
|
||||||
|
const languagePromises = languagesToLoad.map((language_code) => {
|
||||||
return new Promise((resolveLang, rejectLang) => {
|
return new Promise((resolveLang, rejectLang) => {
|
||||||
// Fetch core strings and translations
|
// Fetch core strings and translations
|
||||||
|
|
||||||
@@ -267,6 +268,29 @@ function cacheStrings() {
|
|||||||
function getString(key) {
|
function getString(key) {
|
||||||
|
|
||||||
function fetchString(key) {
|
function fetchString(key) {
|
||||||
|
|
||||||
|
lang_code = getLangCode();
|
||||||
|
|
||||||
|
let result = getCache(`pia_lang_${key}_${lang_code}`, true);
|
||||||
|
|
||||||
|
if (isEmpty(result)) {
|
||||||
|
result = getCache(`pia_lang_${key}_en_us`, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isAppInitialized()) {
|
||||||
|
return fetchString(key);
|
||||||
|
} else {
|
||||||
|
callAfterAppInitialized(() => fetchString(key));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
// Get current language ISO code
|
||||||
|
function getLangCode() {
|
||||||
|
|
||||||
UI_LANG = getSetting("UI_LANG");
|
UI_LANG = getSetting("UI_LANG");
|
||||||
|
|
||||||
let lang_code = 'en_us';
|
let lang_code = 'en_us';
|
||||||
@@ -310,20 +334,7 @@ function getString(key) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
let result = getCache(`pia_lang_${key}_${lang_code}`, true);
|
return lang_code;
|
||||||
|
|
||||||
if (isEmpty(result)) {
|
|
||||||
result = getCache(`pia_lang_${key}_en_us`, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isAppInitialized()) {
|
|
||||||
return fetchString(key);
|
|
||||||
} else {
|
|
||||||
callAfterAppInitialized(() => fetchString(key));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -619,17 +630,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;
|
||||||
}
|
}
|
||||||
@@ -1193,7 +1198,7 @@ const sessionStorageKey = "myScriptExecuted_common_js";
|
|||||||
var completedCalls = []
|
var completedCalls = []
|
||||||
var completedCalls_final = ['cacheSettings', 'cacheStrings', 'cacheDevices'];
|
var completedCalls_final = ['cacheSettings', 'cacheStrings', 'cacheDevices'];
|
||||||
var completedCallsCount = 0;
|
var completedCallsCount = 0;
|
||||||
var completedCallsCount_final = allLanguages.length + 2; // number of language files + cacheDevices + cacheSettings
|
var completedCallsCount_final;
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
// Clearing all the caches
|
// Clearing all the caches
|
||||||
@@ -1249,6 +1254,10 @@ function callAfterAppInitialized(callback) {
|
|||||||
// Check if the code has been executed before by checking sessionStorage
|
// Check if the code has been executed before by checking sessionStorage
|
||||||
function isAppInitialized() {
|
function isAppInitialized() {
|
||||||
// return arraysContainSameValues(getCache("completedCalls").split(',').filter(Boolean), completedCalls_final);
|
// return arraysContainSameValues(getCache("completedCalls").split(',').filter(Boolean), completedCalls_final);
|
||||||
|
|
||||||
|
// loading settings + 1 (or 2 language files if not english) + device cache.
|
||||||
|
completedCallsCount_final = getLangCode() == 'en_us' ? 3 : 4 ;
|
||||||
|
|
||||||
return (parseInt(getCache("completedCallsCount")) >= completedCallsCount_final);
|
return (parseInt(getCache("completedCallsCount")) >= completedCallsCount_final);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -667,7 +667,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 +715,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 +744,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;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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));
|
||||||
});
|
});
|
||||||
|
|
||||||
$('#txtIconFA').html(atob(value))
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
// 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 */) {},
|
||||||
|
|||||||
@@ -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}">`
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -53,8 +53,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 -->
|
||||||
|
|||||||
@@ -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": "",
|
||||||
@@ -557,6 +560,7 @@
|
|||||||
"RandomMAC_hover": "",
|
"RandomMAC_hover": "",
|
||||||
"Reports_Sent_Log": "",
|
"Reports_Sent_Log": "",
|
||||||
"SCAN_SUBNETS_description": "",
|
"SCAN_SUBNETS_description": "",
|
||||||
|
"SCAN_SUBNETS_name": "",
|
||||||
"SYSTEM_TITLE": "",
|
"SYSTEM_TITLE": "",
|
||||||
"Setting_Override": "",
|
"Setting_Override": "",
|
||||||
"Setting_Override_Description": "",
|
"Setting_Override_Description": "",
|
||||||
|
|||||||
@@ -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",
|
||||||
@@ -612,6 +615,7 @@
|
|||||||
"RandomMAC_hover": "Autodetected - indicates if the device randomizes it's MAC address.",
|
"RandomMAC_hover": "Autodetected - indicates if the device randomizes it's MAC address.",
|
||||||
"Reports_Sent_Log": "Protokoll gesendeter Berichte",
|
"Reports_Sent_Log": "Protokoll gesendeter Berichte",
|
||||||
"SCAN_SUBNETS_description": "",
|
"SCAN_SUBNETS_description": "",
|
||||||
|
"SCAN_SUBNETS_name": "",
|
||||||
"SMTP_FORCE_SSL_description": "Force SSL when connecting to your SMTP server.",
|
"SMTP_FORCE_SSL_description": "Force SSL when connecting to your SMTP server.",
|
||||||
"SMTP_FORCE_SSL_name": "Force SSL",
|
"SMTP_FORCE_SSL_name": "Force SSL",
|
||||||
"SMTP_PASS_description": "The SMTP server password. ",
|
"SMTP_PASS_description": "The SMTP server password. ",
|
||||||
|
|||||||
@@ -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.",
|
||||||
@@ -557,6 +560,7 @@
|
|||||||
"RandomMAC_hover": "Autodetected - indicates if the device randomizes it's MAC address.",
|
"RandomMAC_hover": "Autodetected - indicates if the device randomizes it's MAC address.",
|
||||||
"Reports_Sent_Log": "Sent Reports Log",
|
"Reports_Sent_Log": "Sent Reports Log",
|
||||||
"SCAN_SUBNETS_description": "Most on-network scanners (ARP-SCAN, NMAP, NSLOOKUP, DIG, PHOLUS) rely on scanning specific network interfaces and subnets. Check the <a href=\"https://github.com/jokob-sk/NetAlertX/blob/main/docs/SUBNETS.md\" target=\"_blank\">subnets documentation</a> for help on this setting, especially VLANs, what VLANs are supported, or how to figure out the network mask and your interface. <br/> <br/> An alternative to on-network scanners is to enable some other Device scanners/importers that don't rely on NetAlert<sup>X</sup> having access to the network (UNIFI, dhcp.leases, PiHole, etc.). <br/> <br/> Note: The scan time itself depends on the number of IP addresses to check, so set this up carefully with the appropriate network mask and interface.",
|
"SCAN_SUBNETS_description": "Most on-network scanners (ARP-SCAN, NMAP, NSLOOKUP, DIG, PHOLUS) rely on scanning specific network interfaces and subnets. Check the <a href=\"https://github.com/jokob-sk/NetAlertX/blob/main/docs/SUBNETS.md\" target=\"_blank\">subnets documentation</a> for help on this setting, especially VLANs, what VLANs are supported, or how to figure out the network mask and your interface. <br/> <br/> An alternative to on-network scanners is to enable some other Device scanners/importers that don't rely on NetAlert<sup>X</sup> having access to the network (UNIFI, dhcp.leases, PiHole, etc.). <br/> <br/> Note: The scan time itself depends on the number of IP addresses to check, so set this up carefully with the appropriate network mask and interface.",
|
||||||
|
"SCAN_SUBNETS_name": "Networks to scan",
|
||||||
"SYSTEM_TITLE": "System Information",
|
"SYSTEM_TITLE": "System Information",
|
||||||
"Setting_Override": "Override value",
|
"Setting_Override": "Override value",
|
||||||
"Setting_Override_Description": "Enabling this option will override an App supplied default value with the value specified above.",
|
"Setting_Override_Description": "Enabling this option will override an App supplied default value with the value specified above.",
|
||||||
|
|||||||
@@ -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>.",
|
||||||
@@ -724,8 +727,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": "",
|
||||||
|
"CLEAR_NEW_FLAG_name": "",
|
||||||
"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",
|
||||||
@@ -293,6 +295,7 @@
|
|||||||
"Gen_Save": "Enregistrer",
|
"Gen_Save": "Enregistrer",
|
||||||
"Gen_Saved": "Enregistré",
|
"Gen_Saved": "Enregistré",
|
||||||
"Gen_Search": "Recherche",
|
"Gen_Search": "Recherche",
|
||||||
|
"Gen_SelectToPreview": "",
|
||||||
"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",
|
||||||
@@ -557,6 +560,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": "",
|
||||||
"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.",
|
||||||
|
|||||||
@@ -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",
|
||||||
@@ -274,7 +276,7 @@
|
|||||||
"Gen_AreYouSure": "Sei sicuro?",
|
"Gen_AreYouSure": "Sei sicuro?",
|
||||||
"Gen_Backup": "Esegui backup",
|
"Gen_Backup": "Esegui backup",
|
||||||
"Gen_Cancel": "Annulla",
|
"Gen_Cancel": "Annulla",
|
||||||
"Gen_Change": "",
|
"Gen_Change": "Modifica",
|
||||||
"Gen_Copy": "Esegui",
|
"Gen_Copy": "Esegui",
|
||||||
"Gen_DataUpdatedUITakesTime": "OK: l'aggiornamento dell'interfaccia utente potrebbe richiedere del tempo se è in esecuzione una scansione.",
|
"Gen_DataUpdatedUITakesTime": "OK: l'aggiornamento dell'interfaccia utente potrebbe richiedere del tempo se è in esecuzione una scansione.",
|
||||||
"Gen_Delete": "Elimina",
|
"Gen_Delete": "Elimina",
|
||||||
@@ -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",
|
||||||
@@ -557,6 +560,7 @@
|
|||||||
"RandomMAC_hover": "Rilevato automaticamente: indica se il dispositivo genera il suo indirizzo MAC casualmente.",
|
"RandomMAC_hover": "Rilevato automaticamente: indica se il dispositivo genera il suo indirizzo MAC casualmente.",
|
||||||
"Reports_Sent_Log": "Log rapporti inviati",
|
"Reports_Sent_Log": "Log rapporti inviati",
|
||||||
"SCAN_SUBNETS_description": "La maggior parte degli scanner di rete (ARP-SCAN, NMAP, NSLOOKUP, DIG, PHOLUS) si basano sulla scansione di interfacce di rete e sottoreti specifiche. Consulta la <a href=\"https://github.com/jokob-sk/NetAlertX/blob/main/docs/SUBNETS.md\" target=\"_blank\">documentazione sulle sottoreti</a> per assistenza su questa impostazione, in particolare VLAN, quali VLAN sono supportate o come individuare la maschera di rete e l'interfaccia. <br/> <br/> Un'alternativa agli scanner in rete è abilitare altri scanner/importatori di dispositivi che non si affidano a NetAlert<sup>X</sup> che hanno accesso alla rete (UNIFI, dhcp.leases , PiHole, ecc.). <br/> <br/> Nota: il tempo di scansione stesso dipende dal numero di indirizzi IP da controllare, quindi impostalo attentamente con la maschera di rete e l'interfaccia appropriate.",
|
"SCAN_SUBNETS_description": "La maggior parte degli scanner di rete (ARP-SCAN, NMAP, NSLOOKUP, DIG, PHOLUS) si basano sulla scansione di interfacce di rete e sottoreti specifiche. Consulta la <a href=\"https://github.com/jokob-sk/NetAlertX/blob/main/docs/SUBNETS.md\" target=\"_blank\">documentazione sulle sottoreti</a> per assistenza su questa impostazione, in particolare VLAN, quali VLAN sono supportate o come individuare la maschera di rete e l'interfaccia. <br/> <br/> Un'alternativa agli scanner in rete è abilitare altri scanner/importatori di dispositivi che non si affidano a NetAlert<sup>X</sup> che hanno accesso alla rete (UNIFI, dhcp.leases , PiHole, ecc.). <br/> <br/> Nota: il tempo di scansione stesso dipende dal numero di indirizzi IP da controllare, quindi impostalo attentamente con la maschera di rete e l'interfaccia appropriate.",
|
||||||
|
"SCAN_SUBNETS_name": "",
|
||||||
"SYSTEM_TITLE": "Informazioni sistema",
|
"SYSTEM_TITLE": "Informazioni sistema",
|
||||||
"Setting_Override": "Sovrascrivi valore",
|
"Setting_Override": "Sovrascrivi valore",
|
||||||
"Setting_Override_Description": "L'abilitazione di questa opzione sovrascriverà il valore predefinito fornito dall'app con il valore specificato sopra.",
|
"Setting_Override_Description": "L'abilitazione di questa opzione sovrascriverà il valore predefinito fornito dall'app con il valore specificato sopra.",
|
||||||
@@ -655,8 +659,8 @@
|
|||||||
"UI_PRESENCE_name": "Mostra nel grafico delle presenze",
|
"UI_PRESENCE_name": "Mostra nel grafico delle presenze",
|
||||||
"UI_REFRESH_description": "Inserisci il numero di secondi dopo il quale la UI si ricarica. Imposta a <code>0</code> per disabilitare.",
|
"UI_REFRESH_description": "Inserisci il numero di secondi dopo il quale la UI si ricarica. Imposta a <code>0</code> per disabilitare.",
|
||||||
"UI_REFRESH_name": "Aggiorna automaticamente la UI",
|
"UI_REFRESH_name": "Aggiorna automaticamente la UI",
|
||||||
"VERSION_description": "",
|
"VERSION_description": "Valore di supporto della versione o della marca temporale per verificare se l'app è stata aggiornata.",
|
||||||
"VERSION_name": "",
|
"VERSION_name": "Versione o marca temporale",
|
||||||
"devices_old": "Aggiornamento...",
|
"devices_old": "Aggiornamento...",
|
||||||
"general_event_description": "L'evento che hai attivato potrebbe richiedere del tempo prima che i processi in background vengano completati. L'esecuzione è terminata una volta che la coda di esecuzione sottostante si è svuotata (controlla il <a href='/maintenance.php#tab_Logging'>log degli errori</a> se riscontri problemi). <br/> <br/> Coda di esecuzione:",
|
"general_event_description": "L'evento che hai attivato potrebbe richiedere del tempo prima che i processi in background vengano completati. L'esecuzione è terminata una volta che la coda di esecuzione sottostante si è svuotata (controlla il <a href='/maintenance.php#tab_Logging'>log degli errori</a> se riscontri problemi). <br/> <br/> Coda di esecuzione:",
|
||||||
"general_event_title": "Esecuzione di un evento ad-hoc",
|
"general_event_title": "Esecuzione di un evento ad-hoc",
|
||||||
|
|||||||
@@ -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",
|
||||||
@@ -557,6 +560,7 @@
|
|||||||
"RandomMAC_hover": "Autodetektert - indikerer om enheten randomiserer MAC-adressen sin.",
|
"RandomMAC_hover": "Autodetektert - indikerer om enheten randomiserer MAC-adressen sin.",
|
||||||
"Reports_Sent_Log": "Sendte rapport logger",
|
"Reports_Sent_Log": "Sendte rapport logger",
|
||||||
"SCAN_SUBNETS_description": "De fleste skannere på nettet (ARP-Scan, NMAP, NSlookup, Dig, Pholus) er avhengige av å skanne spesifikke nettverksgrensesnitt og undernett. Sjekk <a href=\"https://github.com/jokob-sk/NetAlertX/blob/main/docs/SUBNETS.md\" target=\"_blank\">subnett dokumentasjonen</a> for hjelp på denne innstillingen, spesielt VLAN-er, hvilke VLAN-er som støttes, eller hvordan du kan finne ut nettverksmasken og grensesnittet ditt. <br/> <br/> Et alternativ til skannere på nettet er å aktivere noen andre enhetsskannere/importører som ikke er avhengige av Netalert<sup>X</sup> med tilgang til nettverket (UniFi, DHCP-Leaser, Pihole, osv.). <br/> <br/> Merk: Selve skanningstiden avhenger av antall IP -adresser som skal sjekkes, så sett dette opp nøye med riktig nettverksmaske og grensesnitt.",
|
"SCAN_SUBNETS_description": "De fleste skannere på nettet (ARP-Scan, NMAP, NSlookup, Dig, Pholus) er avhengige av å skanne spesifikke nettverksgrensesnitt og undernett. Sjekk <a href=\"https://github.com/jokob-sk/NetAlertX/blob/main/docs/SUBNETS.md\" target=\"_blank\">subnett dokumentasjonen</a> for hjelp på denne innstillingen, spesielt VLAN-er, hvilke VLAN-er som støttes, eller hvordan du kan finne ut nettverksmasken og grensesnittet ditt. <br/> <br/> Et alternativ til skannere på nettet er å aktivere noen andre enhetsskannere/importører som ikke er avhengige av Netalert<sup>X</sup> med tilgang til nettverket (UniFi, DHCP-Leaser, Pihole, osv.). <br/> <br/> Merk: Selve skanningstiden avhenger av antall IP -adresser som skal sjekkes, så sett dette opp nøye med riktig nettverksmaske og grensesnitt.",
|
||||||
|
"SCAN_SUBNETS_name": "",
|
||||||
"SYSTEM_TITLE": "Systeminformasjon",
|
"SYSTEM_TITLE": "Systeminformasjon",
|
||||||
"Setting_Override": "Overstyr verdi",
|
"Setting_Override": "Overstyr verdi",
|
||||||
"Setting_Override_Description": "Aktivering av dette alternativet vil overstyre en App som leveres standard-verdi med verdien som er spesifisert ovenfor.",
|
"Setting_Override_Description": "Aktivering av dette alternativet vil overstyre en App som leveres standard-verdi med verdien som er spesifisert ovenfor.",
|
||||||
|
|||||||
@@ -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",
|
||||||
@@ -557,6 +560,7 @@
|
|||||||
"RandomMAC_hover": "Auto wykrywanie - oznacza czy urządzenie randomizuje swój adres MAC.",
|
"RandomMAC_hover": "Auto wykrywanie - oznacza czy urządzenie randomizuje swój adres MAC.",
|
||||||
"Reports_Sent_Log": "Wyślij zgłoszenie logów",
|
"Reports_Sent_Log": "Wyślij zgłoszenie logów",
|
||||||
"SCAN_SUBNETS_description": "Większość skanerów sieciowych (ARP-SCAN, NMAP, NSLOOKUP, DIG, PHOLUS) opiera się na konkretnych interfejsach sieciowych oraz podsieci. Sprawdź <a href=\"https://github.com/jokob-sk/NetAlertX/blob/main/docs/SUBNETS.md\" target=\"_blank\"> dokumentacji podsieci</a> jeżeli potrzebujesz pomocy w ustawieniach, a szczególnie z VLAN'ami, jakie VLAN'y są wspierane oraz jak rozgryźć maskę podsieci twojego interfejsu.<br/><br/> Alternatywą do skanerów sieciowych jest uruchomienie innego Skanera Urządzeń/Importera który nie polega by NetAlert<sup>X</sup> miał dostęp do sieci (UNIFI, dhcp.leases, PiHole, itp.).<br/><br/> Notatka: Czas skanu zależy od liczby adresów IP do sprawdzenia, więc ustaw go tak by skanował odpowiedni interfejs i maskę sieciową.",
|
"SCAN_SUBNETS_description": "Większość skanerów sieciowych (ARP-SCAN, NMAP, NSLOOKUP, DIG, PHOLUS) opiera się na konkretnych interfejsach sieciowych oraz podsieci. Sprawdź <a href=\"https://github.com/jokob-sk/NetAlertX/blob/main/docs/SUBNETS.md\" target=\"_blank\"> dokumentacji podsieci</a> jeżeli potrzebujesz pomocy w ustawieniach, a szczególnie z VLAN'ami, jakie VLAN'y są wspierane oraz jak rozgryźć maskę podsieci twojego interfejsu.<br/><br/> Alternatywą do skanerów sieciowych jest uruchomienie innego Skanera Urządzeń/Importera który nie polega by NetAlert<sup>X</sup> miał dostęp do sieci (UNIFI, dhcp.leases, PiHole, itp.).<br/><br/> Notatka: Czas skanu zależy od liczby adresów IP do sprawdzenia, więc ustaw go tak by skanował odpowiedni interfejs i maskę sieciową.",
|
||||||
|
"SCAN_SUBNETS_name": "",
|
||||||
"SYSTEM_TITLE": "Informacje o Systemie",
|
"SYSTEM_TITLE": "Informacje o Systemie",
|
||||||
"Setting_Override": "Nadpisz wartość",
|
"Setting_Override": "Nadpisz wartość",
|
||||||
"Setting_Override_Description": "Włączanie tej opcji nadpisze podstawową wartość na wartość podaną powyżej.",
|
"Setting_Override_Description": "Włączanie tej opcji nadpisze podstawową wartość na wartość podaną powyżej.",
|
||||||
|
|||||||
@@ -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",
|
||||||
@@ -557,6 +560,7 @@
|
|||||||
"RandomMAC_hover": "",
|
"RandomMAC_hover": "",
|
||||||
"Reports_Sent_Log": "",
|
"Reports_Sent_Log": "",
|
||||||
"SCAN_SUBNETS_description": "",
|
"SCAN_SUBNETS_description": "",
|
||||||
|
"SCAN_SUBNETS_name": "",
|
||||||
"SYSTEM_TITLE": "",
|
"SYSTEM_TITLE": "",
|
||||||
"Setting_Override": "",
|
"Setting_Override": "",
|
||||||
"Setting_Override_Description": "",
|
"Setting_Override_Description": "",
|
||||||
|
|||||||
@@ -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> Скопировать данные с устройства",
|
||||||
@@ -274,7 +276,7 @@
|
|||||||
"Gen_AreYouSure": "Вы уверены?",
|
"Gen_AreYouSure": "Вы уверены?",
|
||||||
"Gen_Backup": "Запустить резервное копирование",
|
"Gen_Backup": "Запустить резервное копирование",
|
||||||
"Gen_Cancel": "Отмена",
|
"Gen_Cancel": "Отмена",
|
||||||
"Gen_Change": "",
|
"Gen_Change": "Изменить",
|
||||||
"Gen_Copy": "Запустить",
|
"Gen_Copy": "Запустить",
|
||||||
"Gen_DataUpdatedUITakesTime": "ОК - Обновление UI может занять некоторое время, если сканирование выполняется.",
|
"Gen_DataUpdatedUITakesTime": "ОК - Обновление UI может занять некоторое время, если сканирование выполняется.",
|
||||||
"Gen_Delete": "Удалить",
|
"Gen_Delete": "Удалить",
|
||||||
@@ -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": "Успешное обновление",
|
||||||
@@ -557,6 +560,7 @@
|
|||||||
"RandomMAC_hover": "Автоматически обнаружено — указывает, рандомизирует ли устройство свой MAC-адрес.",
|
"RandomMAC_hover": "Автоматически обнаружено — указывает, рандомизирует ли устройство свой MAC-адрес.",
|
||||||
"Reports_Sent_Log": "Отправить журнал логов",
|
"Reports_Sent_Log": "Отправить журнал логов",
|
||||||
"SCAN_SUBNETS_description": "Большинство сетевых сканеров (ARP-SCAN, NMAP, NSLOOKUP, DIG, PHOLUS) полагаются на сканирование определенных сетевых интерфейсов и подсетей. Дополнительную информацию по этому параметру можно найти в <a href=\"https://github.com/jokob-sk/NetAlertX/blob/main/docs/SUBNETS.md\" target=\"_blank\">документации по подсетям</a>, особенно VLAN, какие VLAN поддерживаются или как разобраться в маске сети и своем интерфейсе. <br/> <br/> Альтернативой сетевым сканерам является включение некоторых других сканеров/импортеров устройств, которые не полагаются на NetAlert<sup>X</sup>, имеющий доступ к сети (UNIFI, dhcp.leases , PiHole и др.). <br/> <br/> Примечание. Само время сканирования зависит от количества проверяемых IP-адресов, поэтому тщательно настройте его, указав соответствующую маску сети и интерфейс.",
|
"SCAN_SUBNETS_description": "Большинство сетевых сканеров (ARP-SCAN, NMAP, NSLOOKUP, DIG, PHOLUS) полагаются на сканирование определенных сетевых интерфейсов и подсетей. Дополнительную информацию по этому параметру можно найти в <a href=\"https://github.com/jokob-sk/NetAlertX/blob/main/docs/SUBNETS.md\" target=\"_blank\">документации по подсетям</a>, особенно VLAN, какие VLAN поддерживаются или как разобраться в маске сети и своем интерфейсе. <br/> <br/> Альтернативой сетевым сканерам является включение некоторых других сканеров/импортеров устройств, которые не полагаются на NetAlert<sup>X</sup>, имеющий доступ к сети (UNIFI, dhcp.leases , PiHole и др.). <br/> <br/> Примечание. Само время сканирования зависит от количества проверяемых IP-адресов, поэтому тщательно настройте его, указав соответствующую маску сети и интерфейс.",
|
||||||
|
"SCAN_SUBNETS_name": "",
|
||||||
"SYSTEM_TITLE": "Системная информация",
|
"SYSTEM_TITLE": "Системная информация",
|
||||||
"Setting_Override": "Переопределить значение",
|
"Setting_Override": "Переопределить значение",
|
||||||
"Setting_Override_Description": "Включение этой опции приведет к переопределению значения по умолчанию, предоставленного приложением, на значение, указанное выше.",
|
"Setting_Override_Description": "Включение этой опции приведет к переопределению значения по умолчанию, предоставленного приложением, на значение, указанное выше.",
|
||||||
@@ -655,8 +659,8 @@
|
|||||||
"UI_PRESENCE_name": "Показать в диаграмме присутствия",
|
"UI_PRESENCE_name": "Показать в диаграмме присутствия",
|
||||||
"UI_REFRESH_description": "Введите количество секунд, по истечении которых пользовательский интерфейс перезагружается. Установите значение <code>0</code>, чтобы отключить.",
|
"UI_REFRESH_description": "Введите количество секунд, по истечении которых пользовательский интерфейс перезагружается. Установите значение <code>0</code>, чтобы отключить.",
|
||||||
"UI_REFRESH_name": "Автоматическое обновление интерфейса",
|
"UI_REFRESH_name": "Автоматическое обновление интерфейса",
|
||||||
"VERSION_description": "",
|
"VERSION_description": "Вспомогательное значение версии или метки времени, позволяющее проверить, было ли приложение обновлено.",
|
||||||
"VERSION_name": "",
|
"VERSION_name": "Версия или временная метка",
|
||||||
"devices_old": "Актуализируется...",
|
"devices_old": "Актуализируется...",
|
||||||
"general_event_description": "Событие, которое вы инициировали, может занять некоторое время, прежде чем фоновые процессы завершатся. Выполнение завершится, как только очередь выполнения, указанная ниже, опустеет (Проверьте <a href='/maintenance.php#tab_Logging'>журнал ошибок</a> при возникновении проблем). <br/> <br/>· · Очередь выполнения:",
|
"general_event_description": "Событие, которое вы инициировали, может занять некоторое время, прежде чем фоновые процессы завершатся. Выполнение завершится, как только очередь выполнения, указанная ниже, опустеет (Проверьте <a href='/maintenance.php#tab_Logging'>журнал ошибок</a> при возникновении проблем). <br/> <br/>· · Очередь выполнения:",
|
||||||
"general_event_title": "Выполнение специального события",
|
"general_event_title": "Выполнение специального события",
|
||||||
|
|||||||
@@ -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",
|
||||||
@@ -557,6 +560,7 @@
|
|||||||
"RandomMAC_hover": "",
|
"RandomMAC_hover": "",
|
||||||
"Reports_Sent_Log": "",
|
"Reports_Sent_Log": "",
|
||||||
"SCAN_SUBNETS_description": "",
|
"SCAN_SUBNETS_description": "",
|
||||||
|
"SCAN_SUBNETS_name": "",
|
||||||
"SYSTEM_TITLE": "",
|
"SYSTEM_TITLE": "",
|
||||||
"Setting_Override": "",
|
"Setting_Override": "",
|
||||||
"Setting_Override_Description": "",
|
"Setting_Override_Description": "",
|
||||||
|
|||||||
@@ -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": "已成功更新",
|
||||||
@@ -557,6 +560,7 @@
|
|||||||
"RandomMAC_hover": "自动检测 - 表示设备是否随机化其 MAC 地址。",
|
"RandomMAC_hover": "自动检测 - 表示设备是否随机化其 MAC 地址。",
|
||||||
"Reports_Sent_Log": "已发送报告日志",
|
"Reports_Sent_Log": "已发送报告日志",
|
||||||
"SCAN_SUBNETS_description": "大多数网络扫描器(ARP-SCAN、NMAP、NSLOOKUP、DIG、PHOLUS)依赖于扫描特定的网络接口和子网。查看<a href=\"https://github.com/jokob-sk/NetAlertX/blob/main/docs/SUBNETS.md\" target=\"_blank\">子网文档</a>以获取有关此设置的帮助,尤其是 VLAN、支持哪些 VLAN,或者如何确定网络掩码和接口。<br/> <br/> 网络扫描器的替代方法是启用一些其他不依赖于 NetAlert<sup>X</sup> 访问网络的设备扫描器/导入器(UNIFI、dhcp.leases、PiHole 等)。<br/> <br/> 注意:扫描时间本身取决于要检查的 IP 地址数量,因此请使用适当的网络掩码和接口仔细设置。",
|
"SCAN_SUBNETS_description": "大多数网络扫描器(ARP-SCAN、NMAP、NSLOOKUP、DIG、PHOLUS)依赖于扫描特定的网络接口和子网。查看<a href=\"https://github.com/jokob-sk/NetAlertX/blob/main/docs/SUBNETS.md\" target=\"_blank\">子网文档</a>以获取有关此设置的帮助,尤其是 VLAN、支持哪些 VLAN,或者如何确定网络掩码和接口。<br/> <br/> 网络扫描器的替代方法是启用一些其他不依赖于 NetAlert<sup>X</sup> 访问网络的设备扫描器/导入器(UNIFI、dhcp.leases、PiHole 等)。<br/> <br/> 注意:扫描时间本身取决于要检查的 IP 地址数量,因此请使用适当的网络掩码和接口仔细设置。",
|
||||||
|
"SCAN_SUBNETS_name": "",
|
||||||
"SYSTEM_TITLE": "系统信息",
|
"SYSTEM_TITLE": "系统信息",
|
||||||
"Setting_Override": "覆盖值",
|
"Setting_Override": "覆盖值",
|
||||||
"Setting_Override_Description": "启用此选项将用上面指定的值覆盖应用程序提供的默认值。",
|
"Setting_Override_Description": "启用此选项将用上面指定的值覆盖应用程序提供的默认值。",
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"code_name": "mikrotik_scan",
|
"code_name": "mikrotik_scan",
|
||||||
"unique_prefix": "MTSCAN",
|
"unique_prefix": "MTSCAN",
|
||||||
"plugin_type": "other",
|
"plugin_type": "device_scanner",
|
||||||
"execution_order" : "Layer_4",
|
"execution_order" : "Layer_4",
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
"data_source": "script",
|
"data_source": "script",
|
||||||
@@ -11,7 +11,7 @@
|
|||||||
"display_name": [
|
"display_name": [
|
||||||
{
|
{
|
||||||
"language_code": "en_us",
|
"language_code": "en_us",
|
||||||
"string": "Mikrotik (Name discovery)"
|
"string": "Mikrotik (Device discovery)"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"icon": [
|
"icon": [
|
||||||
@@ -23,7 +23,7 @@
|
|||||||
"description": [
|
"description": [
|
||||||
{
|
{
|
||||||
"language_code": "en_us",
|
"language_code": "en_us",
|
||||||
"string": "A plugin to discover device names."
|
"string": "A plugin to discover devices via Mikrotik."
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"params": [
|
"params": [
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
# test script by running:
|
|
||||||
# tbc
|
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import pathlib
|
import pathlib
|
||||||
@@ -42,41 +40,34 @@ def main():
|
|||||||
|
|
||||||
mylog('verbose', [f'[{pluginName}] In script'])
|
mylog('verbose', [f'[{pluginName}] In script'])
|
||||||
|
|
||||||
mt_host = get_setting_value('MTSCAN_MT_HOST')
|
# init global variables
|
||||||
mt_port = get_setting_value('MTSCAN_MT_PORT')
|
global MT_HOST, MT_PORT, MT_USER, MT_PASS
|
||||||
mt_user = get_setting_value('MTSCAN_MT_USER')
|
|
||||||
mt_password = get_setting_value('MTSCAN_MT_PASS')
|
|
||||||
|
|
||||||
#mylog('verbose', [f'[{pluginName}] Router: {mt_host}:{mt_port} user: {mt_user}, pass: {mt_password}'])
|
|
||||||
# Create a database connection
|
|
||||||
db = DB() # instance of class DB
|
|
||||||
db.open()
|
|
||||||
|
|
||||||
# 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 a Device_obj instance
|
# Mikrotik settings
|
||||||
device_handler = Device_obj(db)
|
MT_HOST = get_setting_value('MTSCAN_MT_HOST')
|
||||||
|
MT_PORT = get_setting_value('MTSCAN_MT_PORT')
|
||||||
|
MT_USER = get_setting_value('MTSCAN_MT_USER')
|
||||||
|
MT_PASS = get_setting_value('MTSCAN_MT_PASS')
|
||||||
|
|
||||||
# Retrieve devices
|
plugin_objects = get_entries(plugin_objects)
|
||||||
#unknown_devices = device_handler.getUnknown()
|
|
||||||
#mylog('verbose', [f'[{pluginName}] Unknown devices count: {len(unknown_devices)}'])
|
|
||||||
|
|
||||||
all_devices = device_handler.getAll()
|
plugin_objects.write_result_file()
|
||||||
|
|
||||||
mylog('verbose', [f'[{pluginName}] all devices count: {len(all_devices)}'])
|
mylog('verbose', [f'[{pluginName}] Scan finished, found {len(plugin_objects)} devices'])
|
||||||
|
|
||||||
device_map = {d['dev_MAC']:d['dev_LastIP'] for d in all_devices}
|
|
||||||
|
def get_entries(plugin_objects: Plugin_Objects) -> Plugin_Objects:
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# connect router
|
# connect router
|
||||||
api = connect(username=mt_user, password=mt_password, host=mt_host, port=mt_port)
|
api = connect(username=MT_USER, password=MT_PASS, host=MT_HOST, port=MT_PORT)
|
||||||
|
|
||||||
# get dhcp leases
|
# get dhcp leases
|
||||||
leases = api('/ip/dhcp-server/lease/print')
|
leases = api('/ip/dhcp-server/lease/print')
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
for lease in leases:
|
for lease in leases:
|
||||||
lease_id = lease.get('.id')
|
lease_id = lease.get('.id')
|
||||||
address = lease.get('address')
|
address = lease.get('address')
|
||||||
@@ -84,55 +75,30 @@ def main():
|
|||||||
host_name = lease.get('host-name')
|
host_name = lease.get('host-name')
|
||||||
comment = lease.get('comment')
|
comment = lease.get('comment')
|
||||||
last_seen = lease.get('last-seen')
|
last_seen = lease.get('last-seen')
|
||||||
|
status = lease.get('status')
|
||||||
|
|
||||||
mylog('verbose', [f"ID: {lease_id}, Address: {address}, MAC Address: {mac_address}, Host Name: {host_name}, Comment: {comment}, Last Seen: {last_seen}"])
|
mylog('verbose', [f"ID: {lease_id}, Address: {address}, MAC Address: {mac_address}, Host Name: {host_name}, Comment: {comment}, Last Seen: {last_seen}, Status: {status}"])
|
||||||
if mac_address in device_map.keys():
|
|
||||||
device_name = host_name
|
|
||||||
if comment != '':
|
|
||||||
device_name = comment
|
|
||||||
|
|
||||||
|
if (status == "bound"):
|
||||||
plugin_objects.add_object(
|
plugin_objects.add_object(
|
||||||
# "Name-MAC", "LastIP", "IP", "Name","Host","LastSeen","Comment"
|
|
||||||
primaryId = mac_address,
|
primaryId = mac_address,
|
||||||
secondaryId = device_map[mac_address],
|
secondaryId = '',
|
||||||
watched1 = address,
|
watched1 = address,
|
||||||
watched2 = device_name,
|
watched2 = host_name,
|
||||||
watched3 = host_name,
|
watched3 = last_seen,
|
||||||
watched4 = last_seen,
|
watched4 = '',
|
||||||
extra = '',
|
extra = '',
|
||||||
helpVal1 = comment,
|
helpVal1 = comment,
|
||||||
foreignKey = mac_address)
|
foreignKey = mac_address)
|
||||||
|
|
||||||
plugin_objects.write_result_file()
|
|
||||||
except TrapError as e:
|
except TrapError as e:
|
||||||
mylog('error', [f"An error occurred: {e}"])
|
mylog('error', [f"An error occurred: {e}"])
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
mylog('error', [f"Failed to connect to MikroTik API: {e}"])
|
mylog('error', [f"Failed to connect to MikroTik API: {e}"])
|
||||||
|
|
||||||
|
|
||||||
#for device in unknown_devices:
|
|
||||||
# domain_name, dns_server = execute_nslookup(device['dev_LastIP'], timeout)
|
|
||||||
|
|
||||||
# if domain_name != '':
|
|
||||||
# plugin_objects.add_object(
|
|
||||||
# # "MAC", "IP", "Server", "Name"
|
|
||||||
# primaryId = device['dev_MAC'],
|
|
||||||
# secondaryId = device['dev_LastIP'],
|
|
||||||
# watched1 = dns_server,
|
|
||||||
# watched2 = domain_name,
|
|
||||||
# watched3 = '',
|
|
||||||
# watched4 = '',
|
|
||||||
# extra = '',
|
|
||||||
# foreignKey = device['dev_MAC'])
|
|
||||||
|
|
||||||
#plugin_objects.write_result_file()
|
|
||||||
|
|
||||||
|
|
||||||
mylog('verbose', [f'[{pluginName}] Script finished'])
|
mylog('verbose', [f'[{pluginName}] Script finished'])
|
||||||
|
|
||||||
return 0
|
return plugin_objects
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#===============================================================================
|
#===============================================================================
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -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')
|
||||||
|
|
||||||
# Get all plugin configurations
|
# variables to determine operation mode
|
||||||
all_plugins = get_plugins_configs()
|
is_hub = False
|
||||||
|
is_node = False
|
||||||
|
|
||||||
mylog('verbose', [f'[{pluginName}] plugins_to_sync {plugins_to_sync}'])
|
# Check if api_token set
|
||||||
|
if not api_token:
|
||||||
|
mylog('verbose', [f'[{pluginName}] ⚠ ERROR api_token not defined - quitting.'])
|
||||||
|
return -1
|
||||||
|
|
||||||
# Plugins processing
|
# check if this is a hub or a node
|
||||||
index = 0
|
if len(hub_url) > 0 and (send_devices or plugins_to_sync):
|
||||||
for plugin in all_plugins:
|
is_node = True
|
||||||
pref = plugin["unique_prefix"]
|
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'])
|
||||||
|
|
||||||
if pref in plugins_to_sync:
|
# Mode 1: PUSH/SEND (NODE)
|
||||||
index += 1
|
if is_node:
|
||||||
mylog('verbose', [f'[{pluginName}] synching "{pref}" ({index}/{len(plugins_to_sync)})'])
|
# PUSHING/SENDING Plugins
|
||||||
|
|
||||||
# Construct the file path for the plugin's last_result.log file
|
# Get all plugin configurations
|
||||||
plugin_folder = plugin["code_name"]
|
all_plugins = get_plugins_configs()
|
||||||
file_path = f"{INSTALL_PATH}/front/plugins/{plugin_folder}/last_result.log"
|
|
||||||
|
mylog('verbose', [f'[{pluginName}] plugins_to_sync {plugins_to_sync}'])
|
||||||
|
|
||||||
|
for plugin in all_plugins:
|
||||||
|
pref = plugin["unique_prefix"]
|
||||||
|
|
||||||
|
index = 0
|
||||||
|
if pref in 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
|
||||||
|
plugin_folder = plugin["code_name"]
|
||||||
|
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,147 @@ 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'])
|
|
||||||
|
|
||||||
# Devices procesing
|
# PULLING 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):
|
|
||||||
# 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'
|
||||||
|
|
||||||
|
# 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', '')
|
||||||
|
|
||||||
|
# Decode base64 data
|
||||||
|
decoded_data = base64.b64decode(data_base64)
|
||||||
|
|
||||||
|
# Create log file name using node name
|
||||||
|
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())
|
||||||
|
|
||||||
|
|
||||||
|
# Process any received data for the Device DB table
|
||||||
|
# Create the file path
|
||||||
|
|
||||||
# Decode files, rename them, and get the list of files
|
# Decode files, rename them, and get the list of files
|
||||||
files_to_process = decode_and_rename_files(file_dir, file_prefix)
|
files_to_process = decode_and_rename_files(file_dir, file_prefix)
|
||||||
|
|
||||||
# Connect to the App database
|
if len(files_to_process) > 0:
|
||||||
conn = sqlite3.connect(fullDbPath)
|
|
||||||
cursor = conn.cursor()
|
|
||||||
|
|
||||||
# Collect all unique dev_MAC values from the JSON files
|
mylog('verbose', [f'[{pluginName}] Mode 3: RECEIVE (HUB) - This is a HUB as received data found'])
|
||||||
unique_mac_addresses = set()
|
|
||||||
device_data = []
|
|
||||||
|
|
||||||
mylog('verbose', [f'[{pluginName}] Devices files to process: "{files_to_process}"'])
|
# Connect to the App database
|
||||||
|
conn = sqlite3.connect(fullDbPath)
|
||||||
|
cursor = conn.cursor()
|
||||||
|
|
||||||
for file_name in files_to_process:
|
# Collect all unique dev_MAC values from the JSON files
|
||||||
|
unique_mac_addresses = set()
|
||||||
|
device_data = []
|
||||||
|
|
||||||
# only process received .log files, skipping the one logging the progress of this plugin
|
mylog('verbose', [f'[{pluginName}] Devices files to process: "{files_to_process}"'])
|
||||||
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
|
for file_name in files_to_process:
|
||||||
tmp_SyncHubNodeName = ''
|
|
||||||
if len(file_name.split('.')) > 3:
|
# only process received .log files, skipping the one logging the progress of this plugin
|
||||||
tmp_SyncHubNodeName = file_name.split('.')[2]
|
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}"
|
file_path = f"{INSTALL_PATH}/front/plugins/sync/{file_name}"
|
||||||
|
|
||||||
with open(file_path, 'r') as f:
|
with open(file_path, 'r') as f:
|
||||||
data = json.load(f)
|
data = json.load(f)
|
||||||
for device in data['data']:
|
for device in data['data']:
|
||||||
if device['dev_MAC'] not in unique_mac_addresses:
|
if device['dev_MAC'] not in unique_mac_addresses:
|
||||||
device['dev_SyncHubNodeName'] = tmp_SyncHubNodeName
|
device['dev_SyncHubNodeName'] = tmp_SyncHubNodeName
|
||||||
unique_mac_addresses.add(device['dev_MAC'])
|
unique_mac_addresses.add(device['dev_MAC'])
|
||||||
device_data.append(device)
|
device_data.append(device)
|
||||||
|
|
||||||
|
if len(device_data) > 0:
|
||||||
|
# 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'])
|
||||||
|
|
||||||
if len(device_data) > 0:
|
# Filter out existing devices
|
||||||
# Retrieve existing dev_MAC values from the Devices table
|
new_devices = [device for device in device_data if device['dev_MAC'] not in existing_mac_addresses]
|
||||||
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())
|
|
||||||
|
|
||||||
|
# Remove 'rowid' key if it exists
|
||||||
|
for device in new_devices:
|
||||||
|
device.pop('rowid', None)
|
||||||
|
|
||||||
# insert devices into the lats_result.log to manage state
|
mylog('verbose', [f'[{pluginName}] All devices: "{len(device_data)}"'])
|
||||||
for device in device_data:
|
mylog('verbose', [f'[{pluginName}] New devices: "{len(new_devices)}"'])
|
||||||
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
|
# Prepare the insert statement
|
||||||
new_devices = [device for device in device_data if device['dev_MAC'] not in existing_mac_addresses]
|
if new_devices:
|
||||||
|
|
||||||
# Remove 'rowid' key if it exists
|
columns = ', '.join(k for k in new_devices[0].keys() if k != 'rowid')
|
||||||
for device in new_devices:
|
placeholders = ', '.join('?' for k in new_devices[0] if k != 'rowid')
|
||||||
device.pop('rowid', None)
|
sql = f'INSERT INTO Devices ({columns}) VALUES ({placeholders})'
|
||||||
|
|
||||||
mylog('verbose', [f'[{pluginName}] All devices: "{len(device_data)}"'])
|
# Extract values for the new devices
|
||||||
mylog('verbose', [f'[{pluginName}] New devices: "{len(new_devices)}"'])
|
values = [tuple(device.values()) for device in new_devices]
|
||||||
|
|
||||||
# Prepare the insert statement
|
mylog('verbose', [f'[{pluginName}] Inserting Devices SQL : "{sql}"'])
|
||||||
if new_devices:
|
mylog('verbose', [f'[{pluginName}] Inserting Devices VALUES: "{values}"'])
|
||||||
|
|
||||||
columns = ', '.join(k for k in new_devices[0].keys() if k != 'rowid')
|
# Use executemany for batch insertion
|
||||||
placeholders = ', '.join('?' for k in new_devices[0] if k != 'rowid')
|
cursor.executemany(sql, values)
|
||||||
sql = f'INSERT INTO Devices ({columns}) VALUES ({placeholders})'
|
|
||||||
|
|
||||||
# Extract values for the new devices
|
message = f'[{pluginName}] Inserted "{len(new_devices)}" new devices'
|
||||||
values = [tuple(device.values()) for device in new_devices]
|
|
||||||
|
|
||||||
mylog('verbose', [f'[{pluginName}] Inserting Devices SQL : "{sql}"'])
|
mylog('verbose', [message])
|
||||||
mylog('verbose', [f'[{pluginName}] Inserting Devices VALUES: "{values}"'])
|
write_notification(message, 'info', timeNowTZ())
|
||||||
|
|
||||||
# Use executemany for batch insertion
|
# Commit and close the connection
|
||||||
cursor.executemany(sql, values)
|
conn.commit()
|
||||||
|
conn.close()
|
||||||
|
|
||||||
message = f'[{pluginName}] Inserted "{len(new_devices)}" new devices'
|
# log result
|
||||||
|
plugin_objects.write_result_file()
|
||||||
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)
|
||||||
@@ -224,6 +283,36 @@ def send_data(api_token, file_content, encryption_key, plugin_folder, node_name,
|
|||||||
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}"'])
|
||||||
|
|
||||||
|
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 ""
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|||||||
@@ -26,6 +26,274 @@
|
|||||||
],
|
],
|
||||||
"params": [],
|
"params": [],
|
||||||
"settings": [
|
"settings": [
|
||||||
|
{
|
||||||
|
"function": "NOT_RANDOM_MAC",
|
||||||
|
"type": {
|
||||||
|
"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": []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"maxLength": 50,
|
||||||
|
"default_value": [],
|
||||||
|
"options": [],
|
||||||
|
"localized": [],
|
||||||
|
"name": [
|
||||||
|
{
|
||||||
|
"string": "_GLOBAL_LANG_FILES_"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": [
|
||||||
|
{
|
||||||
|
"string": "_GLOBAL_LANG_FILES_"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"function": "ICONS",
|
||||||
|
"type": {
|
||||||
|
"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": []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"maxLength": 50,
|
||||||
|
"default_value": [
|
||||||
|
"PGkgY2xhc3M9ImZhIGZhLWNvbXB1dGVyIj48L2k+",
|
||||||
|
"PGkgY2xhc3M9ImZhIGZhLWV0aGVybmV0Ij48L2k+",
|
||||||
|
"PGkgY2xhc3M9ImZhIGZhLWdhbWVwYWQiPjwvaT4=",
|
||||||
|
"PGkgY2xhc3M9ImZhIGZhLWdsb2JlIj48L2k+",
|
||||||
|
"PGkgY2xhc3M9ImZhIGZhLWxhcHRvcCI+PC9pPg==",
|
||||||
|
"PGkgY2xhc3M9ImZhIGZhLWxpZ2h0YnVsYiI+PC9pPg==",
|
||||||
|
"PGkgY2xhc3M9ImZhIGZhLXNoaWVsZCI+PC9pPg==",
|
||||||
|
"PGkgY2xhc3M9ImZhIGZhLXdpZmkiPjwvaT4=",
|
||||||
|
"PGkgY2xhc3M9ImZhIGZhLWdhbWVwYWQiPjwvaT4="
|
||||||
|
],
|
||||||
|
"options": [],
|
||||||
|
"localized": [],
|
||||||
|
"name": [
|
||||||
|
{
|
||||||
|
"string": "_GLOBAL_LANG_FILES_"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": [
|
||||||
|
{
|
||||||
|
"string": "_GLOBAL_LANG_FILES_"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"function": "REFRESH",
|
||||||
|
"type": {
|
||||||
|
"dataType": "integer",
|
||||||
|
"elements": [
|
||||||
|
{
|
||||||
|
"elementType": "input",
|
||||||
|
"elementOptions": [{ "type": "number" }],
|
||||||
|
"transformers": []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"maxLength": 50,
|
||||||
|
"default_value": 0,
|
||||||
|
"options": [],
|
||||||
|
"localized": [],
|
||||||
|
"name": [
|
||||||
|
{
|
||||||
|
"string": "_GLOBAL_LANG_FILES_"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": [
|
||||||
|
{
|
||||||
|
"string": "_GLOBAL_LANG_FILES_"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"function": "DEV_SECTIONS",
|
||||||
|
"type": {
|
||||||
|
"dataType": "array",
|
||||||
|
"elements": [
|
||||||
|
{
|
||||||
|
"elementType": "select",
|
||||||
|
"elementOptions": [{ "multiple": "true" }],
|
||||||
|
"transformers": []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"maxLength": 50,
|
||||||
|
"default_value": [],
|
||||||
|
"options": ["Tile Cards", "Device Presence"],
|
||||||
|
"localized": [],
|
||||||
|
"name": [
|
||||||
|
{
|
||||||
|
"string": "_GLOBAL_LANG_FILES_"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": [
|
||||||
|
{
|
||||||
|
"string": "_GLOBAL_LANG_FILES_"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"function": "PRESENCE",
|
||||||
|
"type": {
|
||||||
|
"dataType": "array",
|
||||||
|
"elements": [
|
||||||
|
{
|
||||||
|
"elementType": "select",
|
||||||
|
"elementOptions": [{ "multiple": "true" }],
|
||||||
|
"transformers": []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"maxLength": 50,
|
||||||
|
"default_value": ["online", "offline", "archived"],
|
||||||
|
"options": ["online", "offline", "archived"],
|
||||||
|
"localized": [],
|
||||||
|
"name": [
|
||||||
|
{
|
||||||
|
"string": "_GLOBAL_LANG_FILES_"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": [
|
||||||
|
{
|
||||||
|
"string": "_GLOBAL_LANG_FILES_"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"function": "MY_DEVICES",
|
||||||
|
"type": {
|
||||||
|
"dataType": "array",
|
||||||
|
"elements": [
|
||||||
|
{
|
||||||
|
"elementType": "select",
|
||||||
|
"elementOptions": [{ "multiple": "true" }],
|
||||||
|
"transformers": []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"maxLength": 50,
|
||||||
|
"default_value": ["online", "offline", "archived", "new", "down"],
|
||||||
|
"options": ["online", "offline", "archived", "new", "down"],
|
||||||
|
"localized": [],
|
||||||
|
"name": [
|
||||||
|
{
|
||||||
|
"string": "_GLOBAL_LANG_FILES_"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": [
|
||||||
|
{
|
||||||
|
"string": "_GLOBAL_LANG_FILES_"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"function": "device_columns",
|
"function": "device_columns",
|
||||||
"type": {
|
"type": {
|
||||||
|
|||||||
@@ -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="",
|
||||||
|
|||||||
@@ -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,7 +697,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('none', elementOptions, transformers, val = "");
|
} = handleElementOptions('none', elementOptions, transformers, val = "");
|
||||||
|
|
||||||
let value;
|
let value;
|
||||||
@@ -669,12 +710,14 @@ $settingsJSON_DB = json_encode($settings, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX
|
|||||||
|
|
||||||
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 (dataType === 'boolean') {
|
||||||
|
|
||||||
value = $(`#${setCodeName}`).is(':checked') ? 1 : 0;
|
value = $(`#${setCodeName}`).is(':checked') ? 1 : 0;
|
||||||
value = applyTransformers(value, transformers);
|
value = applyTransformers(value, transformers);
|
||||||
|
|
||||||
settingsArray.push([prefix, setCodeName, dataType, value]);
|
settingsArray.push([prefix, setCodeName, dataType, value]);
|
||||||
|
|
||||||
} else if (dataType === "array" ) {
|
} else if (dataType === "array" ) {
|
||||||
@@ -813,7 +856,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) {
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ pluginsPath = applicationPath + '/front/plugins'
|
|||||||
logPath = applicationPath + '/front/log'
|
logPath = applicationPath + '/front/log'
|
||||||
apiPath = applicationPath + '/front/api/'
|
apiPath = applicationPath + '/front/api/'
|
||||||
reportTemplatesPath = applicationPath + '/front/report_templates/'
|
reportTemplatesPath = applicationPath + '/front/report_templates/'
|
||||||
|
fullConfFolder = applicationPath + '/config'
|
||||||
fullConfPath = applicationPath + confPath
|
fullConfPath = applicationPath + confPath
|
||||||
fullDbPath = applicationPath + dbPath
|
fullDbPath = applicationPath + dbPath
|
||||||
vendorsPath = '/usr/share/arp-scan/ieee-oui.txt'
|
vendorsPath = '/usr/share/arp-scan/ieee-oui.txt'
|
||||||
|
|||||||
@@ -213,15 +213,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
|
||||||
);
|
);
|
||||||
""")
|
""")
|
||||||
|
|
||||||
|
|||||||
@@ -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"}):
|
||||||
|
|||||||
@@ -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,13 @@ def sanitize_string(input):
|
|||||||
return input
|
return input
|
||||||
|
|
||||||
|
|
||||||
|
#-------------------------------------------------------------------------------
|
||||||
|
def sanitize_SQL_input(val):
|
||||||
|
if val is None:
|
||||||
|
return ''
|
||||||
|
return val.replace("'", "_")
|
||||||
|
|
||||||
|
|
||||||
#-------------------------------------------------------------------------------
|
#-------------------------------------------------------------------------------
|
||||||
def generate_mac_links (html, deviceUrl):
|
def generate_mac_links (html, deviceUrl):
|
||||||
|
|
||||||
|
|||||||
@@ -11,8 +11,8 @@ import re
|
|||||||
|
|
||||||
|
|
||||||
import conf
|
import conf
|
||||||
from const import fullConfPath, applicationPath
|
from const import fullConfPath, applicationPath, fullConfFolder
|
||||||
from helper import collect_lang_strings, updateSubnets, initOrSetParam, isJsonObject, updateState, setting_value_to_python_type, timeNowTZ
|
from helper import collect_lang_strings, updateSubnets, initOrSetParam, isJsonObject, updateState, setting_value_to_python_type, timeNowTZ, get_setting_value
|
||||||
from logger import mylog
|
from logger import mylog
|
||||||
from api import update_api
|
from api import update_api
|
||||||
from scheduler import schedule_class
|
from scheduler import schedule_class
|
||||||
@@ -29,7 +29,8 @@ from notification import write_notification
|
|||||||
# Check config dictionary
|
# Check config dictionary
|
||||||
|
|
||||||
#-------------------------------------------------------------------------------
|
#-------------------------------------------------------------------------------
|
||||||
def ccd(key, default, config_dir, name, inputtype, options, group, events=None, desc="", regex="", setJsonMetadata=None, overrideTemplate=None):
|
# 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, overriddenByEnv=0):
|
||||||
if events is None:
|
if events is None:
|
||||||
events = []
|
events = []
|
||||||
if setJsonMetadata is None:
|
if setJsonMetadata is None:
|
||||||
@@ -41,7 +42,7 @@ def ccd(key, default, config_dir, name, inputtype, options, group, events=None,
|
|||||||
result = default
|
result = default
|
||||||
|
|
||||||
# Use existing value if already supplied, otherwise default value is used
|
# Use existing value if already supplied, otherwise default value is used
|
||||||
if key in config_dir:
|
if forceDefault == False and key in config_dir:
|
||||||
result = config_dir[key]
|
result = config_dir[key]
|
||||||
|
|
||||||
# Single quotes might break SQL queries, replacing them
|
# Single quotes might break SQL queries, replacing them
|
||||||
@@ -49,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)
|
||||||
@@ -58,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')
|
||||||
|
|
||||||
@@ -70,16 +71,20 @@ def update_or_append(settings_list, item_tuple, key):
|
|||||||
if settings_list is None:
|
if settings_list is None:
|
||||||
settings_list = []
|
settings_list = []
|
||||||
|
|
||||||
# mylog('debug', ['[Import Config] update_or_append debug '])
|
|
||||||
# mylog('debug', ['[Import Config] update_or_append ', settings_list])
|
|
||||||
# mylog('debug', ['[Import Config] update_or_append item_tuple ' , item_tuple])
|
|
||||||
|
|
||||||
for index, item in enumerate(settings_list):
|
for index, item in enumerate(settings_list):
|
||||||
if item[0] == key:
|
if item[0] == key:
|
||||||
settings_list[index] = item_tuple
|
mylog('trace', ['[Import Config] OLD TUPLE : ', item])
|
||||||
mylog('debug', ['[Import Config] FOUND key : ', key])
|
# Keep values marked as "_KEEP_"
|
||||||
|
updated_tuple = tuple(
|
||||||
|
new_val if new_val != "_KEEP_" else old_val
|
||||||
|
for old_val, new_val in zip(item, item_tuple)
|
||||||
|
)
|
||||||
|
mylog('trace', ['[Import Config] NEW TUPLE : ', updated_tuple])
|
||||||
|
settings_list[index] = updated_tuple
|
||||||
|
mylog('trace', ['[Import Config] FOUND key : ', key])
|
||||||
return settings_list
|
return settings_list
|
||||||
|
|
||||||
|
|
||||||
settings_list.append(item_tuple)
|
settings_list.append(item_tuple)
|
||||||
return settings_list
|
return settings_list
|
||||||
|
|
||||||
@@ -140,18 +145,13 @@ 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')
|
||||||
|
|
||||||
# UI
|
# UI
|
||||||
conf.UI_LANG = ccd('UI_LANG', 'English' , c_d, 'Language Interface', '{"dataType":"string", "elements": [{"elementType" : "select", "elementOptions" : [] ,"transformers": []}]}', "['English', 'French', 'German', 'Norwegian', 'Russian', 'Spanish', 'Italian (it_it)', 'Portuguese (pt_br)', 'Polish (pl_pl)', 'Turkish (tr_tr)', 'Chinese (zh_cn)', 'Czech (cs_cz)' ]", 'UI')
|
conf.UI_LANG = ccd('UI_LANG', 'English' , c_d, 'Language Interface', '{"dataType":"string", "elements": [{"elementType" : "select", "elementOptions" : [] ,"transformers": []}]}', "['English', 'French', 'German', 'Norwegian', 'Russian', 'Spanish', 'Italian (it_it)', 'Portuguese (pt_br)', 'Polish (pl_pl)', 'Turkish (tr_tr)', 'Chinese (zh_cn)', 'Czech (cs_cz)' ]", 'UI')
|
||||||
conf.UI_NOT_RANDOM_MAC = ccd('UI_NOT_RANDOM_MAC', [] , c_d, 'Exlude from Random Prefix', '{"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":[]}]}', "[]", 'UI')
|
|
||||||
conf.UI_ICONS = ccd('UI_ICONS', ['PGkgY2xhc3M9J2ZhIGZhLXdpZmknPjwvaT4=', 'PGkgY2xhc3M9ImZhIGZhLWNvbXB1dGVyIj48L2k+', 'PGkgY2xhc3M9ImZhIGZhLWV0aGVybmV0Ij48L2k+', 'PGkgY2xhc3M9ImZhIGZhLWdhbWVwYWQiPjwvaT4', 'PGkgY2xhc3M9ImZhIGZhLWdsb2JlIj48L2k+', 'PGkgY2xhc3M9ImZhIGZhLWxhcHRvcCI+PC9pPg==', 'PGkgY2xhc3M9ImZhIGZhLWxpZ2h0YnVsYiI+PC9pPg==', 'PGkgY2xhc3M9ImZhIGZhLXNoaWVsZCI+PC9pPg==', 'PGkgY2xhc3M9ImZhIGZhLXdpZmkiPjwvaT4', 'PGkgY2xhc3M9J2ZhIGZhLWdhbWVwYWQnPjwvaT4'] , c_d, 'Icons', '{"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":[]}]}', "[]", 'UI')
|
|
||||||
conf.UI_REFRESH = ccd('UI_REFRESH', 0 , c_d, 'Refresh interval', '{"dataType":"integer", "elements": [{"elementType" : "input", "elementOptions" : [{"type": "number"}] ,"transformers": []}]}', "[]", 'UI')
|
|
||||||
conf.UI_DEV_SECTIONS = ccd('UI_DEV_SECTIONS', [] , c_d, 'Show sections', '{"dataType":"array", "elements": [{"elementType" : "select", "elementOptions" : [{"multiple":"true"}] ,"transformers": []}]}', "['Tile Cards', 'Device Presence']", 'UI')
|
|
||||||
conf.UI_PRESENCE = ccd('UI_PRESENCE', ['online', 'offline', 'archived'] , c_d, 'Include in presence', '{"dataType":"array", "elements": [{"elementType" : "select", "elementOptions" : [{"multiple":"true"}] ,"transformers": []}]}', "['online', 'offline', 'archived']", 'UI')
|
|
||||||
conf.UI_MY_DEVICES = ccd('UI_MY_DEVICES', ['online', 'offline', 'archived', 'new', 'down'] , c_d, 'Include in My Devices', '{"dataType":"array", "elements": [{"elementType" : "select", "elementOptions" : [{"multiple":"true"}] ,"transformers": []}]}', "['online', 'offline', 'archived', 'new', 'down']", 'UI')
|
|
||||||
|
|
||||||
# Init timezone in case it changed
|
# Init timezone in case it changed
|
||||||
conf.tz = timezone(conf.TIMEZONE)
|
conf.tz = timezone(conf.TIMEZONE)
|
||||||
@@ -298,23 +298,59 @@ def importConfigs (db, all_plugins):
|
|||||||
# -----------------
|
# -----------------
|
||||||
# Plugins END
|
# Plugins END
|
||||||
|
|
||||||
|
# HANDLE APP_CONF_OVERRIDE via app_conf_override.json
|
||||||
|
# Assuming fullConfFolder is defined elsewhere
|
||||||
|
app_conf_override_path = fullConfFolder + '/app_conf_override.json'
|
||||||
|
|
||||||
|
if os.path.exists(app_conf_override_path):
|
||||||
|
with open(app_conf_override_path, 'r') as f:
|
||||||
|
try:
|
||||||
|
# Load settings_override from the JSON file
|
||||||
|
settings_override = json.load(f)
|
||||||
|
|
||||||
|
# Loop through settings_override dictionary
|
||||||
|
for setting_name, value in settings_override.items():
|
||||||
|
# Ensure the value is treated as a string and passed directly
|
||||||
|
if isinstance(value, str):
|
||||||
|
# Log the value being passed
|
||||||
|
# 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}"])
|
||||||
|
ccd(setting_name, value, c_d, '_KEEP_', '_KEEP_', '_KEEP_', '_KEEP_', None, "_KEEP_", "", None, None, True, 1)
|
||||||
|
else:
|
||||||
|
# Convert to string and log
|
||||||
|
# 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)}"])
|
||||||
|
ccd(setting_name, str(value), c_d, '_KEEP_', '_KEEP_', '_KEEP_', '_KEEP_', None, "_KEEP_", "", None, None, True, 1)
|
||||||
|
|
||||||
|
except json.JSONDecodeError:
|
||||||
|
mylog('none', [f"[Config] [ERROR] Setting override decoding JSON from {app_conf_override_path}"])
|
||||||
|
else:
|
||||||
|
mylog('debug', [f"[Config] File {app_conf_override_path} does not exist."])
|
||||||
|
|
||||||
# Check if app was upgraded
|
# Check if app was upgraded
|
||||||
with open(applicationPath + '/front/buildtimestamp.txt', 'r') as f:
|
with open(applicationPath + '/front/buildtimestamp.txt', 'r') as f:
|
||||||
|
|
||||||
buildTimestamp = int(f.read().strip())
|
buildTimestamp = int(f.read().strip())
|
||||||
|
cur_version = conf.VERSION
|
||||||
|
|
||||||
if str(conf.VERSION) != str(buildTimestamp):
|
mylog('debug', [f"[Config] buildTimestamp: '{buildTimestamp}'"])
|
||||||
|
mylog('debug', [f"[Config] conf.VERSION : '{cur_version}'"])
|
||||||
|
|
||||||
mylog('none', ['[Config] App upgraded 🎉'])
|
if str(cur_version) != str(buildTimestamp):
|
||||||
|
|
||||||
conf.VERSION = ccd('VERSION', buildTimestamp , c_d, 'Version', '{"dataType":"string", "elements": [{"elementType" : "input", "elementOptions" : [{ "readonly": "true" }] ,"transformers": []}]}', '', 'General')
|
mylog('none', ['[Config] App upgraded 🚀'])
|
||||||
|
|
||||||
write_notification(f'[Upgrade] : App upgraded 🎉. Please clear app cache with the 🔄 button in the header and clear the browser cache (shift + browser refresh button).', 'interrupt', timeNowTZ())
|
# 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)
|
||||||
|
|
||||||
|
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()
|
||||||
|
|
||||||
|
|||||||
@@ -249,6 +249,10 @@ 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 +288,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.
|
||||||
|
|||||||
Reference in New Issue
Block a user