Compare commits
474 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4453ea59af | ||
|
|
31ecd6ac8c | ||
|
|
8e8493f638 | ||
|
|
adf24cebb6 | ||
|
|
5ac609e68e | ||
|
|
7f0debb04a | ||
|
|
a51571bd70 | ||
|
|
3af1f67956 | ||
|
|
9143b90bdf | ||
|
|
a892b8b5fb | ||
|
|
db621a110e | ||
|
|
4a3598e840 | ||
|
|
a19e268ea7 | ||
|
|
e9319cace3 | ||
|
|
74b2432729 | ||
|
|
d65b07685f | ||
|
|
a8dc4099e8 | ||
|
|
9c368982ce | ||
|
|
662394618b | ||
|
|
147166e46e | ||
|
|
fb8a7432cd | ||
|
|
fa00fa3004 | ||
|
|
294cfe80f2 | ||
|
|
b45e82b2a0 | ||
|
|
bf2ce3262d | ||
|
|
f468903b00 | ||
|
|
d706a156c0 | ||
|
|
71c631d784 | ||
|
|
460d2f4658 | ||
|
|
f502d93854 | ||
|
|
68fb1b7cbb | ||
|
|
942908d074 | ||
|
|
1aeed6b433 | ||
|
|
698876065c | ||
|
|
7e554242c2 | ||
|
|
129ae92141 | ||
|
|
25647023d0 | ||
|
|
fe752192e1 | ||
|
|
0ea0cd5ee0 | ||
|
|
bac78e926d | ||
|
|
d6125ef4e2 | ||
|
|
bf90ee81c7 | ||
|
|
ae74f1f538 | ||
|
|
3ae0daad3c | ||
|
|
8e7e0afb1e | ||
|
|
d7d7306a85 | ||
|
|
4b26044427 | ||
|
|
0a9ae5e9d9 | ||
|
|
ade1d9997f | ||
|
|
578b992c5a | ||
|
|
52b293a662 | ||
|
|
bd52536107 | ||
|
|
9f44c0de01 | ||
|
|
41b5de9292 | ||
|
|
95d5dbcf68 | ||
|
|
0eff7cc3f1 | ||
|
|
f31c55d6c4 | ||
|
|
6049ba00c7 | ||
|
|
8e4bd246df | ||
|
|
07164429d5 | ||
|
|
cac33fde2b | ||
|
|
5f71e9fc92 | ||
|
|
d834708220 | ||
|
|
6ea3d14480 | ||
|
|
0a11ec5379 | ||
|
|
b65c8022d6 | ||
|
|
55440090fe | ||
|
|
75bcf42225 | ||
|
|
6db9cd2f61 | ||
|
|
eff98257d6 | ||
|
|
2f97c8fd55 | ||
|
|
f03f3f33b1 | ||
|
|
457059cae8 | ||
|
|
5afc82f33e | ||
|
|
06e24b4585 | ||
|
|
482902f6e4 | ||
|
|
6e6b99fa46 | ||
|
|
30f4cba3e5 | ||
|
|
e9c0bf151f | ||
|
|
70d117cb6d | ||
|
|
624e30842a | ||
|
|
7c22f209d1 | ||
|
|
760a3c981f | ||
|
|
b86ab576bd | ||
|
|
1b6e806830 | ||
|
|
612ff7c293 | ||
|
|
74fc7ecdbf | ||
|
|
b6451e6e76 | ||
|
|
55e7bed5a8 | ||
|
|
10b6c9836b | ||
|
|
158ff2453b | ||
|
|
6b92d9f862 | ||
|
|
2fb6e8fb12 | ||
|
|
15d16dcd81 | ||
|
|
28a5b6af2a | ||
|
|
651b346a27 | ||
|
|
df90060adf | ||
|
|
d3228b2d55 | ||
|
|
83edfa66d2 | ||
|
|
40c5be3758 | ||
|
|
e2d0914334 | ||
|
|
0b5cf66451 | ||
|
|
974427f392 | ||
|
|
9d0e6a5a5d | ||
|
|
08b163ebe4 | ||
|
|
709408ca2a | ||
|
|
eae93ef6b2 | ||
|
|
7d57370741 | ||
|
|
43b9b0c032 | ||
|
|
01bf367d40 | ||
|
|
67f7822d1f | ||
|
|
f64ebae3ee | ||
|
|
d1e7606cf5 | ||
|
|
90038f1365 | ||
|
|
e29f8f29ce | ||
|
|
63a922eb0a | ||
|
|
f065b830e9 | ||
|
|
237e73ec8e | ||
|
|
6e78788a36 | ||
|
|
a0fac32c14 | ||
|
|
1ee8fcc8c3 | ||
|
|
042cfa62f5 | ||
|
|
a030912f48 | ||
|
|
01e97e152c | ||
|
|
d7c12ee8d7 | ||
|
|
9c772c5c1b | ||
|
|
7cb45351ed | ||
|
|
dce2443d73 | ||
|
|
a52e315732 | ||
|
|
443128db74 | ||
|
|
9c92252296 | ||
|
|
036ca4d1cb | ||
|
|
431fe4cd8d | ||
|
|
34427689aa | ||
|
|
08f3039f91 | ||
|
|
5514cf0f74 | ||
|
|
607dcf54c6 | ||
|
|
f3b0795e81 | ||
|
|
11e9e1991d | ||
|
|
33fad4403f | ||
|
|
3b7cbba32b | ||
|
|
5302673a96 | ||
|
|
8c4bcbca41 | ||
|
|
299f34709d | ||
|
|
d8e00e7727 | ||
|
|
7cadc57db9 | ||
|
|
fa99ae061a | ||
|
|
177a66ac60 | ||
|
|
1cc471d56c | ||
|
|
76183e504a | ||
|
|
3a1a6c8dac | ||
|
|
8c8c2cba34 | ||
|
|
dc5a9ff6d0 | ||
|
|
bfdbfa5492 | ||
|
|
610cde92f9 | ||
|
|
fdf814ded8 | ||
|
|
eb552ee912 | ||
|
|
003692d51d | ||
|
|
850824c41d | ||
|
|
877654944c | ||
|
|
8aeb2de064 | ||
|
|
fa8e5d92d7 | ||
|
|
926c1180b6 | ||
|
|
946a483455 | ||
|
|
bd3fa5c0fa | ||
|
|
9b0cef2cc2 | ||
|
|
855fdb94f0 | ||
|
|
1b5a15ebb7 | ||
|
|
151667bb52 | ||
|
|
3c81dd171b | ||
|
|
50a175bd56 | ||
|
|
ff288c58e3 | ||
|
|
307e81918e | ||
|
|
2ca5f5d80e | ||
|
|
a6f0a364d4 | ||
|
|
d94297e093 | ||
|
|
223a6556b9 | ||
|
|
ead63a81e0 | ||
|
|
f60962e341 | ||
|
|
de2ee396c8 | ||
|
|
2e3626a8b3 | ||
|
|
2faa0ff9b7 | ||
|
|
4ada56d0d9 | ||
|
|
00d33ee3e2 | ||
|
|
ead57614b6 | ||
|
|
eac915acc8 | ||
|
|
697b60731a | ||
|
|
54612059d7 | ||
|
|
63ca3019e1 | ||
|
|
bcddf6d0c5 | ||
|
|
1cec63601b | ||
|
|
f8be8e2a1f | ||
|
|
661414de28 | ||
|
|
4a33eb71c6 | ||
|
|
38b525f0b8 | ||
|
|
931155ea25 | ||
|
|
35cae3a7d4 | ||
|
|
72bccd0098 | ||
|
|
b743e68155 | ||
|
|
1d525cb84c | ||
|
|
bbd69c60bf | ||
|
|
c591ab9ce3 | ||
|
|
b613775031 | ||
|
|
bd3b15dedc | ||
|
|
0d83ed3179 | ||
|
|
f077c85c2d | ||
|
|
2eb3f9347a | ||
|
|
9daa4491a1 | ||
|
|
02f882b105 | ||
|
|
5203d4959b | ||
|
|
9e264f7a9f | ||
|
|
26e4249f5c | ||
|
|
d63e0d9fd2 | ||
|
|
ebe41ab384 | ||
|
|
1e3cea0f7f | ||
|
|
b2d38c1c55 | ||
|
|
23703e4e22 | ||
|
|
ff6d728dee | ||
|
|
73db99fe2f | ||
|
|
302a687d41 | ||
|
|
6133c2c937 | ||
|
|
ac8bbca37f | ||
|
|
be4ca42803 | ||
|
|
0355d9214d | ||
|
|
d2badf4b51 | ||
|
|
c3e9e84cde | ||
|
|
09b3f44e23 | ||
|
|
205cbcf9eb | ||
|
|
b7b1a9eb48 | ||
|
|
19eb375da1 | ||
|
|
5f59097b0d | ||
|
|
96f18b40d1 | ||
|
|
da169c1173 | ||
|
|
bd11c2ecdf | ||
|
|
c636bf1914 | ||
|
|
0f724e6a1d | ||
|
|
2599e47121 | ||
|
|
bac8ac30aa | ||
|
|
2c8c998a97 | ||
|
|
4b577109c8 | ||
|
|
a07e5b59c3 | ||
|
|
44bf47edc2 | ||
|
|
02eafadf15 | ||
|
|
46e7e7bc01 | ||
|
|
8c96f61b54 | ||
|
|
8a8df3c7b8 | ||
|
|
4cd8aa209b | ||
|
|
e3557ad7d2 | ||
|
|
3a7ff90ce1 | ||
|
|
8a0b8b8a10 | ||
|
|
93e6cc339e | ||
|
|
91ff13df27 | ||
|
|
e49432aee7 | ||
|
|
7ae4465522 | ||
|
|
d14fc6586a | ||
|
|
cb3c0ecee7 | ||
|
|
de561e1ad0 | ||
|
|
09f7b1e394 | ||
|
|
2e9aa37cd2 | ||
|
|
29c3a46170 | ||
|
|
9f2e3ae397 | ||
|
|
32a43881dd | ||
|
|
c5ed0e92e8 | ||
|
|
a5d120b0d3 | ||
|
|
d80bd48294 | ||
|
|
45c5ee24c7 | ||
|
|
ff4b1a16a2 | ||
|
|
74f1177ec6 | ||
|
|
79b81b8bc2 | ||
|
|
c06c4e2ae7 | ||
|
|
a2b9ce9c25 | ||
|
|
0a6c11bdaa | ||
|
|
b682b7cc2f | ||
|
|
2dadae1762 | ||
|
|
ce09ded979 | ||
|
|
9589cfb2cb | ||
|
|
d604591162 | ||
|
|
bb2beda12a | ||
|
|
3853b8a4ec | ||
|
|
b0c47e824d | ||
|
|
e138840ecf | ||
|
|
43e2e7c11f | ||
|
|
3118f4324a | ||
|
|
667b3b6262 | ||
|
|
5cd75b96be | ||
|
|
6be2ba967e | ||
|
|
bef7f87706 | ||
|
|
57c0d590be | ||
|
|
0cf525bd30 | ||
|
|
b3f81967c6 | ||
|
|
df071c0835 | ||
|
|
2c7d71d13c | ||
|
|
f9b16081d6 | ||
|
|
3102b8a76e | ||
|
|
b42cf07b21 | ||
|
|
2f4423481d | ||
|
|
fb88f5f722 | ||
|
|
23ef5759f6 | ||
|
|
0781c77ce2 | ||
|
|
10b3e91b8a | ||
|
|
5e45bf6d4a | ||
|
|
21ae76e77e | ||
|
|
a6b6617fcd | ||
|
|
edd81527d5 | ||
|
|
395d0a7723 | ||
|
|
e4f2e1ac4c | ||
|
|
a04bef9b31 | ||
|
|
03f82d9510 | ||
|
|
8c1a7fb828 | ||
|
|
67651acfc3 | ||
|
|
da1090dd7a | ||
|
|
f4960f5793 | ||
|
|
924ac72401 | ||
|
|
862b1e7a08 | ||
|
|
6f4ae1d29f | ||
|
|
315921873d | ||
|
|
c5211eb8ed | ||
|
|
572aaacf20 | ||
|
|
960d74640d | ||
|
|
e01125c5cb | ||
|
|
01d96cb60b | ||
|
|
3519e4b680 | ||
|
|
ecc6eb5c5d | ||
|
|
f97339020c | ||
|
|
d38691e131 | ||
|
|
4cf02c451b | ||
|
|
03713aae11 | ||
|
|
f5770f4cce | ||
|
|
155cf61e2b | ||
|
|
1bca1095a8 | ||
|
|
0064955533 | ||
|
|
9ac82c9269 | ||
|
|
aeb90e8143 | ||
|
|
8d57c53d0b | ||
|
|
7ab503efd2 | ||
|
|
fdf9626498 | ||
|
|
1ddbeb751e | ||
|
|
0ad3d33402 | ||
|
|
035ec80f3d | ||
|
|
36af285c0a | ||
|
|
c4fc968d2a | ||
|
|
563343b94d | ||
|
|
2d8a5d283f | ||
|
|
13efc0a29c | ||
|
|
8eef4b6390 | ||
|
|
3c633b4b71 | ||
|
|
645a4e68f0 | ||
|
|
9007658e40 | ||
|
|
316cb9ca57 | ||
|
|
bfddccddec | ||
|
|
77f46417a9 | ||
|
|
d5c0f898df | ||
|
|
28e403356b | ||
|
|
07546bc7df | ||
|
|
bf054dfc3f | ||
|
|
78b3228159 | ||
|
|
6fc73aa2eb | ||
|
|
9537de7361 | ||
|
|
059cc4a162 | ||
|
|
72d7996315 | ||
|
|
ecbbcba6aa | ||
|
|
427c0f9d80 | ||
|
|
1524bbd23c | ||
|
|
ea080dde8c | ||
|
|
45be8a0dbb | ||
|
|
a0166f58e9 | ||
|
|
296525f390 | ||
|
|
9040591ae7 | ||
|
|
95fd7435d2 | ||
|
|
0a58cca5c3 | ||
|
|
71686aeab5 | ||
|
|
152729724c | ||
|
|
e347454802 | ||
|
|
0b6071a272 | ||
|
|
5d761e8687 | ||
|
|
9435cd3081 | ||
|
|
ef725103aa | ||
|
|
7568071ce8 | ||
|
|
96f90fa5c9 | ||
|
|
323ca5a8a9 | ||
|
|
09549b1922 | ||
|
|
09339b9b6d | ||
|
|
1fb46add24 | ||
|
|
1dcca191d9 | ||
|
|
584fdb7734 | ||
|
|
525b05db5a | ||
|
|
5d8561818f | ||
|
|
d3d019b487 | ||
|
|
2fbbf220fb | ||
|
|
dbfb49d03d | ||
|
|
c52a615720 | ||
|
|
51365f82b8 | ||
|
|
0e02b1beec | ||
|
|
cc9b08ad04 | ||
|
|
aad1bcede3 | ||
|
|
aa28fe2b0e | ||
|
|
c9a297b72a | ||
|
|
e0cff6b81a | ||
|
|
6262f874a3 | ||
|
|
eb81d0a1e3 | ||
|
|
f4d13f3fb6 | ||
|
|
1dbfa7dc0a | ||
|
|
c6fbf0dd0e | ||
|
|
ab0d4fe259 | ||
|
|
ecf6c1848d | ||
|
|
8647bf2699 | ||
|
|
87b114604c | ||
|
|
fbbe4dc64b | ||
|
|
1a78810691 | ||
|
|
f45717cee4 | ||
|
|
31e358280c | ||
|
|
444333d44f | ||
|
|
4d85c77738 | ||
|
|
d26d6c8b0b | ||
|
|
944619af81 | ||
|
|
1332b73a92 | ||
|
|
c11deeb57f | ||
|
|
294518b146 | ||
|
|
b5be221aec | ||
|
|
8ab3945871 | ||
|
|
73732a7d0c | ||
|
|
5599bbdf31 | ||
|
|
2a085f5703 | ||
|
|
2943da7c07 | ||
|
|
e1e4a1da07 | ||
|
|
8bb9328201 | ||
|
|
9d54273947 | ||
|
|
141ba5d6c1 | ||
|
|
ebd514e278 | ||
|
|
aa1468af34 | ||
|
|
003732c20d | ||
|
|
d762e2b6ae | ||
|
|
c7cb69c914 | ||
|
|
663e5539c8 | ||
|
|
52b130d812 | ||
|
|
788f23b955 | ||
|
|
047f6a1ed2 | ||
|
|
a8fad6ac74 | ||
|
|
3ed406021c | ||
|
|
f600398353 | ||
|
|
3af04246d0 | ||
|
|
95d7856978 | ||
|
|
fd10379c7f | ||
|
|
fe776a1443 | ||
|
|
81842b8521 | ||
|
|
dc7ff4c94d | ||
|
|
c13df2e0c8 | ||
|
|
0846c3914a | ||
|
|
5a2616800f | ||
|
|
9cef7848c5 | ||
|
|
b14345f390 | ||
|
|
b0f1f581dc | ||
|
|
e97a63b814 | ||
|
|
45afc4ee1f | ||
|
|
ec0b1d18ca | ||
|
|
07b0cfdd7b | ||
|
|
44408c8cfc | ||
|
|
01d3d0523e | ||
|
|
436669173b | ||
|
|
003fc557cd | ||
|
|
c1b5a2684b | ||
|
|
2b6cc1f8c7 | ||
|
|
820dc023e8 | ||
|
|
a0544368fb | ||
|
|
f2c6778574 | ||
|
|
62dffc4ac3 | ||
|
|
847fefcc46 | ||
|
|
47680fc64e | ||
|
|
277cfd3a03 | ||
|
|
6ffceee3ca | ||
|
|
fd00db7886 | ||
|
|
c86aa276b9 | ||
|
|
dc36709297 | ||
|
|
b63fb6c966 |
16
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
@@ -9,6 +9,20 @@ body:
|
||||
options:
|
||||
- label: I have searched the existing open and closed issues
|
||||
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
|
||||
attributes:
|
||||
label: Is your feature request related to a problem? Please describe
|
||||
@@ -35,4 +49,4 @@ body:
|
||||
|
||||
Tip: You can attach images or log files by clicking this area to highlight it and then dragging files in.
|
||||
validations:
|
||||
required: true
|
||||
required: true
|
||||
|
||||
4
.github/ISSUE_TEMPLATE/i-have-an-issue.yml
vendored
@@ -1,5 +1,5 @@
|
||||
name: Bug Report
|
||||
description: 'When submitting an issue enable debug and have a look at the docs.'
|
||||
description: 'When submitting an issue enable LOG_LEVEL="trace" and have a look at the docs.'
|
||||
labels: ['bug 🐛']
|
||||
body:
|
||||
- type: checkboxes
|
||||
@@ -64,7 +64,7 @@ body:
|
||||
***Generally speaking, all bug reports should have logs provided.***
|
||||
Tip: You can attach images or log files by clicking this area to highlight it and then dragging files in.
|
||||
Additionally, any additional info? Screenshots? References? Anything that will give us more context about the issue you are encountering!
|
||||
You can use `tail -100 /app/front/log/app.log` in the container if you have troubles getting to the log files.
|
||||
You can use `tail -100 /app/front/log/app.log` in the container if you have trouble getting to the log files.
|
||||
validations:
|
||||
required: false
|
||||
- type: checkboxes
|
||||
|
||||
1
.github/workflows/docker_dev.yml
vendored
@@ -48,7 +48,6 @@ jobs:
|
||||
with:
|
||||
# list of Docker images to use as base name for tags
|
||||
images: |
|
||||
jokobsk/pi.alert_dev
|
||||
jokobsk/netalertx-dev
|
||||
# generate Docker tags based on the following events/attributes
|
||||
tags: |
|
||||
|
||||
22
Dockerfile
@@ -1,24 +1,27 @@
|
||||
FROM alpine:3.19 as builder
|
||||
FROM alpine:3.20 as builder
|
||||
|
||||
ARG INSTALL_DIR=/app
|
||||
|
||||
ENV PYTHONUNBUFFERED 1
|
||||
|
||||
RUN apk add --no-cache bash python3 \
|
||||
# Install build dependencies
|
||||
RUN apk add --no-cache bash python3 python3-dev gcc musl-dev libffi-dev openssl-dev \
|
||||
&& python -m venv /opt/venv
|
||||
|
||||
|
||||
# Enable venv
|
||||
ENV PATH="/opt/venv/bin:$PATH"
|
||||
|
||||
COPY . ${INSTALL_DIR}/
|
||||
|
||||
RUN pip install requests paho-mqtt scapy cron-converter pytz json2table dhcp-leases pyunifi speedtest-cli chardet \
|
||||
|
||||
RUN pip install netifaces tplink-omada-client pycryptodome requests paho-mqtt scapy cron-converter pytz json2table dhcp-leases pyunifi speedtest-cli chardet python-nmap dnspython \
|
||||
&& bash -c "find ${INSTALL_DIR} -type d -exec chmod 750 {} \;" \
|
||||
&& bash -c "find ${INSTALL_DIR} -type f -exec chmod 640 {} \;" \
|
||||
&& bash -c "find ${INSTALL_DIR} -type f \( -name '*.sh' -o -name '*.py' -o -name 'pialert-cli' -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
|
||||
FROM alpine:3.19 as runner
|
||||
FROM alpine:3.20 as runner
|
||||
|
||||
ARG INSTALL_DIR=/app
|
||||
|
||||
@@ -37,15 +40,20 @@ ENV S6_CMD_WAIT_FOR_SERVICES_MAXTIME=0
|
||||
|
||||
RUN apk update --no-cache \
|
||||
&& apk add --no-cache bash zip lsblk gettext-envsubst sudo mtr tzdata s6-overlay \
|
||||
&& apk add --no-cache curl arp-scan iproute2 iproute2-ss nmap nmap-scripts traceroute net-tools net-snmp-tools bind-tools awake ca-certificates \
|
||||
&& apk add --no-cache sqlite php82 php82-fpm php82-cgi php82-curl php82-sqlite3 php82-session \
|
||||
&& apk add --no-cache curl arp-scan iproute2 iproute2-ss nmap nmap-scripts traceroute nbtscan net-tools net-snmp-tools bind-tools awake ca-certificates \
|
||||
&& apk add --no-cache sqlite php83 php83-fpm php83-cgi php83-curl php83-sqlite3 php83-session \
|
||||
&& apk add --no-cache python3 nginx \
|
||||
&& apk add --no-cache dcron \
|
||||
&& ln -s /usr/bin/awake /usr/bin/wakeonlan \
|
||||
&& bash -c "install -d -m 750 -o nginx -g www-data ${INSTALL_DIR} ${INSTALL_DIR}" \
|
||||
&& rm -f /etc/nginx/http.d/default.conf
|
||||
|
||||
COPY --from=builder --chown=nginx:www-data ${INSTALL_DIR}/ ${INSTALL_DIR}/
|
||||
|
||||
# Add crontab file
|
||||
COPY install/crontab /etc/crontabs/root
|
||||
|
||||
# Start all required services
|
||||
RUN ${INSTALL_DIR}/dockerfiles/pre-setup.sh
|
||||
|
||||
HEALTHCHECK --interval=30s --timeout=5s --start-period=15s --retries=2 \
|
||||
|
||||
@@ -29,10 +29,11 @@ COPY --chmod=775 --chown=${USER_ID}:${USER_GID} . ${INSTALL_DIR}/
|
||||
|
||||
# ❗ IMPORTANT - if you modify this file modify the /install/install_dependecies.debian.sh file as well ❗
|
||||
|
||||
|
||||
RUN apt-get install -y \
|
||||
tini snmp ca-certificates curl libwww-perl arp-scan perl apt-utils cron sudo \
|
||||
nginx-light php php-cgi php-fpm php-sqlite3 php-curl sqlite3 dnsutils net-tools \
|
||||
python3 iproute2 nmap python3-pip zip systemctl usbutils traceroute
|
||||
nginx-light php php-cgi php-fpm php-sqlite3 php-curl sqlite3 dnsutils net-tools php-openssl \
|
||||
python3 python3-dev iproute2 nmap python3-pip zip systemctl usbutils traceroute nbtscan
|
||||
|
||||
# Alternate dependencies
|
||||
RUN apt-get install nginx nginx-core mtr php-fpm php8.2-fpm php-cli php8.2 php8.2-sqlite3 -y
|
||||
@@ -41,7 +42,8 @@ RUN phpenmod -v 8.2 sqlite3
|
||||
# Setup virtual python environment and use pip3 to install packages
|
||||
RUN apt-get install -y python3-venv
|
||||
RUN python3 -m venv myenv
|
||||
RUN /bin/bash -c "source myenv/bin/activate && update-alternatives --install /usr/bin/python python /usr/bin/python3 10 && pip3 install requests paho-mqtt scapy cron-converter pytz json2table dhcp-leases pyunifi speedtest-cli chardet"
|
||||
|
||||
RUN /bin/bash -c "source myenv/bin/activate && update-alternatives --install /usr/bin/python python /usr/bin/python3 10 && pip3 install tplink-omada-client pycryptodome requests paho-mqtt scapy cron-converter pytz json2table dhcp-leases pyunifi speedtest-cli chardet python-nmap dnspython cryptography "
|
||||
|
||||
# Create a buildtimestamp.txt to later check if a new version was released
|
||||
RUN date +%s > ${INSTALL_DIR}/front/buildtimestamp.txt
|
||||
|
||||
75
README.md
@@ -1,4 +1,4 @@
|
||||
# 💻🔍 Network security scanner & notification framework
|
||||
# 💻🔍 Network scanner & notification framework
|
||||
|
||||
Get visibility of what's going on on your WIFI/LAN network. Schedule scans for devices, port changes and get alerts if unknown devices or changes are found. Write your own [Plugins](https://github.com/jokob-sk/NetAlertX/tree/main/front/plugins#readme) with auto-generated UI and in-build notification system. Build out and easily maintain your network source of truth (NSoT).
|
||||
|
||||
@@ -12,32 +12,37 @@ Get visibility of what's going on on your WIFI/LAN network. Schedule scans for d
|
||||
|----------------------|----------------------| ----------------------| ----------------------|
|
||||
|
||||
|
||||
| ![Main screen][main] | ![Screen 1][screen1] | ![Screen 5][screen5] |
|
||||
| ![Main screen][main] | ![device_details 1][device_details] | ![Screen network][network] |
|
||||
|----------------------|----------------------| ----------------------|
|
||||
|
||||
![network_setup][network_setup]
|
||||
|
||||
|
||||
Head to [https://netalertx.com/](https://netalertx.com/) for more gifs and screenshots 📷.
|
||||
|
||||
<details>
|
||||
<summary>📷 Click for more screenshots</summary>
|
||||
|
||||
| ![Screen 3][screen3] | ![Screen 4][screen4] | ![Screen 6][screen6] |
|
||||
| ![presence][presence] | ![maintenance][maintenance] | ![settings][settings] |
|
||||
|----------------------|----------------------|----------------------|
|
||||
| ![Screen 8][screen8] | ![Report 2][report2] | ![Screen 9][screen9] |
|
||||
| ![sync_hub][sync_hub] | ![report1][report1] | ![device_nmap][device_nmap] |
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>❓ Why use Net <b>Alert</b><sup>x</sup>?</summary>
|
||||
<summary>❓ Why use Net<b>Alert</b><sup>x</sup>?</summary>
|
||||
|
||||
<hr>
|
||||
|
||||
Most of us don't know what's going on on our home network, but we want our family and data to be safe. _Command-line tools_ are great, but the output can be _hard to understand_ and action if you are not a network specialist.
|
||||
|
||||
Net <b>Alert</b><sup>x</sup> gives you peace of mind. _Visualize and immediately report 📬_ what is going on in your network - this is the first step to enhance your _network security 🔐_.
|
||||
Net<b>Alert</b><sup>x</sup> gives you peace of mind. _Visualize and immediately report 📬_ what is going on in your network - this is the first step to enhance your _network security 🔐_.
|
||||
|
||||
Net <b>Alert</b><sup>x</sup> combines several network and other scanning tools 🔍 with notifications 📧 into one user-friendly package 📦.
|
||||
Net<b>Alert</b><sup>x</sup> combines several network and other scanning tools 🔍 with notifications 📧 into one user-friendly package 📦.
|
||||
|
||||
Set up a _kill switch ☠_ for your network via a smart plug with the available [Home Assistant](https://github.com/jokob-sk/NetAlertX/blob/main/docs/HOME_ASSISTANT.md) integration. Implement custom automations with the [CSV device Exports 📤](https://github.com/jokob-sk/NetAlertX/tree/main/front/plugins/csv_backup), [Webhooks](https://github.com/jokob-sk/NetAlertX/blob/main/docs/WEBHOOK_N8N.md), or [API endpoints](https://github.com/jokob-sk/NetAlertX/blob/main/docs/API.md) features.
|
||||
|
||||
Extend the app if you want to create your own scanner [Plugin](https://github.com/jokob-sk/NetAlertX/tree/main/front/plugins#readme) and handle the results and notifications in Net <b>Alert</b><sup>x</sup>.
|
||||
Extend the app if you want to create your own scanner [Plugin](https://github.com/jokob-sk/NetAlertX/tree/main/front/plugins#readme) and handle the results and notifications in Net<b>Alert</b><sup>x</sup>.
|
||||
|
||||
Looking forward to your contributions if you decide to share your work with the community ❤.
|
||||
|
||||
@@ -47,7 +52,7 @@ Get visibility of what's going on on your WIFI/LAN network. Schedule scans for d
|
||||
|
||||
| Features | Details |
|
||||
|-------------|-------------|
|
||||
| 🔍 | The app scans your network for, **New devices**, **New connections** (re-connections), **Disconnections**, **"Always Connected" devices down**, Devices **IP changes** and **Internet IP address changes**. Discovery & scan methods include: **arp-scan**. **Pi-hole - DB import**, **Pi-hole - DHCP leases import**, **Generic DHCP leases import**. **UNIFI controller import**, **SNMP-enabled router import**. Check the [Plugins](https://github.com/jokob-sk/NetAlertX/tree/main/front/plugins#readme) docs for more info on individual scans. |
|
||||
| 🔍 | The app scans your network for, **New devices**, **New connections** (re-connections), **Disconnections**, **"Always Connected" devices down**, Devices **IP changes** and **Internet IP address changes**. Discovery & scan methods include: **arp-scan**, **Pi-hole - DB import**, **Pi-hole - DHCP leases import**, **Generic DHCP leases import**. **UNIFI controller import**, **SNMP-enabled router import**. Check the [Plugins](https://github.com/jokob-sk/NetAlertX/tree/main/front/plugins#readme) docs for more info on individual scans. |
|
||||
|📧 | Send notifications to more than 80+ services, including Telegram via [Apprise](https://hub.docker.com/r/caronc/apprise), or use [Pushsafer](https://www.pushsafer.com/), [Pushover](https://www.pushover.net/), or [NTFY](https://ntfy.sh/). |
|
||||
|🧩 | Feed your data and device changes into [Home Assistant](https://github.com/jokob-sk/NetAlertX/blob/main/docs/HOME_ASSISTANT.md), read [API endpoints](https://github.com/jokob-sk/NetAlertX/blob/main/docs/API.md), or use [Webhooks](https://github.com/jokob-sk/NetAlertX/blob/main/docs/WEBHOOK_N8N.md) to setup custom automation flows. |
|
||||
|➕ | Build your own scanners with the [Plugin system](https://github.com/jokob-sk/NetAlertX/tree/main/front/plugins#readme) |
|
||||
@@ -60,16 +65,22 @@ Get visibility of what's going on on your WIFI/LAN network. Schedule scans for d
|
||||
|-------------|-------------|
|
||||
| 📥🐳 | [Docker instructions](https://github.com/jokob-sk/NetAlertX/blob/main/dockerfiles/README.md)
|
||||
| 📥💻 | [HW install (experimental 🧪)](https://github.com/jokob-sk/NetAlertX/blob/main/docs/HW_INSTALL.md) |
|
||||
| 📥🟧 | [Unraid App](https://unraid.net/community/apps) |
|
||||
| 📚 | [All Documentation](https://github.com/jokob-sk/NetAlertX/blob/main/docs/README.md) (App Usage and Configuration) |
|
||||
|
||||
> Other Alternatives
|
||||
>
|
||||
> - Check out [leiweibau's on HW installed fork](https://github.com/leiweibau/Pi.Alert/) (maintained)
|
||||
> - Check instructions for [pucherot's original code](https://github.com/pucherot/Pi.Alert/) (unmaintained)
|
||||
> - [WatchYourLAN](https://github.com/aceberg/WatchYourLAN) - Lightweight network IP scanner with web GUI (Open source)
|
||||
> - [Fing](https://www.fing.com/) - Network scanner app for your Internet security (Commercial, Phone App, Proprietary hardware)
|
||||
> - [NetBox](https://netboxlabs.com/) - Network management software (Commercial)
|
||||
|
||||
## 🔔 Get notified what's new
|
||||
|
||||
Get notified about a new release, what new functionality you can use and about breaking changes.
|
||||
|
||||
![Follow and star][follow_star]
|
||||
|
||||
### ⭐ Sponsors
|
||||
|
||||
[](https://github.com/sponsors/jokob-sk)
|
||||
@@ -81,18 +92,20 @@ Thank you to all the wonderful people who are sponsoring this project.
|
||||
<!-- SPONSORS-LIST DO NOT MODIFY BELOW -->
|
||||
| All Sponsors |
|
||||
|---|
|
||||
| [Tony Hanratty](https://github.com/thanratty) |
|
||||
| [iptvcld](https://github.com/iptvcld) |
|
||||
|
||||
<!-- SPONSORS-LIST DO NOT MODIFY ABOVE -->
|
||||
|
||||
| [](https://github.com/sponsors/jokob-sk) | [](https://www.buymeacoffee.com/jokobsk) | [](https://www.patreon.com/user?u=84385063) |
|
||||
| --- | --- | --- |
|
||||
|
||||
|
||||
<details>
|
||||
<summary>Click for more ways to donate</summary>
|
||||
|
||||
<hr>
|
||||
|
||||
| [](https://github.com/sponsors/jokob-sk) | [](https://www.buymeacoffee.com/jokobsk) | [](https://www.patreon.com/user?u=84385063) |
|
||||
| --- | --- | --- |
|
||||
|
||||
- Bitcoin: `1N8tupjeCK12qRVU2XrV17WvKK7LCawyZM`
|
||||
- Ethereum: `0x6e2749Cb42F4411bc98501406BdcD82244e3f9C7`
|
||||
|
||||
@@ -106,11 +119,6 @@ This project would be nothing without the amazing work of the community, with sp
|
||||
|
||||
> [pucherot/Pi.Alert](https://github.com/pucherot/Pi.Alert) (the original creator of PiAlert), [leiweibau](https://github.com/leiweibau/Pi.Alert): Dark mode (and much more), [Macleykun](https://github.com/Macleykun) (Help with Dockerfile clean-up) [Final-Hawk](https://github.com/Final-Hawk) (Help with NTFY, styling and other fixes), [TeroRERO](https://github.com/terorero) (Spanish translations), [Data-Monkey](https://github.com/Data-Monkey), (Split-up of the python.py file and more), [cvc90](https://github.com/cvc90) (Spanish translation and various UI work) to name a few...
|
||||
|
||||
Here is everyone that helped and contributed to this project:
|
||||
|
||||
<a href="https://github.com/jokob-sk/netalertx/graphs/contributors">
|
||||
<img src="https://contri-graphy.yourselfhosted.com/graph?repo=jokob-sk/netalertx&format=svg" />
|
||||
</a>
|
||||
|
||||
## Everything else
|
||||
<!--- --------------------------------------------------------------------- --->
|
||||
@@ -131,18 +139,21 @@ Help out and suggest languages in the [online portal of Weblate](https://hosted.
|
||||
|
||||
|
||||
<!--- --------------------------------------------------------------------- --->
|
||||
[main]: ./docs/img/devices_split.png "Main screen"
|
||||
[screen1]: ./docs/img/device_details.png "Screen 1"
|
||||
[screen2]: ./docs/img/events.png "Screen 2"
|
||||
[screen3]: ./docs/img/presence.png "Screen 3"
|
||||
[screen4]: ./docs/img/maintenance.png "Screen 4"
|
||||
[screen5]: ./docs/img/network.png "Screen 5"
|
||||
[screen6]: ./docs/img/settings.png "Screen 6"
|
||||
[screen7]: ./docs/img/help_faq.png "Screen 7"
|
||||
[screen8]: ./docs/img/plugins_rogue_dhcp.png "Screen 8"
|
||||
[screen9]: ./docs/img/device_nmap.png "Screen 9"
|
||||
[report1]: ./docs/img/4_report_1.jpg "Report sample 1"
|
||||
[report2]: ./docs/img/4_report_2.jpg "Report sample 2"
|
||||
[main_dark]: /docs/img/1_devices_dark.jpg "Main screen dark"
|
||||
[maintain_dark]: /docs/img/5_maintain.jpg "Maintain screen dark"
|
||||
[main]: ./docs/img/devices_split.png "Main screen"
|
||||
[device_details]: ./docs/img/device_details.png "Screen 1"
|
||||
[events]: ./docs/img/events.png "Screen 2"
|
||||
[presence]: ./docs/img/presence.png "Screen 3"
|
||||
[maintenance]: ./docs/img/maintenance.png "Screen 4"
|
||||
[network]: ./docs/img/network.png "Screen 5"
|
||||
[settings]: ./docs/img/settings.png "Screen 6"
|
||||
[network_setup]: ./docs/img/network_setup.gif "Screen 6"
|
||||
[help_faq]: ./docs/img/help_faq.png "Screen 7"
|
||||
[sync_hub]: ./docs/img/sync_hub.png "Screen 8"
|
||||
[notification_center]: ./docs/img/notification_center.png "Screen 8"
|
||||
[sent_reports_text]: ./docs/img/sent_reports_text.png "Screen 8"
|
||||
[device_nmap]: ./docs/img/device_nmap.png "Screen 9"
|
||||
[report1]: ./docs/img/report_sample.png "Report sample 1"
|
||||
[main_dark]: /docs/img/1_devices_dark.jpg "Main screen dark"
|
||||
[maintain_dark]: /docs/img/5_maintain.jpg "Maintain screen dark"
|
||||
[follow_star]: /docs/img/Follow_Releases_and_Star.gif "Follow and Star"
|
||||
|
||||
|
||||
@@ -3,13 +3,12 @@
|
||||
# Generated: 2022-12-30_22-19-40 #
|
||||
# #
|
||||
# Config file for the LAN intruder detection app: #
|
||||
# https://github.com/jokob-sk/NetAlertX #
|
||||
# https://github.com/jokob-sk/NetAlertX #
|
||||
# #
|
||||
#-----------------AUTOGENERATED FILE-----------------#
|
||||
|
||||
# 🔺 Use the Settings UI - only edit when necessary 🔺
|
||||
|
||||
|
||||
# General
|
||||
#---------------------------
|
||||
# Scan using interface eth0
|
||||
@@ -19,17 +18,23 @@
|
||||
# SCAN_SUBNETS = [ '192.168.1.0/24 --interface=eth1', '192.168.1.0/24 --interface=eth0' ]
|
||||
|
||||
SCAN_SUBNETS=['192.168.1.0/24 --interface=eth1']
|
||||
|
||||
TIMEZONE='Europe/Berlin'
|
||||
PIALERT_WEB_PROTECTION=False
|
||||
PIALERT_WEB_PASSWORD='8d969eef6ecad3c29a3a629280e686cf0c3f5d5a86aff3ca12020c923adc6c92'
|
||||
LOADED_PLUGINS = ['ARPSCAN','CSVBCKP','DBCLNP', 'INTRNT','MAINT','NEWDEV','NSLOOKUP','NTFPRCS', 'PHOLUS','SETPWD','SMTP', 'SYNC', 'VNDRPDT', 'WORKFLOWS']
|
||||
|
||||
DAYS_TO_KEEP_EVENTS=90
|
||||
# Used for generating links in emails. Make sure not to add a trailing slash!
|
||||
REPORT_DASHBOARD_URL='http://netalertx'
|
||||
|
||||
# Make sure at least these scanners are enabled for new installs, other defaults are taken from the config.json
|
||||
INTRNT_RUN='schedule'
|
||||
ARPSCAN_RUN='schedule'
|
||||
PHOLUS_RUN='on_new_device'
|
||||
NSLOOKUP_RUN='before_name_updates'
|
||||
|
||||
# Email
|
||||
#---------------------------
|
||||
# Email
|
||||
#-------------------------------------
|
||||
# (add SMTP to LOADED_PLUGINS to load)
|
||||
#-------------------------------------
|
||||
SMTP_RUN='disabled' # use 'on_notification' to enable
|
||||
SMTP_SERVER='smtp.gmail.com'
|
||||
SMTP_PORT=587
|
||||
@@ -41,8 +46,10 @@ SMTP_PASS='password'
|
||||
SMTP_SKIP_TLS=False
|
||||
|
||||
|
||||
# Webhooks
|
||||
#---------------------------
|
||||
# Webhook
|
||||
#-------------------------------------
|
||||
# (add WEBHOOK to LOADED_PLUGINS to load)
|
||||
#-------------------------------------
|
||||
WEBHOOK_RUN='disabled' # use 'on_notification' to enable
|
||||
WEBHOOK_URL='http://n8n.local:5555/webhook-test/aaaaaaaa-aaaa-aaaa-aaaaa-aaaaaaaaaaaa'
|
||||
WEBHOOK_PAYLOAD='json' # webhook payload data format for the "body > attachements > text" attribute
|
||||
@@ -52,15 +59,19 @@ WEBHOOK_PAYLOAD='json' # webhook payload data format for the "bo
|
||||
WEBHOOK_REQUEST_METHOD='GET'
|
||||
|
||||
|
||||
# Apprise
|
||||
#---------------------------
|
||||
# Apprise
|
||||
#-------------------------------------
|
||||
# (add APPRISE to LOADED_PLUGINS to load)
|
||||
#-------------------------------------
|
||||
APPRISE_RUN='disabled' # use 'on_notification' to enable
|
||||
APPRISE_HOST='http://localhost:8000/notify'
|
||||
APPRISE_URL='mailto://smtp-relay.sendinblue.com:587?from=user@gmail.com&name=apprise&user=user@gmail.com&pass=password&to=user@gmail.com'
|
||||
|
||||
|
||||
# NTFY
|
||||
#---------------------------
|
||||
#-------------------------------------
|
||||
# (add NTFY to LOADED_PLUGINS to load)
|
||||
#-------------------------------------
|
||||
NTFY_RUN='disabled' # use 'on_notification' to enable
|
||||
NTFY_HOST='https://ntfy.sh'
|
||||
NTFY_TOPIC='replace_my_secure_topicname_91h889f28'
|
||||
@@ -68,14 +79,18 @@ NTFY_USER='user'
|
||||
NTFY_PASSWORD='passw0rd'
|
||||
|
||||
|
||||
# PUSHSAFER
|
||||
#---------------------------
|
||||
# PUSHSAFER
|
||||
#-------------------------------------
|
||||
# (add PUSHSAFER to LOADED_PLUGINS to load)
|
||||
#-------------------------------------
|
||||
PUSHSAFER_RUN='disabled' # use 'on_notification' to enable
|
||||
PUSHSAFER_TOKEN='ApiKey'
|
||||
|
||||
|
||||
# MQTT
|
||||
#---------------------------
|
||||
# MQTT
|
||||
#-------------------------------------
|
||||
# (add MQTT to LOADED_PLUGINS to load)
|
||||
#-------------------------------------
|
||||
MQTT_RUN='disabled' # use 'on_notification' to enable
|
||||
MQTT_BROKER='192.168.1.2'
|
||||
MQTT_PORT=1883
|
||||
|
||||
13
back/cron_script.sh
Executable file
@@ -0,0 +1,13 @@
|
||||
#!/bin/bash
|
||||
export INSTALL_DIR=/app
|
||||
|
||||
LOG_FILE="${INSTALL_DIR}/front/log/execution_queue.log"
|
||||
|
||||
# Check if there are any entries with cron_restart_backend
|
||||
if grep -q "cron_restart_backend" "$LOG_FILE"; then
|
||||
# Kill all python processes and restart the server
|
||||
pkill -f "python " && (python ${INSTALL_DIR}/server > /dev/null 2>&1 &) && echo 'done'
|
||||
|
||||
# Remove all lines containing cron_restart_backend from the log file
|
||||
sed -i '/cron_restart_backend/d' "$LOG_FILE"
|
||||
fi
|
||||
125
back/pialert-cli
@@ -1,125 +0,0 @@
|
||||
#!/bin/bash
|
||||
SCRIPT=$(readlink -f $0)
|
||||
SCRIPTPATH=$(dirname $SCRIPT)
|
||||
CONFFILENAME="app.conf"
|
||||
SETTING_NAME_TOGGLE="PIALERT_WEB_PROTECTION"
|
||||
SETTING_NAME_PWD="PIALERT_WEB_PASSWORD"
|
||||
PIA_CONF_FILE=${SCRIPTPATH}'/../config/${CONFFILENAME}'
|
||||
|
||||
case $1 in
|
||||
|
||||
help)
|
||||
echo "pialert-cli v0.1 (https://github.com/leiweibau/Pi.Alert)"
|
||||
echo "Usage: pialert-cli <command>"
|
||||
echo ""
|
||||
echo "The is a list of supported commands:"
|
||||
echo ""
|
||||
echo " set_login - Sets the parameter $SETTING_NAME_TOGGLE in the config file to TRUE"
|
||||
echo " - If the parameter is not present, it will be created. Additionally the"
|
||||
echo " default password '123456' is set."
|
||||
echo ""
|
||||
echo " unset_login - Sets the parameter $SETTING_NAME_TOGGLE in the config file to FALSE"
|
||||
echo " - If the parameter is not present, it will be created. Additionally the"
|
||||
echo " default password '123456' is set."
|
||||
echo ""
|
||||
echo " set_password <password> - Sets the new password as a hashed value."
|
||||
echo " - If the $SETTING_NAME_TOGGLE parameter does not exist yet, it will be"
|
||||
echo " created and set to 'True' (login enabled)"
|
||||
echo ""
|
||||
echo " set_autopassword - Sets a new random password as a hashed value and show it plaintext in"
|
||||
echo " the console."
|
||||
echo " - If the $SETTING_NAME_TOGGLE parameter does not exist yet, it will be"
|
||||
echo " created and set to 'True' (login enabled)"
|
||||
echo ""
|
||||
echo ""
|
||||
echo ""
|
||||
;;
|
||||
|
||||
set_login)
|
||||
## Check if $SETTING_NAME_TOGGLE exists
|
||||
CHECK_PROT=$(grep "$SETTING_NAME_TOGGLE" $PIA_CONF_FILE | wc -l)
|
||||
if [ $CHECK_PROT -eq 0 ]
|
||||
then
|
||||
## Create $SETTING_NAME_TOGGLE and enable it
|
||||
sed -i "/^VENDORS_DB.*/a $SETTING_NAME_TOGGLE = True" $PIA_CONF_FILE
|
||||
sed -i "/^$SETTING_NAME_TOGGLE.*/a $SETTING_NAME_PWD = '8d969eef6ecad3c29a3a629280e686cf0c3f5d5a86aff3ca12020c923adc6c92'" $PIA_CONF_FILE
|
||||
else
|
||||
## Switch $SETTING_NAME_TOGGLE to enable
|
||||
sed -i "/$SETTING_NAME_TOGGLE/c\$SETTING_NAME_TOGGLE = True" $PIA_CONF_FILE
|
||||
fi
|
||||
echo "Login is now enabled"
|
||||
;;
|
||||
|
||||
unset_login)
|
||||
## Check if $SETTING_NAME_TOGGLE exists
|
||||
CHECK_PROT=$(grep "$SETTING_NAME_TOGGLE" $PIA_CONF_FILE | wc -l)
|
||||
if [ $CHECK_PROT -eq 0 ]
|
||||
then
|
||||
## Create $SETTING_NAME_TOGGLE and disable it
|
||||
sed -i "/^VENDORS_DB.*/a $SETTING_NAME_TOGGLE = False" $PIA_CONF_FILE
|
||||
sed -i "/^$SETTING_NAME_TOGGLE.*/a $SETTING_NAME_PWD = '8d969eef6ecad3c29a3a629280e686cf0c3f5d5a86aff3ca12020c923adc6c92'" $PIA_CONF_FILE
|
||||
else
|
||||
## Switch $SETTING_NAME_TOGGLE to disable
|
||||
sed -i "/$SETTING_NAME_TOGGLE/c\$SETTING_NAME_TOGGLE = False" $PIA_CONF_FILE
|
||||
fi
|
||||
echo "Login is now disabled"
|
||||
;;
|
||||
|
||||
set_password)
|
||||
PIA_PASS=$2
|
||||
## Check if $SETTING_NAME_TOGGLE exists
|
||||
CHECK_PROT=$(grep "$SETTING_NAME_TOGGLE" $PIA_CONF_FILE | wc -l)
|
||||
if [ $CHECK_PROT -eq 0 ]
|
||||
then
|
||||
## Create $SETTING_NAME_TOGGLE and enable it
|
||||
sed -i "/^VENDORS_DB.*/a $SETTING_NAME_TOGGLE = True" $PIA_CONF_FILE
|
||||
fi
|
||||
## Prepare Hash
|
||||
PIA_PASS_HASH=$(echo -n $PIA_PASS | sha256sum | awk '{print $1}')
|
||||
echo " The hashed password is:"
|
||||
echo " $PIA_PASS_HASH"
|
||||
## Check if the password parameter is set
|
||||
CHECK_PWD=$(grep "$SETTING_NAME_PWD" $PIA_CONF_FILE | wc -l)
|
||||
if [ $CHECK_PWD -eq 0 ]
|
||||
then
|
||||
sed -i "/^$SETTING_NAME_TOGGLE.*/a $SETTING_NAME_PWD = '$PIA_PASS_HASH'" $PIA_CONF_FILE
|
||||
else
|
||||
sed -i "/$SETTING_NAME_PWD/c\$SETTING_NAME_PWD = '$PIA_PASS_HASH'" $PIA_CONF_FILE
|
||||
fi
|
||||
echo ""
|
||||
echo "The new password is set"
|
||||
;;
|
||||
|
||||
set_autopassword)
|
||||
## Check if $SETTING_NAME_TOGGLE exists
|
||||
CHECK_PROT=$(grep "$SETTING_NAME_TOGGLE" $PIA_CONF_FILE | wc -l)
|
||||
if [ $CHECK_PROT -eq 0 ]
|
||||
then
|
||||
## Create $SETTING_NAME_TOGGLE and enable it
|
||||
sed -i "/^VENDORS_DB.*/a $SETTING_NAME_TOGGLE = True" $PIA_CONF_FILE
|
||||
fi
|
||||
## Create autopassword
|
||||
PIA_AUTOPASS=$(cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 8 | head -n 1)
|
||||
echo " The password is: $PIA_AUTOPASS"
|
||||
## Prepare Hash
|
||||
PIA_AUTOPASS_HASH=$(echo -n $PIA_AUTOPASS | sha256sum | awk '{print $1}')
|
||||
echo " The hashed password is:"
|
||||
echo " $PIA_AUTOPASS_HASH"
|
||||
## Check if the password parameter is set
|
||||
CHECK_PWD=$(grep "$SETTING_NAME_PWD" $PIA_CONF_FILE | wc -l)
|
||||
if [ $CHECK_PWD -eq 0 ]
|
||||
then
|
||||
## Create password parameter
|
||||
sed -i "/^$SETTING_NAME_TOGGLE.*/a $SETTING_NAME_PWD = '$PIA_AUTOPASS_HASH'" $PIA_CONF_FILE
|
||||
else
|
||||
## Overwrite password parameter
|
||||
sed -i "/$SETTING_NAME_PWD/c\$SETTING_NAME_PWD = '$PIA_AUTOPASS_HASH'" $PIA_CONF_FILE
|
||||
fi
|
||||
echo ""
|
||||
echo "The new password is set"
|
||||
;;
|
||||
|
||||
*)
|
||||
echo "pialert-cli v0.1 (https://github.com/leiweibau/Pi.Alert)"
|
||||
echo "Use \"pialert-cli help\" for a list of supported commands."
|
||||
esac
|
||||
@@ -12,56 +12,31 @@
|
||||
# ----------------------------------------------------------------------
|
||||
# Main directories to update:
|
||||
# /usr/share/arp-scan
|
||||
# /usr/share/ieee-data
|
||||
# /var/lib/ieee-data
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
echo "---------------------------------------------------------"
|
||||
echo "[INSTALL] Run update_vendors.sh"
|
||||
echo "---------------------------------------------------------"
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
echo Updating... /usr/share/ieee-data/
|
||||
cd /usr/share/ieee-data/ || { echo "could not enter /usr/share/ieee-data directory"; exit 1; }
|
||||
|
||||
sudo mkdir -p 2_backup
|
||||
sudo cp -- *.txt 2_backup
|
||||
sudo cp -- *.csv 2_backup
|
||||
echo ""
|
||||
echo Download Start
|
||||
echo ""
|
||||
sudo curl "$1" -LO https://standards-oui.ieee.org/oui28/mam.csv \
|
||||
-LO https://standards-oui.ieee.org/oui28/mam.csv \
|
||||
-LO https://standards-oui.ieee.org/oui28/mam.txt \
|
||||
-LO https://standards-oui.ieee.org/oui36/oui36.csv \
|
||||
-LO https://standards-oui.ieee.org/oui36/oui36.txt \
|
||||
-LO https://standards-oui.ieee.org/oui/oui.csv \
|
||||
-LO https://standards-oui.ieee.org/oui/oui.txt
|
||||
echo ""
|
||||
echo Download Finished
|
||||
DL_DIR=/usr/share/arp-scan
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
echo ""
|
||||
echo Updating... /usr/share/arp-scan/
|
||||
cd /usr/share/arp-scan || { echo "could not enter /usr/share/arp-scan directory"; exit 1; }
|
||||
echo Updating... $DL_DIR
|
||||
cd $DL_DIR || { echo "could not enter $DL_DIR directory"; exit 1; }
|
||||
|
||||
sudo mkdir -p 2_backup
|
||||
sudo cp -- *.txt 2_backup
|
||||
# Define the URL of the IEEE OUI file
|
||||
IEEE_OUI_URL="http://standards-oui.ieee.org/oui/oui.txt"
|
||||
|
||||
# Update from /usb/lib/ieee-data
|
||||
sudo get-iab -v
|
||||
sudo get-oui -v
|
||||
# Download the file using wget
|
||||
wget "$IEEE_OUI_URL" -O ieee-oui_dl.txt
|
||||
|
||||
# make files readable
|
||||
sudo chmod +r /usr/share/arp-scan/ieee-oui.txt
|
||||
# Filter lines containing "(base 16)" and remove the "(base 16)" string
|
||||
grep "(base 16)" ieee-oui_dl.txt | sed 's/ *(base 16)//' > ieee-oui_new.txt
|
||||
|
||||
# Update from ieee website
|
||||
# sudo get-iab -v -u http://standards-oui.ieee.org/iab/iab.txt
|
||||
# sudo get-oui -v -u http://standards-oui.ieee.org/oui/oui.txt
|
||||
# Combine ieee-oui_new.txt and ieee-oui.txt, and extract unique MAC start values along with vendor names
|
||||
|
||||
# Update from ieee website develop
|
||||
# sudo get-iab -v -u http://standards.ieee.org/develop/regauth/iab/iab.txt
|
||||
# sudo get-oui -v -u http://standards.ieee.org/develop/regauth/oui/oui.txt
|
||||
cat ieee-oui.txt ieee-oui_new.txt >> ieee-oui_all.txt
|
||||
sort ieee-oui_all.txt > ieee-oui_all_sort.txt
|
||||
awk '{$1=$1; print}' ieee-oui_all_sort.txt | sort -u > ieee-oui_all_filtered.txt
|
||||
|
||||
# Update from Sanitized oui (linuxnet.ca)
|
||||
# sudo get-oui -v -u https://linuxnet.ca/ieee/oui.txt
|
||||
|
||||
|
||||
@@ -12,11 +12,9 @@ services:
|
||||
# restart: unless-stopped
|
||||
volumes:
|
||||
# - ${APP_DATA_LOCATION}/netalertx_dev/config:/app/config
|
||||
# - ${APP_DATA_LOCATION}/netalertx/config:/app/config
|
||||
- ${APP_DATA_LOCATION}/netalertx/config:/home/pi/pialert/config
|
||||
- ${APP_DATA_LOCATION}/netalertx/config:/app/config
|
||||
# - ${APP_DATA_LOCATION}/netalertx_dev/db:/app/db
|
||||
# - ${APP_DATA_LOCATION}/netalertx/db:/app/db
|
||||
- ${APP_DATA_LOCATION}/netalertx/db:/home/pi/pialert/db
|
||||
- ${APP_DATA_LOCATION}/netalertx/db:/app/db
|
||||
# (optional) useful for debugging if you have issues setting up the container
|
||||
# - ${LOGS_LOCATION}:/app/front/log
|
||||
# ---------------------------------------------------------------------------
|
||||
@@ -28,13 +26,14 @@ services:
|
||||
- ${APP_DATA_LOCATION}/pihole/etc-pihole/pihole-FTL.db:/etc/pihole/pihole-FTL.db
|
||||
- ${DEV_LOCATION}/server:/app/server
|
||||
- ${DEV_LOCATION}/dockerfiles:/app/dockerfiles
|
||||
- ${APP_DATA_LOCATION}/netalertx/php.ini:/etc/php/8.2/fpm/php.ini
|
||||
# - ${APP_DATA_LOCATION}/netalertx/php.ini:/etc/php/8.2/fpm/php.ini
|
||||
- ${DEV_LOCATION}/install:/app/install
|
||||
- ${DEV_LOCATION}/front/css:/app/front/css
|
||||
- ${DEV_LOCATION}/front/img:/app/front/img
|
||||
- ${DEV_LOCATION}/back/update_vendors.sh:/app/back/update_vendors.sh
|
||||
- ${DEV_LOCATION}/front/lib/AdminLTE:/app/front/lib/AdminLTE
|
||||
- ${DEV_LOCATION}/front/lib:/app/front/lib
|
||||
- ${DEV_LOCATION}/front/js:/app/front/js
|
||||
- ${DEV_LOCATION}/front/report_templates:/front/report_templates
|
||||
- ${DEV_LOCATION}/install/start.debian.sh:/app/install/start.debian.sh
|
||||
- ${DEV_LOCATION}/install/user-mapping.debian.sh:/app/install/user-mapping.debian.sh
|
||||
- ${DEV_LOCATION}/install/install.debian.sh:/app/install/install.debian.sh
|
||||
@@ -42,6 +41,7 @@ services:
|
||||
- ${DEV_LOCATION}/front/api:/app/front/api
|
||||
- ${DEV_LOCATION}/front/php:/app/front/php
|
||||
- ${DEV_LOCATION}/front/deviceDetails.php:/app/front/deviceDetails.php
|
||||
- ${DEV_LOCATION}/front/userNotifications.php:/app/front/userNotifications.php
|
||||
- ${DEV_LOCATION}/front/deviceDetailsTools.php:/app/front/deviceDetailsTools.php
|
||||
- ${DEV_LOCATION}/front/devices.php:/app/front/devices.php
|
||||
- ${DEV_LOCATION}/front/events.php:/app/front/events.php
|
||||
|
||||
@@ -4,24 +4,24 @@
|
||||

|
||||
[](https://github.com/sponsors/jokob-sk)
|
||||
|
||||
# NetAlertX 💻🔍 Network security scanner & notification framework
|
||||
# NetAlertX 💻🔍 Network scanner & notification framework
|
||||
|
||||
| 🐳 [Docker hub](https://registry.hub.docker.com/r/jokobsk/netalertx) | 📑 [Docker guide](https://github.com/jokob-sk/NetAlertX/blob/main/dockerfiles/README.md) |🆕 [Release notes](https://github.com/jokob-sk/NetAlertX/releases) | 📚 [All Docs](https://github.com/jokob-sk/NetAlertX/tree/main/docs) |
|
||||
|----------------------|----------------------| ----------------------| ----------------------|
|
||||
|
||||
<a href="https://raw.githubusercontent.com/jokob-sk/NetAlertX/main/docs/img/devices_split.png" target="_blank">
|
||||
<img src="https://raw.githubusercontent.com/jokob-sk/NetAlertX/main/docs/img/devices_split.png" width="300px" />
|
||||
</a>
|
||||
<a href="https://raw.githubusercontent.com/jokob-sk/NetAlertX/main/docs/img/network.png" target="_blank">
|
||||
<img src="https://raw.githubusercontent.com/jokob-sk/NetAlertX/main/docs/img/network.png" width="300px" />
|
||||
<a href="https://raw.githubusercontent.com/jokob-sk/NetAlertX/main/docs/img/GENERAL/github_social_image.jpg" target="_blank">
|
||||
<img src="https://raw.githubusercontent.com/jokob-sk/NetAlertX/main/docs/img/GENERAL/github_social_image.jpg" width="1000px" />
|
||||
</a>
|
||||
|
||||
Head to [https://netalertx.com/](https://netalertx.com/) for more gifs and screenshots 📷.
|
||||
|
||||
> [!NOTE]
|
||||
> There is also an experimental 🧪 [bare-metal install](https://github.com/jokob-sk/NetAlertX/blob/main/docs/HW_INSTALL.md) method available.
|
||||
|
||||
## 📕 Basic Usage
|
||||
|
||||
- You will have to run the container on the `host` network, e.g:
|
||||
> [!WARNING]
|
||||
> You will have to run the container on the `host` network.
|
||||
|
||||
```yaml
|
||||
docker run -d --rm --network=host \
|
||||
@@ -40,7 +40,7 @@ docker run -d --rm --network=host \
|
||||
| `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` |
|
||||
|`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 `ALWAYS_FRESH_INSTALL=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` |
|
||||
|`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` |
|
||||
|
||||
### Docker paths
|
||||
|
||||
@@ -66,26 +66,16 @@ docker run -d --rm --network=host \
|
||||
- You can modify [app.conf](https://github.com/jokob-sk/NetAlertX/tree/main/config) directly, if needed.
|
||||
- If unavailable, the app generates a default `app.conf` and `app.db` file on the first run.
|
||||
|
||||
#### Important settings
|
||||
### ⚙ Important settings
|
||||
|
||||
These are the most important settings to get at least some output in your Devices screen. Usually, only one approach is used, but you should be able to combine these approaches.
|
||||
These are the most important settings to get at least some output in your Devices screen. Usually, only one approach is used, but you can combine these approaches.
|
||||
|
||||
##### For arp-scan: ARPSCAN_RUN, SCAN_SUBNETS
|
||||
| Scan method | Setting | Description |
|
||||
| :------------- | :------------- | :-------------|
|
||||
| arp-scan, nmap-scan | `SCAN_SUBNETS` | See the documentation on how [to setup SUBNETS, VLANs & limitations](https://github.com/jokob-sk/NetAlertX/blob/main/docs/SUBNETS.md) |
|
||||
| PiHole | `PIHOLE_RUN` | There are 2 approaches how to get PiHole devices imported. Via the PiHole import (`PIHOLE`) plugin or DHCP leases (`DHCPLSS`) plugin. The `PIHOLE` plugin requires you to map the PiHole database, as mentioned above. |
|
||||
| dhcp.leases | `DHCPLSS_RUN` | You need to map `:/etc/myfiles/dhcp.leases` in the `docker-compose.yml` file if you enable this setting. This path has to be matched with a corresponding `DHCPLSS_paths_to_check` setting entry (check the [DHCPLSS plugin readme](https://github.com/jokob-sk/NetAlertX/tree/main/front/plugins/dhcp_leases#overview) for details). |
|
||||
|
||||
- ❗ To use the arp-scan method, you need to set the `SCAN_SUBNETS` variable. See the documentation on how [to setup SUBNETS, VLANs & limitations](https://github.com/jokob-sk/NetAlertX/blob/main/docs/SUBNETS.md)
|
||||
|
||||
##### For pihole: PIHOLE_RUN, DHCPLSS_RUN
|
||||
|
||||
There are 2 approaches how to get PiHole devices imported. Via the PiHole import (PIHOLE) plugin or DHCP leases (DHCPLSS) plugin.
|
||||
|
||||
**PiHole (Device sync)**
|
||||
|
||||
* `PIHOLE_RUN`: You need to map `:/etc/pihole/pihole-FTL.db` in the `docker-compose.yml` file if you enable this setting.
|
||||
|
||||
**DHCP Leases (Device import)**
|
||||
|
||||
* `DHCPLSS_RUN`: You need to map `:/etc/pihole/dhcp.leases` in the `docker-compose.yml` file if you enable this setting.
|
||||
* The above setting has to be matched with a corresponding `DHCPLSS_paths_to_check` setting entry (the path in the container must contain `pihole` as PiHole uses a different format of the `dhcp.leases` file).
|
||||
|
||||
> [!NOTE]
|
||||
> It's recommended to use the same schedule interval for all plugins responsible for discovering new devices.
|
||||
@@ -93,16 +83,17 @@ There are 2 approaches how to get PiHole devices imported. Via the PiHole import
|
||||
|
||||
#### 🧭 Community guides
|
||||
|
||||
Use the official installation guides at first and use community content as suplementary material. Open an issue if you'd like to add your link to the list 🙏
|
||||
Use the official installation guides at first and use community content as supplementary material. Open an issue if you'd like to add your link to the list 🙏
|
||||
|
||||
- 📄 [How to Install Pi.Alert on Your Synology NAS - Marius hosting (English)](https://mariushosting.com/how-to-install-pi-alert-on-your-synology-nas/) (Updated frequently)
|
||||
- 📄 [Using the PiAlert Network Security Scanner on a Raspberry Pi - PiMyLifeUp (English)](https://pimylifeup.com/raspberry-pi-pialert/)
|
||||
- ▶ [How to Setup Pi.Alert on Your Synology NAS - Digital Aloha (English)](https://www.youtube.com/watch?v=M4YhpuRFaUg)
|
||||
- ▶ [Home Lab Network Monitoring - Scotti-BYTE Enterprise Consulting Services](https://www.youtube.com/watch?v=0DryhzrQSJA) (July 2024)
|
||||
- 📄 [How to Install NetAlertX on Your Synology NAS - Marius hosting](https://mariushosting.com/how-to-install-pi-alert-on-your-synology-nas/) (Updated frequently)
|
||||
- 📄 [Using the PiAlert Network Security Scanner on a Raspberry Pi - PiMyLifeUp](https://pimylifeup.com/raspberry-pi-pialert/)
|
||||
- ▶ [How to Setup Pi.Alert on Your Synology NAS - Digital Aloha](https://www.youtube.com/watch?v=M4YhpuRFaUg)
|
||||
- 📄 [시놀/헤놀에서 네트워크 스캐너 Pi.Alert Docker로 설치 및 사용하기 (Korean)](https://blog.dalso.org/article/%EC%8B%9C%EB%86%80-%ED%97%A4%EB%86%80%EC%97%90%EC%84%9C-%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC-%EC%8A%A4%EC%BA%90%EB%84%88-pi-alert-docker%EB%A1%9C-%EC%84%A4%EC%B9%98-%EB%B0%8F-%EC%82%AC%EC%9A%A9) (July 2023)
|
||||
- 📄 [网络入侵探测器Pi.Alert (Chinese)](https://codeantenna.com/a/VgUvIAjZ7J) (May 2023)
|
||||
- ▶ [Pi.Alert auf Synology & Docker by - Jürgen Barth (German)](https://www.youtube.com/watch?v=-ouvA2UNu-A) (March 2023)
|
||||
- ▶ [Top Docker Container for Home Server Security - VirtualizationHowto (English)](https://www.youtube.com/watch?v=tY-w-enLF6Q) (March 2023)
|
||||
- ▶ [Pi.Alert or WatchYourLAN can alert you to unknown devices appearing on your WiFi or LAN network - Danie van der Merwe (English)](https://www.youtube.com/watch?v=v6an9QG2xF0) (November 2022)
|
||||
- ▶ [Top Docker Container for Home Server Security - VirtualizationHowto](https://www.youtube.com/watch?v=tY-w-enLF6Q) (March 2023)
|
||||
- ▶ [Pi.Alert or WatchYourLAN can alert you to unknown devices appearing on your WiFi or LAN network - Danie van der Merwe](https://www.youtube.com/watch?v=v6an9QG2xF0) (November 2022)
|
||||
|
||||
> Ordered by last update time.
|
||||
|
||||
|
||||
@@ -5,12 +5,12 @@ export APP_NAME=netalertx
|
||||
|
||||
# php-fpm setup
|
||||
install -d -o nginx -g www-data /run/php/
|
||||
sed -i "/^;pid/c\pid = /run/php/php8.2-fpm.pid" /etc/php82/php-fpm.conf
|
||||
sed -i "/^listen/c\listen = /run/php/php8.2-fpm.sock" /etc/php82/php-fpm.d/www.conf
|
||||
sed -i "/^;listen.owner/c\listen.owner = nginx" /etc/php82/php-fpm.d/www.conf
|
||||
sed -i "/^;listen.group/c\listen.group = www-data" /etc/php82/php-fpm.d/www.conf
|
||||
sed -i "/^user/c\user = nginx" /etc/php82/php-fpm.d/www.conf
|
||||
sed -i "/^group/c\group = www-data" /etc/php82/php-fpm.d/www.conf
|
||||
sed -i "/^;pid/c\pid = /run/php/php8.3-fpm.pid" /etc/php83/php-fpm.conf
|
||||
sed -i "/^listen/c\listen = /run/php/php8.3-fpm.sock" /etc/php83/php-fpm.d/www.conf
|
||||
sed -i "/^;listen.owner/c\listen.owner = nginx" /etc/php83/php-fpm.d/www.conf
|
||||
sed -i "/^;listen.group/c\listen.group = www-data" /etc/php83/php-fpm.d/www.conf
|
||||
sed -i "/^user/c\user = nginx" /etc/php83/php-fpm.d/www.conf
|
||||
sed -i "/^group/c\group = www-data" /etc/php83/php-fpm.d/www.conf
|
||||
|
||||
# s6 overlay setup
|
||||
mkdir -p /etc/s6-overlay/s6-rc.d/{SetupOneshot,php-fpm/dependencies.d,nginx/dependencies.d}
|
||||
@@ -20,17 +20,17 @@ echo "longrun" > /etc/s6-overlay/s6-rc.d/php-fpm/type
|
||||
echo "longrun" > /etc/s6-overlay/s6-rc.d/nginx/type
|
||||
echo "longrun" > /etc/s6-overlay/s6-rc.d/$APP_NAME/type
|
||||
echo -e "${INSTALL_DIR}/dockerfiles/setup.sh" > /etc/s6-overlay/s6-rc.d/SetupOneshot/up
|
||||
echo -e "#!/bin/execlineb -P\n/usr/sbin/php-fpm82 -F" > /etc/s6-overlay/s6-rc.d/php-fpm/run
|
||||
echo -e "#!/bin/execlineb -P\n/usr/sbin/php-fpm83 -F" > /etc/s6-overlay/s6-rc.d/php-fpm/run
|
||||
echo -e '#!/bin/execlineb -P\nnginx -g "daemon off;"' > /etc/s6-overlay/s6-rc.d/nginx/run
|
||||
echo -e '#!/bin/execlineb -P
|
||||
with-contenv
|
||||
|
||||
importas -u PORT PORT
|
||||
|
||||
if { echo
|
||||
|
||||
if { echo
|
||||
"
|
||||
[INSTALL] 🚀 Starting app (:${PORT})
|
||||
|
||||
|
||||
" }' > /etc/s6-overlay/s6-rc.d/$APP_NAME/run
|
||||
echo -e "python ${INSTALL_DIR}/server" >> /etc/s6-overlay/s6-rc.d/$APP_NAME/run
|
||||
touch /etc/s6-overlay/s6-rc.d/user/contents.d/{SetupOneshot,php-fpm,nginx} /etc/s6-overlay/s6-rc.d/{php-fpm,nginx}/dependencies.d/SetupOneshot
|
||||
@@ -38,4 +38,6 @@ touch /etc/s6-overlay/s6-rc.d/user/contents.d/{SetupOneshot,php-fpm,nginx,$APP_N
|
||||
touch /etc/s6-overlay/s6-rc.d/nginx/dependencies.d/php-fpm
|
||||
touch /etc/s6-overlay/s6-rc.d/$APP_NAME/dependencies.d/nginx
|
||||
|
||||
|
||||
|
||||
rm -f $0
|
||||
|
||||
@@ -8,7 +8,7 @@ export INSTALL_DIR=/app # Specify the installation directory here
|
||||
|
||||
# DO NOT CHANGE ANYTHING BELOW THIS LINE!
|
||||
|
||||
CONF_FILE="app.conf"
|
||||
CONF_FILE="app.conf"
|
||||
NGINX_CONF_FILE=netalertx.conf
|
||||
DB_FILE="app.db"
|
||||
FULL_FILEDB_PATH="${INSTALL_DIR}/db/${DB_FILE}"
|
||||
@@ -28,14 +28,14 @@ fi
|
||||
|
||||
echo "[INSTALL] Copy starter ${DB_FILE} and ${CONF_FILE} if they don't exist"
|
||||
|
||||
# DANGER ZONE: ALWAYS_FRESH_INSTALL
|
||||
# DANGER ZONE: ALWAYS_FRESH_INSTALL
|
||||
if [ "$ALWAYS_FRESH_INSTALL" = true ]; then
|
||||
echo "[INSTALL] ❗ ALERT /db and /config folders are cleared because the ALWAYS_FRESH_INSTALL is set to: $ALWAYS_FRESH_INSTALL❗"
|
||||
|
||||
|
||||
# Delete content of "$INSTALL_DIR/config/"
|
||||
rm -rf "$INSTALL_DIR/config/"*
|
||||
rm -rf "$INSTALL_DIR_OLD/config/"*
|
||||
|
||||
|
||||
# Delete content of "$INSTALL_DIR/db/"
|
||||
rm -rf "$INSTALL_DIR/db/"*
|
||||
rm -rf "$INSTALL_DIR_OLD/db/"*
|
||||
@@ -61,11 +61,11 @@ cp -na "${INSTALL_DIR}/back/${DB_FILE}" "${FULL_FILEDB_PATH}"
|
||||
# if custom variables not set we do not need to do anything
|
||||
if [ -n "${TZ}" ]; then
|
||||
FILECONF="${INSTALL_DIR}/config/${CONF_FILE}"
|
||||
echo "[INSTALL] Setup timezone"
|
||||
echo "[INSTALL] Setup timezone"
|
||||
sed -i "\#^TIMEZONE=#c\TIMEZONE='${TZ}'" "${FILECONF}"
|
||||
|
||||
# set TimeZone in container
|
||||
cp /usr/share/zoneinfo/$TZ /etc/localtime
|
||||
cp /usr/share/zoneinfo/$TZ /etc/localtime
|
||||
echo $TZ > /etc/timezone
|
||||
fi
|
||||
|
||||
@@ -92,10 +92,13 @@ fi
|
||||
|
||||
# Create an empty log files
|
||||
# Create the execution_queue.log and app_front.log files if they don't exist
|
||||
touch "${INSTALL_DIR}"/front/log/{app.log,execution_queue.log,app_front.log,app.php_errors.log,stderr.log,stdout.log}
|
||||
touch "${INSTALL_DIR}"/front/log/{app.log,execution_queue.log,app_front.log,app.php_errors.log,stderr.log,stdout.log,db_is_locked.log}
|
||||
touch "${INSTALL_DIR}"/front/api/user_notifications.json
|
||||
|
||||
echo "[INSTALL] Fixing permissions after copied starter config & DB"
|
||||
chown -R nginx:www-data "${INSTALL_DIR}"/{config,front/log,db}
|
||||
chown -R nginx:www-data "${INSTALL_DIR}"/{config,front/log,db,front/api}
|
||||
chown -R nginx:www-data "${INSTALL_DIR}"/front/api/user_notifications.json
|
||||
|
||||
chmod 750 "${INSTALL_DIR}"/{config,front/log,db}
|
||||
find "${INSTALL_DIR}"/{config,front/log,db} -type f -exec chmod 640 {} \;
|
||||
|
||||
@@ -106,10 +109,14 @@ if [ ! -f "${INSTALL_DIR}/front/buildtimestamp.txt" ]; then
|
||||
chown nginx:www-data "${INSTALL_DIR}/front/buildtimestamp.txt"
|
||||
fi
|
||||
|
||||
# Start crond service in the background
|
||||
echo "[INSTALL] Starting crond service..."
|
||||
crond -f -d 8 > /dev/null 2>&1 &
|
||||
|
||||
echo -e "
|
||||
[ENV] PATH is ${PATH}
|
||||
[ENV] PORT is ${PORT}
|
||||
[ENV] TZ is ${TZ}
|
||||
[ENV] LISTEN_ADDR is ${LISTEN_ADDR}
|
||||
[ENV] ALWAYS_FRESH_INSTALL is ${ALWAYS_FRESH_INSTALL}
|
||||
[ENV] PATH is ${PATH}
|
||||
[ENV] PORT is ${PORT}
|
||||
[ENV] TZ is ${TZ}
|
||||
[ENV] LISTEN_ADDR is ${LISTEN_ADDR}
|
||||
[ENV] ALWAYS_FRESH_INSTALL is ${ALWAYS_FRESH_INSTALL}
|
||||
"
|
||||
|
||||
@@ -6,7 +6,7 @@ Please follow tips 1 - 4 to get a more detailed error.
|
||||
|
||||
When debugging an issue always set the highest log level:
|
||||
|
||||
`LOG_LEVEL='debug'`
|
||||
`LOG_LEVEL='trace'`
|
||||
|
||||
|
||||
## 2. Surfacing errors when container restarts 🔁
|
||||
|
||||
@@ -33,7 +33,7 @@ To edit device information:
|
||||
## Session Info
|
||||
- **Status**: Show device status : On-line / Off-Line
|
||||
- **First Session**: Date and time of the first connection
|
||||
- **Last Session**: Date and time of the last connection
|
||||
- **Last Offline**: Date and time of the last last time the device was offline
|
||||
- **Last IP**: Last known IP used during the last connection
|
||||
- **Static IP**: Check this box to identify devices that always use the
|
||||
same IP
|
||||
|
||||
64
docs/DEV_ENV_SETUP.md
Executable file
@@ -0,0 +1,64 @@
|
||||
## Development environemnt set up
|
||||
|
||||
>[!NOTE]
|
||||
> Replace `/development` with the path where your code files will be stored. The default container name is `netalertx` so there might be a conflict with your running containers.
|
||||
|
||||
## 1. Download the code:
|
||||
|
||||
- `mkdir /development`
|
||||
- `cd /development && git clone https://github.com/jokob-sk/NetAlertX.git`
|
||||
|
||||
## 2. Create a DEV .env_dev file
|
||||
|
||||
`touch /development/.env_dev && sudo nano /development/.env_dev`
|
||||
|
||||
The file content should be following, with your custom values.
|
||||
|
||||
```yaml
|
||||
#--------------------------------
|
||||
#NETALERTX
|
||||
#--------------------------------
|
||||
TZ=Europe/Berlin
|
||||
PORT=22222 # make sure this port is unique on your whole network
|
||||
DEV_LOCATION=/development/NetAlertX
|
||||
APP_DATA_LOCATION=/volume/docker_appdata
|
||||
# ALWAYS_FRESH_INSTALL=true # uncommenting this will always delete the content of /config and /db dirs on boot to simulate a fresh install
|
||||
```
|
||||
|
||||
## 3. Create /db and /config dirs
|
||||
|
||||
Create a folder `netalertx` in the `APP_DATA_LOCATION` (in this example in `/volume/docker_appdata`) with 2 subfolders `db` and `config`.
|
||||
|
||||
- `mkdir /volume/docker_appdata/netalertx`
|
||||
- `mkdir /volume/docker_appdata/netalertx/db`
|
||||
- `mkdir /volume/docker_appdata/netalertx/config`
|
||||
|
||||
## 4. Run the container
|
||||
|
||||
- `cd /development/NetAlertX && sudo docker-compose --env-file ../.env_dev `
|
||||
|
||||
You can then modify the python script without restarting/rebuilding the container every time. Additionally, you can trigger a plugin run via the UI:
|
||||
|
||||

|
||||
|
||||
|
||||
## 💡 Tips
|
||||
|
||||
A quick cheat sheet of useful commands.
|
||||
|
||||
### Removing the container and image
|
||||
|
||||
A command to stop, remove the container and the image (replace `netalertx` and `netalertx-netalertx` with the appropriate values)
|
||||
|
||||
- `sudo docker container stop netalertx ; sudo docker container rm netalertx ; sudo docker image rm netalertx-netalertx`
|
||||
|
||||
### Restart hanging python script
|
||||
|
||||
SSH into the container and kill & restart the main script loop
|
||||
|
||||
- `sudo docker exec -it netalertx /bin/bash`
|
||||
- `pkill -f "python /app/server" && python /app/server & `
|
||||
|
||||
|
||||
|
||||
|
||||
41
docs/FILE_PERMISSIONS.md
Executable file
@@ -0,0 +1,41 @@
|
||||
# Managing File Permissions for NetAlertX on Nginx with Docker
|
||||
|
||||
> [!TIP]
|
||||
> If you are facing permission issues, try to start the container without mapping your volumes. If that works, then the issue is permission related. You can try e.g., the following command:
|
||||
> ```
|
||||
> docker run -d --rm --network=host \
|
||||
> -e TZ=Europe/Berlin \
|
||||
> -e PORT=20211 \
|
||||
> jokobsk/netalertx:latest
|
||||
> ```
|
||||
NetAlertX runs on an Nginx web server. On Alpine Linux, Nginx operates as the `nginx` user (user ID 101, group ID 82 - `www-data`). Consequently, files accessed or written by the NetAlertX application are owned by `nginx:www-data`.
|
||||
|
||||
Upon starting, NetAlertX changes the ownership of files on the host system mapped to `/app/config` and `/app/db` in the container to `nginx:www-data`. This ensures that Nginx can access and write to these files. Since the user in the Docker container is mapped to a user on the host system by ID:GID, the files in `/app/config` and `/app/db` on the host system are owned by a user with the same ID and GID (ID 101 and GID 82). On different systems, this ID:GID may belong to different users (on Debian, the user with ID 82 is `uuidd`), or there may not be a user with ID 82 at all.
|
||||
|
||||
While this generally isn't problematic, it can cause issues for host system users needing to access these files (e.g., backup scripts). If users other than root need access to these files, it is recommended to add those users to the group with GID 82. If that group doesn't exist, it should be created.
|
||||
|
||||
### Permissions Table for Individual Folders
|
||||
|
||||
| Folder | User | User ID | Group | Group ID | Permissions | Notes |
|
||||
|----------------|--------|---------|-----------|----------|-------------|---------------------------------------------------------------------|
|
||||
| `/app/config` | nginx | 101 | www-data | 82 | rwxr-xr-x | Ensure `nginx` can read/write; other users can read if in `www-data` |
|
||||
| `/app/db` | nginx | 101 | www-data | 82 | rwxr-xr-x | Same as above |
|
||||
|
||||
### Steps to Add Users to Group
|
||||
|
||||
1. **Check if group exists:**
|
||||
```sh
|
||||
getent group www-data
|
||||
```
|
||||
|
||||
2. **Create group if it does not exist:**
|
||||
```sh
|
||||
sudo groupadd -g 82 www-data
|
||||
```
|
||||
|
||||
3. **Add user to group:**
|
||||
```sh
|
||||
sudo usermod -aG www-data <username>
|
||||
```
|
||||
|
||||
Replace `<username>` with the actual username that requires access.
|
||||
@@ -38,7 +38,7 @@ Some examples how to apply the above:
|
||||
|
||||
Some useful frontend JavaScript functions:
|
||||
|
||||
- `getDeviceDataByMacAddress(macAddress, devicesColumn)` - method to retrieve any device data (database column) based on MAC address in the frontend
|
||||
- `getDeviceDataByMac(macAddress, devicesColumn)` - method to retrieve any device data (database column) based on MAC address in the frontend
|
||||
- `getString(string stringKey)` - method to retrieve translated strings in the frontend
|
||||
- `getSetting(string stringKey)` - method to retrieve settings in the frontend
|
||||
|
||||
|
||||
@@ -1,9 +1,27 @@
|
||||
# Migration form PiAlert to NetAlertX
|
||||
|
||||
> [!TIP]
|
||||
> In short: The application will auto-migrate the database, config, and all device information. A ticker message on top will be displayed until you update your docker mount points. Even so, it's always good to have a [backup strategy](https://github.com/jokob-sk/NetAlertX/blob/main/docs/BACKUPS.md) in place.
|
||||
> [!WARNING]
|
||||
> Follow this guide only after you you downloaded and started NetAlert X at least once after previously using the PiAlert image.
|
||||
|
||||
The migration should be pretty straightforward. The application installation folder in the docker container has changed from `/home/pi/pialert` to `/app`. That means the new mount points are:
|
||||
## STEPS:
|
||||
|
||||
> [!TIP]
|
||||
> In short: The application will auto-migrate the database, config, and all device information. A ticker message on top will be displayed until you update your docker mount points. It's always good to have a [backup strategy](https://github.com/jokob-sk/NetAlertX/blob/main/docs/BACKUPS.md) in place.
|
||||
|
||||
1. Backup your current config and database (optional `devices.csv` to have a backup) (See bellow tip if facing issues)
|
||||
2. Stop the container
|
||||
2. Update the Docker file mount locations in your `docker-compose.yml` or docker run command (See bellow **New Docker mount locations**).
|
||||
3. Rename the DB and conf files to `app.db` and `app.conf` and place them in the appropriate location.
|
||||
4. Start the Container
|
||||
|
||||
|
||||
> [!TIP]
|
||||
> If you have troubles accessing past backups, config or database files you can copy them into the newly mapped directories, for example by running this command in the container: `cp -r /app/config /home/pi/pialert/config/old_backup_files`. This should create a folder in the `config` directory called `old_backup_files` conatining all the files in that location. Another approach is to map the old location and the new one at the same time to copy things over.
|
||||
|
||||
|
||||
### New Docker mount locations
|
||||
|
||||
The application installation folder in the docker container has changed from `/home/pi/pialert` to `/app`. That means the new mount points are:
|
||||
|
||||
| Old mount point | New mount point |
|
||||
|----------------------|---------------|
|
||||
@@ -22,8 +40,10 @@ The migration should be pretty straightforward. The application installation fol
|
||||
> [!NOTE]
|
||||
> The application uses symlinks linking the old db and config locations to the new ones, so data loss should not occur. [Backup strategies](https://github.com/jokob-sk/NetAlertX/blob/main/docs/BACKUPS.md) are still recommended to backup your setup.
|
||||
|
||||
In summary, docker file mount locations in your `docker-compose.yml` or docker run command have changed. Examples follow.
|
||||
|
||||
# Examples
|
||||
|
||||
Exmaples of docker files with the new mount points.
|
||||
|
||||
## Example 1: Mapping folders
|
||||
|
||||
|
||||
55
docs/NOTIFICATIONS.md
Executable file
@@ -0,0 +1,55 @@
|
||||
# Notifications 📧
|
||||
|
||||
There are 4 ways how to influence notifications:
|
||||
|
||||
1. On the device itself
|
||||
2. On the settings of the plugin
|
||||
3. Globally
|
||||
4. Ignoring devices
|
||||
|
||||
> [!NOTE]
|
||||
> It's recommended to use the same schedule interval for all plugins responsible for scanning devices, otherwise false positives might be reported if different devices are discovered by different plugins. Check the **Settings** > **Enabled settings** section for a warning:
|
||||
> 
|
||||
|
||||
|
||||
## Device settings 💻
|
||||
|
||||

|
||||
|
||||
There are 4 settings on the device for influencing notifications. You can:
|
||||
|
||||
1. Completely disable the scanning of the device
|
||||
2. **Alert all events**, connections, disconnections, IP changes (noisy, usually not recommended)
|
||||
3. **Alert down** - alerts when a device goes down. This setting overrides disabled Alert All Events, so you will get a notification of a device going down even if you don't have Alert All Events ticked.
|
||||
4. **Skip repeated notifications**, if for example you know there is a temporary issue and want to pause the same notification for this device for a given time.
|
||||
|
||||
## Plugin settings 🔌
|
||||
|
||||

|
||||
|
||||
On almost all plugins there are 2 core settings, `<plugin>_WATCH` and `<plugin>_REPORT_ON`.
|
||||
|
||||
1. `<plugin>_WATCH` specifies the columns which the app should watch. If watched columns change the device state is considered changed. This changed status is then used to decide to send out notifications based on the `<plugin>_REPORT_ON` setting.
|
||||
2. `<plugin>_REPORT_ON` let's you specify on which events the app should notify you. This is related to the `<plugin>_WATCH` setting. So if you select `watched-changed` and in `<plugin>_WATCH` you only select `Watched_Value1`, then a notification is triggered if `Watched_Value1` is changed from the previous value, but no notification is send if `Watched_Value2` changes.
|
||||
|
||||
Click the **Read more in the docs.** Link at the top of each plugin to get more details on how the given plugin works.
|
||||
|
||||
## Global settings ⚙
|
||||
|
||||

|
||||
|
||||
In Notification Processing settings, you can specify blanket rules. These allow you to specify exceptions to the Plugin and Device settings and will override those.
|
||||
|
||||
1. Notify on (`NTFPRCS_INCLUDED_SECTIONS`) allows you to specify which events trigger notifications. Usual setups will have `new_devices`, `down_devices`, and possibly `events` set. Setting `plugin` might be too noisy for most setups. More info in the [NTFPRCS plugin](/front/plugins/notification_processing/README.md)
|
||||
2. Alert down after (`NTFPRCS_alert_down_time`) is useful if you want to wait for some time before the system sends out a down notification for a device. This is related to the on-device **Alert down** setting and only devices with this checked will trigger a down notification.
|
||||
3. A filter to allow you to set device-specific exceptions to New devices being added to the app.
|
||||
4. A filter to allow you to set device-specific exceptions to generated Events.
|
||||
|
||||
## Ignoring devices 🔕
|
||||
|
||||

|
||||
|
||||
You can completely ignore detected devices globally. This could be because your instance detects docker containers, you want to ignore devices from a specific manufacturer via MAC rules or you want to ignore devices on a specific IP range.
|
||||
|
||||
1. Ignored MACs (`NEWDEV_ignored_MACs`) - List of MACs to ignore.
|
||||
2. Ignored IPs (`NEWDEV_ignored_IPs`) - List of IPs to ignore.
|
||||
736
docs/PLUGINS_DEV.md
Executable file
@@ -0,0 +1,736 @@
|
||||
## 🌟 Create a custom plugin: Overview
|
||||
|
||||
NetAlertX comes with a plugin system to feed events from third-party scripts into the UI and then send notifications, if desired. The highlighted core functionality this plugin system supports, is:
|
||||
|
||||
* dynamic creation of a simple UI to interact with the discovered objects,
|
||||
* filtering of displayed values in the Devices UI
|
||||
* surface settings of plugins in the UI,
|
||||
* different column types for reported values to e.g. link back to a device
|
||||
* import objects into existing NetAlertX database tables
|
||||
|
||||
> (Currently, update/overwriting of existing objects is only supported for devices via the `CurrentScan` table.)
|
||||
|
||||
### 🎥 Watch the video:
|
||||
|
||||
[](https://youtu.be/cdbxlwiWhv8)
|
||||
|
||||
### 📸 Screenshots
|
||||
|
||||
| ![Screen 1][screen1] | ![Screen 2][screen2] | ![Screen 3][screen3] |
|
||||
|----------------------|----------------------| ----------------------|
|
||||
| ![Screen 4][screen4] | ![Screen 5][screen5] |
|
||||
|
||||
## Use cases
|
||||
|
||||
Example use cases for plugins could be:
|
||||
|
||||
* Monitor a web service and alert me if it's down
|
||||
* Import devices from dhcp.leases files instead/complementary to using PiHole or arp-scans
|
||||
* Creating ad-hoc UI tables from existing data in the NetAlertX database, e.g. to show all open ports on devices, to list devices that disconnected in the last hour, etc.
|
||||
* Using other device discovery methods on the network and importing the data as new devices
|
||||
* Creating a script to create FAKE devices based on user input via custom settings
|
||||
* ...at this point the limitation is mostly the creativity rather than the capability (there might be edge cases and a need to support more form controls for user input off custom settings, but you probably get the idea)
|
||||
|
||||
If you wish to develop a plugin, please check the existing plugin structure. Once the settings are saved by the user they need to be removed from the `app.conf` file manually if you want to re-initialize them from the `config.json` of the plugin.
|
||||
|
||||
Again, please read the below carefully if you'd like to contribute with a plugin yourself. This documentation file might be outdated, so double-check the sample plugins as well.
|
||||
|
||||
## ⚠ Disclaimer
|
||||
|
||||
Follow the below very carefully and check example plugin(s) if you'd like to write one yourself. Plugin UI is not my priority right now, happy to approve PRs if you are interested in extending/improving the UI experience (See [Frontend guidelines](/docs/FRONTEND_DEVELOPMENT.md)). Example improvements for the taking:
|
||||
|
||||
* Making the tables sortable/filterable
|
||||
* Using the same approach to display table data as in the Devices section (solves above)
|
||||
* Adding form controls supported to display the data (Currently supported ones are listed in the section "UI settings in database_column_definitions" below)
|
||||
* ...
|
||||
|
||||
## ❗ Known limitations:
|
||||
|
||||
These issues will be hopefully fixed with time, so please don't report them. Instead, if you know how, feel free to investigate and submit a PR to fix the below. Keep the PRs small as it's easier to approve them:
|
||||
|
||||
* Existing plugin objects are sometimes not interpreted correctly and a new object is created instead, resulting in duplicate entries. (race condition?)
|
||||
* Occasional (experienced twice) hanging of processing plugin script file.
|
||||
* UI displays outdated values until the API endpoints get refreshed.
|
||||
|
||||
## Plugin file structure overview
|
||||
|
||||
> ⚠️Folder name must be the same as the code name value in: `"code_name": "<value>"`
|
||||
> Unique prefix needs to be unique compared to the other settings prefixes, e.g.: the prefix `APPRISE` is already in use.
|
||||
|
||||
| File | Required (plugin type) | Description |
|
||||
|----------------------|----------------------|----------------------|
|
||||
| `config.json` | yes | Contains the plugin configuration (manifest) including the settings available to the user. |
|
||||
| `script.py` | no | The Python script itself. You may call any valid linux command. |
|
||||
| `last_result.log` | no | The file used to interface between NetAlertX and the plugin. Required for a script plugin if you want to feed data into the app. |
|
||||
| `script.log` | no | Logging output (recommended) |
|
||||
| `README.md` | yes | Any setup considerations or overview |
|
||||
|
||||
More on specifics below.
|
||||
|
||||
### Column order and values
|
||||
|
||||
> [!IMPORTANT]
|
||||
> Spend some time reading and trying to understand the below table. This is the interface between the Plugins and the core application.
|
||||
|
||||
| Order | Represented Column | Value Required | Description |
|
||||
|----------------------|----------------------|----------------------|----------------------|
|
||||
| 0 | `Object_PrimaryID` | yes | The primary ID used to group Events under. |
|
||||
| 1 | `Object_SecondaryID` | no | Optional secondary ID to create a relationship beween other entities, such as a MAC address |
|
||||
| 2 | `DateTime` | yes | When the event occured in the format `2023-01-02 15:56:30` |
|
||||
| 3 | `Watched_Value1` | yes | A value that is watched and users can receive notifications if it changed compared to the previously saved entry. For example IP address |
|
||||
| 4 | `Watched_Value2` | no | As above |
|
||||
| 5 | `Watched_Value3` | no | As above |
|
||||
| 6 | `Watched_Value4` | no | As above |
|
||||
| 7 | `Extra` | no | Any other data you want to pass and display in NetAlertX and the notifications |
|
||||
| 8 | `ForeignKey` | no | A foreign key that can be used to link to the parent object (usually a MAC address) |
|
||||
|
||||
> [!NOTE]
|
||||
> De-duplication is run once an hour on the `Plugins_Objects` database table and duplicate entries with the same value in columns `Object_PrimaryID`, `Object_SecondaryID`, `Plugin` (auto-filled based on `unique_prefix` of the plugin), `UserData` (can be populated with the `"type": "textbox_save"` column type) are removed.
|
||||
|
||||
# config.json structure
|
||||
|
||||
The `config.json` file is the manifest of the plugin. It contains mainly settings definitions and the mapping of Plugin objects to NetAlertX objects.
|
||||
|
||||
## Execution order
|
||||
|
||||
The execution order is used to specify wwhen a plugin is executed. This is useful if a plugin has access and surfaces more information than others. If a device is detected by 2 plugins and inserted into the `CurrentScan` table, the plugin with the higher priority (e.g.: `Level_0` is a higher priority than `Level_1`) will insert it's values first. These values (devices) will be then prioritized over any values inserted later.
|
||||
|
||||
```json
|
||||
{
|
||||
"execution_order" : "Layer_0"
|
||||
}
|
||||
```
|
||||
|
||||
## Supported data sources
|
||||
|
||||
Currently, these data sources are supported (valid `data_source` value).
|
||||
|
||||
| Name | `data_source` value | Needs to return a "table"* | Overview (more details on this page below) |
|
||||
|----------------------|----------------------|----------------------|----------------------|
|
||||
| Script | `script` | no | Executes any linux command in the `CMD` setting. |
|
||||
| NetAlertX DB query | `app-db-query` | yes | Executes a SQL query on the NetAlertX database in the `CMD` setting. |
|
||||
| Template | `template` | no | Used to generate internal settings, such as default values. |
|
||||
| External SQLite DB query | `sqlite-db-query` | yes | Executes a SQL query from the `CMD` setting on an external SQLite database mapped in the `DB_PATH` setting. |
|
||||
| Plugin type | `plugin_type` | no | Specifies the type of the plugin and in which section the Plugin settings are displayed ( one of `general/system/scanner/other/publisher` ). |
|
||||
|
||||
> * "Needs to return a "table" means that the application expects a `last_result.log` file with some results. It's not a blocker, however warnings in the `app.log` might be logged.
|
||||
|
||||
> 🔎Example
|
||||
>```json
|
||||
>"data_source": "app-db-query"
|
||||
>```
|
||||
If you want to display plugin objects or import devices into the app, data sources have to return a "table" of the exact structure as outlined above.
|
||||
|
||||
You can show or hide the UI on the "Plugins" page and "Plugins" tab for a plugin on devices via the `show_ui` property:
|
||||
|
||||
> 🔎Example
|
||||
>```json
|
||||
> "show_ui": true,
|
||||
> ```
|
||||
|
||||
### "data_source": "script"
|
||||
|
||||
If the `data_source` is set to `script` the `CMD` setting (that you specify in the `settings` array section in the `config.json`) contains an executable Linux command, that usually generates a `last_result.log` file (not required if you don't import any data into the app). The `last_result.log` file needs to be saved in the same folder as the plugin.
|
||||
|
||||
> [!IMPORTANT]
|
||||
> A lot of the work is taken care of by the [`plugin_helper.py` library](/front/plugins/plugin_helper.py). You don't need to manage the `last_result.log` file if using the helper objects. Check other `script.py` of other plugins for details (The [Undicoverables plugins `script.py` file](/front/plugins/undiscoverables/script.py) is a good example).
|
||||
|
||||
The content of the `last_result.log` file needs to contain the columns as defined in the "Column order and values" section above. The order of columns can't be changed. After every scan it should contain only the results from the latest scan/execution.
|
||||
|
||||
- The format of the `last_result.log` is a `csv`-like file with the pipe `|` as a separator.
|
||||
- 9 (nine) values need to be supplied, so every line needs to contain 8 pipe separators. Empty values are represented by `null`.
|
||||
- Don't render "headers" for these "columns".
|
||||
Every scan result/event entry needs to be on a new line.
|
||||
- You can find which "columns" need to be present, and if the value is required or optional, in the "Column order and values" section.
|
||||
- The order of these "columns" can't be changed.
|
||||
|
||||
#### 🔎 last_result.log examples
|
||||
|
||||
Valid CSV:
|
||||
|
||||
```csv
|
||||
|
||||
https://www.google.com|null|2023-01-02 15:56:30|200|0.7898|null|null|null|null
|
||||
https://www.duckduckgo.com|192.168.0.1|2023-01-02 15:56:30|200|0.9898|null|null|Best search engine|ff:ee:ff:11:ff:11
|
||||
|
||||
```
|
||||
|
||||
Invalid CSV with different errors on each line:
|
||||
|
||||
```csv
|
||||
|
||||
https://www.google.com|null|2023-01-02 15:56:30|200|0.7898||null|null|null
|
||||
https://www.duckduckgo.com|null|2023-01-02 15:56:30|200|0.9898|null|null|Best search engine|
|
||||
|https://www.duckduckgo.com|null|2023-01-02 15:56:30|200|0.9898|null|null|Best search engine|null
|
||||
null|192.168.1.1|2023-01-02 15:56:30|200|0.9898|null|null|Best search engine
|
||||
https://www.duckduckgo.com|192.168.1.1|2023-01-02 15:56:30|null|0.9898|null|null|Best search engine
|
||||
https://www.google.com|null|2023-01-02 15:56:30|200|0.7898|||
|
||||
https://www.google.com|null|2023-01-02 15:56:30|200|0.7898|
|
||||
|
||||
```
|
||||
|
||||
### "data_source": "app-db-query"
|
||||
|
||||
If the `data_source` is set to `app-db-query`, the `CMD` setting needs to contain a SQL query rendering the columns as defined in the "Column order and values" section above. The order of columns is important.
|
||||
|
||||
This SQL query is executed on the `app.db` SQLite database file.
|
||||
|
||||
> 🔎Example
|
||||
>
|
||||
> SQL query example:
|
||||
>
|
||||
> ```SQL
|
||||
> SELECT dv.dev_Name as Object_PrimaryID,
|
||||
> cast(dv.dev_LastIP as VARCHAR(100)) || ':' || cast( SUBSTR(ns.Port ,0, INSTR(ns.Port , '/')) as VARCHAR(100)) as Object_SecondaryID,
|
||||
> datetime() as DateTime,
|
||||
> ns.Service as Watched_Value1,
|
||||
> ns.State as Watched_Value2,
|
||||
> 'null' as Watched_Value3,
|
||||
> 'null' as Watched_Value4,
|
||||
> ns.Extra as Extra,
|
||||
> dv.dev_MAC as ForeignKey
|
||||
> FROM
|
||||
> (SELECT * FROM Nmap_Scan) ns
|
||||
> LEFT JOIN
|
||||
> (SELECT dev_Name, dev_MAC, dev_LastIP FROM Devices) dv
|
||||
> ON ns.MAC = dv.dev_MAC
|
||||
> ```
|
||||
>
|
||||
> Required `CMD` setting example with above query (you can set `"type": "label"` if you want it to make uneditable in the UI):
|
||||
>
|
||||
> ```json
|
||||
> {
|
||||
> "function": "CMD",
|
||||
> "type": {"dataType":"string", "elements": [{"elementType" : "input", "elementOptions" : [] ,"transformers": []}]},
|
||||
> "default_value":"SELECT dv.dev_Name as Object_PrimaryID, cast(dv.dev_LastIP as VARCHAR(100)) || ':' || cast( SUBSTR(ns.Port ,0, INSTR(ns.Port , '/')) as VARCHAR(100)) as Object_SecondaryID, datetime() as DateTime, ns.Service as Watched_Value1, ns.State as Watched_Value2, 'null' as Watched_Value3, 'null' as Watched_Value4, ns.Extra as Extra FROM (SELECT * FROM Nmap_Scan) ns LEFT JOIN (SELECT dev_Name, dev_MAC, dev_LastIP FROM Devices) dv ON ns.MAC = dv.dev_MAC",
|
||||
> "options": [],
|
||||
> "localized": ["name", "description"],
|
||||
> "name" : [{
|
||||
> "language_code":"en_us",
|
||||
> "string" : "SQL to run"
|
||||
> }],
|
||||
> "description": [{
|
||||
> "language_code":"en_us",
|
||||
> "string" : "This SQL query is used to populate the coresponding UI tables under the Plugins section."
|
||||
> }]
|
||||
> }
|
||||
> ```
|
||||
|
||||
### "data_source": "template"
|
||||
|
||||
In most cases, it is used to initialize settings. Check the `newdev_template` plugin for details.
|
||||
|
||||
### "data_source": "sqlite-db-query"
|
||||
|
||||
You can execute a SQL query on an external database connected to the current NetAlertX database via a temporary `EXTERNAL_<unique prefix>.` prefix.
|
||||
|
||||
For example for `PIHOLE` (`"unique_prefix": "PIHOLE"`) it is `EXTERNAL_PIHOLE.`. The external SQLite database file has to be mapped in the container to the path specified in the `DB_PATH` setting:
|
||||
|
||||
> 🔎Example
|
||||
>
|
||||
>```json
|
||||
> ...
|
||||
>{
|
||||
> "function": "DB_PATH",
|
||||
> "type": {"dataType":"string", "elements": [{"elementType" : "input", "elementOptions" : [{"readonly": "true"}] ,"transformers": []}]},
|
||||
> "default_value":"/etc/pihole/pihole-FTL.db",
|
||||
> "options": [],
|
||||
> "localized": ["name", "description"],
|
||||
> "name" : [{
|
||||
> "language_code":"en_us",
|
||||
> "string" : "DB Path"
|
||||
> }],
|
||||
> "description": [{
|
||||
> "language_code":"en_us",
|
||||
> "string" : "Required setting for the <code>sqlite-db-query</code> plugin type. Is used to mount an external SQLite database and execute the SQL query stored in the <code>CMD</code> setting."
|
||||
> }]
|
||||
> }
|
||||
> ...
|
||||
>```
|
||||
|
||||
The actual SQL query you want to execute is then stored as a `CMD` setting, similar to a Plugin of the `app-db-query` plugin type. The format has to adhere to the format outlined in the "Column order and values" section above.
|
||||
|
||||
> 🔎Example
|
||||
>
|
||||
> Notice the `EXTERNAL_PIHOLE.` prefix.
|
||||
>
|
||||
>```json
|
||||
>{
|
||||
> "function": "CMD",
|
||||
> "type": {"dataType":"string", "elements": [{"elementType" : "input", "elementOptions" : [] ,"transformers": []}]},
|
||||
> "default_value":"SELECT hwaddr as Object_PrimaryID, cast('http://' || (SELECT ip FROM EXTERNAL_PIHOLE.network_addresses WHERE network_id = id ORDER BY lastseen DESC, ip LIMIT 1) as VARCHAR(100)) || ':' || cast( SUBSTR((SELECT name FROM EXTERNAL_PIHOLE.network_addresses WHERE network_id = id ORDER BY lastseen DESC, ip LIMIT 1), 0, INSTR((SELECT name FROM EXTERNAL_PIHOLE.network_addresses WHERE network_id = id ORDER BY lastseen DESC, ip LIMIT 1), '/')) as VARCHAR(100)) as Object_SecondaryID, datetime() as DateTime, macVendor as Watched_Value1, lastQuery as Watched_Value2, (SELECT name FROM EXTERNAL_PIHOLE.network_addresses WHERE network_id = id ORDER BY lastseen DESC, ip LIMIT 1) as Watched_Value3, 'null' as Watched_Value4, '' as Extra, hwaddr as ForeignKey FROM EXTERNAL_PIHOLE.network WHERE hwaddr NOT LIKE 'ip-%' AND hwaddr <> '00:00:00:00:00:00'; ",
|
||||
> "options": [],
|
||||
> "localized": ["name", "description"],
|
||||
> "name" : [{
|
||||
> "language_code":"en_us",
|
||||
> "string" : "SQL to run"
|
||||
> }],
|
||||
> "description": [{
|
||||
> "language_code":"en_us",
|
||||
> "string" : "This SQL query is used to populate the coresponding UI tables under the Plugins section. This particular one selects data from a mapped PiHole SQLite database and maps it to the corresponding Plugin columns."
|
||||
> }]
|
||||
> }
|
||||
> ```
|
||||
|
||||
## 🕳 Filters
|
||||
|
||||
Plugin entries can be filtered in the UI based on values entered into filter fields. The `txtMacFilter` textbox/field contains the Mac address of the currently viewed device, or simply a Mac address that's available in the `mac` query string (`<url>?mac=aa:22:aa:22:aa:22:aa`).
|
||||
|
||||
| Property | Required | Description |
|
||||
|----------------------|----------------------|----------------------|
|
||||
| `compare_column` | yes | Plugin column name that's value is used for comparison (**Left** side of the equation) |
|
||||
| `compare_operator` | yes | JavaScript comparison operator |
|
||||
| `compare_field_id` | yes | The `id` of a input text field containing a value is used for comparison (**Right** side of the equation)|
|
||||
| `compare_js_template` | yes | JavaScript code used to convert left and right side of the equation. `{value}` is replaced with input values. |
|
||||
| `compare_use_quotes` | yes | If `true` then the end result of the `compare_js_template` i swrapped in `"` quotes. Use to compare strings. |
|
||||
|
||||
Filters are only applied if a filter is specified, and the `txtMacFilter` is not `undefined`, or empty (`--`).
|
||||
|
||||
> 🔎Example:
|
||||
>
|
||||
> ```json
|
||||
> "data_filters": [
|
||||
> {
|
||||
> "compare_column" : "Object_PrimaryID",
|
||||
> "compare_operator" : "==",
|
||||
> "compare_field_id": "txtMacFilter",
|
||||
> "compare_js_template": "'{value}'.toString()",
|
||||
> "compare_use_quotes": true
|
||||
> }
|
||||
> ],
|
||||
> ```
|
||||
>
|
||||
>1. On the `pluginsCore.php` page is an input field with the id `txtMacFilter`:
|
||||
>
|
||||
>```html
|
||||
><input class="form-control" id="txtMacFilter" type="text" value="--">
|
||||
>```
|
||||
>
|
||||
>2. This input field is initialized via the `&mac=` query string.
|
||||
>
|
||||
>3. The app then proceeds to use this Mac value from this field and compares it to the value of the `Object_PrimaryID` database field. The `compare_operator` is `==`.
|
||||
>
|
||||
>4. Both values, from the database field `Object_PrimaryID` and from the `txtMacFilter` are wrapped and evaluated with the `compare_js_template`, that is `'{value}.toString()'`.
|
||||
>
|
||||
>5. `compare_use_quotes` is set to `true` so `'{value}'.toString()` is wrappe dinto `"` quotes.
|
||||
>
|
||||
>6. This results in for example this code:
|
||||
>
|
||||
>```javascript
|
||||
> // left part of the expression coming from compare_column and right from the input field
|
||||
> // notice the added quotes ()") around the left and right part of teh expression
|
||||
> "eval('ac:82:ac:82:ac:82".toString()')" == "eval('ac:82:ac:82:ac:82".toString()')"
|
||||
>```
|
||||
>
|
||||
|
||||
|
||||
### 🗺 Mapping the plugin results into a database table
|
||||
|
||||
Plugin results are always inserted into the standard `Plugin_Objects` database table. Optionally, NetAlertX can take the results of the plugin execution, and insert these results into an additional database table. This is enabled by with the property `"mapped_to_table"` in the `config.json` file. The mapping of the columns is defined in the `database_column_definitions` array.
|
||||
|
||||
> [!NOTE]
|
||||
> If results are mapped to the `CurrentScan` table, the data is then included into the regular scan loop, so for example notification for devices are sent out.
|
||||
|
||||
|
||||
>🔍 Example:
|
||||
>
|
||||
>For example, this approach is used to implement the `DHCPLSS` plugin. The script parses all supplied "dhcp.leases" files, gets the results in the generic table format outlined in the "Column order and values" section above, takes individual values, and inserts them into the `CurrentScan` database table in the NetAlertX database. All this is achieved by:
|
||||
>
|
||||
>1. Specifying the database table into which the results are inserted by defining `"mapped_to_table": "CurrentScan"` in the root of the `config.json` file as shown below:
|
||||
>
|
||||
>```json
|
||||
>{
|
||||
> "code_name": "dhcp_leases",
|
||||
> "unique_prefix": "DHCPLSS",
|
||||
> ...
|
||||
> "data_source": "script",
|
||||
> "localized": ["display_name", "description", "icon"],
|
||||
> "mapped_to_table": "CurrentScan",
|
||||
> ...
|
||||
>}
|
||||
>```
|
||||
>2. Defining the target column with the `mapped_to_column` property for individual columns in the `database_column_definitions` array of the `config.json` file. For example in the `DHCPLSS` plugin, I needed to map the value of the `Object_PrimaryID` column returned by the plugin, to the `cur_MAC` column in the NetAlertX database table `CurrentScan`. Notice the `"mapped_to_column": "cur_MAC"` key-value pair in the sample below.
|
||||
>
|
||||
>```json
|
||||
>{
|
||||
> "column": "Object_PrimaryID",
|
||||
> "mapped_to_column": "cur_MAC",
|
||||
> "css_classes": "col-sm-2",
|
||||
> "show": true,
|
||||
> "type": "device_mac",
|
||||
> "default_value":"",
|
||||
> "options": [],
|
||||
> "localized": ["name"],
|
||||
> "name":[{
|
||||
> "language_code":"en_us",
|
||||
> "string" : "MAC address"
|
||||
> }]
|
||||
> }
|
||||
>```
|
||||
>
|
||||
>3. That's it. The app takes care of the rest. It loops thru the objects discovered by the plugin, takes the results line-by-line, and inserts them into the database table specified in `"mapped_to_table"`. The columns are translated from the generic plugin columns to the target table columns via the `"mapped_to_column"` property in the column definitions.
|
||||
|
||||
> [!NOTE]
|
||||
> You can create a column mapping with a default value via the `mapped_to_column_data` property. This means that the value of the given column will always be this value. That also menas that the `"column": "NameDoesntMatter"` is not important as there is no database source column.
|
||||
|
||||
|
||||
>🔍 Example:
|
||||
>
|
||||
>```json
|
||||
>{
|
||||
> "column": "NameDoesntMatter",
|
||||
> "mapped_to_column": "cur_ScanMethod",
|
||||
> "mapped_to_column_data": {
|
||||
> "value": "DHCPLSS"
|
||||
> },
|
||||
> "css_classes": "col-sm-2",
|
||||
> "show": true,
|
||||
> "type": "device_mac",
|
||||
> "default_value":"",
|
||||
> "options": [],
|
||||
> "localized": ["name"],
|
||||
> "name":[{
|
||||
> "language_code":"en_us",
|
||||
> "string" : "MAC address"
|
||||
> }]
|
||||
> }
|
||||
>```
|
||||
|
||||
#### params
|
||||
|
||||
> [!IMPORTANT]
|
||||
> An esier way to access settings in scripts is the `get_setting_value` method.
|
||||
> ```python
|
||||
> from helper import get_setting_value
|
||||
>
|
||||
> ...
|
||||
> NTFY_TOPIC = get_setting_value('NTFY_TOPIC')
|
||||
> ...
|
||||
>
|
||||
> ```
|
||||
|
||||
The `params` array in the `config.json` is used to enable the user to change the parameters of the executed script. For example, the user wants to monitor a specific URL.
|
||||
|
||||
> 🔎 Example:
|
||||
> Passing user-defined settings to a command. Let's say, you want to have a script, that is called with a user-defined parameter called `urls`:
|
||||
>
|
||||
> ```bash
|
||||
> root@server# python3 /app/front/plugins/website_monitor/script.py urls=https://google.com,https://duck.com
|
||||
> ```
|
||||
|
||||
* You can allow the user to add URLs to a setting with the `function` property set to a custom name, such as `urls_to_check` (this is not a reserved name from the section "Supported settings `function` values" below).
|
||||
* You specify the parameter `urls` in the `params` section of the `config.json` the following way (`WEBMON_` is the plugin prefix automatically added to all the settings):
|
||||
```json
|
||||
{
|
||||
"params" : [
|
||||
{
|
||||
"name" : "urls",
|
||||
"type" : "setting",
|
||||
"value" : "WEBMON_urls_to_check"
|
||||
}]
|
||||
}
|
||||
```
|
||||
* Then you use this setting as an input parameter for your command in the `CMD` setting. Notice `urls={urls}` in the below json:
|
||||
|
||||
```json
|
||||
{
|
||||
"function": "CMD",
|
||||
"type": {"dataType":"string", "elements": [{"elementType" : "input", "elementOptions" : [] ,"transformers": []}]},
|
||||
"default_value":"python3 /app/front/plugins/website_monitor/script.py urls={urls}",
|
||||
"options": [],
|
||||
"localized": ["name", "description"],
|
||||
"name" : [{
|
||||
"language_code":"en_us",
|
||||
"string" : "Command"
|
||||
}],
|
||||
"description": [{
|
||||
"language_code":"en_us",
|
||||
"string" : "Command to run"
|
||||
}]
|
||||
}
|
||||
```
|
||||
|
||||
During script execution, the app will take the command `"python3 /app/front/plugins/website_monitor/script.py urls={urls}"`, take the `{urls}` wildcard and replace it with the value from the `WEBMON_urls_to_check` setting. This is because:
|
||||
|
||||
1. The app checks the `params` entries
|
||||
2. It finds `"name" : "urls"`
|
||||
3. Checks the type of the `urls` params and finds `"type" : "setting"`
|
||||
4. Gets the setting name from `"value" : "WEBMON_urls_to_check"`
|
||||
- IMPORTANT: in the `config.json` this setting is identified by `"function":"urls_to_check"`, not `"function":"WEBMON_urls_to_check"`
|
||||
- You can also use a global setting, or a setting from a different plugin
|
||||
5. The app gets the user defined value from the setting with the code name `WEBMON_urls_to_check`
|
||||
- let's say the setting with the code name `WEBMON_urls_to_check` contains 2 values entered by the user:
|
||||
- `WEBMON_urls_to_check=['https://google.com','https://duck.com']`
|
||||
6. The app takes the value from `WEBMON_urls_to_check` and replaces the `{urls}` wildcard in the setting where `"function":"CMD"`, so you go from:
|
||||
- `python3 /app/front/plugins/website_monitor/script.py urls={urls}`
|
||||
- to
|
||||
- `python3 /app/front/plugins/website_monitor/script.py urls=https://google.com,https://duck.com`
|
||||
|
||||
Below are some general additional notes, when defining `params`:
|
||||
|
||||
- `"name":"name_value"` - is used as a wildcard replacement in the `CMD` setting value by using curly brackets `{name_value}`. The wildcard is replaced by the result of the `"value" : "param_value"` and `"type":"type_value"` combo configuration below.
|
||||
- `"type":"<sql|setting>"` - is used to specify the type of the params, currently only 2 supported (`sql`,`setting`).
|
||||
- `"type":"sql"` - will execute the SQL query specified in the `value` property. The sql query needs to return only one column. The column is flattened and separated by commas (`,`), e.g: `SELECT dev_MAC from DEVICES` -> `Internet,74:ac:74:ac:74:ac,44:44:74:ac:74:ac`. This is then used to replace the wildcards in the `CMD` setting.
|
||||
- `"type":"setting"` - The setting code name. A combination of the value from `unique_prefix` + `_` + `function` value, or otherwise the code name you can find in the Settings page under the Setting display name, e.g. `PIHOLE_RUN`.
|
||||
- `"value": "param_value"` - Needs to contain a setting code name or SQL query without wildcards.
|
||||
- `"timeoutMultiplier" : true` - used to indicate if the value should multiply the max timeout for the whole script run by the number of values in the given parameter.
|
||||
- `"base64": true` - use base64 encoding to pass the value to the script (e.g. if there are spaces)
|
||||
|
||||
|
||||
> 🔎Example:
|
||||
>
|
||||
> ```json
|
||||
> {
|
||||
> "params" : [{
|
||||
> "name" : "ips",
|
||||
> "type" : "sql",
|
||||
> "value" : "SELECT dev_LastIP from DEVICES",
|
||||
> "timeoutMultiplier" : true
|
||||
> },
|
||||
> {
|
||||
> "name" : "macs",
|
||||
> "type" : "sql",
|
||||
> "value" : "SELECT dev_MAC from DEVICES"
|
||||
> },
|
||||
> {
|
||||
> "name" : "timeout",
|
||||
> "type" : "setting",
|
||||
> "value" : "NMAP_RUN_TIMEOUT"
|
||||
> },
|
||||
> {
|
||||
> "name" : "args",
|
||||
> "type" : "setting",
|
||||
> "value" : "NMAP_ARGS",
|
||||
> "base64" : true
|
||||
> }]
|
||||
> }
|
||||
> ```
|
||||
|
||||
|
||||
#### ⚙ Setting object structure
|
||||
|
||||
> [!NOTE]
|
||||
> The settings flow and when Plugin specific settings are applied is described under the [Settings system](/docs/SETTINGS_SYSTEM.md).
|
||||
|
||||
Required attributes are:
|
||||
|
||||
| Property | Description |
|
||||
| -------- | ----------- |
|
||||
| `"function"` | Specifies the function the setting drives or a simple unique code name. See Supported settings function values for options. |
|
||||
| `"type"` | Specifies the form control used for the setting displayed in the Settings page and what values are accepted. Supported options include: |
|
||||
| | - `{"dataType":"string", "elements": [{"elementType" : "input", "elementOptions" : [{"type":"password"}] ,"transformers": ["sha256"]}]}` |
|
||||
| `"localized"` | A list of properties on the current JSON level that need to be localized. |
|
||||
| `"name"` | Displayed on the Settings page. An array of localized strings. See Localized strings below. |
|
||||
| `"description"` | Displayed on the Settings page. An array of localized strings. See Localized strings below. |
|
||||
| (optional) `"events"` | Specifies whether to generate an execution button next to the input field of the setting. Supported values: |
|
||||
| | - `"test"` - For notification plugins testing |
|
||||
| | - `"run"` - Regular plugins testing |
|
||||
| (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. |
|
||||
|
||||
|
||||
##### Supported settings `function` values
|
||||
|
||||
You can have any `"function": "my_custom_name"` custom name, however, the ones listed below have a specific functionality attached to them. If you use a custom name, then the setting is mostly used as an input parameter for the `params` section.
|
||||
|
||||
| Setting | Description |
|
||||
| ------- | ----------- |
|
||||
| `RUN` | (required) Specifies when the service is executed. |
|
||||
| | Supported Options: |
|
||||
| | - "disabled" - do not run |
|
||||
| | - "once" - run on app start or on settings saved |
|
||||
| | - "schedule" - if included, then a `RUN_SCHD` setting needs to be specified to determine the schedule |
|
||||
| | - "always_after_scan" - run always after a scan is finished |
|
||||
| | - "before_name_updates" - run before device names are updated (for name discovery plugins) |
|
||||
| | - "on_new_device" - run when a new device is detected |
|
||||
| | - "before_config_save" - run before the config is marked as saved. Useful if your plugin needs to modify the `app.conf` file. |
|
||||
| `RUN_SCHD` | (required if you include "schedule" in the above `RUN` function) Cron-like scheduling is used if the `RUN` setting is set to `schedule`. |
|
||||
| `CMD` | (required) Specifies the command that should be executed. |
|
||||
| `API_SQL` | (not implemented) Generates a `table_` + `code_name` + `.json` file as per [API docs](https://github.com/jokob-sk/NetAlertX/blob/main/docs/API.md). |
|
||||
| `RUN_TIMEOUT` | (optional) Specifies the maximum execution time of the script. If not specified, a default value of 10 seconds is used to prevent hanging. |
|
||||
| `WATCH` | (optional) Specifies which database columns are watched for changes for this particular plugin. If not specified, no notifications are sent. |
|
||||
| `REPORT_ON` | (optional) Specifies when to send a notification. Supported options are: |
|
||||
| | - `new` means a new unique (unique combination of PrimaryId and SecondaryId) object was discovered. |
|
||||
| | - `watched-changed` - means that selected `Watched_ValueN` columns changed |
|
||||
| | - `watched-not-changed` - reports even on events where selected `Watched_ValueN` did not change |
|
||||
| | - `missing-in-last-scan` - if the object is missing compared to previous scans |
|
||||
|
||||
|
||||
> 🔎 Example:
|
||||
>
|
||||
> ```json
|
||||
> {
|
||||
> "function": "RUN",
|
||||
> "type": {"dataType":"string", "elements": [{"elementType" : "select", "elementOptions" : [] ,"transformers": []}]},
|
||||
> "default_value":"disabled",
|
||||
> "options": ["disabled", "once", "schedule", "always_after_scan", "on_new_device"],
|
||||
> "localized": ["name", "description"],
|
||||
> "name" :[{
|
||||
> "language_code":"en_us",
|
||||
> "string" : "When to run"
|
||||
> }],
|
||||
> "description": [{
|
||||
> "language_code":"en_us",
|
||||
> "string" : "Enable a regular scan of your services. If you select <code>schedule</code> the scheduling settings from below are applied. If you select <code>once</code> the scan is run only once on start of the application (container) for the time specified in <a href=\"#WEBMON_RUN_TIMEOUT\"><code>WEBMON_RUN_TIMEOUT</code> setting</a>."
|
||||
> }]
|
||||
> }
|
||||
> ```
|
||||
|
||||
##### 🌍Localized strings
|
||||
|
||||
- `"language_code":"<en_us|es_es|de_de>"` - code name of the language string. Only these three are currently supported. At least the `"language_code":"en_us"` variant has to be defined.
|
||||
- `"string"` - The string to be displayed in the given language.
|
||||
|
||||
> 🔎 Example:
|
||||
>
|
||||
> ```json
|
||||
>
|
||||
> {
|
||||
> "language_code":"en_us",
|
||||
> "string" : "When to run"
|
||||
> }
|
||||
>
|
||||
> ```
|
||||
|
||||
##### UI settings in database_column_definitions
|
||||
|
||||
The UI will adjust how columns are displayed in the UI based on the resolvers definition of the `database_column_definitions` object. These are the supported form controls and related functionality:
|
||||
|
||||
- Only columns with `"show": true` and also with at least an English translation will be shown in the UI.
|
||||
|
||||
| Supported Types | Description |
|
||||
| -------------- | ----------- |
|
||||
| `label` | Displays a column only. |
|
||||
| `textarea_readonly` | Generates a read only text area and cleans up the text to display it somewhat formatted with new lines preserved. |
|
||||
| See below for information on `threshold`, `replace`. | |
|
||||
| | |
|
||||
| `options` Property | Used in conjunction with types like `threshold`, `replace`, `regex`. |
|
||||
| `options_params` Property | Used in conjunction with a `"options": "[{value}]"` template and `text.select`/`list.select`. Can specify SQL query (needs to return 2 columns `SELECT dev_Name as name, dev_Mac as id`) or Setting (not tested) to populate the dropdown. Check example below or have a look at the `NEWDEV` plugin `config.json` file. |
|
||||
| `threshold` | The `options` array contains objects ordered from the lowest `maximum` to the highest. The corresponding `hexColor` is used for the value background color if it's less than the specified `maximum` but more than the previous one in the `options` array. |
|
||||
| `replace` | The `options` array contains objects with an `equals` property, which is compared to the "value." If the values are the same, the string in `replacement` is displayed in the UI instead of the actual "value". |
|
||||
| `regex` | Applies a regex to the value. The `options` array contains objects with an `type` (must be set to `regex`) and `param` (must contain the regex itself) property. |
|
||||
| | |
|
||||
| Type Definitions | |
|
||||
| `device_mac` | The value is considered to be a MAC address, and a link pointing to the device with the given MAC address is generated. |
|
||||
| `device_ip` | The value is considered to be an IP address. A link pointing to the device with the given IP is generated. The IP is checked against the last detected IP address and translated into a MAC address, which is then used for the link itself. |
|
||||
| `device_name_mac` | The value is considered to be a MAC address, and a link pointing to the device with the given IP is generated. The link label is resolved as the target device name. |
|
||||
| `url` | The value is considered to be a URL, so a link is generated. |
|
||||
| `textbox_save` | Generates an editable and saveable text box that saves values in the database. Primarily intended for the `UserData` database column in the `Plugins_Objects` table. |
|
||||
| `url_http_https` | Generates two links with the `https` and `http` prefix as lock icons. |
|
||||
| `eval` | Evaluates as JavaScript. Use the variable `value` to use the given column value as input (e.g. `'<b>${value}<b>'` (replace ' with ` in your code) ) |
|
||||
|
||||
|
||||
> [!NOTE]
|
||||
> Supports chaining. You can chain multiple resolvers with `.`. For example `regex.url_http_https`. This will apply the `regex` resolver and then the `url_http_https` resolver.
|
||||
|
||||
|
||||
```json
|
||||
"function": "dev_DeviceType",
|
||||
"type": {"dataType":"string", "elements": [{"elementType" : "select", "elementOptions" : [] ,"transformers": []}]},
|
||||
"maxLength": 30,
|
||||
"default_value": "",
|
||||
"options": ["{value}"],
|
||||
"options_params" : [
|
||||
{
|
||||
"name" : "value",
|
||||
"type" : "sql",
|
||||
"value" : "SELECT '' as id, '' as name UNION SELECT dev_DeviceType as id, dev_DeviceType as name FROM (SELECT dev_DeviceType FROM Devices UNION SELECT 'Smartphone' UNION SELECT 'Tablet' UNION SELECT 'Laptop' UNION SELECT 'PC' UNION SELECT 'Printer' UNION SELECT 'Server' UNION SELECT 'NAS' UNION SELECT 'Domotic' UNION SELECT 'Game Console' UNION SELECT 'SmartTV' UNION SELECT 'Clock' UNION SELECT 'House Appliance' UNION SELECT 'Phone' UNION SELECT 'AP' UNION SELECT 'Gateway' UNION SELECT 'Firewall' UNION SELECT 'Switch' UNION SELECT 'WLAN' UNION SELECT 'Router' UNION SELECT 'Other') AS all_devices ORDER BY id;"
|
||||
},
|
||||
{
|
||||
"name" : "uilang",
|
||||
"type" : "setting",
|
||||
"value" : "UI_LANG"
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
|
||||
```json
|
||||
{
|
||||
"column": "Watched_Value1",
|
||||
"css_classes": "col-sm-2",
|
||||
"show": true,
|
||||
"type": "threshold",
|
||||
"default_value":"",
|
||||
"options": [
|
||||
{
|
||||
"maximum": 199,
|
||||
"hexColor": "#792D86"
|
||||
},
|
||||
{
|
||||
"maximum": 299,
|
||||
"hexColor": "#5B862D"
|
||||
},
|
||||
{
|
||||
"maximum": 399,
|
||||
"hexColor": "#7D862D"
|
||||
},
|
||||
{
|
||||
"maximum": 499,
|
||||
"hexColor": "#BF6440"
|
||||
},
|
||||
{
|
||||
"maximum": 599,
|
||||
"hexColor": "#D33115"
|
||||
}
|
||||
],
|
||||
"localized": ["name"],
|
||||
"name":[{
|
||||
"language_code":"en_us",
|
||||
"string" : "Status code"
|
||||
}]
|
||||
},
|
||||
{
|
||||
"column": "Status",
|
||||
"show": true,
|
||||
"type": "replace",
|
||||
"default_value":"",
|
||||
"options": [
|
||||
{
|
||||
"equals": "watched-not-changed",
|
||||
"replacement": "<i class='fa-solid fa-square-check'></i>"
|
||||
},
|
||||
{
|
||||
"equals": "watched-changed",
|
||||
"replacement": "<i class='fa-solid fa-triangle-exclamation'></i>"
|
||||
},
|
||||
{
|
||||
"equals": "new",
|
||||
"replacement": "<i class='fa-solid fa-circle-plus'></i>"
|
||||
}
|
||||
],
|
||||
"localized": ["name"],
|
||||
"name":[{
|
||||
"language_code":"en_us",
|
||||
"string" : "Status"
|
||||
}]
|
||||
},
|
||||
{
|
||||
"column": "Watched_Value3",
|
||||
"css_classes": "col-sm-1",
|
||||
"show": true,
|
||||
"type": "regex.url_http_https",
|
||||
"default_value":"",
|
||||
"options": [
|
||||
{
|
||||
"type": "regex",
|
||||
"param": "([\\d.:]+)"
|
||||
}
|
||||
],
|
||||
"localized": ["name"],
|
||||
"name":[{
|
||||
"language_code":"en_us",
|
||||
"string" : "HTTP/s links"
|
||||
},
|
||||
{
|
||||
"language_code":"es_es",
|
||||
"string" : "N/A"
|
||||
}]
|
||||
}
|
||||
```
|
||||
|
||||
[screen1]: https://raw.githubusercontent.com/jokob-sk/NetAlertX/main/docs/img/plugins.png "Screen 1"
|
||||
[screen2]: https://raw.githubusercontent.com/jokob-sk/NetAlertX/main/docs/img/plugins_settings.png "Screen 2"
|
||||
[screen3]: https://raw.githubusercontent.com/jokob-sk/NetAlertX/main/docs/img/plugins_json_settings.png "Screen 3"
|
||||
[screen4]: https://raw.githubusercontent.com/jokob-sk/NetAlertX/main/docs/img/plugins_json_ui.png "Screen 4"
|
||||
[screen5]: https://raw.githubusercontent.com/jokob-sk/NetAlertX/main/docs/img/plugins_device_details.png "Screen 5"
|
||||
@@ -30,9 +30,11 @@ There is also an in-app Help / FAQ section that should be answering frequently a
|
||||
- [Subnets and VLANs configuration for arp-scan](/docs/SUBNETS.md)
|
||||
- [SMTP server config](/docs/SMTP.md)
|
||||
- [Custom Icon configuration and support](/docs/ICONS.md)
|
||||
- [Notifications](/docs/NOTIFICATIONS.md)
|
||||
- [Better name resolution with Reverse DNS](/docs/REVERSE_DNS.md)
|
||||
- [Network treemap configuration](/docs/NETWORK_TREE.md)
|
||||
- [Backups](/docs/BACKUPS.md)
|
||||
- [Plugins overview](/front/plugins/README.md)
|
||||
|
||||
#### 🐛 Debugging help & tips
|
||||
|
||||
@@ -40,6 +42,7 @@ There is also an in-app Help / FAQ section that should be answering frequently a
|
||||
- [Debugging UI not showing](/docs/WEB_UI_PORT_DEBUG.md)
|
||||
- [Invalid JSON errors debug help](/docs/DEBUG_INVALID_JSON.md)
|
||||
- [Troubleshooting Plugins](/docs/DEBUG_PLUGINS.md)
|
||||
- [File Permissions](/docs/FILE_PERMISSIONS.md)
|
||||
|
||||
#### 🔝 Popular/Suggested
|
||||
|
||||
@@ -63,10 +66,11 @@ There is also an in-app Help / FAQ section that should be answering frequently a
|
||||
|
||||
#### 👩💻For Developers👨💻
|
||||
|
||||
- [Setting up your DEV environment](/docs/DEV_ENV_SETUP.md)
|
||||
- [Server APP code structure](/server/README.md)
|
||||
- [Database structure](/docs/DATABASE.md)
|
||||
- [API endpoints details](/docs/API.md)
|
||||
- [Plugin system details and how to develop your own](/front/plugins/README.md)
|
||||
- [Plugin development guide](/docs/PLUGINS_DEV.md)
|
||||
- [Settings system](/docs/SETTINGS_SYSTEM.md)
|
||||
- [New Version notifications](/docs/VERSIONS.md)
|
||||
- [Frontend development tips](/docs/FRONTEND_DEVELOPMENT.md)
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
# Subnets configuration for arp-scan
|
||||
# Subnets configuration
|
||||
|
||||
You need to specify the network interface and the network mask. You can also configure multiple subnets and specify VLANS (see exceptions below).
|
||||
|
||||
> [!TIP]
|
||||
> You may need to increase the time between scans `ARPSCAN_RUN_SCHD` and the timeout `ARPSCAN_RUN_TIMEOUT` settings when adding more subnets. If the timeout setting is exceeded, the scan is cancelled to prevent application hanging from rogue plugins. Check [debugging plugins](/docs/DEBUG_PLUGINS.md) for more tips.
|
||||
> You may need to increase the time between scans `ARPSCAN_RUN_SCHD` and the timeout `ARPSCAN_RUN_TIMEOUT` (and similar setting on related plugins) when adding more subnets. If the timeout setting is exceeded, the scan is cancelled to prevent application hanging from rogue plugins. Check [debugging plugins](/docs/DEBUG_PLUGINS.md) for more tips.
|
||||
|
||||
## Examples
|
||||
|
||||
> [!NOTE]
|
||||
> Please use the UI to configure settings as that ensures that the config file is in the correct format. Edit `app.conf` directly only when really necessary.
|
||||
> 
|
||||
> 
|
||||
|
||||
* Examples for one and two subnets (❗ Note the `['...', '...']` format):
|
||||
* 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']`
|
||||
* Examples for one and two subnets (❗ Note the `['...','...']` format):
|
||||
* 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']`
|
||||
|
||||
|
||||
## Explanation
|
||||
@@ -27,7 +27,7 @@ The arp-scan time itself depends on the number of IP addresses to check.
|
||||
> The number of IPs to check depends on the [network mask](https://www.calculator.net/ip-subnet-calculator.html) you set on the `SCAN_SUBNETS` setting.
|
||||
> For example, a `/24` mask results in 256 IPs to check, whereas a `/16` mask checks around 65,536. Every IP takes a couple of seconds. This means that with an incorrect configuration, the arp-scan will take hours to complete instead of seconds.
|
||||
|
||||
Specify the network filter (which **significantly** speeds up the scan process). For example, the filter `192.168.1.0/24` covers IP ranges 192.168.1.0 to 192.168.1.255.
|
||||
Specify the network filter (which **significantly** speeds up the scan process). For example, the filter `192.168.1.0/24` covers IP ranges `192.168.1.0` to `192.168.1.255`.
|
||||
|
||||
### Network interface (adapter)
|
||||
|
||||
|
||||
@@ -1,12 +1,51 @@
|
||||
# Debugging inaccessible UI
|
||||
|
||||
When opening an issue please :
|
||||
## 1. Port conflicts
|
||||
|
||||
When opening an issue please:
|
||||
|
||||
1. Include a screenshot of what you see when accessing `HTTP://<your rpi IP>/20211` (or your custom port)
|
||||
1. [Follow steps 1, 2, 3, 4 on this page](https://github.com/jokob-sk/NetAlertX/blob/main/docs/DEBUG_TIPS.md)
|
||||
1. Execute the following in the container to see the processes and their ports and submit a screenshot of the result:
|
||||
1. `sudo apt-get install lsof`
|
||||
1. `sudo apk add lsof`
|
||||
1. `sudo lsof -i`
|
||||
1. Try running the `nginx` command in the container
|
||||
1. if you get `nginx: [emerg] bind() to 0.0.0.0:20211 failed (98: Address in use)` try using a different port number
|
||||
|
||||
|
||||

|
||||

|
||||
|
||||
## 2. JavaScript issues
|
||||
|
||||
Check for browser console (F12 browser dev console) errors + check different browsers.
|
||||
|
||||
## 3. Clear the app cache and cached JavaScript files
|
||||
|
||||
Refresh the browser cache (usually shoft + refresh), try a private window, or different browsers. Please also refresh the app cache by clicking the 🔃 (reload) button in the header of the application.
|
||||
|
||||
## 4. Disable proxy
|
||||
|
||||
If you have any reverse proxy or similar, try disabling it.
|
||||
|
||||
## 5. Disable your firewall
|
||||
|
||||
If you are using a firewall, try to temporarily disabling it.
|
||||
|
||||
## 6. Post your docker start details
|
||||
|
||||
If you haven't, post your docker compose/run command.
|
||||
|
||||
## 7. Check for errors in your PHP/NGINX error logs
|
||||
|
||||
In the container execute:
|
||||
|
||||
`cat /var/log/nginx/error.log`
|
||||
|
||||
`cat /app/front/log/app.php_errors.log`
|
||||
|
||||
## 8. Make sure permissions are correct
|
||||
|
||||
> [!TIP]
|
||||
> You can try to start the container without mapping the `/app/config` and `/app/db` dirs and if the UI shows up then the issue is most likely related to your file system permissions or file ownership.
|
||||
|
||||
Please read the [Permissions troubleshooting guide](/docs/FILE_PERMISSIONS.md) and provide a screesnhot of the permissions and ownership in the `/app/db` and `app/config` directories.
|
||||
|
Before Width: | Height: | Size: 172 KiB |
|
Before Width: | Height: | Size: 198 KiB |
|
Before Width: | Height: | Size: 43 KiB |
|
Before Width: | Height: | Size: 32 KiB |
BIN
docs/img/Follow_Releases_and_Star.gif
Executable file
|
After Width: | Height: | Size: 119 KiB |
BIN
docs/img/GENERAL/github_social_image.jpg
Executable file
|
After Width: | Height: | Size: 292 KiB |
BIN
docs/img/NOTIFICATIONS/Device-notification-settings.png
Executable file
|
After Width: | Height: | Size: 110 KiB |
BIN
docs/img/NOTIFICATIONS/Global-notification-settings.png
Executable file
|
After Width: | Height: | Size: 70 KiB |
BIN
docs/img/NOTIFICATIONS/NEWDEV_ignores.png
Executable file
|
After Width: | Height: | Size: 58 KiB |
BIN
docs/img/NOTIFICATIONS/Plugin-notification-settings.png
Executable file
|
After Width: | Height: | Size: 122 KiB |
BIN
docs/img/NOTIFICATIONS/Schedules_out-of-sync.png
Executable file
|
After Width: | Height: | Size: 45 KiB |
BIN
docs/img/SUBNETS/subnets-setting-location.png
Executable file
|
After Width: | Height: | Size: 57 KiB |
BIN
docs/img/YouTube_thumbnail.png
Executable file
|
After Width: | Height: | Size: 183 KiB |
|
Before Width: | Height: | Size: 132 KiB After Width: | Height: | Size: 170 KiB |
|
Before Width: | Height: | Size: 157 KiB After Width: | Height: | Size: 173 KiB |
|
Before Width: | Height: | Size: 187 KiB After Width: | Height: | Size: 197 KiB |
|
Before Width: | Height: | Size: 186 KiB After Width: | Height: | Size: 189 KiB |
|
Before Width: | Height: | Size: 211 KiB After Width: | Height: | Size: 210 KiB |
|
Before Width: | Height: | Size: 174 KiB After Width: | Height: | Size: 240 KiB |
|
Before Width: | Height: | Size: 109 KiB After Width: | Height: | Size: 129 KiB |
|
Before Width: | Height: | Size: 123 KiB After Width: | Height: | Size: 146 KiB |
BIN
docs/img/multi_edit.png
Executable file
|
After Width: | Height: | Size: 148 KiB |
|
Before Width: | Height: | Size: 141 KiB After Width: | Height: | Size: 216 KiB |
BIN
docs/img/network_setup.gif
Executable file
|
After Width: | Height: | Size: 1.1 MiB |
BIN
docs/img/notification_center.png
Executable file
|
After Width: | Height: | Size: 112 KiB |
|
Before Width: | Height: | Size: 218 KiB After Width: | Height: | Size: 168 KiB |
|
Before Width: | Height: | Size: 202 KiB After Width: | Height: | Size: 201 KiB |
|
Before Width: | Height: | Size: 116 KiB |
|
Before Width: | Height: | Size: 92 KiB After Width: | Height: | Size: 134 KiB |
|
Before Width: | Height: | Size: 146 KiB After Width: | Height: | Size: 180 KiB |
BIN
docs/img/report_sample.png
Executable file
|
After Width: | Height: | Size: 202 KiB |
BIN
docs/img/sent_reports_text.png
Executable file
|
After Width: | Height: | Size: 100 KiB |
|
Before Width: | Height: | Size: 174 KiB After Width: | Height: | Size: 167 KiB |
BIN
docs/img/sync_hub.png
Executable file
|
After Width: | Height: | Size: 84 KiB |
@@ -25,13 +25,33 @@
|
||||
color:white;
|
||||
background-color: black;
|
||||
font-family: 'Courier New', monospace;
|
||||
font-size: .85em;
|
||||
|
||||
}
|
||||
.logs-row textarea
|
||||
{
|
||||
width: 100%;
|
||||
}
|
||||
#tab_Logging .actions{
|
||||
display:contents;
|
||||
position: relative;
|
||||
padding: 0.4em
|
||||
|
||||
}
|
||||
|
||||
#tab_Logging .actions .toggle{
|
||||
|
||||
margin: 0.5em;
|
||||
height: 3em;
|
||||
}
|
||||
|
||||
.logs-row .button{
|
||||
margin: 2px;
|
||||
}
|
||||
.logs-row
|
||||
{
|
||||
align-content: center;
|
||||
width: 100%;
|
||||
display: grid;
|
||||
margin: 5px;
|
||||
}
|
||||
.logs-size
|
||||
@@ -48,12 +68,11 @@
|
||||
}
|
||||
.logs-small
|
||||
{
|
||||
height: 100px;
|
||||
height: 150px;
|
||||
}
|
||||
.log-file
|
||||
{
|
||||
width: 145px;
|
||||
display: inline-grid;
|
||||
|
||||
font-size: 20px;
|
||||
}
|
||||
.log-purge
|
||||
@@ -108,6 +127,10 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
Main Sections
|
||||
----------------------------------------------------------------------------- */
|
||||
.content-header h1{
|
||||
font-size:24px;
|
||||
}
|
||||
|
||||
.content-header {
|
||||
padding-top: 5px;
|
||||
}
|
||||
@@ -144,6 +167,32 @@
|
||||
{
|
||||
background-color: rgba(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
.unread-notifications-bell
|
||||
{
|
||||
position: absolute;
|
||||
top: 3px;
|
||||
margin-left: 15px;
|
||||
display: none;
|
||||
|
||||
}
|
||||
|
||||
.navbar-custom-menu .bg-yellow
|
||||
{
|
||||
color: white !important;
|
||||
}
|
||||
|
||||
.header-status-locked-db
|
||||
{
|
||||
width: 25px;
|
||||
height: 25px;
|
||||
color: red;
|
||||
position: absolute;
|
||||
top: 12px;
|
||||
right: -17px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.navbar-nav > li > a
|
||||
{
|
||||
line-height: 30px;
|
||||
@@ -183,9 +232,15 @@
|
||||
.content-wrapper,
|
||||
.right-side,
|
||||
.main-footer {
|
||||
margin-left: 150px;
|
||||
margin-left: 150px;
|
||||
}
|
||||
|
||||
#settingsPage
|
||||
{
|
||||
display: grid;
|
||||
}
|
||||
|
||||
|
||||
@media (max-width: 767px) {
|
||||
.main-header .logo {
|
||||
width: 100%;
|
||||
@@ -200,6 +255,15 @@
|
||||
.main-footer {
|
||||
margin-left: 0px;
|
||||
}
|
||||
.fixed .content-wrapper, .fixed .right-side {
|
||||
padding-top: 50px;
|
||||
}
|
||||
.main-sidebar {
|
||||
padding-top: 50px;
|
||||
}
|
||||
.content-header {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.sidebar-open .content-wrapper,
|
||||
@@ -481,28 +545,41 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
Notification float banner
|
||||
----------------------------------------------------------------------------- */
|
||||
.pa_alert_notification {
|
||||
.notification_modal {
|
||||
|
||||
text-align: center;
|
||||
font-size: large;
|
||||
font-weight: bold;
|
||||
color: #258744;
|
||||
|
||||
background-color: #d4edda;
|
||||
border-color: #c3e6cb;
|
||||
border-radius: 5px;
|
||||
|
||||
max-width: 1000px;
|
||||
/* 80% wrapper 1250px */
|
||||
left: 0;
|
||||
right: 0;
|
||||
width: 80%;
|
||||
z-index: 9999;
|
||||
|
||||
position: fixed;
|
||||
top: 30px;
|
||||
margin: auto;
|
||||
transform: translate(0, 0);
|
||||
|
||||
top: 100px;
|
||||
display: none;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
.modal_green
|
||||
{
|
||||
color: #258744;
|
||||
background-color: #d4edda;
|
||||
border-color: #c3e6cb;
|
||||
}
|
||||
|
||||
.modal_red
|
||||
{
|
||||
color: rgb(245, 245, 245);
|
||||
background-color: #e2acaa;
|
||||
border-color: #d41001;
|
||||
}
|
||||
|
||||
.modal_grey
|
||||
{
|
||||
color: white;
|
||||
background-color: darkgrey;
|
||||
border-color: #000000;
|
||||
}
|
||||
|
||||
/* ticker setup */
|
||||
.ticker-li
|
||||
{
|
||||
@@ -672,6 +749,11 @@ height: 50px;
|
||||
/* report */
|
||||
/* --------------------------------------------------------- */
|
||||
|
||||
#notificationData
|
||||
{
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
#notificationData textarea{
|
||||
width: 100%;
|
||||
}
|
||||
@@ -718,6 +800,11 @@ height: 50px;
|
||||
}
|
||||
}
|
||||
|
||||
.settingswrap
|
||||
{
|
||||
margin-bottom: 100px;
|
||||
}
|
||||
|
||||
.settingswrap .metadata
|
||||
{
|
||||
display: none;
|
||||
@@ -728,11 +815,16 @@ height: 50px;
|
||||
float: right;
|
||||
}
|
||||
|
||||
.padding-bottom
|
||||
{
|
||||
padding-bottom: 100px;
|
||||
}
|
||||
|
||||
.settings-group
|
||||
{
|
||||
font-size: 20px;
|
||||
padding-top: 7px;
|
||||
padding-bottom: 9px;
|
||||
padding-bottom: 9px;
|
||||
}
|
||||
|
||||
.overview-section .small-box .icon
|
||||
@@ -743,12 +835,12 @@ height: 50px;
|
||||
|
||||
.overview-section
|
||||
{
|
||||
border: solid;
|
||||
/* border-top: solid;
|
||||
border-width: medium;
|
||||
border-width: medium;
|
||||
border-width: 1px;
|
||||
border-radius: 15px;
|
||||
margin-bottom: 3px;
|
||||
border-radius: 15px;
|
||||
margin-bottom: 3px; */
|
||||
|
||||
}
|
||||
|
||||
@@ -761,6 +853,7 @@ height: 50px;
|
||||
font-size: 20px;
|
||||
padding-top: 7px;
|
||||
padding-bottom: 9px;
|
||||
margin-left: -20px;
|
||||
|
||||
}
|
||||
|
||||
@@ -820,7 +913,28 @@ height: 50px;
|
||||
|
||||
#settingsPage .overview-setting-value{
|
||||
display:unset;
|
||||
|
||||
}
|
||||
|
||||
.overview-setting-value-wrap
|
||||
{
|
||||
padding-left: 1px;
|
||||
text-overflow: clip ;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
#settingsPage .small-box
|
||||
{
|
||||
padding-left: 10px;
|
||||
padding-right: 10px;
|
||||
}
|
||||
|
||||
#settingsPage .small-box .inner
|
||||
{
|
||||
padding-left: 0px;
|
||||
padding-right: 0px;
|
||||
}
|
||||
|
||||
#settingsPage .panel-title{
|
||||
/* display: inline-block; */
|
||||
/* width: 120px; */
|
||||
@@ -829,6 +943,17 @@ height: 50px;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.padding-5px
|
||||
{
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
.text-overflow-hidden
|
||||
{
|
||||
overflow: hidden;
|
||||
text-overflow: clip;
|
||||
}
|
||||
|
||||
|
||||
.settings_content {
|
||||
padding: 10px;
|
||||
@@ -875,6 +1000,84 @@ input[readonly] {
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.interactable-option:hover::before {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.interactable-option::before {
|
||||
content: '1x 📝 | 2x 🚮';
|
||||
position: sticky;
|
||||
right: 0px;
|
||||
top: 0;
|
||||
color: white;
|
||||
float: right;
|
||||
background-color: rgb(0, 0, 0);
|
||||
transition: opacity 0.5s;
|
||||
opacity: 0.1;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.interactable-option:hover {
|
||||
transition: background-color 2s;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.settingsImportedTimestamp
|
||||
{
|
||||
padding: 25px;
|
||||
}
|
||||
|
||||
.settingsSearchWrap
|
||||
{
|
||||
padding:10px;
|
||||
}
|
||||
|
||||
.settings-sticky-bottom-section {
|
||||
position: fixed;
|
||||
z-index: 999;
|
||||
background-color: #5B5B66;
|
||||
/* opacity: 0.8; */
|
||||
bottom: 30px;
|
||||
border-radius: 5px;
|
||||
margin:1px;
|
||||
border-width: 1px;
|
||||
border-style: solid;
|
||||
border-color: inherit;
|
||||
/* width: 87%; */
|
||||
}
|
||||
|
||||
.settings-sticky-bottom-section:hover {
|
||||
border-color: #258744;
|
||||
}
|
||||
|
||||
.settings-sticky-bottom-section .form-group
|
||||
{
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
|
||||
.clear-filter
|
||||
{
|
||||
opacity: 0.5;
|
||||
cursor: pointer;
|
||||
width: 14px;
|
||||
position: absolute;
|
||||
right: -6px;
|
||||
top: 6px;
|
||||
|
||||
}
|
||||
|
||||
.clear-filter:hover
|
||||
{
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.saveSettingsWrapper button
|
||||
{
|
||||
width:70%;
|
||||
margin-top:20px;
|
||||
margin-left:15%;
|
||||
margin-bottom:20px;
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------- */
|
||||
/* Devices page */
|
||||
@@ -937,16 +1140,35 @@ input[readonly] {
|
||||
height: 1.5em !important;
|
||||
}
|
||||
|
||||
#TileCards .tile .inner
|
||||
{
|
||||
color: white;
|
||||
}
|
||||
|
||||
#dropdownIcon li svg, #dropdownIcon li i{
|
||||
#dropdownIcon li svg, #dropdownIcon li i, .dropdownIcon li svg, .dropdownIcon li i, .icon-maxsize svg, .icon-maxsize i{
|
||||
height: 1.5em !important;
|
||||
}
|
||||
|
||||
|
||||
#tableDevices .badge i
|
||||
{
|
||||
height: 1em !important;
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------- */
|
||||
/* MODAL popups */
|
||||
/* ----------------------------------------------------------------- */
|
||||
|
||||
#modal-input-textarea
|
||||
{
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.modal-field-input
|
||||
{
|
||||
margin: 4%;
|
||||
width: 92%;
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------- */
|
||||
/* NETWORK page */
|
||||
@@ -1050,7 +1272,29 @@ input[readonly] {
|
||||
/* background-color:red; */
|
||||
}
|
||||
|
||||
.sort-btn {
|
||||
|
||||
right: 5px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.networkNodeTabHeaders
|
||||
{
|
||||
max-width: 200px;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
text-wrap: nowrap;
|
||||
}
|
||||
|
||||
@media (max-width: 767px) {
|
||||
|
||||
.networkNodeTabHeaders .node-name
|
||||
{
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------- */
|
||||
/* PLUGINS page */
|
||||
|
||||
@@ -17,9 +17,10 @@ html {
|
||||
background-color: #353c42;
|
||||
}
|
||||
|
||||
body {
|
||||
background-color: #353c42;
|
||||
color: #bec5cb;
|
||||
body, .bg-yellow, .callout.callout-warning, .alert-warning, .label-warning, .modal-warning .modal-body {
|
||||
|
||||
background-color: #353c42 !important;
|
||||
color: #bec5cb !important;
|
||||
}
|
||||
h4 {
|
||||
color: #44def1;
|
||||
|
||||
@@ -350,6 +350,18 @@
|
||||
<input class="form-control" id="txtNetworkPort" type="text" value="--">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group" >
|
||||
<label class="col-sm-3 control-label"><?= lang('DevDetail_MainInfo_SSID');?></label>
|
||||
<div class="col-sm-9">
|
||||
<input class="form-control" id="txtSSID" type="text" value="--">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group" >
|
||||
<label class="col-sm-3 control-label"><?= lang('DevDetail_MainInfo_Network_Site');?></label>
|
||||
<div class="col-sm-9">
|
||||
<input class="form-control" id="txtNetworkSite" type="text" value="--">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
@@ -500,14 +512,10 @@
|
||||
<!-- Buttons -->
|
||||
<div class="col-xs-12">
|
||||
<div class="pull-right">
|
||||
<button type="button" class="btn btn-default pa-btn pa-btn-delete" style="margin-left:0px;"
|
||||
id="btnDeleteEvents" onclick="askDeleteDeviceEvents()"> <?= lang('DevDetail_button_DeleteEvents');?> </button>
|
||||
<button type="button" class="btn btn-default pa-btn pa-btn-delete" style="margin-left:0px;"
|
||||
id="btnDelete" onclick="askDeleteDevice()"> <?= lang('DevDetail_button_Delete');?> </button>
|
||||
<button type="button" class="btn btn-default pa-btn" style="margin-left:6px;"
|
||||
id="btnRestore" onclick="getDeviceData(true)"> <?= lang('DevDetail_button_Reset');?> </button>
|
||||
<button type="button" disabled class="btn btn-primary pa-btn" style="margin-left:6px; "
|
||||
id="btnSave" onclick="setDeviceData()" > <?= lang('DevDetail_button_Save');?> </button>
|
||||
<button type="button" class="btn btn-default pa-btn pa-btn-delete" style="margin-left:0px;" id="btnDeleteEvents" onclick="askDeleteDeviceEvents()"> <?= lang('DevDetail_button_DeleteEvents');?> </button>
|
||||
<button type="button" class="btn btn-default pa-btn pa-btn-delete" style="margin-left:0px;" id="btnDelete" onclick="askDeleteDevice()"> <?= lang('DevDetail_button_Delete');?> </button>
|
||||
<!-- <button type="button" class="btn btn-default pa-btn" style="margin-left:6px;" id="btnRestore" onclick="getDeviceData(true)"> <?= lang('DevDetail_button_Reset');?> </button> -->
|
||||
<button type="button" disabled class="btn btn-primary pa-btn" style="margin-left:6px; " id="btnSave" onclick="setDeviceData()" > <?= lang('DevDetail_button_Save');?> </button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -627,8 +635,8 @@
|
||||
<script src="lib/AdminLTE/bower_components/fullcalendar/dist/fullcalendar.min.js"></script>
|
||||
<script src="lib/AdminLTE/bower_components/fullcalendar/dist/locale-all.js"></script>
|
||||
<!-- ----------------------------------------------------------------------- -->
|
||||
<script src="js/ui_components.js"></script>
|
||||
<script src="js/db_methods.js"></script>
|
||||
|
||||
|
||||
<!-- ----------------------------------------------------------------------- -->
|
||||
|
||||
<!-- Dark-Mode Patch -->
|
||||
@@ -816,20 +824,20 @@ function initializeCombos () {
|
||||
initializeCombo ( '#dropdownDevices', 'getDevices', 'txtFromDevice', false);
|
||||
|
||||
// Initiate dropdown
|
||||
// function initSettingDropdown(settingKey, // Identifier for the setting
|
||||
// valuesArray, // Array of values to be pre-selected in the dropdown
|
||||
// targetLocation, // ID of the HTML element where dropdown should be rendered (will be replaced)
|
||||
// callbackToGenerateEntries, // Callback function to generate entries based on options
|
||||
// targetField, // Target field or element where selected value should be applied or updated
|
||||
// nameTransformer) // callback to transform name
|
||||
// generateSetOptions(settingKey, // Identifier for the setting
|
||||
// valuesArray, // Array of values to be pre-selected in the dropdown
|
||||
// targetLocation, // ID of the HTML element where dropdown should be rendered (will be replaced)
|
||||
// callbackToGenerateEntries, // Callback function to generate entries based on options
|
||||
// targetField, // Target field or element where selected value should be applied or updated
|
||||
// nameTransformer) // callback to transform name
|
||||
|
||||
|
||||
initSettingDropdown("NEWDEV_dev_Icon", [], "dropdownIcon_tmp", genListWithInputSet, 'txtIcon', atob )
|
||||
initSettingDropdown("NEWDEV_dev_DeviceType", [], "dropdownDeviceType_tmp", genListWithInputSet, 'txtDeviceType' )
|
||||
initSettingDropdown("NEWDEV_dev_Owner", [], "dropdownOwner_tmp", genListWithInputSet, 'txtOwner' )
|
||||
initSettingDropdown("NEWDEV_dev_Group", [], "dropdownGroup_tmp", genListWithInputSet, 'txtGroup' )
|
||||
initSettingDropdown("NEWDEV_dev_Location", [], "dropdownLocation_tmp", genListWithInputSet, 'txtLocation' )
|
||||
initSettingDropdown("NEWDEV_dev_Network_Node_MAC_ADDR", [], "dropdownNetworkNodeMac_tmp", genListWithInputSet, 'txtNetworkNodeMac' )
|
||||
generateOptionsOrSetOptions("NEWDEV_dev_Icon", [], "dropdownIcon_tmp", genListWithInputSet, 'txtIcon', ["base64"])
|
||||
generateOptionsOrSetOptions("NEWDEV_dev_DeviceType", [], "dropdownDeviceType_tmp", genListWithInputSet, 'txtDeviceType' )
|
||||
generateOptionsOrSetOptions("NEWDEV_dev_Owner", [], "dropdownOwner_tmp", genListWithInputSet, 'txtOwner' )
|
||||
generateOptionsOrSetOptions("NEWDEV_dev_Group", [], "dropdownGroup_tmp", genListWithInputSet, 'txtGroup' )
|
||||
generateOptionsOrSetOptions("NEWDEV_dev_Location", [], "dropdownLocation_tmp", genListWithInputSet, 'txtLocation' )
|
||||
generateOptionsOrSetOptions("NEWDEV_dev_Network_Node_MAC_ADDR", [], "dropdownNetworkNodeMac_tmp", genListWithInputSet, 'txtNetworkNodeMac' )
|
||||
|
||||
// Initialize static combos
|
||||
initializeComboSkipRepeated ();
|
||||
@@ -1214,6 +1222,8 @@ function getDeviceData (readAllData=false) {
|
||||
$('#txtComments').val ('--');
|
||||
$('#txtNetworkNodeMac').val ('--');
|
||||
$('#txtNetworkPort').val ('--');
|
||||
$('#txtNetworkSite').val ('--');
|
||||
$('#txtSSID').val ('--');
|
||||
|
||||
$('#txtFirstConnection').val ('--');
|
||||
$('#txtLastConnection').val ('--');
|
||||
@@ -1297,7 +1307,7 @@ function getDeviceData (readAllData=false) {
|
||||
networkParentMac = deviceData['dev_Network_Node_MAC_ADDR']
|
||||
if(networkParentMac)
|
||||
{
|
||||
networkParentMacName = getDeviceDataByMacAddress(deviceData['dev_Network_Node_MAC_ADDR'], "dev_Name")
|
||||
networkParentMacName = getDeviceDataByMac(deviceData['dev_Network_Node_MAC_ADDR'], "dev_Name")
|
||||
} else
|
||||
{
|
||||
networkParentMacName = '--'
|
||||
@@ -1318,6 +1328,8 @@ function getDeviceData (readAllData=false) {
|
||||
$('#txtNetworkNodeMac').val ( networkParentMacName) ;
|
||||
$('#txtNetworkNodeMac').attr ('data-mynodemac', deviceData['dev_Network_Node_MAC_ADDR']);
|
||||
$('#txtNetworkPort').val (deviceData['dev_Network_Node_port']);
|
||||
$('#txtNetworkSite').val (deviceData['dev_NetworkSite']);
|
||||
$('#txtSSID').val (deviceData['dev_SSID']);
|
||||
// disabling network node configuration if root Internet node
|
||||
toggleNetworkConfiguration(mac == 'Internet')
|
||||
|
||||
@@ -1441,19 +1453,23 @@ function setDeviceData (direction='', refreshCallback='') {
|
||||
return;
|
||||
}
|
||||
|
||||
showSpinner()
|
||||
|
||||
// update data to server
|
||||
$.get('php/server/devices.php?action=setDeviceData&mac='+ mac
|
||||
+ '&name=' + $('#txtName').val()
|
||||
+ '&owner=' + $('#txtOwner').val()
|
||||
+ '&name=' + encodeURIComponent($('#txtName').val())
|
||||
+ '&owner=' + encodeURIComponent($('#txtOwner').val())
|
||||
+ '&type=' + $('#txtDeviceType').val()
|
||||
+ '&vendor=' + $('#txtVendor').val()
|
||||
+ '&vendor=' + encodeURIComponent($('#txtVendor').val())
|
||||
+ '&icon=' + encodeURIComponent($('#txtIcon').val())
|
||||
+ '&favorite=' + ($('#chkFavorite')[0].checked * 1)
|
||||
+ '&group=' + $('#txtGroup').val()
|
||||
+ '&location=' + $('#txtLocation').val()
|
||||
+ '&comments=' + $('#txtComments').val()
|
||||
+ '&group=' + encodeURIComponent($('#txtGroup').val())
|
||||
+ '&location=' + encodeURIComponent($('#txtLocation').val())
|
||||
+ '&comments=' + encodeURIComponent($('#txtComments').val())
|
||||
+ '&networknode=' + $('#txtNetworkNodeMac').attr('data-mynodemac')
|
||||
+ '&networknodeport=' + $('#txtNetworkPort').val()
|
||||
+ '&ssid=' + $('#txtSSID').val()
|
||||
+ '&networksite=' + $('#txtNetworkSite').val()
|
||||
+ '&staticIP=' + ($('#chkStaticIP')[0].checked * 1)
|
||||
+ '&scancycle=' + ($('#txtScanCycle').val() == "yes" ? "1" : "0")
|
||||
+ '&alertevents=' + ($('#chkAlertEvents')[0].checked * 1)
|
||||
@@ -1463,32 +1479,37 @@ function setDeviceData (direction='', refreshCallback='') {
|
||||
+ '&archived=' + ($('#chkArchived')[0].checked * 1)
|
||||
, function(msg) {
|
||||
|
||||
showMessage (msg);
|
||||
showMessage (msg);
|
||||
|
||||
|
||||
// clear session storage
|
||||
setCache("#dropdownOwner","");
|
||||
setCache("#dropdownDeviceType","");
|
||||
setCache("#dropdownGroup","");
|
||||
setCache("#dropdownLocation","");
|
||||
setCache("#dropdownNetworkNodeMac","");
|
||||
// clear session storage
|
||||
setCache("#dropdownOwner","");
|
||||
setCache("#dropdownDeviceType","");
|
||||
setCache("#dropdownGroup","");
|
||||
setCache("#dropdownLocation","");
|
||||
setCache("#dropdownNetworkNodeMac","");
|
||||
|
||||
// Remove navigation prompt "Are you sure you want to leave..."
|
||||
window.onbeforeunload = null;
|
||||
somethingChanged = false;
|
||||
// Remove navigation prompt "Are you sure you want to leave..."
|
||||
window.onbeforeunload = null;
|
||||
somethingChanged = false;
|
||||
|
||||
// refresh API
|
||||
updateApi()
|
||||
// refresh API
|
||||
updateApi()
|
||||
|
||||
// Callback fuction
|
||||
if (typeof refreshCallback == 'function') {
|
||||
refreshCallback(direction);
|
||||
}
|
||||
hideSpinner()
|
||||
|
||||
// Callback fuction
|
||||
if (typeof refreshCallback == 'function') {
|
||||
refreshCallback(direction);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
function askSkipNotifications () {
|
||||
// Check MAC
|
||||
@@ -1735,7 +1756,7 @@ function setTextValue (textElement, textValue) {
|
||||
if(textElement == "txtNetworkNodeMac")
|
||||
{
|
||||
$('#'+textElement).attr ('data-mynodemac', textValue);
|
||||
$('#'+textElement).val (getDeviceDataByMacAddress(textValue, "dev_Name"));
|
||||
$('#'+textElement).val (getDeviceDataByMac(textValue, "dev_Name"));
|
||||
} else
|
||||
{
|
||||
$('#'+textElement).attr ('data-myvalue', textValue);
|
||||
|
||||
@@ -77,16 +77,16 @@
|
||||
<?= lang("DevDetail_Nmap_Scans_desc") ?>
|
||||
</div>
|
||||
|
||||
<button type="button" id="piamanualnmap_fast" class="btn btn-primary pa-btn" style="margin-bottom: 20px; margin-left: 10px; margin-right: 10px;" onclick="manualnmapscan(getDeviceDataByMacAddress(getMac(), 'dev_LastIP'), 'fast')">
|
||||
<button type="button" id="piamanualnmap_fast" class="btn btn-primary pa-btn" style="margin-bottom: 20px; margin-left: 10px; margin-right: 10px;" onclick="manualnmapscan(getDeviceDataByMac(getMac(), 'dev_LastIP'), 'fast')">
|
||||
<?= lang("DevDetail_Loading") ?>
|
||||
</button>
|
||||
<button type="button" id="piamanualnmap_normal" class="btn btn-primary pa-btn" style="margin-bottom: 20px; margin-left: 10px; margin-right: 10px;" onclick="manualnmapscan(getDeviceDataByMacAddress(getMac(), 'dev_LastIP'), 'normal')">
|
||||
<button type="button" id="piamanualnmap_normal" class="btn btn-primary pa-btn" style="margin-bottom: 20px; margin-left: 10px; margin-right: 10px;" onclick="manualnmapscan(getDeviceDataByMac(getMac(), 'dev_LastIP'), 'normal')">
|
||||
<?= lang("DevDetail_Loading") ?>
|
||||
</button>
|
||||
<button type="button" id="piamanualnmap_detail" class="btn btn-primary pa-btn" style="margin-bottom: 20px; margin-left: 10px; margin-right: 10px;" onclick="manualnmapscan(getDeviceDataByMacAddress(getMac(), 'dev_LastIP'), 'detail')">
|
||||
<button type="button" id="piamanualnmap_detail" class="btn btn-primary pa-btn" style="margin-bottom: 20px; margin-left: 10px; margin-right: 10px;" onclick="manualnmapscan(getDeviceDataByMac(getMac(), 'dev_LastIP'), 'detail')">
|
||||
<?= lang("DevDetail_Loading") ?>
|
||||
</button>
|
||||
<button type="button" id="piamanualnmap_skipdiscovery" class="btn btn-primary pa-btn" style="margin-bottom: 20px; margin-left: 10px; margin-right: 10px;" onclick="manualnmapscan(getDeviceDataByMacAddress(getMac(), 'dev_LastIP'), 'skipdiscovery')">
|
||||
<button type="button" id="piamanualnmap_skipdiscovery" class="btn btn-primary pa-btn" style="margin-bottom: 20px; margin-left: 10px; margin-right: 10px;" onclick="manualnmapscan(getDeviceDataByMac(getMac(), 'dev_LastIP'), 'skipdiscovery')">
|
||||
<?= lang("DevDetail_Loading") ?>
|
||||
</button>
|
||||
|
||||
@@ -155,7 +155,7 @@
|
||||
$( "#tracerouteoutput" ).empty();
|
||||
$.ajax({
|
||||
method: "GET",
|
||||
url: "./php/server/traceroute.php?action=get&ip=" + getDeviceDataByMacAddress(getMac(), 'dev_LastIP') + "",
|
||||
url: "./php/server/traceroute.php?action=get&ip=" + getDeviceDataByMac(getMac(), 'dev_LastIP') + "",
|
||||
beforeSend: function() { $('#tracerouteoutput').addClass("ajax_scripts_loading"); },
|
||||
complete: function() { $('#tracerouteoutput').removeClass("ajax_scripts_loading"); },
|
||||
success: function(data, textStatus) {
|
||||
@@ -170,7 +170,7 @@
|
||||
$( "#nslookupoutput" ).empty();
|
||||
$.ajax({
|
||||
method: "GET",
|
||||
url: "./php/server/nslookup.php?action=get&ip=" + getDeviceDataByMacAddress(getMac(), 'dev_LastIP') + "",
|
||||
url: "./php/server/nslookup.php?action=get&ip=" + getDeviceDataByMac(getMac(), 'dev_LastIP') + "",
|
||||
beforeSend: function() { $('#nslookupoutput').addClass("ajax_scripts_loading"); },
|
||||
complete: function() { $('#nslookupoutput').removeClass("ajax_scripts_loading"); },
|
||||
success: function(data, textStatus) {
|
||||
|
||||
@@ -25,7 +25,6 @@
|
||||
checkPermissions([$dbPath, $confPath]);
|
||||
?>
|
||||
|
||||
<script src="js/ui_components.js"></script>
|
||||
|
||||
<!-- Page ------------------------------------------------------------------ -->
|
||||
<div class="content-wrapper">
|
||||
@@ -43,77 +42,7 @@
|
||||
|
||||
<!-- Tile toggle cards ------------------------------------------------------- -->
|
||||
<div class="row" id="TileCards">
|
||||
<!-- top small box 1 ------------------------------------------------------- -->
|
||||
<div class="col-lg-2 col-sm-4 col-xs-6">
|
||||
<a href="#" onclick="javascript: initializeDatatable('my');">
|
||||
<div class="small-box bg-aqua">
|
||||
<div class="inner"><h3 id="devicesMy"> -- </h3>
|
||||
<p class="infobox_label"><?= lang('Device_Shortcut_AllDevices');?></p>
|
||||
</div>
|
||||
<div class="icon"><i class="fa fa-laptop text-aqua-40"></i></div>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- top small box 2 ------------------------------------------------------- -->
|
||||
<div class="col-lg-2 col-sm-4 col-xs-6">
|
||||
<a href="#" onclick="javascript: initializeDatatable('connected');">
|
||||
<div class="small-box bg-green">
|
||||
<div class="inner"><h3 id="devicesConnected"> -- </h3>
|
||||
<p class="infobox_label"><?= lang('Device_Shortcut_Connected');?></p>
|
||||
</div>
|
||||
<div class="icon"><i class="fa fa-plug text-green-40"></i></div>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- top small box 3 ------------------------------------------------------- -->
|
||||
<div class="col-lg-2 col-sm-4 col-xs-6">
|
||||
<a href="#" onclick="javascript: initializeDatatable('favorites');">
|
||||
<div class="small-box bg-yellow">
|
||||
<div class="inner"><h3 id="devicesFavorites"> -- </h3>
|
||||
<p class="infobox_label"><?= lang('Device_Shortcut_Favorites');?></p>
|
||||
</div>
|
||||
<div class="icon"><i class="fa fa-star text-yellow-40"></i></div>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- top small box 4 ------------------------------------------------------- -->
|
||||
<div class="col-lg-2 col-sm-4 col-xs-6">
|
||||
<a href="#" onclick="javascript: initializeDatatable('new');">
|
||||
<div class="small-box bg-yellow">
|
||||
<div class="inner"><h3 id="devicesNew"> -- </h3>
|
||||
<p class="infobox_label"><?= lang('Device_Shortcut_NewDevices');?></p>
|
||||
</div>
|
||||
<div class="icon"><i class="ion ion-plus-round text-yellow-40"></i></div>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- top small box 5 ------------------------------------------------------- -->
|
||||
<div class="col-lg-2 col-sm-4 col-xs-6">
|
||||
<a href="#" onclick="javascript: initializeDatatable('down');">
|
||||
<div class="small-box bg-red">
|
||||
<div class="inner"><h3 id="devicesDown"> -- </h3>
|
||||
<p class="infobox_label"><?= lang('Device_Shortcut_DownAlerts');?></p>
|
||||
</div>
|
||||
<div class="icon"><i class="fa fa-warning text-red-40"></i></div>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- top small box 6 ------------------------------------------------------- -->
|
||||
<div class="col-lg-2 col-sm-4 col-xs-6">
|
||||
<a href="#" onclick="javascript: initializeDatatable('archived');">
|
||||
<div class="small-box bg-gray top_small_box_gray_text">
|
||||
<div class="inner"><h3 id="devicesArchived"> -- </h3>
|
||||
<p class="infobox_label"><?= lang('Device_Shortcut_Archived');?></p>
|
||||
</div>
|
||||
<div class="icon"><i class="fa fa-eye-slash text-gray-40"></i></div>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
<!-- Placeholder ------------------------------------------------------- -->
|
||||
|
||||
</div>
|
||||
|
||||
@@ -127,7 +56,7 @@
|
||||
</div>
|
||||
<div class="box-body">
|
||||
<div class="chart">
|
||||
<script src="lib/AdminLTE/bower_components/chart.js/Chart.js"></script>
|
||||
<script src="lib/AdminLTE/bower_components/chart.js/Chart.js?v=<?php include 'php/templates/version.php'; ?>"></script>
|
||||
<canvas id="OnlineChart" style="width:100%; height: 150px; margin-bottom: 15px;"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
@@ -214,9 +143,7 @@
|
||||
var tableOrder = [[3,'desc'], [0,'asc']];
|
||||
|
||||
var tableColumnHide = [];
|
||||
var columnsStr = '[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18]';
|
||||
var tableColumnOrder = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18];
|
||||
var tableColumnVisible = tableColumnOrder;
|
||||
|
||||
//initialize the table headers in the correct order
|
||||
var headersDefaultOrder = [
|
||||
getString('Device_TableHead_Name'),
|
||||
@@ -237,9 +164,18 @@
|
||||
getString('Device_TableHead_Connected_Devices'),
|
||||
getString('Device_TableHead_Location'),
|
||||
getString('Device_TableHead_Vendor'),
|
||||
getString('Device_TableHead_Port')
|
||||
getString('Device_TableHead_Port'),
|
||||
getString('Device_TableHead_GUID'),
|
||||
getString('Device_TableHead_SyncHubNodeName'),
|
||||
getString('Device_TableHead_NetworkSite'),
|
||||
getString('Device_TableHead_SSID')
|
||||
];
|
||||
|
||||
// generate default order lists of given length
|
||||
var columnsStr = JSON.stringify(Array.from({ length: headersDefaultOrder.length }, (_, i) => i));
|
||||
var tableColumnOrder = Array.from({ length: headersDefaultOrder.length }, (_, i) => i);
|
||||
var tableColumnVisible = tableColumnOrder;
|
||||
|
||||
// Read parameters & Initialize components
|
||||
showSpinner();
|
||||
main();
|
||||
@@ -251,7 +187,11 @@ function main () {
|
||||
handleLoadingDialog()
|
||||
|
||||
// Hide UI elements as per settings
|
||||
hideUIelements("UI_DEV_SECTIONS")
|
||||
// setTimeout(() => {
|
||||
hideUIelements("UI_DEV_SECTIONS")
|
||||
|
||||
// }, 10);
|
||||
|
||||
|
||||
// get from cookie if available (need to use decodeURI as saved as part of URI in PHP)
|
||||
cookieColumnsVisibleStr = decodeURI(getCookie("Front_Devices_Columns_Visible")).replaceAll('%2C',',')
|
||||
@@ -311,9 +251,7 @@ function main () {
|
||||
|
||||
// Initialize components with parameters
|
||||
|
||||
initializeDatatable(getUrlAnchor('my'));
|
||||
|
||||
|
||||
initializeDatatable(getUrlAnchor('my_devices'));
|
||||
|
||||
// check if data outdated and show spinner if so
|
||||
handleLoadingDialog()
|
||||
@@ -348,21 +286,47 @@ function getDevicesTotals(devicesData) {
|
||||
if (getCache("getDevicesTotals") !== "") {
|
||||
resultJSON = getCache("getDevicesTotals");
|
||||
} else {
|
||||
// combined query
|
||||
const devices = filterDataByStatus(devicesData, 'my');
|
||||
const connectedDevices = filterDataByStatus(devicesData, 'connected');
|
||||
const favoritesDevices = filterDataByStatus(devicesData, 'favorites');
|
||||
const newDevices = filterDataByStatus(devicesData, 'new');
|
||||
const downDevices = filterDataByStatus(devicesData, 'down');
|
||||
const archivedDevices = filterDataByStatus(devicesData, 'archived');
|
||||
|
||||
// Define filter conditions and corresponding objects
|
||||
const filters = [
|
||||
{ status: 'my_devices', color: 'bg-aqua', label: getString('Device_Shortcut_AllDevices'), icon: 'fa-laptop' },
|
||||
{ status: 'all', color: 'bg-aqua', label: getString('Gen_All_Devices'), icon: 'fa-laptop' },
|
||||
{ status: 'connected', color: 'bg-green', label: getString('Device_Shortcut_Connected'), icon: 'fa-plug' },
|
||||
{ status: 'favorites', color: 'bg-yellow', label: getString('Device_Shortcut_Favorites'), icon: 'fa-star' },
|
||||
{ status: 'new', color: 'bg-yellow', label: getString('Device_Shortcut_NewDevices'), icon: 'fa-plus' },
|
||||
{ status: 'down', color: 'bg-red', label: getString('Device_Shortcut_DownOnly'), icon: 'fa-warning' },
|
||||
{ status: 'archived', color: 'bg-gray', label: getString('Device_Shortcut_Archived'), icon: 'fa-eye-slash' },
|
||||
{ status: 'offline', color: 'bg-gray', label: getString('Gen_Offline'), icon: 'fa-xmark' }
|
||||
];
|
||||
|
||||
$('#devicesMy').html (devices.length);
|
||||
$('#devicesConnected').html (connectedDevices.length);
|
||||
$('#devicesFavorites').html (favoritesDevices.length);
|
||||
$('#devicesNew').html (newDevices.length);
|
||||
$('#devicesDown').html (downDevices.length);
|
||||
$('#devicesArchived').html (archivedDevices.length);
|
||||
// Initialize an empty array to store the final objects
|
||||
let dataArray = [];
|
||||
|
||||
// Loop through each filter condition
|
||||
filters.forEach(filter => {
|
||||
// Calculate count dynamically based on filter condition
|
||||
let count = filterDataByStatus(devicesData, filter.status).length;
|
||||
|
||||
// Check any condition to skip adding the object to dataArray
|
||||
if (
|
||||
(['', 'False'].includes(getSetting('UI_hide_empty')) || (getSetting('UI_hide_empty') == "True" && count > 0)) &&
|
||||
(getSetting('UI_shown_cards') == "" || getSetting('UI_shown_cards').includes(filter.status))
|
||||
) {
|
||||
dataArray.push({
|
||||
onclickEvent: `initializeDatatable('${filter.status}')`,
|
||||
color: filter.color,
|
||||
title: count,
|
||||
label: filter.label,
|
||||
icon: filter.icon
|
||||
});
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
// render info boxes/tile cards
|
||||
renderInfoboxes(
|
||||
dataArray
|
||||
)
|
||||
|
||||
// save to cache
|
||||
setCache("getDevicesTotals", resultJSON);
|
||||
@@ -371,12 +335,29 @@ function getDevicesTotals(devicesData) {
|
||||
// console.log(resultJSON);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Render the info boxes/tiles on top
|
||||
function renderInfoboxes(customData) {
|
||||
$.ajax({
|
||||
url: 'php/components/tile_cards.php', // PHP script URL
|
||||
type: 'POST', // Use POST method to send data
|
||||
dataType: 'html', // Expect HTML response
|
||||
data: { items: JSON.stringify(customData) }, // Send customData as JSON
|
||||
success: function(response) {
|
||||
$('#TileCards').html(response); // Replace container content with fetched HTML
|
||||
},
|
||||
error: function(xhr, status, error) {
|
||||
console.error('Error fetching infoboxes:', error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Define a function to filter data based on deviceStatus
|
||||
function filterDataByStatus(data, status) {
|
||||
return data.filter(function(item) {
|
||||
switch (status) {
|
||||
case 'my':
|
||||
case 'my_devices':
|
||||
to_display = getSetting('UI_MY_DEVICES');
|
||||
|
||||
let result = true;
|
||||
@@ -393,16 +374,16 @@ function filterDataByStatus(data, status) {
|
||||
result = false;
|
||||
}
|
||||
|
||||
return result; // Include all items for 'my' status
|
||||
return result; // Include all items for 'my_devices' status
|
||||
case 'connected':
|
||||
return item.dev_PresentLastScan === 1;
|
||||
case 'favorites':
|
||||
return item.dev_Favorite === 1;
|
||||
case 'new':
|
||||
return item.dev_NewDevice === 1;
|
||||
case 'offline':
|
||||
return item.dev_PresentLastScan === 0;
|
||||
case 'down':
|
||||
return (item.dev_PresentLastScan === 0 && item.dev_AlertDeviceDown !== 0) || item.dev_PresentLastScan === 0;
|
||||
case 'down_only':
|
||||
return (item.dev_PresentLastScan === 0 && item.dev_AlertDeviceDown !== 0);
|
||||
case 'archived':
|
||||
return item.dev_Archived === 1;
|
||||
@@ -419,7 +400,7 @@ function getDeviceStatus(item)
|
||||
if(item.dev_NewDevice === 1)
|
||||
{
|
||||
return 'New';
|
||||
}
|
||||
}
|
||||
else if(item.dev_PresentLastScan === 1)
|
||||
{
|
||||
return 'On-line';
|
||||
@@ -445,7 +426,7 @@ function initializeDatatable (status) {
|
||||
|
||||
if(!status)
|
||||
{
|
||||
status = 'my'
|
||||
status = 'my_devices'
|
||||
}
|
||||
|
||||
// Save status selected
|
||||
@@ -453,12 +434,14 @@ function initializeDatatable (status) {
|
||||
|
||||
// Define color & title for the status selected
|
||||
switch (deviceStatus) {
|
||||
case 'my': tableTitle = getString('Device_Shortcut_AllDevices'); color = 'aqua'; break;
|
||||
case 'my_devices': tableTitle = getString('Device_Shortcut_AllDevices'); color = 'aqua'; break;
|
||||
case 'connected': tableTitle = getString('Device_Shortcut_Connected'); color = 'green'; break;
|
||||
case 'all': tableTitle = getString('Gen_All_Devices'); color = 'aqua'; break;
|
||||
case 'favorites': tableTitle = getString('Device_Shortcut_Favorites'); color = 'yellow'; break;
|
||||
case 'new': tableTitle = getString('Device_Shortcut_NewDevices'); color = 'yellow'; break;
|
||||
case 'down': tableTitle = getString('Device_Shortcut_DownAlerts'); color = 'red'; break;
|
||||
case 'down': tableTitle = getString('Device_Shortcut_DownOnly'); color = 'red'; break;
|
||||
case 'archived': tableTitle = getString('Device_Shortcut_Archived'); color = 'gray'; break;
|
||||
case 'offline': tableTitle = getString('Gen_Offline'); color = 'gray'; break;
|
||||
default: tableTitle = getString('Device_Shortcut_Devices'); color = 'gray'; break;
|
||||
}
|
||||
|
||||
@@ -508,7 +491,11 @@ function initializeDatatable (status) {
|
||||
getNumberOfChildren(item.dev_MAC, result.data) || 0,
|
||||
item.dev_Location || "",
|
||||
item.dev_Vendor || "",
|
||||
item.dev_Network_Node_port || 0
|
||||
item.dev_Network_Node_port || 0,
|
||||
item.dev_GUID || "",
|
||||
item.dev_SyncHubNodeName || "",
|
||||
item.dev_NetworkSite || "",
|
||||
item.dev_SSID || ""
|
||||
];
|
||||
|
||||
var newRow = [];
|
||||
@@ -623,7 +610,11 @@ function initializeDatatable (status) {
|
||||
// Dates
|
||||
{targets: [mapIndx(6), mapIndx(7)],
|
||||
'createdCell': function (td, cellData, rowData, row, col) {
|
||||
$(td).html (translateHTMLcodes (cellData));
|
||||
var result = cellData.toString(); // Convert to string
|
||||
if (result.includes("+")) { // Check if timezone offset is present
|
||||
result = result.split('+')[0]; // Remove timezone offset
|
||||
}
|
||||
$(td).html (translateHTMLcodes (result));
|
||||
} },
|
||||
|
||||
// Random MAC
|
||||
@@ -641,17 +632,27 @@ function initializeDatatable (status) {
|
||||
{targets: [mapIndx(10)],
|
||||
'createdCell': function (td, cellData, rowData, row, col) {
|
||||
|
||||
// console.log(cellData)
|
||||
switch (cellData) {
|
||||
case 'Down': color='red'; break;
|
||||
case 'New': color='yellow'; break;
|
||||
case 'On-line': color='green'; break;
|
||||
case 'Off-line': color='gray text-white'; break;
|
||||
case 'Archived': color='gray text-white'; break;
|
||||
default: color='aqua'; break;
|
||||
};
|
||||
devData = getDeviceDataByMac(rowData[mapIndx(11)])
|
||||
|
||||
if (devData.dev_PresentLastScan == 1)
|
||||
{
|
||||
css = "green text-white statusOnline"
|
||||
icon = '<i class="fa-solid fa-plug"></i>'
|
||||
} else if (devData.dev_PresentLastScan != 1 && devData.dev_AlertDeviceDown == 1)
|
||||
{
|
||||
css = "red text-white statusDown"
|
||||
icon = '<i class="fa-solid fa-triangle-exclamation"></i>'
|
||||
} else if(devData.dev_PresentLastScan != 1)
|
||||
{
|
||||
css = "gray text-white statusOffline"
|
||||
icon = '<i class="fa-solid fa-xmark"></i>'
|
||||
} else
|
||||
{
|
||||
css = "gray text-white statusUnknown"
|
||||
icon = '<i class="fa-solid fa-question"></i>'
|
||||
}
|
||||
|
||||
$(td).html ('<a href="deviceDetails.php?mac='+ rowData[mapIndx(11)] +'" class="badge bg-'+ color +'">'+ cellData.replace('-', '') +'</a>');
|
||||
$(td).html (`<a href="deviceDetails.php?mac=${rowData[mapIndx(11)]}" class="badge bg-${css}">${icon} ${cellData.replace('-', '')}</a>`);
|
||||
} },
|
||||
],
|
||||
|
||||
|
||||
@@ -145,6 +145,9 @@
|
||||
<th><?= lang('Events_TableHead_IP');?></th>
|
||||
<th><?= lang('Events_TableHead_IPOrder');?></th>
|
||||
<th><?= lang('Events_TableHead_AdditionalInfo');?></th>
|
||||
<th>N/A</th>
|
||||
<th>MAC</th>
|
||||
<th><?= lang('Events_TableHead_PendingAlert');?></th>
|
||||
</tr>
|
||||
</thead>
|
||||
</table>
|
||||
@@ -236,7 +239,7 @@ function initializeDatatable () {
|
||||
'pageLength' : tableRows,
|
||||
|
||||
'columnDefs' : [
|
||||
{visible: false, targets: [0,5,6,7,8,10] },
|
||||
{visible: false, targets: [0,5,6,7,8,10,11,12,13] },
|
||||
{className: 'text-center', targets: [] },
|
||||
{orderData: [8], targets: 7 },
|
||||
{orderData: [10], targets: 9 },
|
||||
@@ -251,6 +254,13 @@ function initializeDatatable () {
|
||||
{targets: [3,4,5,6,7],
|
||||
"createdCell": function (td, cellData, rowData, row, col) {
|
||||
$(td).html (translateHTMLcodes (cellData));
|
||||
} },
|
||||
|
||||
// PendingAlert
|
||||
{targets: [14],
|
||||
"createdCell": function (td, cellData, rowData, row, col) {
|
||||
// console.log(cellData);
|
||||
$(td).html (cellData);
|
||||
} }
|
||||
],
|
||||
|
||||
@@ -331,7 +341,7 @@ function getEvents (p_eventsType) {
|
||||
$('#tableEventsBox')[0].className = 'box box-' + color;
|
||||
$('#tableEventsTitle').html (tableTitle);
|
||||
|
||||
// Coluumns Visibility
|
||||
// Columns Visibility
|
||||
$('#tableEvents').DataTable().column(3).visible (!sesionCols);
|
||||
$('#tableEvents').DataTable().column(4).visible (!sesionCols);
|
||||
$('#tableEvents').DataTable().column(5).visible (sesionCols);
|
||||
|
||||
@@ -147,19 +147,6 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h4 class="panel-title">
|
||||
<a data-toggle="collapse" data-parent="#accordion_det" href="#collapse301">
|
||||
<?= lang('HelpFAQ_Cat_Detail_301_head_a');?> "<?= lang('DevDetail_EveandAl_ScanCycle');?>" <?= lang('HelpFAQ_Cat_Detail_301_head_b');?></a>
|
||||
</h4>
|
||||
</div>
|
||||
<div id="collapse301" class="panel-collapse collapse" style="font-size: 16px;">
|
||||
<div class="panel-body">
|
||||
<?= lang('HelpFAQ_Cat_Detail_301_text');?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h4 class="panel-title">
|
||||
|
||||
|
Before Width: | Height: | Size: 6.0 KiB After Width: | Height: | Size: 8.1 KiB |
BIN
front/img/NetAlertX_logo_notification.png
Executable file
|
After Width: | Height: | Size: 9.2 KiB |
BIN
front/img/NetAlertX_logo_notification_old.png
Executable file
|
After Width: | Height: | Size: 6.0 KiB |
BIN
front/img/NetAlertX_logo_old.png
Executable file
|
After Width: | Height: | Size: 5.6 KiB |
BIN
front/img/NetAlertX_logo_red.png
Executable file
|
After Width: | Height: | Size: 6.0 KiB |
BIN
front/img/NetAlertX_logo_white.png
Executable file
|
After Width: | Height: | Size: 5.6 KiB |
BIN
front/img/NetAlertX_logo_yellow.png
Executable file
|
After Width: | Height: | Size: 5.6 KiB |
365
front/img/svg/netalertx_red_1_backup.svg
Executable file
@@ -0,0 +1,365 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
width="200"
|
||||
height="200"
|
||||
viewBox="0 0 52.916667 52.916668"
|
||||
version="1.1"
|
||||
id="svg5"
|
||||
inkscape:version="1.1.2 (b8e25be833, 2022-02-05)"
|
||||
sodipodi:docname="netalertx_red_1_backup.svg"
|
||||
inkscape:export-filename="C:\Users\jokob\netalertx_red_1.png"
|
||||
inkscape:export-xdpi="96"
|
||||
inkscape:export-ydpi="96"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<sodipodi:namedview
|
||||
id="namedview7"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="true"
|
||||
inkscape:document-units="mm"
|
||||
showgrid="false"
|
||||
inkscape:zoom="3.6203867"
|
||||
inkscape:cx="154.67961"
|
||||
inkscape:cy="88.664562"
|
||||
inkscape:window-width="3440"
|
||||
inkscape:window-height="1377"
|
||||
inkscape:window-x="-8"
|
||||
inkscape:window-y="-8"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="layer6"
|
||||
units="px"
|
||||
width="50px" />
|
||||
<defs
|
||||
id="defs2">
|
||||
<inkscape:path-effect
|
||||
effect="powermask"
|
||||
id="path-effect51283"
|
||||
is_visible="true"
|
||||
lpeversion="1"
|
||||
uri="#mask-powermask-path-effect51283"
|
||||
invert="false"
|
||||
hide_mask="false"
|
||||
background="true"
|
||||
background_color="#ffffffff" />
|
||||
<inkscape:path-effect
|
||||
effect="powermask"
|
||||
id="path-effect51278"
|
||||
is_visible="true"
|
||||
lpeversion="1"
|
||||
uri="#mask-powermask-path-effect51278"
|
||||
invert="false"
|
||||
hide_mask="false"
|
||||
background="true"
|
||||
background_color="#ffffffff" />
|
||||
<inkscape:path-effect
|
||||
effect="powermask"
|
||||
id="path-effect51273"
|
||||
is_visible="true"
|
||||
lpeversion="1"
|
||||
uri="#mask-powermask-path-effect51273"
|
||||
invert="false"
|
||||
hide_mask="false"
|
||||
background="true"
|
||||
background_color="#ffffffff" />
|
||||
<inkscape:path-effect
|
||||
effect="powermask"
|
||||
id="path-effect48754"
|
||||
is_visible="true"
|
||||
lpeversion="1"
|
||||
uri="#mask-powermask-path-effect48754"
|
||||
invert="false"
|
||||
hide_mask="false"
|
||||
background="true"
|
||||
background_color="#ffffffff" />
|
||||
<clipPath
|
||||
clipPathUnits="userSpaceOnUse"
|
||||
id="clipPath48972">
|
||||
<path
|
||||
style="fill:#000000;stroke-width:0.280643"
|
||||
id="path48974"
|
||||
width="56.128242"
|
||||
height="56.128246"
|
||||
x="-18.924671"
|
||||
y="-56.198174"
|
||||
transform="rotate(45.438374)"
|
||||
mask="none"
|
||||
sodipodi:type="rect" />
|
||||
</clipPath>
|
||||
<mask
|
||||
maskUnits="userSpaceOnUse"
|
||||
id="mask49405">
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:60.8695px;line-height:1.25;font-family:Amiri;-inkscape-font-specification:Amiri;display:inline;stroke-width:1.52174"
|
||||
x="66.930733"
|
||||
y="78.642288"
|
||||
id="text49409"
|
||||
transform="scale(1.4861626,0.67287388)"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan49407"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:'Tw Cen MT';-inkscape-font-specification:'Tw Cen MT';fill:#ffffff;stroke-width:1.52174"
|
||||
x="66.930733"
|
||||
y="78.642288">A</tspan></text>
|
||||
</mask>
|
||||
<clipPath
|
||||
clipPathUnits="userSpaceOnUse"
|
||||
id="clipPath50306">
|
||||
<circle
|
||||
style="mix-blend-mode:normal;fill:#d40000;stroke-width:0.176318"
|
||||
id="circle50308"
|
||||
cy="26.458334"
|
||||
cx="26.458334"
|
||||
r="26.458334"
|
||||
clip-path="url(#clipPath48972)"
|
||||
transform="matrix(1.0038771,0,0.00391255,1.0073928,-0.04603368,-0.1228191)" />
|
||||
</clipPath>
|
||||
<clipPath
|
||||
clipPathUnits="userSpaceOnUse"
|
||||
id="clipPath48972-7">
|
||||
<path
|
||||
style="fill:#000000;stroke-width:0.280643"
|
||||
id="path48974-5"
|
||||
width="56.128242"
|
||||
height="56.128246"
|
||||
x="-18.924671"
|
||||
y="-56.198174"
|
||||
transform="rotate(45.438374)"
|
||||
mask="none"
|
||||
sodipodi:type="rect" />
|
||||
</clipPath>
|
||||
<clipPath
|
||||
clipPathUnits="userSpaceOnUse"
|
||||
id="clipPath50306-6">
|
||||
<circle
|
||||
style="mix-blend-mode:normal;fill:#d40000;stroke-width:0.176318"
|
||||
id="circle50308-5"
|
||||
cy="26.458334"
|
||||
cx="26.458334"
|
||||
r="26.458334"
|
||||
clip-path="url(#clipPath48972)"
|
||||
transform="matrix(1.0038771,0,0.00391255,1.0073928,-0.04603368,-0.1228191)" />
|
||||
</clipPath>
|
||||
<mask
|
||||
maskUnits="userSpaceOnUse"
|
||||
id="mask-powermask-path-effect51273">
|
||||
<path
|
||||
style="fill:#000000"
|
||||
id="path51263"
|
||||
sodipodi:type="arc"
|
||||
sodipodi:cx="66.211845"
|
||||
sodipodi:cy="37.490814"
|
||||
sodipodi:rx="3.9464016"
|
||||
sodipodi:ry="1.4616301"
|
||||
sodipodi:start="0"
|
||||
sodipodi:end="0.031086059"
|
||||
sodipodi:open="true"
|
||||
sodipodi:arc-type="arc"
|
||||
d="m 70.158247,37.490814 a 3.9464016,1.4616301 0 0 1 -0.0019,0.04543" />
|
||||
</mask>
|
||||
<mask
|
||||
maskUnits="userSpaceOnUse"
|
||||
id="mask-powermask-path-effect51278">
|
||||
<path
|
||||
style="fill:#000000"
|
||||
id="path51267"
|
||||
sodipodi:type="arc"
|
||||
sodipodi:cx="66.211845"
|
||||
sodipodi:cy="37.490814"
|
||||
sodipodi:rx="3.9464016"
|
||||
sodipodi:ry="1.4616301"
|
||||
sodipodi:start="0"
|
||||
sodipodi:end="0.031086059"
|
||||
sodipodi:open="true"
|
||||
sodipodi:arc-type="arc" />
|
||||
</mask>
|
||||
<mask
|
||||
maskUnits="userSpaceOnUse"
|
||||
id="mask-powermask-path-effect51283">
|
||||
<path
|
||||
style="fill:#000000"
|
||||
id="path51271"
|
||||
sodipodi:type="arc"
|
||||
sodipodi:cx="66.211845"
|
||||
sodipodi:cy="37.490814"
|
||||
sodipodi:rx="3.9464016"
|
||||
sodipodi:ry="1.4616301"
|
||||
sodipodi:start="0"
|
||||
sodipodi:end="0.031086059"
|
||||
sodipodi:open="true"
|
||||
sodipodi:arc-type="arc" />
|
||||
</mask>
|
||||
<filter
|
||||
id="mask-powermask-path-effect51273_inverse"
|
||||
inkscape:label="filtermask-powermask-path-effect51273"
|
||||
style="color-interpolation-filters:sRGB"
|
||||
height="100"
|
||||
width="100"
|
||||
x="-50"
|
||||
y="-50">
|
||||
<feColorMatrix
|
||||
id="mask-powermask-path-effect51273_primitive1"
|
||||
values="1"
|
||||
type="saturate"
|
||||
result="fbSourceGraphic" />
|
||||
<feColorMatrix
|
||||
id="mask-powermask-path-effect51273_primitive2"
|
||||
values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0 "
|
||||
in="fbSourceGraphic" />
|
||||
</filter>
|
||||
</defs>
|
||||
<g
|
||||
inkscape:groupmode="layer"
|
||||
id="layer3"
|
||||
inkscape:label="Red 1"
|
||||
style="display:inline">
|
||||
<circle
|
||||
style="fill:#ff2a2a;stroke-width:0.176318"
|
||||
id="path31-8"
|
||||
cy="26.458334"
|
||||
cx="26.458334"
|
||||
r="26.458334" />
|
||||
</g>
|
||||
<g
|
||||
inkscape:label="Black"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
style="display:inline">
|
||||
<ellipse
|
||||
style="fill:#000000;stroke-width:0.176146"
|
||||
id="path31"
|
||||
cy="26.51001"
|
||||
cx="26.458334"
|
||||
rx="26.458334"
|
||||
ry="26.406658" />
|
||||
<circle
|
||||
style="display:inline;fill:#ffffff;stroke-width:0.176318"
|
||||
id="path31-89"
|
||||
cy="26.458334"
|
||||
cx="126.45834"
|
||||
r="26.458334"
|
||||
mask="url(#mask49405)"
|
||||
transform="translate(-99.990036,0.02979629)" />
|
||||
</g>
|
||||
<g
|
||||
inkscape:groupmode="layer"
|
||||
id="layer2"
|
||||
inkscape:label="A - Layer 2"
|
||||
style="display:inline">
|
||||
<rect
|
||||
style="fill:#ffffff;stroke-width:0.328992"
|
||||
id="rect48998"
|
||||
width="26.0966"
|
||||
height="6.0620313"
|
||||
x="13.255443"
|
||||
y="41.262722" />
|
||||
</g>
|
||||
<g
|
||||
inkscape:groupmode="layer"
|
||||
id="g48055"
|
||||
inkscape:label="Red top"
|
||||
style="display:inline;mix-blend-mode:normal">
|
||||
<circle
|
||||
style="mix-blend-mode:normal;fill:#d40000;stroke-width:0.176318"
|
||||
id="circle48752"
|
||||
cy="26.458334"
|
||||
cx="26.458334"
|
||||
r="26.458334"
|
||||
clip-path="url(#clipPath48972)"
|
||||
transform="matrix(1.0038771,0,0.00391255,1.0073928,-0.04603368,-0.1228191)" />
|
||||
<ellipse
|
||||
style="display:inline;mix-blend-mode:normal;fill:#000000;stroke-width:0.43638"
|
||||
id="path50080"
|
||||
clip-path="url(#clipPath50306)"
|
||||
ry="13.739323"
|
||||
rx="16.735666"
|
||||
cy="22.874514"
|
||||
cx="26.36149" />
|
||||
<path
|
||||
style="fill:#000000"
|
||||
id="path51325"
|
||||
sodipodi:type="arc"
|
||||
sodipodi:cx="16.772207"
|
||||
sodipodi:cy="26.090099"
|
||||
sodipodi:rx="4.1291056"
|
||||
sodipodi:ry="7.6004772"
|
||||
sodipodi:start="0"
|
||||
sodipodi:end="0.031086059"
|
||||
sodipodi:arc-type="slice"
|
||||
d="m 20.901313,26.090099 a 4.1291056,7.6004772 0 0 1 -0.002,0.236231 l -4.127111,-0.236231 z" />
|
||||
<path
|
||||
style="fill:#d40000"
|
||||
id="path51717"
|
||||
sodipodi:type="arc"
|
||||
sodipodi:cx="26.441042"
|
||||
sodipodi:cy="-26.531424"
|
||||
sodipodi:rx="10.418671"
|
||||
sodipodi:ry="9.5820541"
|
||||
sodipodi:start="0.82219863"
|
||||
sodipodi:end="2.3054129"
|
||||
sodipodi:arc-type="slice"
|
||||
d="m 33.532115,-19.511189 a 10.418671,9.5820541 0 0 1 -14.074736,0.09049 l 6.983663,-7.110726 z"
|
||||
transform="matrix(1,0,0.0048047,-0.99998846,0,0)" />
|
||||
<path
|
||||
style="fill:#ffffff;stroke-width:0.276214"
|
||||
d="M 145.28835,50.354872 C 127.01317,34.62734 98.057144,30.012421 73.710372,38.947003 c -6.518003,2.391924 -14.288822,6.834002 -19.265958,11.01311 -1.198654,1.006465 -2.270358,1.829935 -2.381565,1.829935 -0.111206,0 -5.210052,-5.102002 -11.33077,-11.337781 L 29.603503,29.114489 30.822139,27.851613 c 0.670251,-0.69458 2.51592,-2.384634 4.101489,-3.755674 C 50.725112,10.43241 69.462577,2.3767456 90.736164,0.10085492 95.380582,-0.39601422 106.33043,-0.31105699 111.03786,0.25837091 133.04363,2.9202648 151.46536,11.26468 167.83762,25.986722 l 3.30701,2.97369 -2.29392,2.320103 c -1.26165,1.276057 -6.58213,6.517685 -11.82329,11.648065 l -9.52936,9.327957 z"
|
||||
id="path52311"
|
||||
transform="scale(0.26458333)" />
|
||||
<path
|
||||
style="fill:#ffffff;stroke-width:0.276214"
|
||||
d="M 86.40648,86.72667 73.376068,73.416681 74.667403,72.441306 c 4.72517,-3.569034 10.347316,-6.07197 16.730552,-7.448313 4.112758,-0.886786 12.327485,-0.880604 16.463455,0.01239 6.55399,1.415066 12.32233,4.049211 17.21441,7.861065 l 1.05592,0.822759 -1.33214,1.364507 c -0.73267,0.750478 -6.73901,6.678655 -13.34742,13.173726 l -12.015288,11.80922 z"
|
||||
id="path52350"
|
||||
transform="scale(0.26458333)"
|
||||
inkscape:export-filename="C:\Users\jokob\path52350.png"
|
||||
inkscape:export-xdpi="96"
|
||||
inkscape:export-ydpi="96" />
|
||||
</g>
|
||||
<g
|
||||
inkscape:groupmode="layer"
|
||||
id="layer6"
|
||||
inkscape:label="Circle">
|
||||
<path
|
||||
style="fill:#000000"
|
||||
id="path50026"
|
||||
sodipodi:type="arc"
|
||||
sodipodi:cx="71.071762"
|
||||
sodipodi:cy="34.677177"
|
||||
sodipodi:rx="1.7174155"
|
||||
sodipodi:ry="5.5907354"
|
||||
sodipodi:start="0"
|
||||
sodipodi:end="0.031086059"
|
||||
sodipodi:open="true"
|
||||
sodipodi:arc-type="arc"
|
||||
mask="url(#mask-powermask-path-effect51273)"
|
||||
d="m 72.789178,34.677177 a 1.7174155,5.5907354 0 0 1 -8.3e-4,0.173766"
|
||||
inkscape:path-effect="#path-effect51273" />
|
||||
<path
|
||||
style="fill:#ffffff;stroke-width:0.276214"
|
||||
d="m 151.08883,181.46994 -2.76213,-2.60427 -48.802077,-0.009 -48.802075,-0.009 -2.292573,2.48592 c -1.260915,1.36726 -2.431589,2.48592 -2.601499,2.48592 -0.869396,0 -9.118995,-6.36599 -13.713669,-10.58246 l -2.688104,-2.46684 34.973647,-35.11455 c 19.235503,-19.313 35.0611,-35.11454 35.167986,-35.11454 0.106889,0 16.093094,15.82967 35.524894,35.17705 l 35.33055,35.17705 -2.48592,2.35505 c -3.08951,2.92687 -7.41515,6.40509 -11.09719,8.92319 -1.54594,1.05725 -2.85105,1.91728 -2.90024,1.9112 -0.0492,-0.006 -1.33242,-1.183 -2.8516,-2.61535 z m -38.4631,-38.32188 -13.050732,-13.05073 -13.050727,13.05073 -13.050725,13.05072 h 26.101452 26.101452 z"
|
||||
id="path52389"
|
||||
transform="scale(0.26458333)"
|
||||
inkscape:export-filename="C:\Users\jokob\path52389.png"
|
||||
inkscape:export-xdpi="96"
|
||||
inkscape:export-ydpi="96" />
|
||||
<path
|
||||
style="fill:#d40000;stroke-width:0.276214"
|
||||
d="M 86.538548,86.634546 C 73.69451,73.514255 73.655832,73.471276 74.253912,72.984129 c 3.966911,-3.231117 10.195336,-6.133881 16.482252,-7.68157 2.339787,-0.576001 3.282272,-0.646986 8.700728,-0.655316 6.728828,-0.01035 8.365758,0.223077 13.326638,1.900338 4.59135,1.552325 7.8315,3.224336 11.49958,5.934101 l 1.61476,1.192897 -2.31005,2.336325 c -1.27053,1.284978 -7.22284,7.16236 -13.22736,13.060849 L 99.423152,99.796276 Z"
|
||||
id="path52465"
|
||||
transform="scale(0.26458333)" />
|
||||
<path
|
||||
style="fill:#d40000;stroke-width:0.276214"
|
||||
d="M 145.01213,49.92143 C 129.67534,37.157691 107.61173,31.765107 86.040533,35.508132 73.800823,37.631961 63.910278,42.259414 52.74134,51.087703 l -0.683951,0.540616 -1.950068,-1.921684 C 49.034784,48.649709 44.035189,43.574887 38.997108,38.429251 l -9.160146,-9.355699 2.40156,-2.290793 C 39.310252,20.037198 49.728886,13.01193 58.630455,8.9867218 78.42597,0.03536962 99.380149,-2.3494504 120.42912,1.9533417 138.21485,5.5890676 153.70529,13.558756 168.1679,26.51451 l 2.71597,2.432989 -2.99218,3.016385 c -1.6457,1.65901 -6.90751,6.843636 -11.69291,11.521393 l -8.70072,8.505009 z"
|
||||
id="path52504"
|
||||
transform="scale(0.26458333)"
|
||||
inkscape:export-filename="C:\Users\jokob\path52504.png"
|
||||
inkscape:export-xdpi="96"
|
||||
inkscape:export-ydpi="96" />
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 13 KiB |
@@ -78,7 +78,7 @@ if (isset ($_SESSION["login"]) == FALSE || $_SESSION["login"] != 1)
|
||||
<meta http-equiv="Pragma" content="no-cache" />
|
||||
<meta http-equiv="Expires" content="0" />
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<title>Pi-Alert | Log in</title>
|
||||
<title>Net Alert X | Log in</title>
|
||||
<!-- 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">
|
||||
<!-- Bootstrap 3.3.7 -->
|
||||
@@ -147,7 +147,6 @@ if ($ENABLED_DARKMODE === True) {
|
||||
<button type="button" class="close" data-dismiss="alert" aria-hidden="true"><3E></button>
|
||||
<h4><i class="icon fa <?php echo $login_icon;?>"></i><?php echo $login_headline;?></h4>
|
||||
<p><?php echo $login_info;?></p>
|
||||
<p><?= lang('Login_Psw_run');?><br><span style="border: solid 1px yellow; padding: 2px;"> /app/back/pialert-cli set_password <?= lang('Login_Psw_new');?></span><br><?= lang('Login_Psw_folder');?></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
var timerRefreshData = ''
|
||||
var modalCallbackFunction = '';
|
||||
|
||||
var emptyArr = ['undefined', "", undefined, null, 'null'];
|
||||
var UI_LANG = "English";
|
||||
var settingsJSON = {}
|
||||
@@ -124,8 +124,10 @@ function cacheSettings()
|
||||
settingsData.forEach((set) => {
|
||||
|
||||
resolvedOptions = createArray(set.Options)
|
||||
resolvedOptionsOld = resolvedOptions
|
||||
setPlugObj = {};
|
||||
options_params = [];
|
||||
resolved = ""
|
||||
|
||||
// proceed only if first option item contains something to resolve
|
||||
if( !set.Code_Name.includes("__metadata") &&
|
||||
@@ -144,12 +146,21 @@ function cacheSettings()
|
||||
if(options_params != [])
|
||||
{
|
||||
// handles only strings of length == 1
|
||||
resolvedOptions = `["${resolveParams(options_params, resolvedOptions[0])}"]`
|
||||
|
||||
resolved = resolveParams(options_params, resolvedOptions[0])
|
||||
|
||||
if(resolved.includes('"')) // check if list of strings
|
||||
{
|
||||
resolvedOptions = `[${resolved}]`
|
||||
} else // one value only
|
||||
{
|
||||
resolvedOptions = `["${resolved}"]`
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setCache(`pia_set_${set.Code_Name}`, set.Value)
|
||||
setCache(`pia_set_${set.Code_Name}`, set.Value)
|
||||
setCache(`pia_set_opt_${set.Code_Name}`, resolvedOptions)
|
||||
});
|
||||
}).then(() => handleSuccess('cacheSettings', resolve())).catch(() => handleFailure('cacheSettings', reject("cacheSettings already completed"))); // handle AJAX synchronization
|
||||
@@ -170,6 +181,7 @@ function getSettingOptions (key) {
|
||||
if (result == "")
|
||||
{
|
||||
console.log(`Setting options with key "${key}" not found`)
|
||||
result = []
|
||||
}
|
||||
|
||||
return result;
|
||||
@@ -201,7 +213,7 @@ function cacheStrings()
|
||||
if(!getCache('completedCalls').includes('cacheStrings'))
|
||||
{
|
||||
// handle core strings and translations
|
||||
var allLanguages = ["en_us", "es_es", "de_de", "fr_fr", "it_it", "ru_ru", "nb_no"]; // needs to be same as in lang.php
|
||||
var allLanguages = ["en_us", "es_es", "de_de", "fr_fr", "it_it", "ru_ru", "nb_no", "pl_pl", "zh_cn"]; // needs to be same as in lang.php
|
||||
|
||||
allLanguages.forEach(function (language_code) {
|
||||
$.get(`php/templates/language/${language_code}.json?nocache=${Date.now()}`, function (res) {
|
||||
@@ -230,7 +242,7 @@ function cacheStrings()
|
||||
// Get translated language string
|
||||
function getString (key) {
|
||||
|
||||
// handle initial laod to make sure everything is set-up and cached
|
||||
// handle initial load to make sure everything is set-up and cached
|
||||
handleFirstLoad(getString)
|
||||
|
||||
UI_LANG = getSetting("UI_LANG");
|
||||
@@ -254,12 +266,24 @@ function getString (key) {
|
||||
case 'Norwegian':
|
||||
lang_code = 'nb_no';
|
||||
break;
|
||||
case 'Italian':
|
||||
case 'Polish (pl_pl)':
|
||||
lang_code = 'pl_pl';
|
||||
break;
|
||||
case 'Portuguese (pt_br)':
|
||||
lang_code = 'pt_br';
|
||||
break;
|
||||
case 'Turkish (tr_tr)':
|
||||
lang_code = 'tr_tr';
|
||||
break;
|
||||
case 'Italian (it_it)':
|
||||
lang_code = 'it_it';
|
||||
break;
|
||||
case 'Russian':
|
||||
lang_code = 'ru_ru';
|
||||
break;
|
||||
case 'Chinese (zh_cn)':
|
||||
lang_code = 'zh_cn';
|
||||
break;
|
||||
}
|
||||
result = getCache(`pia_lang_${key}_${lang_code}`, true);
|
||||
|
||||
@@ -272,162 +296,7 @@ function getString (key) {
|
||||
return result;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Modal dialog handling
|
||||
// -----------------------------------------------------------------------------
|
||||
function showModalOK (title, message, callbackFunction) {
|
||||
showModalOk (title, message, callbackFunction)
|
||||
}
|
||||
function showModalOk (title, message, callbackFunction) {
|
||||
// set captions
|
||||
$('#modal-ok-title').html (title);
|
||||
$('#modal-ok-message').html (message);
|
||||
|
||||
if(callbackFunction!= null)
|
||||
{
|
||||
$("#modal-ok-OK").click(function()
|
||||
{
|
||||
callbackFunction()
|
||||
});
|
||||
}
|
||||
|
||||
// Show modal
|
||||
$('#modal-ok').modal('show');
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
function showModalDefault (title, message, btnCancel, btnOK, callbackFunction) {
|
||||
// set captions
|
||||
$('#modal-default-title').html (title);
|
||||
$('#modal-default-message').html (message);
|
||||
$('#modal-default-cancel').html (btnCancel);
|
||||
$('#modal-default-OK').html (btnOK);
|
||||
modalCallbackFunction = callbackFunction;
|
||||
|
||||
// Show modal
|
||||
$('#modal-default').modal('show');
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
function showModalDefaultStrParam (title, message, btnCancel, btnOK, callbackFunction, param='') {
|
||||
// set captions
|
||||
$('#modal-str-title').html (title);
|
||||
$('#modal-str-message').html (message);
|
||||
$('#modal-str-cancel').html (btnCancel);
|
||||
$('#modal-str-OK').html (btnOK);
|
||||
$("#modal-str-OK").off("click"); //remove existing handlers
|
||||
$('#modal-str-OK').on('click', function (){
|
||||
$('#modal-str').modal('hide');
|
||||
callbackFunction(param)
|
||||
})
|
||||
|
||||
// Show modal
|
||||
$('#modal-str').modal('show');
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
function showModalWarning (title, message, btnCancel=getString('Gen_Cancel'), btnOK=getString('Gen_Okay'), callbackFunction=null) {
|
||||
// set captions
|
||||
$('#modal-warning-title').html (title);
|
||||
$('#modal-warning-message').html (message);
|
||||
$('#modal-warning-cancel').html (btnCancel);
|
||||
$('#modal-warning-OK').html (btnOK);
|
||||
|
||||
if ( callbackFunction != null)
|
||||
{
|
||||
modalCallbackFunction = callbackFunction;
|
||||
}
|
||||
|
||||
// Show modal
|
||||
$('#modal-warning').modal('show');
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
function showModalInput (title, message, btnCancel=getString('Gen_Cancel'), btnOK=getString('Gen_Okay'), callbackFunction=null) {
|
||||
// set captions
|
||||
$('#modal-input-title').html (title);
|
||||
$('#modal-input-message').html (message);
|
||||
$('#modal-input-cancel').html (btnCancel);
|
||||
$('#modal-input-OK').html (btnOK);
|
||||
|
||||
if ( callbackFunction != null)
|
||||
{
|
||||
modalCallbackFunction = callbackFunction;
|
||||
}
|
||||
|
||||
// Show modal
|
||||
$('#modal-input').modal('show');
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
function modalDefaultOK () {
|
||||
// Hide modal
|
||||
$('#modal-default').modal('hide');
|
||||
|
||||
// timer to execute function
|
||||
window.setTimeout( function() {
|
||||
window[modalCallbackFunction]();
|
||||
}, 100);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
function modalDefaultInput () {
|
||||
// Hide modal
|
||||
$('#modal-input').modal('hide');
|
||||
|
||||
// timer to execute function
|
||||
window.setTimeout( function() {
|
||||
window[modalCallbackFunction]();
|
||||
}, 100);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
function modalWarningOK () {
|
||||
// Hide modal
|
||||
$('#modal-warning').modal('hide');
|
||||
|
||||
// timer to execute function
|
||||
window.setTimeout( function() {
|
||||
window[modalCallbackFunction]();
|
||||
}, 100);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
function showMessage (textMessage="") {
|
||||
if (textMessage.toLowerCase().includes("error") ) {
|
||||
// show error
|
||||
alert (textMessage);
|
||||
} else {
|
||||
// show temporal notification
|
||||
$("#alert-message").html (textMessage);
|
||||
$("#notification").fadeIn(1, function () {
|
||||
window.setTimeout( function() {
|
||||
$("#notification").fadeOut(500)
|
||||
}, 3000);
|
||||
} );
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
function showTickerAnnouncement(textMessage = "") {
|
||||
if (textMessage.toLowerCase().includes("error")) {
|
||||
// show error
|
||||
alert(textMessage);
|
||||
} else {
|
||||
// show permanent notification
|
||||
$("#ticker-message").html(textMessage);
|
||||
$("#tickerAnnouncement").removeClass("myhidden");
|
||||
// Move the tickerAnnouncement element to ticker_announcement_plc
|
||||
$("#tickerAnnouncement").appendTo("#ticker_announcement_plc");
|
||||
|
||||
// var $ticker = $('#tickerAnnouncement');
|
||||
// var $tickerMessage = $('#ticker-message');
|
||||
|
||||
// Clone the ticker message to create continuous scrolling effect
|
||||
// $tickerMessage.clone().appendTo($ticker);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
@@ -709,6 +578,22 @@ function debugTimer () {
|
||||
$('#pageTitle').html (new Date().getSeconds());
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
function secondsSincePageLoad() {
|
||||
// Get the current time
|
||||
var currentTime = Date.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
|
||||
var secondsAgo = Math.floor(timeDifference / 1000);
|
||||
|
||||
return secondsAgo;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Open url in new tab
|
||||
@@ -767,7 +652,7 @@ function navigateToDeviceWithIp (ip) {
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
function getNameByMacAddress(macAddress) {
|
||||
return getDeviceDataByMacAddress(macAddress, "dev_Name")
|
||||
return getDeviceDataByMac(macAddress, "dev_Name")
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
@@ -861,10 +746,14 @@ function isRandomMAC(mac)
|
||||
// ---------------------------------------------------------
|
||||
// Generate an array object from a string representation of an array
|
||||
function createArray(input) {
|
||||
// Empty array
|
||||
if (input === '[]') {
|
||||
return [];
|
||||
// Is already array, return
|
||||
if (Array.isArray(input)) {
|
||||
return input;
|
||||
}
|
||||
// Empty array
|
||||
if (input === '[]' || input === '') {
|
||||
return [];
|
||||
}
|
||||
|
||||
// Regex pattern for brackets
|
||||
const patternBrackets = /(^\s*\[)|(\]\s*$)/g;
|
||||
@@ -916,7 +805,7 @@ function isRandomMAC(mac)
|
||||
// -----------------------------------------------------------------------------
|
||||
// A function to get a device property using the mac address as key and DB column nakme as parameter
|
||||
// for the value to be returned
|
||||
function getDeviceDataByMacAddress(macAddress, dbColumn) {
|
||||
function getDeviceDataByMac(macAddress, dbColumn) {
|
||||
|
||||
const sessionDataKey = 'devicesListAll_JSON';
|
||||
const devicesCache = getCache(sessionDataKey);
|
||||
@@ -931,7 +820,14 @@ function getDeviceDataByMacAddress(macAddress, dbColumn) {
|
||||
for (const device of devices) {
|
||||
if (device["dev_MAC"].toLowerCase() === macAddress.toLowerCase()) {
|
||||
|
||||
if(dbColumn)
|
||||
{
|
||||
return device[dbColumn];
|
||||
}
|
||||
else
|
||||
{
|
||||
return device
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1013,6 +909,15 @@ function getGuid() {
|
||||
// -----------------------------------------------------------------------------
|
||||
function showSpinner(stringKey='Loading')
|
||||
{
|
||||
|
||||
if(stringKey == "")
|
||||
{
|
||||
text = ''
|
||||
} else
|
||||
{
|
||||
text = getString(stringKey)
|
||||
}
|
||||
|
||||
if($("#loadingSpinner").length)
|
||||
{
|
||||
$("#loadingSpinner").show();
|
||||
@@ -1024,7 +929,7 @@ function showSpinner(stringKey='Loading')
|
||||
<div class="pa_semitransparent-panel"></div>
|
||||
<div class="panel panel-default pa_spinner">
|
||||
<table>
|
||||
<td width="130px" align="middle">${getString(stringKey)}</td>
|
||||
<td width="130px" align="middle">${text}</td>
|
||||
<td><i class="ion ion-ios-loop-strong fa-spin fa-2x fa-fw"></td>
|
||||
</table>
|
||||
</div>
|
||||
@@ -1047,7 +952,8 @@ function updateApi()
|
||||
{
|
||||
|
||||
// value has to be in format event|param. e.g. run|ARPSCAN
|
||||
action = `update_api|devices,appevents`
|
||||
action = `${getGuid()}|update_api|devices,appevents`
|
||||
|
||||
|
||||
$.ajax({
|
||||
method: "POST",
|
||||
@@ -1148,8 +1054,18 @@ function resolveParams(params, template) {
|
||||
// If the parameter type is 'setting', retrieve setting value
|
||||
if (param.type == "setting") {
|
||||
var value = getSetting(param.value);
|
||||
|
||||
// Remove brackets and single quotes, replace them with double quotes
|
||||
value = value.replace('[','').replace(']','').replace(/'/g, '"');
|
||||
|
||||
// Split the string into an array, remove empty elements
|
||||
const arr = value.split(',').filter(Boolean);
|
||||
|
||||
// Join the array elements with commas
|
||||
const result = arr.join(', ');
|
||||
|
||||
// Replace placeholder with setting value
|
||||
template = template.replace("{" + param.name + "}", value);
|
||||
template = template.replace("{" + param.name + "}", result);
|
||||
} else {
|
||||
// If the parameter type is not 'setting', use the provided value
|
||||
template = template.replace("{" + param.name + "}", param.value);
|
||||
@@ -1183,155 +1099,131 @@ function arraysContainSameValues(arr1, arr2) {
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
// Define a unique key for storing the flag in sessionStorage
|
||||
var sessionStorageKey = "myScriptExecuted_common_js";
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Clearing all the caches
|
||||
function clearCache()
|
||||
{
|
||||
showSpinner()
|
||||
resetInitializedFlag()
|
||||
window.location.reload()
|
||||
}
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
function resetInitializedFlag()
|
||||
{
|
||||
// Clear local storage
|
||||
localStorage.clear();
|
||||
// Set the flag in sessionStorage to indicate that the code and cahce needs to be reloaded
|
||||
sessionStorage.setItem(sessionStorageKey, "false");
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// check if cache needs to be refreshed because of setting changes
|
||||
$.get('api/app_state.json?nocache=' + Date.now(), function(appState) {
|
||||
|
||||
console.log(appState["settingsImported"]*1000)
|
||||
|
||||
importedMiliseconds = parseInt((appState["settingsImported"]*1000));
|
||||
|
||||
lastReloaded = parseInt(sessionStorage.getItem(sessionStorageKey + '_time'));
|
||||
|
||||
if(importedMiliseconds > lastReloaded)
|
||||
{
|
||||
console.log("Cache needs to be refreshed because of setting changes");
|
||||
setTimeout(() => {
|
||||
resetInitializedFlag()
|
||||
location.reload();
|
||||
|
||||
}, 500);
|
||||
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Display spinner and reload page if not yet initialized
|
||||
function handleFirstLoad(callback)
|
||||
{
|
||||
if(!app_common_init)
|
||||
{
|
||||
setTimeout(function() {
|
||||
|
||||
callback();
|
||||
}, 1000);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Check if the code has been executed before by checking sessionStorage
|
||||
var app_common_init = sessionStorage.getItem(sessionStorageKey) === "true";
|
||||
const sessionStorageKey = "myScriptExecuted_common_js";
|
||||
var completedCalls = []
|
||||
var completedCalls_final = ['cacheSettings', 'cacheStrings', 'cacheDevices'];
|
||||
|
||||
// Define a function that will execute the code only once
|
||||
function executeOnce() {
|
||||
// -----------------------------------------------------------------------------
|
||||
// Clearing all the caches
|
||||
function clearCache() {
|
||||
showSpinner();
|
||||
sessionStorage.clear();
|
||||
localStorage.clear();
|
||||
setTimeout(() => {
|
||||
window.location.reload();
|
||||
}, 500);
|
||||
}
|
||||
|
||||
if (!arraysContainSameValues(getCache(completedCalls), completedCalls_final)) {
|
||||
// -----------------------------------------------------------------------------
|
||||
// Function to check if cache needs to be refreshed because of setting changes
|
||||
function checkSettingChanges() {
|
||||
$.get('api/app_state.json?nocache=' + Date.now(), function(appState) {
|
||||
const importedMilliseconds = parseInt(appState["settingsImported"] * 1000);
|
||||
const lastReloaded = parseInt(sessionStorage.getItem(sessionStorageKey + '_time'));
|
||||
|
||||
showSpinner()
|
||||
|
||||
// Use cache to keep track of completed AJAX calls
|
||||
var tmp_completedCalls = getCache("completedCalls")
|
||||
|
||||
// initialize from cache if values present
|
||||
if(tmp_completedCalls != "")
|
||||
{
|
||||
completedCalls = tmp_completedCalls.split(',');
|
||||
if (importedMilliseconds > lastReloaded) {
|
||||
console.log("Cache needs to be refreshed because of setting changes");
|
||||
setTimeout(() => {
|
||||
clearCache();
|
||||
}, 500);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// cache everything in the right order
|
||||
cacheStrings()
|
||||
.then(cacheSettings)
|
||||
.then(cacheDevices)
|
||||
.then(() => {
|
||||
// All callbacks have completed
|
||||
console.log("✅ All AJAX callbacks have completed");
|
||||
// location.reload()
|
||||
onAllCallsComplete();
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error("Error:", error);
|
||||
});
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Display spinner and reload page if not yet initialized
|
||||
async function handleFirstLoad(callback) {
|
||||
if (!isAppInitialized()) {
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
callback();
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Check if the code has been executed before by checking sessionStorage
|
||||
function isAppInitialized() {
|
||||
return arraysContainSameValues(getCache("completedCalls").split(',').filter(Boolean), completedCalls_final);
|
||||
}
|
||||
|
||||
// Define a function that will execute the code only once
|
||||
async function executeOnce() {
|
||||
showSpinner();
|
||||
|
||||
if (!isAppInitialized()) {
|
||||
try {
|
||||
await cacheStrings();
|
||||
await cacheSettings();
|
||||
await cacheDevices();
|
||||
console.log("✅ All AJAX callbacks have completed");
|
||||
onAllCallsComplete();
|
||||
} catch (error) {
|
||||
console.error("Error:", error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Function to handle successful completion of an AJAX call
|
||||
const handleSuccess = (callName) => {
|
||||
console.log(`AJAX call successful: ${callName} `);
|
||||
// store completed call
|
||||
completedCalls.push(callName)
|
||||
|
||||
setCache('completedCalls', mergeUniqueArrays(getCache('completedCalls').split(','), [callName]))
|
||||
|
||||
console.log(`AJAX call successful: ${callName}`);
|
||||
completedCalls.push(callName);
|
||||
setCache('completedCalls', mergeUniqueArrays(getCache('completedCalls').split(','), [callName]));
|
||||
};
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Function to handle failure of an AJAX call
|
||||
const handleFailure = (callName, callback) => {
|
||||
// Handle AJAX call failure here
|
||||
console.error(`AJAX call ${callName} failed`);
|
||||
|
||||
// try until successful
|
||||
// callback()
|
||||
// Implement retry logic here if needed
|
||||
};
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Function to execute when all AJAX calls have completed
|
||||
const onAllCallsComplete = () => {
|
||||
|
||||
// Merge local completedCalls with completedCalls_cached array - ensure uniqueness
|
||||
// if cache contains values, merge those two arrays
|
||||
completedCalls = mergeUniqueArrays(getCache('completedCalls').split(','), completedCalls)
|
||||
|
||||
// Update cache with merged completedCalls
|
||||
completedCalls = mergeUniqueArrays(getCache('completedCalls').split(','), completedCalls);
|
||||
setCache('completedCalls', completedCalls);
|
||||
|
||||
// Set the flag in sessionStorage to indicate that the code has been executed
|
||||
// and save time when last time the page was initialized
|
||||
sessionStorage.setItem(sessionStorageKey, "true");
|
||||
const millisecondsNow = Date.now();
|
||||
sessionStorage.setItem(sessionStorageKey + '_time', millisecondsNow);
|
||||
// Check if all necessary strings are initialized
|
||||
if (areAllStringsInitialized()) {
|
||||
sessionStorage.setItem(sessionStorageKey, "true");
|
||||
const millisecondsNow = Date.now();
|
||||
sessionStorage.setItem(sessionStorageKey + '_time', millisecondsNow);
|
||||
|
||||
console.log('✔ Cache intialized');
|
||||
console.log('✔ Cache initialized');
|
||||
} else {
|
||||
// If not all strings are initialized, retry initialization
|
||||
console.log('❌ Not all strings are initialized. Retrying...');
|
||||
executeOnce();
|
||||
return;
|
||||
}
|
||||
|
||||
// Call any other initialization functions here if needed
|
||||
|
||||
// No need for location.reload() here
|
||||
};
|
||||
|
||||
// Function to check if all necessary strings are initialized
|
||||
const areAllStringsInitialized = () => {
|
||||
// Implement logic to check if all necessary strings are initialized
|
||||
// Return true if all strings are initialized, false otherwise
|
||||
return getString('UI_LANG') != ""
|
||||
};
|
||||
|
||||
// Call the function to execute the code
|
||||
executeOnce();
|
||||
|
||||
// Set timer for page refresh if configured
|
||||
// Set timer for regular checks
|
||||
setTimeout(() => {
|
||||
|
||||
// page refresh if configured
|
||||
const refreshTime = getSetting("UI_REFRESH");
|
||||
if (refreshTime && refreshTime !== "0" && refreshTime !== "") {
|
||||
newTimerRefreshData(clearCache, parseInt(refreshTime)*1000);
|
||||
}
|
||||
|
||||
// Check if page needs to refresh due to setting changes
|
||||
checkSettingChanges()
|
||||
|
||||
}, 10000);
|
||||
|
||||
|
||||
@@ -1342,3 +1234,4 @@ console.log("init common.js");
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,18 +1,109 @@
|
||||
// -----------------------------------------------------------------------------
|
||||
// General utilities to interact with teh database
|
||||
// General utilities to interact with the database
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
// // Call to render lists
|
||||
// renderList(
|
||||
// options,
|
||||
// callbackToGenerateEntries,
|
||||
// valuesArray,
|
||||
// placeholder,
|
||||
// targetField,
|
||||
// transformers
|
||||
// );
|
||||
|
||||
// --------------------------------------------------
|
||||
// Read data and place intotarget location, callback processies the results
|
||||
function readData(sqlQuery, processDataCallback, valuesArray, targetLocation, targetField, nameTransformer) {
|
||||
var apiUrl = `php/server/dbHelper.php?action=read&rawSql=${encodeURIComponent(sqlQuery)}`;
|
||||
$.get(apiUrl, function(data) {
|
||||
// Process the JSON data using the provided callback function
|
||||
function renderList(
|
||||
options,
|
||||
processDataCallback,
|
||||
valuesArray,
|
||||
placeholder,
|
||||
targetField,
|
||||
transformers
|
||||
) {
|
||||
// Check if there are options provided
|
||||
if (options.length > 0) {
|
||||
// Determine if the first option's name is an SQL query
|
||||
const sqlQuery = isSQLQuery(options[0].name) ? options[0].name : "";
|
||||
|
||||
data = JSON.parse(data)
|
||||
// If there is an SQL query, fetch additional options
|
||||
if (sqlQuery) {
|
||||
// remove first item containing the SQL query
|
||||
options.shift();
|
||||
|
||||
var htmlResult = processDataCallback(data, valuesArray, targetField, nameTransformer);
|
||||
const apiUrl = `php/server/dbHelper.php?action=read&rawSql=${encodeURIComponent(sqlQuery)}`;
|
||||
|
||||
// Place the resulting HTML into the specified placeholder div
|
||||
$("#" + targetLocation).replaceWith(htmlResult);
|
||||
});
|
||||
}
|
||||
$.get(apiUrl, function (sqlOptionsData) {
|
||||
|
||||
// Parse the returned SQL data
|
||||
const sqlOption = JSON.parse(sqlOptionsData);
|
||||
|
||||
// Concatenate options from SQL query with the supplied options
|
||||
options = options.concat(sqlOption);
|
||||
|
||||
|
||||
// Process the combined options
|
||||
setTimeout(() => {
|
||||
processDataCallback(
|
||||
options,
|
||||
valuesArray,
|
||||
targetField,
|
||||
transformers,
|
||||
placeholder
|
||||
);
|
||||
}, 1);
|
||||
});
|
||||
} else {
|
||||
// No SQL query, directly process the supplied options
|
||||
setTimeout(() => {
|
||||
processDataCallback(
|
||||
options,
|
||||
valuesArray,
|
||||
targetField,
|
||||
transformers,
|
||||
placeholder
|
||||
);
|
||||
}, 1);
|
||||
}
|
||||
} else {
|
||||
// No options provided, directly process with empty options
|
||||
setTimeout(() => {
|
||||
processDataCallback(
|
||||
options,
|
||||
valuesArray,
|
||||
targetField,
|
||||
transformers,
|
||||
placeholder
|
||||
);
|
||||
}, 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// --------------------------------------------------
|
||||
// Check if database is locked
|
||||
function checkDbLock() {
|
||||
$.ajax({
|
||||
url: "log/db_is_locked.log", // Replace with the actual path to your PHP file
|
||||
type: "GET",
|
||||
|
||||
success: function (response) {
|
||||
console.log(response);
|
||||
if (response == 0) {
|
||||
// console.log('Database is not locked');
|
||||
$(".header-status-locked-db").hide();
|
||||
} else {
|
||||
console.log("🟥 Database is locked:");
|
||||
console.log(response);
|
||||
$(".header-status-locked-db").show();
|
||||
}
|
||||
},
|
||||
error: function () {
|
||||
console.log("🟥 Error checking database lock status");
|
||||
$(".header-status-locked-db").show();
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
setInterval(checkDbLock(), 1000);
|
||||
|
||||
409
front/js/modal.js
Executable file
@@ -0,0 +1,409 @@
|
||||
// -----------------------------------------------------------------------------
|
||||
// Modal dialog handling
|
||||
// -----------------------------------------------------------------------------
|
||||
var modalCallbackFunction = "";
|
||||
|
||||
function showModalOK(title, message, callbackFunction) {
|
||||
showModalOk(title, message, callbackFunction);
|
||||
}
|
||||
function showModalOk(title, message, callbackFunction) {
|
||||
// set captions
|
||||
$("#modal-ok-title").html(title);
|
||||
$("#modal-ok-message").html(message);
|
||||
|
||||
if (callbackFunction != null) {
|
||||
$("#modal-ok-OK").click(function () {
|
||||
callbackFunction();
|
||||
});
|
||||
}
|
||||
|
||||
// Show modal
|
||||
$("#modal-ok").modal("show");
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
function showModalDefault(title, message, btnCancel, btnOK, callbackFunction) {
|
||||
// set captions
|
||||
$("#modal-default-title").html(title);
|
||||
$("#modal-default-message").html(message);
|
||||
$("#modal-default-cancel").html(btnCancel);
|
||||
$("#modal-default-OK").html(btnOK);
|
||||
modalCallbackFunction = callbackFunction;
|
||||
|
||||
// Show modal
|
||||
$("#modal-default").modal("show");
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
function showModalDefaultStrParam(
|
||||
title,
|
||||
message,
|
||||
btnCancel,
|
||||
btnOK,
|
||||
callbackFunction,
|
||||
param = ""
|
||||
) {
|
||||
// set captions
|
||||
$("#modal-str-title").html(title);
|
||||
$("#modal-str-message").html(message);
|
||||
$("#modal-str-cancel").html(btnCancel);
|
||||
$("#modal-str-OK").html(btnOK);
|
||||
$("#modal-str-OK").off("click"); //remove existing handlers
|
||||
$("#modal-str-OK").on("click", function () {
|
||||
$("#modal-str").modal("hide");
|
||||
callbackFunction(param);
|
||||
});
|
||||
|
||||
// Show modal
|
||||
$("#modal-str").modal("show");
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
function showModalWarning(
|
||||
title,
|
||||
message,
|
||||
btnCancel = getString("Gen_Cancel"),
|
||||
btnOK = getString("Gen_Okay"),
|
||||
callbackFunction = null
|
||||
) {
|
||||
// set captions
|
||||
$("#modal-warning-title").html(title);
|
||||
$("#modal-warning-message").html(message);
|
||||
$("#modal-warning-cancel").html(btnCancel);
|
||||
$("#modal-warning-OK").html(btnOK);
|
||||
|
||||
if (callbackFunction != null) {
|
||||
modalCallbackFunction = callbackFunction;
|
||||
}
|
||||
|
||||
// Show modal
|
||||
$("#modal-warning").modal("show");
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
function showModalInput(
|
||||
title,
|
||||
message,
|
||||
btnCancel = getString("Gen_Cancel"),
|
||||
btnOK = getString("Gen_Okay"),
|
||||
callbackFunction = null
|
||||
) {
|
||||
prefix = "modal-input";
|
||||
|
||||
// set captions
|
||||
$(`#${prefix}-title`).html(title);
|
||||
$(`#${prefix}-message`).html(message);
|
||||
$(`#${prefix}-cancel`).html(btnCancel);
|
||||
$(`#${prefix}-OK`).html(btnOK);
|
||||
|
||||
if (callbackFunction != null) {
|
||||
modalCallbackFunction = callbackFunction;
|
||||
}
|
||||
|
||||
// Show modal
|
||||
$(`#${prefix}`).modal("show");
|
||||
|
||||
setTimeout(function () {
|
||||
$(`#${prefix}-textarea`).focus();
|
||||
}, 500);
|
||||
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
function showModalFieldInput(
|
||||
title,
|
||||
message,
|
||||
btnCancel = getString("Gen_Cancel"),
|
||||
btnOK = getString("Gen_Okay"),
|
||||
curValue = "",
|
||||
callbackFunction = null
|
||||
) {
|
||||
// set captions
|
||||
prefix = "modal-field-input";
|
||||
|
||||
$(`#${prefix}-title`).html(title);
|
||||
$(`#${prefix}-message`).html(message);
|
||||
$(`#${prefix}-cancel`).html(btnCancel);
|
||||
$(`#${prefix}-OK`).html(btnOK);
|
||||
|
||||
if (callbackFunction != null) {
|
||||
modalCallbackFunction = callbackFunction;
|
||||
}
|
||||
|
||||
$(`#${prefix}-field`).val(curValue);
|
||||
|
||||
setTimeout(function () {
|
||||
$(`#${prefix}-field`).focus();
|
||||
}, 500);
|
||||
|
||||
// Show modal
|
||||
$(`#${prefix}`).modal("show");
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
function modalDefaultOK() {
|
||||
// Hide modal
|
||||
$("#modal-default").modal("hide");
|
||||
|
||||
// timer to execute function
|
||||
window.setTimeout(function () {
|
||||
window[modalCallbackFunction]();
|
||||
}, 100);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
function modalDefaultInput() {
|
||||
// Hide modal
|
||||
$("#modal-input").modal("hide");
|
||||
|
||||
// timer to execute function
|
||||
window.setTimeout(function () {
|
||||
window[modalCallbackFunction]();
|
||||
}, 100);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
function modalDefaultFieldInput() {
|
||||
// Hide modal
|
||||
$("#modal-field-input").modal("hide");
|
||||
|
||||
// timer to execute function
|
||||
window.setTimeout(function () {
|
||||
modalCallbackFunction();
|
||||
}, 100);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
function modalWarningOK() {
|
||||
// Hide modal
|
||||
$("#modal-warning").modal("hide");
|
||||
|
||||
// timer to execute function
|
||||
window.setTimeout(function () {
|
||||
window[modalCallbackFunction]();
|
||||
}, 100);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
function showMessage(textMessage = "", timeout = 3000, colorClass = "modal_green") {
|
||||
if (textMessage.toLowerCase().includes("error")) {
|
||||
// show error
|
||||
alert(textMessage);
|
||||
} else {
|
||||
// show temporary notification
|
||||
$("#notification_modal").removeClass(); // remove all classes
|
||||
$("#notification_modal").addClass("alert alert-dimissible notification_modal"); // add default ones
|
||||
$("#notification_modal").addClass(colorClass); // add color modifiers
|
||||
|
||||
// message
|
||||
$("#alert-message").html(textMessage);
|
||||
|
||||
// timeout
|
||||
$("#notification_modal").fadeIn(1, function () {
|
||||
window.setTimeout(function () {
|
||||
$("#notification_modal").fadeOut(500);
|
||||
}, timeout);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
function showTickerAnnouncement(textMessage = "") {
|
||||
if (textMessage.toLowerCase().includes("error")) {
|
||||
// show error
|
||||
alert(textMessage);
|
||||
} else {
|
||||
// show permanent notification
|
||||
$("#ticker-message").html(textMessage);
|
||||
$("#tickerAnnouncement").removeClass("myhidden");
|
||||
// Move the tickerAnnouncement element to ticker_announcement_plc
|
||||
$("#tickerAnnouncement").appendTo("#ticker_announcement_plc");
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Keyboard bindings
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
$(document).ready(function () {
|
||||
$(document).on("keydown", function (event) {
|
||||
// ESC key is pressed
|
||||
if (event.keyCode === 27) {
|
||||
// Trigger modal dismissal
|
||||
$(".modal").modal("hide");
|
||||
}
|
||||
|
||||
// Enter key is pressed
|
||||
if (event.keyCode === 13) {
|
||||
$(".modal:visible").find(".btn-modal-submit").click(); // Trigger the click event of the OK button in visible modals
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Backend notification Polling
|
||||
// -----------------------------------------------------------------------------
|
||||
// Function to check for notifications
|
||||
function checkNotification() {
|
||||
const notificationEndpoint = 'php/server/utilNotification.php?action=get_unread_notifications';
|
||||
const phpEndpoint = 'php/server/utilNotification.php';
|
||||
|
||||
$.ajax({
|
||||
url: notificationEndpoint,
|
||||
type: 'GET',
|
||||
success: function(response) {
|
||||
// console.log(response);
|
||||
|
||||
if(response != "[]")
|
||||
{
|
||||
|
||||
// Find the oldest unread notification with level "interrupt"
|
||||
const oldestInterruptNotification = response.find(notification => notification.read === 0 && notification.level === "interrupt");
|
||||
const allUnreadNotification = response.filter(notification => notification.read === 0 && notification.level === "alert");
|
||||
|
||||
if (oldestInterruptNotification) {
|
||||
// Show modal dialog with the oldest unread notification
|
||||
|
||||
const decodedContent = decodeURIComponent(oldestInterruptNotification.content);
|
||||
|
||||
showModalOK("Notification", decodedContent, function() {
|
||||
// Mark the notification as read
|
||||
$.ajax({
|
||||
url: phpEndpoint,
|
||||
type: 'GET',
|
||||
data: {
|
||||
action: 'mark_notification_as_read',
|
||||
guid: oldestInterruptNotification.guid
|
||||
},
|
||||
success: function(response) {
|
||||
console.log(response);
|
||||
// After marking the notification as read, check for the next one
|
||||
checkNotification();
|
||||
},
|
||||
error: function(xhr, status, error) {
|
||||
console.error("Error marking notification as read:", status, error);
|
||||
},
|
||||
complete:function() {
|
||||
hideSpinner();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
handleUnreadNotifications(allUnreadNotification.length)
|
||||
|
||||
|
||||
}
|
||||
},
|
||||
error: function() {
|
||||
console.warn(`🟥 Error checking ${notificationEndpoint}`)
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Handling unread notifications favicon + bell floating number bublbe
|
||||
function handleUnreadNotifications(count) {
|
||||
$('#unread-notifications-bell-count').html(count);
|
||||
if (count > 0) {
|
||||
$('#unread-notifications-bell-count').show();
|
||||
// Change the favicon to show there are notifications
|
||||
$('#favicon').attr('href', 'img/NetAlertX_logo_notification.png');
|
||||
// Update the title to include the count
|
||||
document.title = `(${count}) ` + originalTitle;
|
||||
} else {
|
||||
$('#unread-notifications-bell-count').hide();
|
||||
// Change the favicon back to the original
|
||||
$('#favicon').attr('href', 'img/NetAlertX_logo.png');
|
||||
// Revert the title to the original title
|
||||
document.title = originalTitle;
|
||||
}
|
||||
}
|
||||
|
||||
// Store the original title of the document
|
||||
var originalTitle = document.title;
|
||||
|
||||
|
||||
// Start checking for notifications periodically
|
||||
setInterval(checkNotification, 3000);
|
||||
|
||||
// --------------------------------------------------
|
||||
// User notification handling methods
|
||||
// --------------------------------------------------
|
||||
|
||||
const phpEndpoint = 'php/server/utilNotification.php';
|
||||
|
||||
// --------------------------------------------------
|
||||
// Write a notification
|
||||
function write_notification(content, level) {
|
||||
|
||||
$.ajax({
|
||||
url: phpEndpoint, // Change this to the path of your PHP script
|
||||
type: 'GET',
|
||||
data: {
|
||||
action: 'write_notification',
|
||||
content: content,
|
||||
level: level
|
||||
},
|
||||
success: function(response) {
|
||||
console.log('Notification written successfully.');
|
||||
},
|
||||
error: function(xhr, status, error) {
|
||||
console.error('Error writing notification:', error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// --------------------------------------------------
|
||||
// Write a notification
|
||||
function markNotificationAsRead(guid) {
|
||||
|
||||
$.ajax({
|
||||
url: phpEndpoint,
|
||||
type: 'GET',
|
||||
data: {
|
||||
action: 'mark_notification_as_read',
|
||||
guid: guid
|
||||
},
|
||||
success: function(response) {
|
||||
console.log(response);
|
||||
// Perform any further actions after marking the notification as read here
|
||||
showMessage(getString("Gen_Okay"))
|
||||
},
|
||||
error: function(xhr, status, error) {
|
||||
console.error("Error marking notification as read:", status, error);
|
||||
},
|
||||
complete: function() {
|
||||
// Perform any cleanup tasks here
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// --------------------------------------------------
|
||||
// Remove a notification
|
||||
function removeNotification(guid) {
|
||||
|
||||
$.ajax({
|
||||
url: phpEndpoint,
|
||||
type: 'GET',
|
||||
data: {
|
||||
action: 'remove_notification',
|
||||
guid: guid
|
||||
},
|
||||
success: function(response) {
|
||||
console.log(response);
|
||||
// Perform any further actions after marking the notification as read here
|
||||
showMessage(getString("Gen_Okay"))
|
||||
},
|
||||
error: function(xhr, status, error) {
|
||||
console.error("Error removing notification:", status, error);
|
||||
},
|
||||
complete: function() {
|
||||
// Perform any cleanup tasks here
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -1,171 +1,814 @@
|
||||
// -------------------------------------------------------------------
|
||||
// Get all plugin prefixes of a given type
|
||||
function getPluginsByType(pluginsData, pluginType, onlyEnabled)
|
||||
{
|
||||
// Get all plugin prefixes of a given type
|
||||
function getPluginsByType(pluginsData, pluginType, onlyEnabled) {
|
||||
var result = [];
|
||||
|
||||
var result = []
|
||||
|
||||
pluginsData.forEach((plug) => {
|
||||
|
||||
if(plug.plugin_type == pluginType)
|
||||
{
|
||||
// collect all, or if only enabled, check if NOT disabled
|
||||
if (onlyEnabled == false || (onlyEnabled && getSetting(plug.unique_prefix + '_RUN') != 'disabled')) {
|
||||
result.push(plug.unique_prefix)
|
||||
}
|
||||
pluginsData.forEach((plug) => {
|
||||
if (plug.plugin_type == pluginType) {
|
||||
// collect all, or if only enabled, check if NOT disabled
|
||||
if (
|
||||
onlyEnabled == false ||
|
||||
(onlyEnabled && getSetting(plug.unique_prefix + "_RUN") != "disabled")
|
||||
) {
|
||||
result.push(plug.unique_prefix);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
// Get plugin code name base on prefix
|
||||
function getPluginCodeName(pluginsData, prefix) {
|
||||
var result = "";
|
||||
|
||||
pluginsData.forEach((plug) => {
|
||||
if (plug.unique_prefix == prefix) {
|
||||
id = plug.code_name;
|
||||
|
||||
// console.log(id)
|
||||
result = plug.code_name;
|
||||
}
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
// Get plugin type base on prefix
|
||||
function getPluginType(pluginsData, prefix) {
|
||||
var result = "core";
|
||||
|
||||
pluginsData.forEach((plug) => {
|
||||
if (plug.unique_prefix == prefix) {
|
||||
id = plug.plugin_type;
|
||||
|
||||
// console.log(id)
|
||||
result = plug.plugin_type;
|
||||
}
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
// Get plugin config based on prefix
|
||||
function getPluginConfig(pluginsData, prefix) {
|
||||
result = "";
|
||||
|
||||
pluginsData.forEach((plug) => {
|
||||
if (plug.unique_prefix == prefix) {
|
||||
// console.log(id)
|
||||
result = plug;
|
||||
}
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
// Generate plugin HTML card based on prefixes in an array
|
||||
function pluginCards(prefixesOfEnabledPlugins, includeSettings) {
|
||||
html = "";
|
||||
|
||||
prefixesOfEnabledPlugins.forEach((prefix) => {
|
||||
includeSettings_html = "";
|
||||
|
||||
includeSettings.forEach((set) => {
|
||||
includeSettings_html += `
|
||||
<div class="col-sm-6 overview-setting-value-wrap">
|
||||
<a href="#${prefix + "_" + set}" onclick="toggleAllSettings()">
|
||||
<div class="overview-setting-value pointer" title="${
|
||||
prefix + "_" + set
|
||||
}">
|
||||
<code>${getSetting(prefix + "_" + set)}</code>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
`;
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
// Get plugin type base on prefix
|
||||
function getPluginCodeName(pluginsData, prefix)
|
||||
{
|
||||
var result = ""
|
||||
|
||||
pluginsData.forEach((plug) => {
|
||||
|
||||
if (plug.unique_prefix == prefix ) {
|
||||
id = plug.code_name;
|
||||
|
||||
// console.log(id)
|
||||
result = plug.code_name;
|
||||
}
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
// Get plugin type base on prefix
|
||||
function getPluginType(pluginsData, prefix)
|
||||
{
|
||||
var result = "core"
|
||||
|
||||
pluginsData.forEach((plug) => {
|
||||
|
||||
if (plug.unique_prefix == prefix ) {
|
||||
id = plug.plugin_type;
|
||||
|
||||
// console.log(id)
|
||||
result = plug.plugin_type;
|
||||
}
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
// Generate plugin HTML card based on prefixes in an array
|
||||
function pluginCards(prefixesOfEnabledPlugins, includeSettings)
|
||||
{
|
||||
html = ""
|
||||
|
||||
prefixesOfEnabledPlugins.forEach((prefix) => {
|
||||
|
||||
includeSettings_html = ''
|
||||
|
||||
includeSettings.forEach((set) => {
|
||||
|
||||
includeSettings_html += `
|
||||
<a href="#${prefix + '_' + set}" onclick="toggleAllSettings()">
|
||||
<div class="overview-setting-value pointer" title="${prefix + '_' + set}">
|
||||
<code>${getSetting(prefix + '_' + set)}</code>
|
||||
</div>
|
||||
</a>
|
||||
`
|
||||
|
||||
});
|
||||
|
||||
html += `
|
||||
<div class="col-sm-4 ">
|
||||
<div class="small-box bg-green " >
|
||||
<div class="inner ">
|
||||
html += `
|
||||
<div class="col-xs-6 col-sm-4 col-md-3 col-lg-2 col-xxl-1 padding-5px">
|
||||
<div class="small-box bg-green col-sm-12 " >
|
||||
<div class="inner col-sm-12">
|
||||
<a href="#${prefix}_header" onclick="toggleAllSettings('open')">
|
||||
<h5 class="card-title">
|
||||
<b>${getString(prefix + "_display_name")}</b>
|
||||
</h5>
|
||||
</a>
|
||||
${includeSettings_html}
|
||||
</div>
|
||||
<a href="#${prefix}_header" onclick="toggleAllSettings('open')">
|
||||
<h5 class="card-title">
|
||||
<b>${getString(prefix+"_display_name")}</b>
|
||||
</h5>
|
||||
</a>
|
||||
${includeSettings_html}
|
||||
</div>
|
||||
<a href="#${prefix}_header" onclick="toggleAllSettings('open')">
|
||||
<div class="icon"> ${getString(prefix+"_icon")} </div>
|
||||
<div class="icon"> ${getString(prefix + "_icon")} </div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
`
|
||||
`;
|
||||
});
|
||||
|
||||
return html;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Open or close all settings
|
||||
// -----------------------------------------------------------------------------
|
||||
function toggleAllSettings(openOrClose = "") {
|
||||
inStr = " in";
|
||||
allOpen = true;
|
||||
openIcon = "fa-angle-double-down";
|
||||
closeIcon = "fa-angle-double-up";
|
||||
|
||||
$(".panel-collapse").each(function () {
|
||||
if ($(this).attr("class").indexOf(inStr) == -1) {
|
||||
allOpen = false;
|
||||
}
|
||||
});
|
||||
|
||||
if (allOpen == false || openOrClose == "open") {
|
||||
// open all
|
||||
openAllSettings();
|
||||
$("#toggleSettings").attr(
|
||||
"class",
|
||||
$("#toggleSettings").attr("class").replace(openIcon, closeIcon)
|
||||
);
|
||||
} else {
|
||||
// close all
|
||||
$('div[data-myid="collapsible"]').each(function () {
|
||||
$(this).attr("class", "panel-collapse collapse ");
|
||||
});
|
||||
|
||||
return html;
|
||||
$("#toggleSettings").attr(
|
||||
"class",
|
||||
$("#toggleSettings").attr("class").replace(closeIcon, openIcon)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function openAllSettings() {
|
||||
$('div[data-myid="collapsible"]').each(function () {
|
||||
$(this).attr("class", "panel-collapse collapse in");
|
||||
});
|
||||
$('div[data-myid="collapsible"]').each(function () {
|
||||
$(this).attr("style", "height:inherit");
|
||||
});
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Open or close all settings
|
||||
// -----------------------------------------------------------------------------
|
||||
function toggleAllSettings(openOrClose = '')
|
||||
{
|
||||
inStr = ' in';
|
||||
allOpen = true;
|
||||
openIcon = 'fa-angle-double-down';
|
||||
closeIcon = 'fa-angle-double-up';
|
||||
// -------------------------------------------------------------------
|
||||
// Checks if all schedules are the same
|
||||
function schedulesAreSynchronized(prefixesOfEnabledPlugins, pluginsData) {
|
||||
plug_schedules = [];
|
||||
|
||||
$('.panel-collapse').each(function(){
|
||||
if($(this).attr('class').indexOf(inStr) == -1)
|
||||
{
|
||||
allOpen = false;
|
||||
prefixesOfEnabledPlugins.forEach((prefix) => {
|
||||
pluginsData.forEach((plug) => {
|
||||
if (plug.unique_prefix == prefix) {
|
||||
plug_schedules.push(
|
||||
getSetting(prefix + "_RUN_SCHD").replace(/\s/g, "")
|
||||
); // replace all white characters to compare them easier
|
||||
}
|
||||
})
|
||||
|
||||
if(allOpen == false || openOrClose == 'open')
|
||||
{
|
||||
// open all
|
||||
$('div[data-myid="collapsible"]').each(function(){$(this).attr('class', 'panel-collapse collapse in')})
|
||||
$('div[data-myid="collapsible"]').each(function(){$(this).attr('style', 'height:inherit')})
|
||||
$('#toggleSettings').attr('class', $('#toggleSettings').attr('class').replace(openIcon, closeIcon))
|
||||
|
||||
}
|
||||
else{
|
||||
// close all
|
||||
$('div[data-myid="collapsible"]').each(function(){$(this).attr('class', 'panel-collapse collapse ')})
|
||||
$('#toggleSettings').attr('class', $('#toggleSettings').attr('class').replace(closeIcon, openIcon))
|
||||
}
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
// Check if all plug_schedules are the same
|
||||
if (plug_schedules.length > 0) {
|
||||
const firstSchedule = plug_schedules[0];
|
||||
return plug_schedules.every((schedule) => schedule === firstSchedule);
|
||||
}
|
||||
|
||||
return true; // Return true if no schedules are found
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
// Checks if all schedules are the same
|
||||
function schedulesAreSynchronized(prefixesOfEnabledPlugins, pluginsData)
|
||||
{
|
||||
plug_schedules = []
|
||||
// -------------------------------------------------------------------
|
||||
// Checks if value is already encoded
|
||||
function isSHA256(value) {
|
||||
// Check if the value is a string and has a length of 64 characters
|
||||
if (typeof value === "string" && value.length === 64) {
|
||||
// Check if the value contains only hexadecimal characters
|
||||
return /^[0-9a-fA-F]+$/.test(value);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
prefixesOfEnabledPlugins.forEach((prefix) => {
|
||||
pluginsData.forEach((plug) => {
|
||||
// -------------------------------------------------------------------
|
||||
// Utility function to check if the value is already Base64
|
||||
function isBase64(value) {
|
||||
const base64Regex =
|
||||
/^(?:[A-Za-z0-9+\/]{4})*?(?:[A-Za-z0-9+\/]{2}==|[A-Za-z0-9+\/]{3}=)?$/;
|
||||
return base64Regex.test(value);
|
||||
}
|
||||
|
||||
if (plug.unique_prefix == prefix) {
|
||||
|
||||
plug_schedules.push(getSetting(prefix+"_RUN_SCHD").replace(/\s/g, "")) // replace all white characters to compare them easier
|
||||
// -------------------------------------------------------------------
|
||||
// Validation
|
||||
// -------------------------------------------------------------------
|
||||
function settingsCollectedCorrectly(settingsArray, settingsJSON_DB) {
|
||||
// check if the required UI_LANG setting is in the array - if not something went wrong
|
||||
$.each(settingsArray, function (index, value) {
|
||||
if (value[1] == "UI_LANG") {
|
||||
if (isEmpty(value[3]) == true) {
|
||||
console.log(`⚠ Error: Required setting UI_LANG not found`);
|
||||
showModalOk("ERROR", getString("settings_missing_block"));
|
||||
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const settingsCodeNames = settingsJSON_DB.map((setting) => setting.Code_Name);
|
||||
const detailedCodeNames = settingsArray.map((item) => item[1]);
|
||||
|
||||
const missingCodeNamesOnPage = detailedCodeNames.filter(
|
||||
(codeName) => !settingsCodeNames.includes(codeName)
|
||||
);
|
||||
const missingCodeNamesInDB = settingsCodeNames.filter(
|
||||
(codeName) => !detailedCodeNames.includes(codeName)
|
||||
);
|
||||
|
||||
// check if the number of settings on the page and in the DB are the same
|
||||
if (missingCodeNamesOnPage.length !== missingCodeNamesInDB.length) {
|
||||
console.log(
|
||||
`⚠ Error: The following settings are missing in the DB or on the page (Reload page to fix):`
|
||||
);
|
||||
console.log(missingCodeNamesOnPage);
|
||||
console.log(missingCodeNamesInDB);
|
||||
|
||||
showModalOk("ERROR", getString("settings_missing_block"));
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// all OK
|
||||
return true;
|
||||
}
|
||||
// -------------------------------------------------------------------
|
||||
// Manipulating Editable List options
|
||||
// -------------------------------------------------------------------
|
||||
|
||||
// ---------------------------------------------------------
|
||||
// Add item to list
|
||||
function addList(element, clearInput = true) {
|
||||
const fromId = $(element).attr("my-input-from");
|
||||
const toId = $(element).attr("my-input-to");
|
||||
|
||||
const input = $(`#${fromId}`).val();
|
||||
|
||||
console.log(`fromId | toId | input : ${fromId} | ${toId} | ${input}`);
|
||||
|
||||
const newOption = $("<option class='interactable-option'></option>")
|
||||
.attr("value", input)
|
||||
.text(input);
|
||||
|
||||
const el = $(`#${toId}`).append(newOption);
|
||||
|
||||
// clear input
|
||||
if (clearInput) {
|
||||
$(`#${fromId}`).val("");
|
||||
}
|
||||
|
||||
// Initialize interaction options only for the newly added option
|
||||
initListInteractionOptions(newOption);
|
||||
|
||||
settingsChanged();
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------
|
||||
function removeFromList(element) {
|
||||
settingsChanged();
|
||||
$(`#${$(element).attr("my-input-to")}`)
|
||||
.find("option:last")
|
||||
.remove();
|
||||
}
|
||||
// ---------------------------------------------------------
|
||||
function addInterface() {
|
||||
ipMask = $("#ipMask").val();
|
||||
ipInterface = $("#ipInterface").val();
|
||||
|
||||
full = ipMask + " --interface=" + ipInterface;
|
||||
|
||||
console.log(full);
|
||||
|
||||
if (ipMask == "" || ipInterface == "") {
|
||||
showModalOk(
|
||||
"Validation error",
|
||||
"Specify both, the network mask and the interface"
|
||||
);
|
||||
} else {
|
||||
$("#SCAN_SUBNETS").append(
|
||||
$("<option disabled></option>").attr("value", full).text(full)
|
||||
);
|
||||
|
||||
$("#ipMask").val("");
|
||||
$("#ipInterface").val("");
|
||||
|
||||
settingsChanged();
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
// Function to remove an item from the select element
|
||||
function removeOptionItem(option) {
|
||||
settingsChanged();
|
||||
option.remove();
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
// Update value of an item from the select element
|
||||
function updateOptionItem(option, value) {
|
||||
settingsChanged();
|
||||
option.html(value);
|
||||
option.val(value);
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
// Remove all options
|
||||
function removeAllOptions(element) {
|
||||
settingsChanged();
|
||||
$(`#${$(element).attr("my-input-to")}`).empty();
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
// Function to initialize remove functionality on select options
|
||||
|
||||
// Counter to track number of clicks
|
||||
let clickCounter = 0;
|
||||
|
||||
// Function to initialize list interaction options
|
||||
function initListInteractionOptions(element) {
|
||||
if (element) {
|
||||
$options = $(element);
|
||||
} else {
|
||||
$options = $(`.interactable-option`);
|
||||
}
|
||||
|
||||
// Attach click event listener to options
|
||||
$options.on("click", function () {
|
||||
const $option = $(this);
|
||||
|
||||
// Increment click counter
|
||||
clickCounter++;
|
||||
|
||||
// Delay to capture multiple clicks
|
||||
setTimeout(() => {
|
||||
// Perform action based on click count
|
||||
if (clickCounter === 1) {
|
||||
// Single-click action
|
||||
showModalFieldInput(
|
||||
`<i class="fa-regular fa-pen-to-square"></i> ${getString(
|
||||
"Gen_Update_Value"
|
||||
)}`,
|
||||
getString("settings_update_item_warning"),
|
||||
getString("Gen_Cancel"),
|
||||
getString("Gen_Update"),
|
||||
$option.html(),
|
||||
function () {
|
||||
updateOptionItem($option, $(`#modal-field-input-field`).val());
|
||||
}
|
||||
);
|
||||
} else if (clickCounter === 2) {
|
||||
// Double-click action
|
||||
removeOptionItem($option);
|
||||
}
|
||||
|
||||
// Reset click counter
|
||||
clickCounter = 0;
|
||||
}, 300); // Adjust delay as needed
|
||||
});
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
// Function to filter rows based on input text
|
||||
function filterRows(inputText) {
|
||||
|
||||
// open everything if input text is empty
|
||||
if (!inputText) {
|
||||
inputText = "";
|
||||
|
||||
$(".panel").each(function () {
|
||||
var $panel = $(this);
|
||||
var $panelHeader = $panel.find('.panel-heading');
|
||||
var $panelBody = $panel.find('.panel-collapse');
|
||||
|
||||
$panel.show()
|
||||
$panelHeader.show()
|
||||
$panelBody.collapse('show');
|
||||
|
||||
$panelBody.find(".table_row:not(.docs)").each(function () {
|
||||
var $row = $(this)
|
||||
var rowId = $row.attr("id");
|
||||
var isMetadataRow = rowId && rowId.endsWith("__metadata");
|
||||
if (!isMetadataRow) {
|
||||
$row.show()
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
} else{
|
||||
// filter
|
||||
|
||||
$(".panel").each(function () {
|
||||
var $panel = $(this);
|
||||
var $panelHeader = $panel.find('.panel-heading');
|
||||
var $panelBody = $panel.find('.panel-collapse');
|
||||
|
||||
var anyVisible = false; // Flag to check if any row is visible
|
||||
|
||||
$panelBody.find(".table_row:not(.docs)").each(function () {
|
||||
var $row = $(this);
|
||||
|
||||
// Check if the row ID ends with "__metadata"
|
||||
var rowId = $row.attr("id");
|
||||
var isMetadataRow = rowId && rowId.endsWith("__metadata");
|
||||
|
||||
// Always hide metadata rows
|
||||
if (isMetadataRow) {
|
||||
$row.hide();
|
||||
return; // Skip further processing for metadata rows
|
||||
}
|
||||
|
||||
var description = $row.find(".setting_description").text().toLowerCase();
|
||||
var codeName = $row.find(".setting_name code").text().toLowerCase();
|
||||
|
||||
if (
|
||||
description.includes(inputText.toLowerCase()) ||
|
||||
codeName.includes(inputText.toLowerCase())
|
||||
) {
|
||||
$row.show();
|
||||
anyVisible = true; // Set the flag to true if at least one row is visible
|
||||
} else {
|
||||
$row.hide();
|
||||
}
|
||||
});
|
||||
|
||||
// Determine whether to hide or show the panel based on visibility of rows
|
||||
if (anyVisible) {
|
||||
$panelBody.collapse('show'); // Ensure the panel body is shown if there are visible rows
|
||||
$panelHeader.show(); // Show the panel header
|
||||
$panel.show(); // Show the entire panel if there are visible rows
|
||||
} else {
|
||||
$panelBody.collapse('hide'); // Hide the panel body if no rows are visible
|
||||
$panelHeader.hide(); // Hide the panel header if no rows are visible
|
||||
$panel.hide(); // Hide the entire panel if no rows are visible
|
||||
}
|
||||
});
|
||||
|
||||
// Check if all plug_schedules are the same
|
||||
if (plug_schedules.length > 0) {
|
||||
const firstSchedule = plug_schedules[0];
|
||||
return plug_schedules.every((schedule) => schedule === firstSchedule);
|
||||
}
|
||||
|
||||
return true; // Return true if no schedules are found
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
setTimeout(() => {
|
||||
// Event listener for input change
|
||||
$("#settingsSearch").on("input", function () {
|
||||
var searchText = $(this).val();
|
||||
// hide the setting overview dashboard
|
||||
$("#settingsOverview").collapse("hide");
|
||||
|
||||
filterRows(searchText);
|
||||
});
|
||||
|
||||
// Event listener for input focus
|
||||
// var firstFocus = true;
|
||||
$("#settingsSearch").on("focus", function () {
|
||||
openAllSettings();
|
||||
});
|
||||
}, 1000);
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// handling events on the backend initiated by the front end END
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
// ---------------------------------------------------------
|
||||
// UNUSED?
|
||||
function getParam(targetId, key, skipCache = false) {
|
||||
skipCacheQuery = "";
|
||||
|
||||
if (skipCache) {
|
||||
skipCacheQuery = "&skipcache";
|
||||
}
|
||||
|
||||
// get parameter value
|
||||
$.get(
|
||||
"php/server/parameters.php?action=get&defaultValue=0¶meter=" +
|
||||
key +
|
||||
skipCacheQuery,
|
||||
function (data) {
|
||||
var result = data;
|
||||
|
||||
result = result.replaceAll('"', "");
|
||||
|
||||
document.getElementById(targetId).innerHTML = result.replaceAll('"', "");
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Show/hide the metadata settings
|
||||
// -----------------------------------------------------------------------------
|
||||
function toggleMetadata(element) {
|
||||
const id = $(element).attr("my-to-toggle");
|
||||
|
||||
$(`#${id}`).toggle();
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------
|
||||
// Helper methods
|
||||
// ---------------------------------------------------------
|
||||
// Toggle readonly mode of the target element specified by the id in the "my-input-toggle-readonly" attribute
|
||||
function overrideToggle(element) {
|
||||
settingsChanged();
|
||||
|
||||
targetId = $(element).attr("my-input-toggle-readonly");
|
||||
|
||||
inputElement = $(`#${targetId}`)[0];
|
||||
|
||||
if (!inputElement) {
|
||||
console.error("Input element not found!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (inputElement.type === "text" || inputElement.type === "password") {
|
||||
inputElement.readOnly = !inputElement.readOnly;
|
||||
} else if (inputElement.type === "checkbox") {
|
||||
inputElement.disabled = !inputElement.disabled;
|
||||
} else {
|
||||
console.warn(
|
||||
"Unsupported input type. Only text, password, and checkbox inputs are supported."
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Generate options or set options based on the provided parameters
|
||||
function generateOptionsOrSetOptions(
|
||||
codeName,
|
||||
valuesArray, // Array of values to be pre-selected in the dropdown
|
||||
placeholder, // ID of the HTML element where dropdown should be rendered (will be replaced)
|
||||
processDataCallback, // Callback function to generate entries based on options
|
||||
targetField, // Target field or element where selected value should be applied or updated
|
||||
transformers = [] // Transformers to be applied to the values
|
||||
) {
|
||||
|
||||
console.log(codeName);
|
||||
|
||||
// NOTE {value} options to replace with a setting or SQL value are handled in the cacheSettings() function
|
||||
options = arrayToObject(createArray(getSettingOptions(codeName)))
|
||||
|
||||
// Call to render lists
|
||||
renderList(
|
||||
options,
|
||||
processDataCallback,
|
||||
valuesArray,
|
||||
placeholder,
|
||||
targetField,
|
||||
transformers
|
||||
);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
// ------------------------------------------------------------
|
||||
// Function to apply transformers to a value
|
||||
function applyTransformers(val, transformers) {
|
||||
transformers.forEach((transformer) => {
|
||||
switch (transformer) {
|
||||
case "sha256":
|
||||
// Implement sha256 hashing logic
|
||||
if (!isSHA256(val)) {
|
||||
val = CryptoJS.SHA256(val).toString(CryptoJS.enc.Hex);
|
||||
}
|
||||
break;
|
||||
case "base64":
|
||||
// Implement base64 logic
|
||||
if (!isBase64(val)) {
|
||||
val = btoa(val);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
console.warn(`Unknown transformer: ${transformer}`);
|
||||
}
|
||||
});
|
||||
return val;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------
|
||||
// Function to reverse transformers applied to a value
|
||||
function reverseTransformers(val, transformers) {
|
||||
transformers.reverse().forEach((transformer) => {
|
||||
switch (transformer) {
|
||||
case "sha256":
|
||||
// Reversing sha256 is not possible since it's a one-way hash function
|
||||
console.warn("Reversing sha256 is not possible");
|
||||
break;
|
||||
case "base64":
|
||||
// Implement base64 decoding logic
|
||||
if (isBase64(val)) {
|
||||
val = atob(val);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
console.warn(`Unknown transformer: ${transformer}`);
|
||||
}
|
||||
});
|
||||
return val;
|
||||
}
|
||||
|
||||
|
||||
// ------------------------------------------------------------
|
||||
// Function to initialize relevant variables based on HTML element
|
||||
const handleElementOptions = (codeName, elementOptions, transformers, val) => {
|
||||
let inputType = "text";
|
||||
let readOnly = "";
|
||||
let isMultiSelect = false;
|
||||
let cssClasses = "";
|
||||
let placeholder = "";
|
||||
let suffix = "";
|
||||
let separator = "";
|
||||
let editable = false;
|
||||
let valRes = val;
|
||||
let sourceIds = [];
|
||||
let getStringKey = "";
|
||||
let onClick = "alert('Not implemented');";
|
||||
|
||||
elementOptions.forEach((option) => {
|
||||
if (option.prefillValue) {
|
||||
valRes = option.prefillValue === "null" ? "" : option.prefillValue;
|
||||
}
|
||||
if (option.type) {
|
||||
inputType = option.type;
|
||||
}
|
||||
if (option.readonly === "true") {
|
||||
readOnly = `readonly`;
|
||||
}
|
||||
if (option.multiple === "true") {
|
||||
isMultiSelect = true;
|
||||
}
|
||||
if (option.editable === "true") {
|
||||
editable = true;
|
||||
}
|
||||
if (option.cssClasses) {
|
||||
cssClasses = option.cssClasses;
|
||||
}
|
||||
if (option.placeholder) {
|
||||
placeholder = option.placeholder;
|
||||
}
|
||||
if (option.suffix) {
|
||||
suffix = option.suffix;
|
||||
}
|
||||
if (option.sourceSuffixes) {
|
||||
$.each(option.sourceSuffixes, function (index, suf) {
|
||||
sourceIds.push(codeName + suf);
|
||||
});
|
||||
}
|
||||
if (option.separator) {
|
||||
separator = option.separator;
|
||||
}
|
||||
if (option.getStringKey) {
|
||||
getStringKey = option.getStringKey;
|
||||
}
|
||||
if (option.onClick) {
|
||||
onClick = option.onClick;
|
||||
}
|
||||
});
|
||||
|
||||
if (transformers.includes("sha256")) {
|
||||
inputType = "password";
|
||||
}
|
||||
|
||||
return {
|
||||
inputType,
|
||||
readOnly,
|
||||
isMultiSelect,
|
||||
cssClasses,
|
||||
placeholder,
|
||||
suffix,
|
||||
sourceIds,
|
||||
separator,
|
||||
editable,
|
||||
valRes,
|
||||
getStringKey,
|
||||
onClick,
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Data processors
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
// --------------------------------------------------
|
||||
// Creates an object from an array
|
||||
function arrayToObject(array) {
|
||||
const obj = [];
|
||||
array.forEach((item, index) => {
|
||||
obj.push({ id: item, name: item })
|
||||
});
|
||||
return obj;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Processor to generate options
|
||||
function generateOptions(options, valuesArray, targetField, transformers, placeholder) {
|
||||
var optionsHtml = "";
|
||||
|
||||
resultArray = []
|
||||
selectedArray = []
|
||||
cssClass = ""
|
||||
|
||||
// determine if options or values are used in teh listing
|
||||
if (valuesArray.length > 0 && options.length > 0){
|
||||
|
||||
// multiselect list -> options only + selected the ones in valuesArray
|
||||
resultArray = options;
|
||||
selectedArray = valuesArray
|
||||
|
||||
} else if (valuesArray.length > 0 && options.length == 0){
|
||||
|
||||
// editable list -> values only
|
||||
resultArray = arrayToObject(valuesArray)
|
||||
cssClass = "interactable-option" // generates [1x 📝 | 2x 🚮]
|
||||
} else if (options.length > 0){
|
||||
|
||||
// dropdown -> options only (value == 1 STRING not ARRAY)
|
||||
resultArray = options;
|
||||
}
|
||||
|
||||
resultArray.forEach(function(item) {
|
||||
|
||||
labelName = item.name
|
||||
|
||||
if(labelName != '❌None')
|
||||
{
|
||||
labelName = reverseTransformers(labelName, transformers)
|
||||
}
|
||||
|
||||
// needs to happen always if options ued as source
|
||||
let selected = options.length != 0 && valuesArray.includes(item.id) ? 'selected' : '';
|
||||
|
||||
optionsHtml += `<option class="${cssClass}" value="${item.id}" ${selected}>${labelName}</option>`;
|
||||
});
|
||||
|
||||
// Place the resulting HTML into the specified placeholder div
|
||||
$("#" + placeholder).replaceWith(optionsHtml);
|
||||
}
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Processor to generate a list
|
||||
function generateList(options, valuesArray, targetField, transformers, placeholder) {
|
||||
var listHtml = "";
|
||||
options.forEach(function(item) {
|
||||
|
||||
labelName = item.name
|
||||
|
||||
if(labelName != '❌None')
|
||||
{
|
||||
labelName = reverseTransformers(labelName, transformers)
|
||||
}
|
||||
|
||||
let selected = valuesArray.includes(item.id) ? 'selected' : '';
|
||||
|
||||
listHtml += `<li ${selected}>${labelName}</li>`;
|
||||
});
|
||||
|
||||
// Place the resulting HTML into the specified placeholder div
|
||||
$("#" + placeholder).replaceWith(listHtml);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Processor to generate a list in the deviceDetails page
|
||||
function genListWithInputSet(options, valuesArray, targetField, transformers, placeholder) {
|
||||
|
||||
var listHtml = "";
|
||||
|
||||
|
||||
options.forEach(function(item) {
|
||||
|
||||
let selected = valuesArray.includes(item.id) ? 'selected' : '';
|
||||
|
||||
// console.log(item);
|
||||
|
||||
labelName = item.name
|
||||
|
||||
if(labelName != '❌None')
|
||||
{
|
||||
labelName = reverseTransformers(labelName, transformers)
|
||||
// console.log(transformers);
|
||||
}
|
||||
|
||||
listHtml += `<li ${selected}>
|
||||
<a href="javascript:void(0)" onclick="setTextValue('${targetField}','${item.id}')">${labelName}</a>
|
||||
</li>`;
|
||||
|
||||
});
|
||||
|
||||
// Place the resulting HTML into the specified placeholder div
|
||||
$("#" + placeholder).replaceWith(listHtml);
|
||||
}
|
||||
|
||||
32
front/js/tests.js
Executable file
@@ -0,0 +1,32 @@
|
||||
// --------------------------------------------------
|
||||
// Check if database is locked
|
||||
function lockDatabase(delay=20) {
|
||||
$.ajax({
|
||||
url: 'php/server/dbHelper.php', // Replace with the actual path to your PHP file
|
||||
type: 'GET',
|
||||
data: { action: 'lockDatabase', delay: delay },
|
||||
success: function(response) {
|
||||
console.log('Executed');
|
||||
},
|
||||
error: function() {
|
||||
console.log('Error ocurred');
|
||||
}
|
||||
});
|
||||
|
||||
let times = delay;
|
||||
let countdownInterval = setInterval(() => {
|
||||
times--;
|
||||
console.log(`Remaining time: ${times} seconds`);
|
||||
|
||||
if (times <= 0) {
|
||||
clearInterval(countdownInterval);
|
||||
console.log('Countdown finished');
|
||||
}
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -13,13 +13,13 @@
|
||||
// -----------------------------------------------------------------------------
|
||||
function initDeviceSelectors() {
|
||||
|
||||
console.log(devicesList)
|
||||
// console.log(devicesList)
|
||||
// Retrieve device list from session variable
|
||||
var devicesListAll_JSON = getCache('devicesListAll_JSON');
|
||||
|
||||
var devicesList = JSON.parse(devicesListAll_JSON);
|
||||
|
||||
console.log(devicesList);
|
||||
// console.log(devicesList);
|
||||
|
||||
|
||||
// Check if both device list exists
|
||||
@@ -74,78 +74,54 @@ function initDeviceSelectors() {
|
||||
}
|
||||
|
||||
}, 10);
|
||||
|
||||
}
|
||||
|
||||
|
||||
// --------------------------------------------------------
|
||||
//Initialize Select2 Elements and make them sortable
|
||||
|
||||
$(function () {
|
||||
// Iterate over each Select2 dropdown
|
||||
$('.select2').each(function() {
|
||||
var selectEl = $(this).select2();
|
||||
// // -----------------------------------------------------------------------------
|
||||
// // (ASYNC) Initiate dropdown
|
||||
// function generateSetOptions(settingKey, // Identifier for the setting
|
||||
// valuesArray, // Array of values to be pre-selected in the dropdown
|
||||
// targetLocation, // ID of the HTML element where dropdown should be rendered (will be replaced)
|
||||
// callbackToGenerateEntries, // Callback function to generate entries based on options
|
||||
// targetField, // Target field or element where selected value should be applied or updated
|
||||
// nameTransformer) // callback to transform the name (e.g. base64)
|
||||
// {
|
||||
|
||||
// Apply sortable functionality to the dropdown's dropdown-container
|
||||
selectEl.next().children().children().children().sortable({
|
||||
containment: 'parent',
|
||||
update: function () {
|
||||
var sortedValues = $(this).children().map(function() {
|
||||
return $(this).attr('title');
|
||||
}).get();
|
||||
|
||||
var sortedOptions = selectEl.find('option').sort(function(a, b) {
|
||||
return sortedValues.indexOf($(a).text()) - sortedValues.indexOf($(b).text());
|
||||
});
|
||||
|
||||
// Replace all options in selectEl
|
||||
selectEl.empty().append(sortedOptions);
|
||||
|
||||
// Trigger change event on Select2
|
||||
selectEl.trigger('change');
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Initiate dropdown
|
||||
function initSettingDropdown(settingKey, // Identifier for the setting
|
||||
valuesArray, // Array of values to be pre-selected in the dropdown
|
||||
targetLocation, // ID of the HTML element where dropdown should be rendered (will be replaced)
|
||||
callbackToGenerateEntries, // Callback function to generate entries based on options
|
||||
targetField, // Target field or element where selected value should be applied or updated
|
||||
nameTransformer) // callback to transform the name (e.g. base64)
|
||||
{
|
||||
|
||||
var optionsHtml = ""
|
||||
// var optionsHtml = ""
|
||||
|
||||
optionsArray = createArray(getSettingOptions(settingKey))
|
||||
// // NOTE {value} options to replace with a setting or SQL value are handled in the cacheSettings() function
|
||||
// optionsArray = createArray(getSettingOptions(settingKey))
|
||||
|
||||
// check if the result is a SQL query
|
||||
if(isSQLQuery(optionsArray[0]))
|
||||
{
|
||||
readData(optionsArray[0], callbackToGenerateEntries, valuesArray, targetLocation, targetField, nameTransformer);
|
||||
|
||||
} else // this should be already an array, e.g. from a setting or pre-defined
|
||||
{
|
||||
optionsArray.forEach(option => {
|
||||
let selected = valuesArray.includes(option) ? 'selected' : '';
|
||||
optionsHtml += `<option value="${option}" ${selected}>${option}</option>`;
|
||||
});
|
||||
|
||||
// Replace the specified placeholder div with the resulting HTML
|
||||
setTimeout(() => {
|
||||
// // check if the result is a SQL query
|
||||
// if(optionsArray.length > 0 && isSQLQuery(optionsArray[0]))
|
||||
// {
|
||||
|
||||
$("#" + targetLocation).replaceWith(optionsHtml);
|
||||
// if (settingKey == "NEWDEV_dev_Network_Node_MAC_ADDR") {
|
||||
// console.log("isSQLQuery in generateSetOptions");
|
||||
|
||||
// }
|
||||
// readData(optionsArray[0], callbackToGenerateEntries, valuesArray, targetLocation, targetField, nameTransformer);
|
||||
|
||||
// } else // this should be already an array, e.g. from a setting or pre-defined
|
||||
// {
|
||||
// optionsArray.forEach(option => {
|
||||
// let selected = valuesArray.includes(option) ? 'selected' : '';
|
||||
// optionsHtml += `<option value="${option}" ${selected}>${option}</option>`;
|
||||
// });
|
||||
|
||||
// // Replace the specified placeholder div with the resulting HTML
|
||||
// setTimeout(() => {
|
||||
|
||||
// $("#" + targetLocation).replaceWith(optionsHtml);
|
||||
|
||||
}, 50);
|
||||
|
||||
}
|
||||
}
|
||||
// }, 50);
|
||||
// }
|
||||
|
||||
|
||||
|
||||
// }
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
@@ -179,88 +155,6 @@ function hideUIelements(settingKey) {
|
||||
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Data processors
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Processor to generate options for a dropdown menu
|
||||
function generateDropdownOptions(data, valuesArray, targetField, nameTransformer) {
|
||||
var optionsHtml = "";
|
||||
data.forEach(function(item) {
|
||||
|
||||
labelName = item.name
|
||||
|
||||
// console.log(nameTransformer);
|
||||
// console.log(labelName);
|
||||
|
||||
// if(nameTransformer && nameTransformer != '' && labelName != '❌None')
|
||||
// {
|
||||
// console.log(labelName);
|
||||
// labelName = nameTransformer(labelName)
|
||||
// console.log(labelName);
|
||||
// }
|
||||
|
||||
let selected = valuesArray.includes(item.id) ? 'selected' : '';
|
||||
|
||||
optionsHtml += `<option value="${item.id}" ${selected}>${labelName}</option>`;
|
||||
});
|
||||
return `${optionsHtml}`;
|
||||
}
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Processor to generate a list
|
||||
function generateList(data, valuesArray, targetField, nameTransformer) {
|
||||
var listHtml = "";
|
||||
data.forEach(function(item) {
|
||||
|
||||
labelName = item.name
|
||||
|
||||
if(nameTransformer && nameTransformer != '' && labelName != '❌None')
|
||||
{
|
||||
labelName = nameTransformer(labelName)
|
||||
}
|
||||
|
||||
let selected = valuesArray.includes(item.id) ? 'selected' : '';
|
||||
|
||||
listHtml += `<li ${selected}>${labelName}</li>`;
|
||||
});
|
||||
|
||||
return listHtml;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Processor to generate a list in the deviceDetails page
|
||||
function genListWithInputSet(data, valuesArray, targetField, nameTransformer) {
|
||||
|
||||
var listHtml = "";
|
||||
|
||||
console.log(data);
|
||||
data.forEach(function(item) {
|
||||
|
||||
let selected = valuesArray.includes(item.id) ? 'selected' : '';
|
||||
|
||||
console.log(item);
|
||||
|
||||
labelName = item.name
|
||||
|
||||
if(nameTransformer && nameTransformer != '' && labelName != '❌None')
|
||||
{
|
||||
labelName = nameTransformer(labelName)
|
||||
}
|
||||
|
||||
listHtml += `<li ${selected}>
|
||||
<a href="javascript:void(0)" onclick="setTextValue('${targetField}','${item.id}')">${labelName}</a>
|
||||
</li>`;
|
||||
|
||||
});
|
||||
|
||||
return listHtml;
|
||||
}
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Updates the icon preview
|
||||
function updateIconPreview (inputId) {
|
||||
@@ -277,10 +171,162 @@ function updateIconPreview (inputId) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Generic function to copy text to clipboard
|
||||
function copyToClipboard(buttonElement) {
|
||||
const text = $(buttonElement).data('text');
|
||||
if (navigator.clipboard && navigator.clipboard.writeText) {
|
||||
navigator.clipboard.writeText(text).then(() => {
|
||||
showMessage('Copied to clipboard: ' + text, 1500);
|
||||
}).catch(err => {
|
||||
console.error('Failed to copy: ', err);
|
||||
});
|
||||
} else {
|
||||
// Fallback to execCommand if Clipboard API is not available
|
||||
const tempInput = document.createElement('input');
|
||||
tempInput.value = text;
|
||||
document.body.appendChild(tempInput);
|
||||
tempInput.select();
|
||||
try {
|
||||
document.execCommand('copy');
|
||||
showMessage('Copied to clipboard: ' + text, 1500);
|
||||
} catch (err) {
|
||||
console.error('Failed to copy: ', err);
|
||||
}
|
||||
document.body.removeChild(tempInput);
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Simple Sortable Table columns
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
function sortColumn(element) {
|
||||
var th = $(element).closest('th');
|
||||
var table = th.closest('table');
|
||||
var columnIndex = th.index();
|
||||
var ascending = !th.data('asc');
|
||||
sortTable(table, columnIndex, ascending);
|
||||
th.data('asc', ascending);
|
||||
}
|
||||
|
||||
function sortTable(table, columnIndex, ascending) {
|
||||
var tbody = table.find('tbody');
|
||||
var rows = tbody.find('tr').toArray().sort(comparer(columnIndex));
|
||||
if (!ascending) {
|
||||
rows = rows.reverse();
|
||||
}
|
||||
for (var i = 0; i < rows.length; i++) {
|
||||
tbody.append(rows[i]);
|
||||
}
|
||||
}
|
||||
|
||||
function comparer(index) {
|
||||
return function(a, b) {
|
||||
var valA = getCellValue(a, index);
|
||||
var valB = getCellValue(b, index);
|
||||
return $.isNumeric(valA) && $.isNumeric(valB) ? valA - valB : valA.localeCompare(valB);
|
||||
};
|
||||
}
|
||||
|
||||
function getCellValue(row, index) {
|
||||
return $(row).children('td').eq(index).text();
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// handling events on the backend initiated by the front end START
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
modalEventStatusId = 'modal-message-front-event'
|
||||
|
||||
// --------------------------------------------------------
|
||||
// Calls a backend function to add a front-end event (specified by the attributes 'data-myevent' and 'data-myparam-plugin' on the passed element) to an execution queue
|
||||
function addToExecutionQueue_settingEvent(element)
|
||||
{
|
||||
|
||||
// value has to be in format event|param. e.g. run|ARPSCAN
|
||||
action = `${getGuid()}|${$(element).attr('data-myevent')}|${$(element).attr('data-myparam-plugin')}`
|
||||
|
||||
$.ajax({
|
||||
method: "POST",
|
||||
url: "php/server/util.php",
|
||||
data: { function: "addToExecutionQueue", action: action },
|
||||
success: function(data, textStatus) {
|
||||
// showModalOk ('Result', data );
|
||||
|
||||
// show message
|
||||
showModalOk(getString("general_event_title"), `${getString("general_event_description")} <br/> <br/> <code id='${modalEventStatusId}'></code>`);
|
||||
|
||||
updateModalState()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// --------------------------------------------------------
|
||||
// Updating the execution queue in in modal pop-up
|
||||
function updateModalState() {
|
||||
setTimeout(function() {
|
||||
// Fetch the content from the log file using an AJAX request
|
||||
$.ajax({
|
||||
url: '/log/execution_queue.log',
|
||||
type: 'GET',
|
||||
success: function(data) {
|
||||
// Update the content of the HTML element (e.g., a div with id 'logContent')
|
||||
$('#'+modalEventStatusId).html(data);
|
||||
|
||||
updateModalState();
|
||||
},
|
||||
error: function() {
|
||||
// Handle error, such as the file not being found
|
||||
$('#logContent').html('Error: Log file not found.');
|
||||
}
|
||||
});
|
||||
}, 2000);
|
||||
}
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// initialize
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
initDeviceSelectors();
|
||||
|
||||
setTimeout(() => {
|
||||
|
||||
initDeviceSelectors();
|
||||
|
||||
|
||||
// --------------------------------------------------------
|
||||
//Initialize Select2 Elements and make them sortable
|
||||
|
||||
$(function () {
|
||||
// Iterate over each Select2 dropdown
|
||||
$('.select2').each(function() {
|
||||
var selectEl = $(this).select2();
|
||||
|
||||
// Apply sortable functionality to the dropdown's dropdown-container
|
||||
selectEl.next().children().children().children().sortable({
|
||||
containment: 'parent',
|
||||
update: function () {
|
||||
var sortedValues = $(this).children().map(function() {
|
||||
return $(this).attr('title');
|
||||
}).get();
|
||||
|
||||
var sortedOptions = selectEl.find('option').sort(function(a, b) {
|
||||
return sortedValues.indexOf($(a).text()) - sortedValues.indexOf($(b).text());
|
||||
});
|
||||
|
||||
// Replace all options in selectEl
|
||||
selectEl.empty().append(sortedOptions);
|
||||
|
||||
// Trigger change event on Select2
|
||||
selectEl.trigger('change');
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
}, 500);
|
||||
|
||||
|
||||
console.log("init ui_components.js")
|
||||
1
front/lib/crypto/crypto-js.min.js
vendored
Executable file
@@ -311,6 +311,10 @@ $db->close();
|
||||
<option value="16"><?= lang('Device_TableHead_Location');?></option>
|
||||
<option value="17"><?= lang('Device_TableHead_Vendor');?></option>
|
||||
<option value="18"><?= lang('Device_TableHead_Port');?></option>
|
||||
<option value="19"><?= lang('Device_TableHead_GUID');?></option>
|
||||
<option value="20"><?= lang('Device_TableHead_SyncHubNodeName');?></option>
|
||||
<option value="21"><?= lang('Device_TableHead_NetworkSite');?></option>
|
||||
<option value="22"><?= lang('Device_TableHead_SSID');?></option>
|
||||
</select>
|
||||
<span class="input-group-addon"><i title="<?= lang('Gen_Save');?>" class="fa fa-save pointer" onclick="saveSelectedColumns();"></i></span>
|
||||
</div>
|
||||
@@ -327,37 +331,37 @@ $db->close();
|
||||
<div class="tab-pane" id="tab_DBTools">
|
||||
<div class="db_info_table">
|
||||
<div class="db_info_table_row">
|
||||
<div class="db_tools_table_cell_a" style="">
|
||||
<div class="db_tools_table_cell_a" >
|
||||
<button type="button" class="btn btn-default pa-btn pa-btn-delete bg-red dbtools-button" id="btnDeleteMAC" onclick="askDeleteDevicesWithEmptyMACs()"><?= lang('Maintenance_Tool_del_empty_macs');?></button>
|
||||
</div>
|
||||
<div class="db_tools_table_cell_b"><?= lang('Maintenance_Tool_del_empty_macs_text');?></div>
|
||||
</div>
|
||||
<div class="db_info_table_row">
|
||||
<div class="db_tools_table_cell_a" style="">
|
||||
<div class="db_tools_table_cell_a" >
|
||||
<button type="button" class="btn btn-default pa-btn pa-btn-delete bg-red dbtools-button" id="btnDeleteMAC" onclick="askDeleteAllDevices()"><?= lang('Maintenance_Tool_del_alldev');?></button>
|
||||
</div>
|
||||
<div class="db_tools_table_cell_b"><?= lang('Maintenance_Tool_del_alldev_text');?></div>
|
||||
</div>
|
||||
<div class="db_info_table_row">
|
||||
<div class="db_tools_table_cell_a" style="">
|
||||
<div class="db_tools_table_cell_a" >
|
||||
<button type="button" class="btn btn-default pa-btn pa-btn-delete bg-red dbtools-button" id="btnDeleteUnknown" onclick="askDeleteUnknown()"><?= lang('Maintenance_Tool_del_unknowndev');?></button>
|
||||
</div>
|
||||
<div class="db_tools_table_cell_b"><?= lang('Maintenance_Tool_del_unknowndev_text');?></div>
|
||||
</div>
|
||||
<div class="db_info_table_row">
|
||||
<div class="db_tools_table_cell_a" style="">
|
||||
<div class="db_tools_table_cell_a" >
|
||||
<button type="button" class="btn btn-default pa-btn pa-btn-delete bg-red dbtools-button" id="btnDeleteEvents" onclick="askDeleteEvents()"><?= lang('Maintenance_Tool_del_allevents');?></button>
|
||||
</div>
|
||||
<div class="db_tools_table_cell_b"><?= lang('Maintenance_Tool_del_allevents_text');?></div>
|
||||
</div>
|
||||
<div class="db_info_table_row">
|
||||
<div class="db_tools_table_cell_a" style="">
|
||||
<div class="db_tools_table_cell_a" >
|
||||
<button type="button" class="btn btn-default pa-btn pa-btn-delete bg-red dbtools-button" id="btnDeleteEvents30" onclick="askDeleteEvents30()"><?= lang('Maintenance_Tool_del_allevents30');?></button>
|
||||
</div>
|
||||
<div class="db_tools_table_cell_b"><?= lang('Maintenance_Tool_del_allevents30_text');?></div>
|
||||
</div>
|
||||
<div class="db_info_table_row">
|
||||
<div class="db_tools_table_cell_a" style="">
|
||||
<div class="db_tools_table_cell_a" >
|
||||
<button type="button" class="btn btn-default pa-btn pa-btn-delete bg-red dbtools-button" id="btnDeleteActHistory" onclick="askDeleteActHistory()"><?= lang('Maintenance_Tool_del_ActHistory');?></button>
|
||||
</div>
|
||||
<div class="db_tools_table_cell_b"><?= lang('Maintenance_Tool_del_ActHistory_text');?></div>
|
||||
@@ -366,161 +370,90 @@ $db->close();
|
||||
</div>
|
||||
<div class="tab-pane" id="tab_BackupRestore">
|
||||
<div class="db_info_table">
|
||||
<div class="db_info_table_row">
|
||||
<div class="db_tools_table_cell_a" >
|
||||
<button type="button" class="btn btn-default pa-btn bg-green dbtools-button" id="btnExportCSV" onclick="ExportCSV()"><?= lang('Maintenance_Tool_ExportCSV');?></button>
|
||||
</div>
|
||||
<div class="db_tools_table_cell_b"><?= lang('Maintenance_Tool_ExportCSV_text');?></div>
|
||||
</div>
|
||||
<div class="db_info_table_row">
|
||||
<div class="db_tools_table_cell_a" style="">
|
||||
<button type="button" class="btn btn-default pa-btn pa-btn-delete bg-red dbtools-button" id="btnPiaBackupDBtoArchive" onclick="askPiaBackupDBtoArchive()"><?= lang('Maintenance_Tool_backup');?></button>
|
||||
<div class="db_tools_table_cell_a" >
|
||||
<button type="button" class="btn btn-default pa-btn pa-btn-delete bg-red dbtools-button" id="btnImportCSV" onclick="askImportCSV()"><?= lang('Maintenance_Tool_ImportCSV');?></button>
|
||||
</div>
|
||||
<div class="db_tools_table_cell_b"><?= lang('Maintenance_Tool_ImportCSV_text');?></div>
|
||||
</div>
|
||||
<div class="db_info_table_row">
|
||||
<div class="db_tools_table_cell_a" >
|
||||
<button type="button" class="btn btn-default pa-btn pa-btn-delete bg-red dbtools-button" id="btnImportPastedCSV" onclick="askImportPastedCSV()"><?= lang('Maintenance_Tool_ImportPastedCSV');?></button>
|
||||
</div>
|
||||
<div class="db_tools_table_cell_b"><?= lang('Maintenance_Tool_ImportPastedCSV_text');?></div>
|
||||
</div>
|
||||
<div class="db_info_table_row">
|
||||
<div class="db_tools_table_cell_a" >
|
||||
<button type="button" class="btn btn-default pa-btn bg-green dbtools-button" id="btnPiaBackupDBtoArchive" onclick="askPiaBackupDBtoArchive()"><?= lang('Maintenance_Tool_backup');?></button>
|
||||
</div>
|
||||
<div class="db_tools_table_cell_b"><?= lang('Maintenance_Tool_backup_text');?></div>
|
||||
</div>
|
||||
<div class="db_info_table_row">
|
||||
<div class="db_tools_table_cell_a" style="">
|
||||
<div class="db_tools_table_cell_a" >
|
||||
<button type="button" class="btn btn-default pa-btn pa-btn-delete bg-red dbtools-button" id="btnPiaRestoreDBfromArchive" onclick="askPiaRestoreDBfromArchive()"><?= lang('Maintenance_Tool_restore');?><br><?php echo $latestbackup_date;?></button>
|
||||
</div>
|
||||
<div class="db_tools_table_cell_b"><?= lang('Maintenance_Tool_restore_text');?></div>
|
||||
</div>
|
||||
<div class="db_info_table_row">
|
||||
<div class="db_tools_table_cell_a" style="">
|
||||
<div class="db_tools_table_cell_a" >
|
||||
<button type="button" class="btn btn-default pa-btn pa-btn-delete bg-red dbtools-button" id="btnPiaPurgeDBBackups" onclick="askPiaPurgeDBBackups()"><?= lang('Maintenance_Tool_purgebackup');?></button>
|
||||
</div>
|
||||
<div class="db_tools_table_cell_b"><?= lang('Maintenance_Tool_purgebackup_text');?></div>
|
||||
</div>
|
||||
<div class="db_info_table_row">
|
||||
<div class="db_tools_table_cell_a" style="">
|
||||
<button type="button" class="btn btn-default pa-btn bg-green dbtools-button" id="btnExportCSV" onclick="askExportCSV()"><?= lang('Maintenance_Tool_ExportCSV');?></button>
|
||||
</div>
|
||||
<div class="db_tools_table_cell_b"><?= lang('Maintenance_Tool_ExportCSV_text');?></div>
|
||||
</div>
|
||||
<div class="db_info_table_row">
|
||||
<div class="db_tools_table_cell_a" style="">
|
||||
<button type="button" class="btn btn-default pa-btn pa-btn-delete bg-red dbtools-button" id="btnImportCSV" onclick="askImportCSV()"><?= lang('Maintenance_Tool_ImportCSV');?></button>
|
||||
</div>
|
||||
<div class="db_tools_table_cell_b"><?= lang('Maintenance_Tool_ImportCSV_text');?></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- ---------------------------Logging-------------------------------------------- -->
|
||||
<div class="tab-pane" id="tab_Logging">
|
||||
<div class="db_info_table">
|
||||
<div class="log-area box box-solid box-primary">
|
||||
<div class="row logs-row">
|
||||
<textarea id="app_log" class="logs" cols="70" rows="10" wrap='off' readonly >
|
||||
<?php
|
||||
if(filesize("./log/app.log") > 2000000)
|
||||
{
|
||||
echo file_get_contents( "./log/app.log", false, null, -2000000);
|
||||
}
|
||||
else{
|
||||
echo file_get_contents( "./log/app.log" );
|
||||
}
|
||||
|
||||
?>
|
||||
</textarea>
|
||||
</div>
|
||||
<div class="row logs-row" >
|
||||
<div>
|
||||
<div class="log-file">app.log <div class="logs-size"><?php echo number_format((filesize("./log/app.log") / 1000000),2,",",".") . ' MB';?>
|
||||
<span class="span-padding"><a href="./log/app.log" target="_blank"><i class="fa fa-download"></i> </a></span>
|
||||
</div></div>
|
||||
<div class="log-purge">
|
||||
<button class="btn btn-primary" onclick="logManage('app.log','cleanLog')"><?= lang('Gen_Purge');?></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="log-area box box-solid box-primary">
|
||||
<div class="row logs-row">
|
||||
<textarea id="app_front_log" class="logs" cols="70" rows="10" wrap='off' readonly><?php echo file_get_contents( "./log/app_front.log" ); ?>
|
||||
</textarea>
|
||||
</div>
|
||||
<div class="row logs-row" >
|
||||
<div>
|
||||
<div class="log-file">app_front.log<div class="logs-size"><?php echo number_format((filesize("./log/app_front.log") / 1000000),2,",",".") . ' MB';?>
|
||||
<span class="span-padding"><a href="./log/app_front.log"><i class="fa fa-download"></i> </a></span>
|
||||
</div></div>
|
||||
<div class="log-purge">
|
||||
<button class="btn btn-primary" onclick="logManage('app_front.log','cleanLog')"><?= lang('Gen_Purge');?></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="log-area box box-solid box-primary">
|
||||
<div class="row logs-row">
|
||||
<textarea id="app_php_log" class="logs" cols="70" rows="10" wrap='off' readonly><?php echo file_get_contents( "./log/app.php_errors.log" ); ?>
|
||||
</textarea>
|
||||
</div>
|
||||
<div class="row logs-row" >
|
||||
<div>
|
||||
<div class="log-file">app.php_errors.log<div class="logs-size"><?php echo number_format((filesize("./log/app.php_errors.log") / 1000000),2,",",".") . ' MB';?>
|
||||
<span class="span-padding"><a href="./log/app.php_errors.log"><i class="fa fa-download"></i> </a></span>
|
||||
</div></div>
|
||||
<div class="log-purge">
|
||||
<button class="btn btn-primary" onclick="logManage('app.php_errors.log','cleanLog')"><?= lang('Gen_Purge');?></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="log-area box box-solid box-primary ">
|
||||
<div class="row logs-row">
|
||||
<textarea id="nginx_error_log" class="logs logs-small" cols="70" rows="10" wrap='off' readonly><?php echo file_get_contents( "/var/log/nginx/error.log" ); ?>
|
||||
</textarea>
|
||||
</div>
|
||||
<div class="row logs-row" >
|
||||
<div>
|
||||
<div class="log-file" title="/var/log/nginx/error.log">nginx/error.log</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="log-area box box-solid box-primary">
|
||||
<div class="row logs-row">
|
||||
<textarea id="stdout_log" class="logs logs-small" cols="70" rows="10" wrap='off' readonly><?php echo file_get_contents( "./log/stdout.log" ); ?>
|
||||
</textarea>
|
||||
</div>
|
||||
<div class="row logs-row" >
|
||||
<div>
|
||||
<div class="log-file">stdout.log<div class="logs-size"><?php echo number_format((filesize("./log/stdout.log") / 1000000),2,",",".") . ' MB';?>
|
||||
<span class="span-padding"><a href="./log/stdout.log"><i class="fa fa-download"></i> </a></span>
|
||||
</div></div>
|
||||
<div class="log-purge">
|
||||
<button class="btn btn-primary" onclick="logManage('stdout.log','cleanLog')"><?= lang('Gen_Purge');?></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="log-area box box-solid box-primary">
|
||||
<div class="row logs-row">
|
||||
<textarea id="stderr_log" class="logs logs-small" cols="70" rows="10" wrap='off' readonly><?php echo file_get_contents( "./log/stderr.log" ); ?>
|
||||
</textarea>
|
||||
</div>
|
||||
<div class="row logs-row" >
|
||||
<div>
|
||||
<div class="log-file">stderr.log<div class="logs-size"><?php echo number_format((filesize("./log/stderr.log") / 1000000),2,",",".") . ' MB';?>
|
||||
<span class="span-padding"><a href="./log/stderr.log"><i class="fa fa-download"></i> </a></span>
|
||||
</div></div>
|
||||
<div class="log-purge">
|
||||
<button class="btn btn-primary" onclick="logManage('stderr.log','cleanLog')"><?= lang('Gen_Purge');?></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="container">
|
||||
<div class="row actions">
|
||||
<div class="col-sm-2">
|
||||
<div class="form-check toggle">
|
||||
<label class="form-check-label pointer" for="logsAutoRefresh">
|
||||
<input class="form-check-input" type="checkbox" id="logsAutoRefresh" onchange="toggleAutoRefresh()" />
|
||||
Auto-refresh
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<!-- ---------------------------Bulk edit -------------------------------------------- -->
|
||||
<div class="tab-pane" id="tab_multiEdit">
|
||||
<div class="db_info_table">
|
||||
<div class="box box-solid">
|
||||
<?php
|
||||
require 'multiEditCore.php';
|
||||
?>
|
||||
<div class="col-sm-2">
|
||||
<div class="form-check toggle">
|
||||
<label class="form-check-label pointer" for="logsAutoScroll">
|
||||
<input class="form-check-input" type="checkbox" checked id="logsAutoScroll" />
|
||||
Auto-scroll
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-8">
|
||||
<div class="form-inline toggle">
|
||||
<input class="form-control" type="text" id="logsFilter" oninput="applyFilter()" placeholder="Filter lines with text..." />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="db_info_table">
|
||||
|
||||
<div id="logsPlc"></div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- ---------------------------Bulk edit -------------------------------------------- -->
|
||||
<div class="tab-pane" id="tab_multiEdit">
|
||||
<div class="db_info_table">
|
||||
<div class="box box-solid">
|
||||
<?php
|
||||
require 'multiEditCore.php';
|
||||
?>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<!-- ------------------------------------------------------------------------------ -->
|
||||
</div>
|
||||
<!-- ------------------------------------------------------------------------------ -->
|
||||
|
||||
</div>
|
||||
|
||||
@@ -551,7 +484,6 @@ $db->close();
|
||||
|
||||
|
||||
|
||||
|
||||
<script>
|
||||
|
||||
var emptyArr = ['undefined', "", undefined, null];
|
||||
@@ -559,76 +491,92 @@ var selectedTab = 'tab_Settings_id';
|
||||
|
||||
initializeTabs();
|
||||
|
||||
// -----------------------------------------------------------
|
||||
// delete devices with emty macs
|
||||
function askDeleteDevicesWithEmptyMACs () {
|
||||
// Ask
|
||||
showModalWarning('<?= lang('Maintenance_Tool_del_empty_macs_noti');?>', '<?= lang('Maintenance_Tool_del_empty_macs_noti_text');?>',
|
||||
'Cancel', 'Delete', 'deleteDevicesWithEmptyMACs');
|
||||
}
|
||||
// -----------------------------------------------------------
|
||||
function deleteDevicesWithEmptyMACs()
|
||||
{
|
||||
// Delete device
|
||||
$.get('php/server/devices.php?action=deleteAllWithEmptyMACs', function(msg) {
|
||||
showMessage (msg);
|
||||
write_notification(`[Maintenance] All devices witout a Mac manually deleted`, 'info')
|
||||
});
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------
|
||||
// delete all devices
|
||||
function askDeleteAllDevices () {
|
||||
// Ask
|
||||
showModalWarning('<?= lang('Maintenance_Tool_del_alldev_noti');?>', '<?= lang('Maintenance_Tool_del_alldev_noti_text');?>',
|
||||
'<?= lang('Gen_Cancel');?>', '<?= lang('Gen_Delete');?>', 'deleteAllDevices');
|
||||
}
|
||||
// -----------------------------------------------------------
|
||||
function deleteAllDevices()
|
||||
{
|
||||
// Delete device
|
||||
$.get('php/server/devices.php?action=deleteAllDevices', function(msg) {
|
||||
showMessage (msg);
|
||||
write_notification(`[Maintenance] All devices manually deleted`, 'info')
|
||||
});
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------
|
||||
// delete all (unknown) devices
|
||||
function askDeleteUnknown () {
|
||||
// Ask
|
||||
showModalWarning('<?= lang('Maintenance_Tool_del_unknowndev_noti');?>', '<?= lang('Maintenance_Tool_del_unknowndev_noti_text');?>',
|
||||
'<?= lang('Gen_Cancel');?>', '<?= lang('Gen_Delete');?>', 'deleteUnknownDevices');
|
||||
}
|
||||
// -----------------------------------------------------------
|
||||
function deleteUnknownDevices()
|
||||
{
|
||||
// Execute
|
||||
$.get('php/server/devices.php?action=deleteUnknownDevices', function(msg) {
|
||||
showMessage (msg);
|
||||
write_notification(`[Maintenance] Unknown devices manually deleted`, 'info')
|
||||
});
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------
|
||||
// delete all Events
|
||||
function askDeleteEvents () {
|
||||
// Ask
|
||||
showModalWarning('<?= lang('Maintenance_Tool_del_allevents_noti');?>', '<?= lang('Maintenance_Tool_del_allevents_noti_text');?>',
|
||||
'<?= lang('Gen_Cancel');?>', '<?= lang('Gen_Delete');?>', 'deleteEvents');
|
||||
}
|
||||
// -----------------------------------------------------------
|
||||
function deleteEvents()
|
||||
{
|
||||
// Execute
|
||||
$.get('php/server/devices.php?action=deleteEvents', function(msg) {
|
||||
showMessage (msg);
|
||||
write_notification(`[Maintenance] Events manually deleted (all)`, 'info')
|
||||
});
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------
|
||||
// delete all Events older than 30 days
|
||||
function askDeleteEvents30 () {
|
||||
// Ask
|
||||
showModalWarning('<?= lang('Maintenance_Tool_del_allevents30_noti');?>', '<?= lang('Maintenance_Tool_del_allevents30_noti_text');?>',
|
||||
'<?= lang('Gen_Cancel');?>', '<?= lang('Gen_Delete');?>', 'deleteEvents30');
|
||||
}
|
||||
// -----------------------------------------------------------
|
||||
function deleteEvents30()
|
||||
{
|
||||
// Execute
|
||||
$.get('php/server/devices.php?action=deleteEvents30', function(msg) {
|
||||
showMessage (msg);
|
||||
write_notification(`[Maintenance] Events manually deleted (last 30 days kep)`, 'info')
|
||||
});
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------
|
||||
// delete History
|
||||
function askDeleteActHistory () {
|
||||
// Ask
|
||||
@@ -643,6 +591,7 @@ function deleteActHistory()
|
||||
});
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------
|
||||
// Backup DB to Archive
|
||||
function askPiaBackupDBtoArchive () {
|
||||
// Ask
|
||||
@@ -657,6 +606,7 @@ function PiaBackupDBtoArchive()
|
||||
});
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------
|
||||
// Restore DB from Archive
|
||||
function askPiaRestoreDBfromArchive () {
|
||||
// Ask
|
||||
@@ -671,6 +621,7 @@ function PiaRestoreDBfromArchive()
|
||||
});
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------
|
||||
// Purge Backups
|
||||
function askPiaPurgeDBBackups() {
|
||||
// Ask
|
||||
@@ -685,18 +636,47 @@ function PiaPurgeDBBackups()
|
||||
});
|
||||
}
|
||||
|
||||
// Export CSV
|
||||
function askExportCSV() {
|
||||
// -----------------------------------------------------------
|
||||
// Restart Backend Python Server
|
||||
|
||||
function askRestartBackend() {
|
||||
// Ask
|
||||
showModalWarning('<?= lang('Maintenance_Tool_ExportCSV_noti');?>', '<?= lang('Maintenance_Tool_ExportCSV_noti_text');?>',
|
||||
'<?= lang('Gen_Cancel');?>', '<?= lang('Gen_Okay');?>', 'ExportCSV');
|
||||
showModalWarning('<?= lang('Maint_RestartServer');?>', '<?= lang('Maint_Restart_Server_noti_text');?>',
|
||||
'<?= lang('Gen_Cancel');?>', '<?= lang('Maint_RestartServer');?>', 'restartBackend');
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------
|
||||
function restartBackend() {
|
||||
|
||||
modalEventStatusId = 'modal-message-front-event'
|
||||
|
||||
// Execute
|
||||
$.ajax({
|
||||
method: "POST",
|
||||
url: "php/server/util.php",
|
||||
data: { function: "addToExecutionQueue", action: `${getGuid()}|cron_restart_backend` },
|
||||
success: function(data, textStatus) {
|
||||
// showModalOk ('Result', data );
|
||||
|
||||
// show message
|
||||
showModalOk(getString("general_event_title"), `${getString("general_event_description")} <br/> <br/> <code id='${modalEventStatusId}'></code>`);
|
||||
|
||||
updateModalState()
|
||||
|
||||
write_notification('[Maintenance] App manually restarted', 'info')
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------
|
||||
// Export CSV
|
||||
function ExportCSV()
|
||||
{
|
||||
// Execute
|
||||
openInNewTab("php/server/devices.php?action=ExportCSV")
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------
|
||||
// Import CSV
|
||||
function askImportCSV() {
|
||||
// Ask
|
||||
@@ -708,6 +688,27 @@ function ImportCSV()
|
||||
// Execute
|
||||
$.get('php/server/devices.php?action=ImportCSV', function(msg) {
|
||||
showMessage (msg);
|
||||
write_notification(`[Maintenance] Devices imported from CSV file`, 'info')
|
||||
});
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------
|
||||
// Import pasted CSV
|
||||
function askImportPastedCSV() {
|
||||
|
||||
// Add new icon as base64 string
|
||||
showModalInput ('<i class="fa fa-square-plus pointer"></i> <?= lang('Maintenance_Tool_ImportCSV_noti');?>', '<?= lang('Maintenance_Tool_ImportPastedCSV_noti_text');?>',
|
||||
'<?= lang('Gen_Cancel');?>', '<?= lang('Gen_Okay');?>', 'ImportPastedCSV');
|
||||
}
|
||||
|
||||
function ImportPastedCSV()
|
||||
{
|
||||
var csv = $('#modal-input-textarea').val();
|
||||
csvBase64 = btoa(csv)
|
||||
// Execute
|
||||
$.post('php/server/devices.php?action=ImportCSV', { content: csvBase64 }, function(msg) {
|
||||
showMessage(msg);
|
||||
write_notification(`[Maintenance] Devices imported from pasted content`, 'info');
|
||||
});
|
||||
}
|
||||
|
||||
@@ -777,41 +778,29 @@ function performLogManage() {
|
||||
data: { function: logFileAction, settings: targetLogFile },
|
||||
success: function(data, textStatus) {
|
||||
showModalOk ('Result', data );
|
||||
write_notification(`[Maintenance] Log file "${targetLogFile}" manually purged`, 'info')
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// --------------------------------------------------------
|
||||
// scroll down the log areas
|
||||
function scrollDown()
|
||||
{
|
||||
setTimeout(() => {
|
||||
|
||||
function scrollDown() {
|
||||
|
||||
var elementToCheck = $("#tab_Logging_id");
|
||||
|
||||
// Check if the parent <li> is active
|
||||
if (elementToCheck.parent().hasClass("active")) {
|
||||
{
|
||||
var areaIDs = ['app_log', 'app_front_log', 'IP_changes_log', 'stdout_log', 'stderr_log', 'app_pholus_log', 'app_pholus_lastrun_log', 'app_php_log'];
|
||||
|
||||
for (let i = 0; i < areaIDs.length; i++) {
|
||||
var textAreas = $("#logsPlc textarea");
|
||||
|
||||
var tempArea = $('#' + areaIDs[i]);
|
||||
|
||||
if (tempArea.length > 0)
|
||||
{
|
||||
$(tempArea[0]).scrollTop(tempArea[0].scrollHeight);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
textAreas.each(function() {
|
||||
$(this).scrollTop(this.scrollHeight);
|
||||
});
|
||||
}
|
||||
|
||||
}, 200);
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
// --------------------------------------------------------
|
||||
// Manage displayed columns
|
||||
// --------------------------------------------------------
|
||||
@@ -922,18 +911,83 @@ function initializeTabs () {
|
||||
$('a[data-toggle="tab"]').on('shown.bs.tab', function (e) {
|
||||
var target = $(e.target).attr("href") // activated tab
|
||||
});
|
||||
|
||||
hideSpinner();
|
||||
|
||||
}, 50);
|
||||
}, 50);
|
||||
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
// Logs render functionality
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Manages the auto-refresh functionality for the logs
|
||||
let intervalId;
|
||||
|
||||
// load footer asynchronously not to block the page load/other sections
|
||||
function toggleAutoRefresh() {
|
||||
const checkbox = $('#logsAutoRefresh')[0];
|
||||
|
||||
if (checkbox.checked) {
|
||||
intervalId = setInterval(renderLogs, 1000);
|
||||
} else {
|
||||
clearInterval(intervalId);
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Manages thefilter application
|
||||
function applyFilter() {
|
||||
const filterText = $("#logsFilter").val().toLowerCase();
|
||||
|
||||
$(".logs").each(function() {
|
||||
const originalText = $(this).data('originalText') || $(this).val();
|
||||
|
||||
if (!$(this).data('originalText')) {
|
||||
$(this).data('originalText', originalText);
|
||||
}
|
||||
|
||||
const filteredLines = originalText.split('\n').filter(line =>
|
||||
line.toLowerCase().includes(filterText)
|
||||
);
|
||||
|
||||
$(this).val(filteredLines.join('\n'));
|
||||
});
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Renders all the logs
|
||||
function renderLogs(customData) {
|
||||
$.ajax({
|
||||
url: 'php/components/logs.php', // PHP script URL
|
||||
type: 'POST', // Use POST method to send data
|
||||
dataType: 'html', // Expect HTML response
|
||||
// data: { items: JSON.stringify(customData) }, // Send customData as JSON
|
||||
success: function(response) {
|
||||
$('#logsPlc').html(response); // Replace container content with fetched HTML
|
||||
|
||||
applyFilter();
|
||||
|
||||
if($('#logsAutoScroll')[0].checked)
|
||||
{
|
||||
scrollDown(); // scroll down the logs
|
||||
}
|
||||
|
||||
},
|
||||
error: function(xhr, status, error) {
|
||||
console.error('Error fetching infoboxes:', error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Init
|
||||
window.onload = function asyncFooter()
|
||||
{
|
||||
initializeSelectedColumns();
|
||||
scrollDown();
|
||||
initializeTabs();
|
||||
renderLogs();
|
||||
|
||||
// initializeTabs();
|
||||
|
||||
$("#lastCommit").append('<a href="https://github.com/jokob-sk/NetAlertX/commits" target="_blank"><img alt="GitHub last commit" src="https://img.shields.io/github/last-commit/jokob-sk/netalertx/main?logo=github"></a>');
|
||||
|
||||
@@ -942,14 +996,8 @@ window.onload = function asyncFooter()
|
||||
|
||||
}
|
||||
|
||||
// scroll to the latest log entrie sat teh bottom of the file
|
||||
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<link rel="stylesheet" href="lib/AdminLTE/bower_components/select2/dist/css/select2.min.css">
|
||||
<script src="lib/AdminLTE/bower_components/select2/dist/js/select2.full.min.js"></script>
|
||||
<script src="lib/AdminLTE/bower_components/jquery-ui/jquery-ui.min.js"></script>
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,27 +1,33 @@
|
||||
|
||||
|
||||
|
||||
|
||||
<div class="col-md-12">
|
||||
<div class="callout callout-warning">
|
||||
<h4><?= lang('Gen_Warning');?></h4>
|
||||
|
||||
<p><?= lang('Device_MultiEdit_Backup');?></p>
|
||||
</div>
|
||||
<div class="box box-default">
|
||||
|
||||
<div class="box-header">
|
||||
|
||||
<h3 class="box-title"><?= lang('Gen_Selected_Devices');?></h3>
|
||||
|
||||
</div>
|
||||
<div class="deviceSelector"></div>
|
||||
</div>
|
||||
<div class="deviceSelector col-md-9" style="z-index:5"></div>
|
||||
|
||||
<div class="callout callout-warning">
|
||||
<h4><?= lang('Gen_Warning');?></h4>
|
||||
|
||||
<p><?= lang('Device_MultiEdit_Backup');?></p>
|
||||
<div class="col-md-3">
|
||||
<button type="button" class="btn btn-default" onclick="markAllSelected()">
|
||||
<i class="fa-solid fa-circle-check"></i> <?= lang('Gen_Add_All');?>
|
||||
</button>
|
||||
<button type="button" class="btn btn-default" onclick="markAllNotSelected()">
|
||||
<i class="fa-solid fa-circle-xmark"></i> <?= lang('Gen_Remove_All');?>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
<div class="col-md-12">
|
||||
<div class="box box-default">
|
||||
|
||||
@@ -65,7 +71,9 @@
|
||||
// Get plugin and settings data from API endpoints
|
||||
function getData(){
|
||||
|
||||
$.get('api/table_settings.json?nocache=' + Date.now(), function(res) {
|
||||
// some race condition, need to implement delay
|
||||
setTimeout(() => {
|
||||
$.get('api/table_settings.json?nocache=' + Date.now(), function(res) {
|
||||
|
||||
settingsData = res["data"];
|
||||
|
||||
@@ -92,26 +100,35 @@
|
||||
// Append form groups to the column
|
||||
for (let j = i * elementsPerColumn; j < Math.min((i + 1) * elementsPerColumn, columns.length); j++) {
|
||||
|
||||
let inputType;
|
||||
const setTypeObject = JSON.parse(columns[j].Type.replace(/'/g, '"'));
|
||||
// console.log(setTypeObject);
|
||||
const lastElementObj = setTypeObject.elements[setTypeObject.elements.length - 1]
|
||||
|
||||
switch (columns[j].Type) {
|
||||
case 'integer.checkbox':
|
||||
case 'checkbox':
|
||||
inputType = 'checkbox';
|
||||
break;
|
||||
case 'text.select':
|
||||
inputType = 'text.select';
|
||||
break;
|
||||
default:
|
||||
inputType = 'text';
|
||||
break;
|
||||
}
|
||||
|
||||
if (inputType === 'text.select') {
|
||||
const { elementType, elementOptions = [], transformers = [] } = lastElementObj;
|
||||
const {
|
||||
inputType,
|
||||
readOnly,
|
||||
isMultiSelect,
|
||||
cssClasses,
|
||||
placeholder,
|
||||
suffix,
|
||||
sourceIds,
|
||||
separator,
|
||||
editable,
|
||||
valRes,
|
||||
getStringKey,
|
||||
onClick
|
||||
} = handleElementOptions('none', elementOptions, transformers, val = "");
|
||||
|
||||
targetLocation = columns[j].Code_Name + "_initSettingDropdown"
|
||||
// console.log(setTypeObject);
|
||||
// console.log(inputType);
|
||||
|
||||
initSettingDropdown(columns[j].Code_Name, [], targetLocation, generateDropdownOptions)
|
||||
// render based on element type
|
||||
if (lastElementObj.elementType === 'select') {
|
||||
|
||||
targetLocation = columns[j].Code_Name + "_generateSetOptions"
|
||||
|
||||
generateOptionsOrSetOptions(columns[j].Code_Name, [], targetLocation, generateOptions)
|
||||
|
||||
// Handle Icons as tehy need a preview
|
||||
if(columns[j].Code_Name == 'NEWDEV_dev_Icon')
|
||||
@@ -137,15 +154,11 @@
|
||||
}
|
||||
|
||||
|
||||
} else {
|
||||
} else if (lastElementObj.elementType === 'input'){
|
||||
|
||||
// Add classes specifically for checkboxes
|
||||
if (inputType === 'checkbox') {
|
||||
inputClass = 'checkbox';
|
||||
|
||||
} else {
|
||||
inputClass = 'form-control';
|
||||
}
|
||||
inputType === 'checkbox' ? inputClass = 'checkbox' : inputClass = 'form-control';
|
||||
|
||||
|
||||
input = `<input class="${inputClass}"
|
||||
id="${columns[j].Code_Name}"
|
||||
@@ -181,6 +194,9 @@
|
||||
|
||||
|
||||
})
|
||||
|
||||
}, 500);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -190,6 +206,39 @@
|
||||
return $('.deviceSelector select').val().join(',');
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Select All
|
||||
function markAllSelected() {
|
||||
// Get the <select> element with the class 'deviceSelector'
|
||||
var selectElement = $('.deviceSelector select');
|
||||
|
||||
// Iterate over each option within the select element
|
||||
selectElement.find('option').each(function() {
|
||||
// Mark each option as selected
|
||||
$(this).prop('selected', true);
|
||||
});
|
||||
|
||||
// Trigger the 'change' event to notify Bootstrap Select of the changes
|
||||
selectElement.trigger('change');
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// UN-Select All
|
||||
function markAllNotSelected() {
|
||||
// Get the <select> element with the class 'deviceSelector'
|
||||
var selectElement = $('.deviceSelector select');
|
||||
|
||||
// Iterate over each option within the select element
|
||||
selectElement.find('option').each(function() {
|
||||
// Unselect each option
|
||||
$(this).prop('selected', false);
|
||||
});
|
||||
|
||||
// Trigger the 'change' event to notify Bootstrap Select of the changes
|
||||
selectElement.trigger('change');
|
||||
}
|
||||
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Update specified field over the specified DB column and selected entry(ies)
|
||||
@@ -210,7 +259,7 @@
|
||||
columnValue = inputElement.is(':checked') ? 1 : 0;
|
||||
} else {
|
||||
// For other input types (like textboxes), simply retrieve their values
|
||||
columnValue = inputElement.val();
|
||||
columnValue = encodeURIComponent(inputElement.val());
|
||||
}
|
||||
|
||||
var targetColumns = inputElement.attr('data-my-targetColumns');
|
||||
@@ -244,6 +293,8 @@ function executeAction(action, whereColumnName, key, targetColumns, newTargetCol
|
||||
// update API endpoints to refresh the UI
|
||||
updateApi()
|
||||
|
||||
write_notification(`[Multi edit] Executed "${action}" on Columns "${targetColumns}" matching "${key}"`, 'info')
|
||||
|
||||
} else {
|
||||
showMessage(getString('Gen_LockedDB'));
|
||||
}
|
||||
@@ -267,9 +318,9 @@ function askDeleteSelectedDevices () {
|
||||
// Delete selected devices
|
||||
function deleteSelectedDevices()
|
||||
{
|
||||
|
||||
executeAction('delete', 'dev_MAC', selectorMacs() )
|
||||
|
||||
macs_tmp = selectorMacs()
|
||||
executeAction('delete', 'dev_MAC', macs_tmp )
|
||||
write_notification('[Multi edit] Manually deleted devices with MACs:' + macs_tmp, 'info')
|
||||
}
|
||||
|
||||
|
||||
@@ -279,8 +330,7 @@ getData();
|
||||
</script>
|
||||
|
||||
<!-- ----------------------------------------------------------------------- -->
|
||||
<script src="js/ui_components.js"></script>
|
||||
<script src="js/db_methods.js"></script>
|
||||
|
||||
<!-- ----------------------------------------------------------------------- -->
|
||||
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
define('badge_offline', '<div class="badge bg-red text-white" style="width: 60px;">Offline</div>');
|
||||
define('circle_online', '<div class="badge bg-green text-white" style="width: 10px; height: 10px; padding:2px; margin-top: -25px;"> </div>');
|
||||
define('circle_offline', '<div class="badge bg-red text-white" style="width: 10px; height: 10px; padding:2px; margin-top: -25px;"> </div>');
|
||||
define('sortable_column', ' <span class="sort-btn" onclick="sortColumn(this)"><i class="fa-solid fa-arrow-up-short-wide"></i></span>');
|
||||
|
||||
?>
|
||||
|
||||
@@ -49,10 +50,10 @@
|
||||
|
||||
$decoded_icon = base64_decode($icon);
|
||||
$idFromMac = str_replace(":", "_", $node_mac);
|
||||
$str_tab_header = '<li class="'.$activetab.'">
|
||||
$str_tab_header = '<li class="networkNodeTabHeaders '.$activetab.' " >
|
||||
|
||||
<a href="#'.$idFromMac.'" data-mytabmac="'.$node_mac.'" id="'.$idFromMac.'_id" data-toggle="tab" >' // _id is added so it doesn't conflict with AdminLTE tab behavior
|
||||
.'<div class="icon">'.$decoded_icon.' </div>'.$node_name.' ' .$str_port.$node_badge.
|
||||
<a href="#'.$idFromMac.'" data-mytabmac="'.$node_mac.'" id="'.$idFromMac.'_id" data-toggle="tab" title="'.$node_name.' ">' // _id is added so it doesn't conflict with AdminLTE tab behavior
|
||||
.'<div class="icon">'.$decoded_icon.' </div> <span class="node-name">'.$node_name.'</span>' .$str_port.$node_badge.
|
||||
'</a>
|
||||
</li>';
|
||||
|
||||
@@ -140,13 +141,18 @@
|
||||
';
|
||||
|
||||
$str_table = ' <table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="col-sm-1" >Port</th>
|
||||
<th class="col-sm-1" >'.lang('Network_Table_State').'</th>
|
||||
<th class="col-sm-2" >'.lang('Network_Table_Hostname').sortable_column.'</th>
|
||||
<th class="col-sm-1" >'.lang('Network_Table_IP').sortable_column.'</th>
|
||||
<th class="col-sm-3" >'.lang('Network_ManageLeaf').'</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<th class="col-sm-1" >Port</th>
|
||||
<th class="col-sm-1" >'.lang('Network_Table_State').'</th>
|
||||
<th class="col-sm-2" >'.lang('Network_Table_Hostname').'</th>
|
||||
<th class="col-sm-1" >'.lang('Network_Table_IP').'</th>
|
||||
<th class="col-sm-3" >'.lang('Network_ManageLeaf').'</th>
|
||||
|
||||
</tr>';
|
||||
|
||||
// Prepare Array for Devices with Port value
|
||||
@@ -383,13 +389,18 @@
|
||||
<i class="fa fa-laptop"></i> '.lang('Network_UnassignedDevices').'
|
||||
</h3>
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="col-sm-1" ></th>
|
||||
<th class="col-sm-1" >'.lang('Network_Table_State').'</th>
|
||||
<th class="col-sm-2" >'.lang('Network_Table_Hostname').sortable_column.'</th>
|
||||
<th class="col-sm-1" >'.lang('Network_Table_IP').sortable_column.'</th>
|
||||
<th class="col-sm-3" >'.lang('Network_Assign').'</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<th class="col-sm-1" ></th>
|
||||
<th class="col-sm-1" >'.lang('Network_Table_State').'</th>
|
||||
<th class="col-sm-2" >'.lang('Network_Table_Hostname').'</th>
|
||||
<th class="col-sm-1" >'.lang('Network_Table_IP').'</th>
|
||||
<th class="col-sm-3" >'.lang('Network_Assign').'</th>
|
||||
|
||||
</tr>';
|
||||
|
||||
$str_table_rows = "";
|
||||
@@ -453,7 +464,7 @@
|
||||
<script src="lib/treeviz/require.js"></script>
|
||||
|
||||
|
||||
<script>
|
||||
<script defer>
|
||||
$.get('php/server/devices.php?action=getDevicesList&status=all&forceDefaultOrder', function(data) {
|
||||
|
||||
rawData = JSON.parse (data)
|
||||
@@ -517,8 +528,10 @@
|
||||
for(var i in list)
|
||||
{
|
||||
//... of the current node
|
||||
if(list[i].parentMac == node.mac && !hiddenMacs.includes(list[i].parentMac))
|
||||
|
||||
if(list[i].parentMac.toLowerCase() == node.mac.toLowerCase() && !hiddenMacs.includes(list[i].parentMac))
|
||||
{
|
||||
|
||||
visibleNodesCount++
|
||||
|
||||
// and process them
|
||||
@@ -535,7 +548,6 @@
|
||||
{
|
||||
parentNodesCount++
|
||||
}
|
||||
|
||||
|
||||
return {
|
||||
name: node.name,
|
||||
@@ -559,7 +571,6 @@
|
||||
|
||||
function getHierarchy()
|
||||
{
|
||||
|
||||
for(i in deviceListGlobal)
|
||||
{
|
||||
if(deviceListGlobal[i].mac == 'Internet')
|
||||
@@ -604,10 +615,10 @@
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Handle network node click - select correct tab in teh bottom table
|
||||
// Handle network node click - select correct tab in the bottom table
|
||||
function handleNodeClick(event)
|
||||
{
|
||||
console.log(event.target.offsetParent.offsetParent)
|
||||
// console.log(event.target.offsetParent.offsetParent)
|
||||
|
||||
const targetTabMAC = $(event.target.offsetParent.offsetParent).attr("data-mytreemacmain");
|
||||
|
||||
@@ -619,14 +630,24 @@
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
var myTree;
|
||||
var visibleTreeArea = $(window).height()-135;
|
||||
var treeAreaHeight = visibleTreeArea > 800 ? 800 : visibleTreeArea;
|
||||
var visibleTreeArea = $(window).height()-155;
|
||||
var nodeWidth = 160;
|
||||
var emSize;
|
||||
var nodeHeight;
|
||||
var sizeCoefficient = 1
|
||||
|
||||
function initTree(myHierarchy)
|
||||
{
|
||||
console.log(myHierarchy)
|
||||
|
||||
// calculate the drawing area based on teh tree width and available screen size
|
||||
var treeAreaHeight = visibleTreeArea > 800 ? 800 : visibleTreeArea;
|
||||
let screenWidth = $('.content-header').width();
|
||||
let treeWidth = (nodeWidth + 20) * parentNodesCount;
|
||||
let treeAreaWidth = screenWidth < treeWidth ? treeWidth : screenWidth;
|
||||
|
||||
// init the drawing area size
|
||||
$("#networkTree").attr('style', `height:${treeAreaHeight}px; width:${treeAreaWidth}px`)
|
||||
|
||||
if(myHierarchy.type == "")
|
||||
{
|
||||
@@ -643,7 +664,7 @@
|
||||
// nodeHeight = ((emSize*100*0.30).toFixed(0))
|
||||
nodeHeight = ((emSize*100*0.30).toFixed(0))
|
||||
|
||||
$("#networkTree").attr('style', `height:${treeAreaHeight}px; width:${$('.content-header').width()}px`)
|
||||
console.log(Treeviz);
|
||||
|
||||
myTree = Treeviz.create({
|
||||
htmlId: "networkTree",
|
||||
@@ -692,7 +713,7 @@
|
||||
>
|
||||
<div class="netNodeText">
|
||||
<strong>${devicePort} ${deviceIcon}
|
||||
<span class="spanNetworkTree anonymizeDev">${nodeData.data.name}</span>
|
||||
<span class="spanNetworkTree anonymizeDev" >${nodeData.data.name}</span>
|
||||
</strong>
|
||||
${collapseExpandHtml}
|
||||
</div>
|
||||
@@ -707,8 +728,8 @@
|
||||
secondaryAxisNodeSpacing: 0.3,
|
||||
nodeHeight: nodeHeight.toString(),
|
||||
marginTop: '5',
|
||||
hasZoom: false,
|
||||
hasPan: false,
|
||||
hasZoom: true,
|
||||
hasPan: true,
|
||||
// marginLeft: '15',
|
||||
idKey: "id",
|
||||
hasFlatData: false,
|
||||
@@ -717,8 +738,8 @@
|
||||
onNodeClick: (nodeData) => handleNodeClick(nodeData),
|
||||
relationnalField: "children",
|
||||
});
|
||||
|
||||
|
||||
console.log(myHierarchy)
|
||||
|
||||
myTree.refresh(myHierarchy);
|
||||
|
||||
@@ -852,7 +873,6 @@
|
||||
// init Assign/Unassign buttons
|
||||
initButtons()
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
94
front/php/components/logs.php
Executable file
@@ -0,0 +1,94 @@
|
||||
<?php
|
||||
|
||||
require '../server/init.php';
|
||||
|
||||
// Function to render the log area component
|
||||
function renderLogArea($params) {
|
||||
$fileName = isset($params['fileName']) ? $params['fileName'] : '';
|
||||
$filePath = isset($params['filePath']) ? $params['filePath'] : '';
|
||||
$textAreaCssClass = isset($params['textAreaCssClass']) ? $params['textAreaCssClass'] : '';
|
||||
$buttons = isset($params['buttons']) ? $params['buttons'] : [];
|
||||
$content = "";
|
||||
|
||||
if (filesize($filePath) > 2000000) {
|
||||
$content = file_get_contents($filePath, false, null, -2000000);
|
||||
} else {
|
||||
$content = file_get_contents($filePath);
|
||||
}
|
||||
|
||||
// Prepare the download button HTML if filePath starts with /app/front
|
||||
$downloadButtonHtml = '';
|
||||
if (strpos($filePath, '/app/front') === 0) {
|
||||
$downloadButtonHtml = '
|
||||
<span class="span-padding">
|
||||
<a href="' . htmlspecialchars(str_replace('/app/front', '', $filePath)) . '" target="_blank">
|
||||
<i class="fa fa-download"></i>
|
||||
</a>
|
||||
</span>';
|
||||
}
|
||||
|
||||
// Prepare buttons HTML
|
||||
$buttonsHtml = '';
|
||||
$totalButtons = count($buttons);
|
||||
if ($totalButtons > 0) {
|
||||
$colClass = 12 / $totalButtons;
|
||||
// Use $colClass in your HTML generation or further logic
|
||||
} else {
|
||||
// Handle case where $buttons array is empty
|
||||
$colClass = 12;
|
||||
}
|
||||
|
||||
foreach ($buttons as $button) {
|
||||
$labelStringCode = isset($button['labelStringCode']) ? $button['labelStringCode'] : '';
|
||||
$event = isset($button['event']) ? $button['event'] : '';
|
||||
|
||||
$buttonsHtml .= '
|
||||
<div class="button-wrap col-sm-' . $colClass . ' col-xs-' . $colClass . '">
|
||||
<button class="btn btn-primary col-sm-12 col-xs-12" onclick="' . htmlspecialchars($event) . '">' . lang($labelStringCode) . '</button>
|
||||
</div>';
|
||||
}
|
||||
|
||||
|
||||
// Render the log area HTML
|
||||
$html = '
|
||||
<div class="log-area box box-solid box-primary">
|
||||
<div class="row logs-row col-sm-12 col-xs-12">
|
||||
<textarea class="' . htmlspecialchars($textAreaCssClass) . '" cols="70" rows="20" wrap="off" readonly>'
|
||||
. htmlspecialchars($content) .
|
||||
'</textarea>
|
||||
</div>
|
||||
<div class="row logs-row">
|
||||
<div class="log-file col-sm-6 col-xs-12">' . htmlspecialchars($fileName) . '
|
||||
<div class="logs-size">' . number_format((filesize($filePath) / 1000000), 2, ",", ".") . ' MB'
|
||||
. $downloadButtonHtml .
|
||||
'</div>
|
||||
</div>
|
||||
<div class="col-sm-6 col-xs-12">'
|
||||
. $buttonsHtml .
|
||||
'</div>
|
||||
</div>
|
||||
</div>';
|
||||
|
||||
return $html;
|
||||
}
|
||||
|
||||
// Load default data from JSON file
|
||||
$defaultDataFile = 'logs_defaults.json';
|
||||
$defaultData = file_exists($defaultDataFile) ? json_decode(file_get_contents($defaultDataFile), true) : [];
|
||||
|
||||
// Check if JSON data is submitted via POST
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['items'])) {
|
||||
$items = json_decode($_POST['items'], true);
|
||||
} else {
|
||||
$items = $defaultData;
|
||||
}
|
||||
|
||||
// Render the log areas with the retrieved or default data
|
||||
$html = '';
|
||||
foreach ($items as $item) {
|
||||
$html .= renderLogArea($item);
|
||||
}
|
||||
|
||||
echo $html;
|
||||
exit();
|
||||
?>
|
||||
98
front/php/components/logs_defaults.json
Executable file
@@ -0,0 +1,98 @@
|
||||
[
|
||||
{
|
||||
"buttons": [
|
||||
{
|
||||
"labelStringCode": "Maint_PurgeLog",
|
||||
"event": "logManage('app.log', 'cleanLog')"
|
||||
},
|
||||
{
|
||||
"labelStringCode": "Maint_RestartServer",
|
||||
"event": "askRestartBackend()"
|
||||
}
|
||||
],
|
||||
"fileName": "app.log",
|
||||
"filePath": "/app/front/log/app.log",
|
||||
"textAreaCssClass": "logs"
|
||||
|
||||
},
|
||||
{
|
||||
"buttons": [
|
||||
{
|
||||
"labelStringCode": "Maint_PurgeLog",
|
||||
"event": "logManage('app_front.log', 'cleanLog')"
|
||||
}
|
||||
],
|
||||
"fileName": "app_front.log",
|
||||
"filePath": "/app/front/log/app_front.log",
|
||||
"textAreaCssClass": "logs logs-small"
|
||||
},
|
||||
{
|
||||
"buttons": [
|
||||
{
|
||||
"labelStringCode": "Maint_PurgeLog",
|
||||
"event": "logManage('app.php_errors.log', 'cleanLog')"
|
||||
}
|
||||
],
|
||||
"fileName": "app.php_errors.log",
|
||||
"filePath": "/app/front/log/app.php_errors.log",
|
||||
"textAreaCssClass": "logs logs-small"
|
||||
},
|
||||
{
|
||||
"buttons": [
|
||||
{
|
||||
"labelStringCode": "Maint_PurgeLog",
|
||||
"event": "logManage('execution_queue.log', 'cleanLog')"
|
||||
}
|
||||
],
|
||||
"fileName": "execution_queue.log",
|
||||
"filePath": "/app/front/log/execution_queue.log",
|
||||
"textAreaCssClass": "logs logs-small"
|
||||
},
|
||||
{
|
||||
"buttons": [
|
||||
],
|
||||
"fileName": "nginx/error.log",
|
||||
"filePath": "/var/log/nginx/error.log",
|
||||
"textAreaCssClass": "logs logs-small"
|
||||
},
|
||||
{
|
||||
"buttons": [
|
||||
{
|
||||
"labelStringCode": "Maint_PurgeLog",
|
||||
"event": "logManage('app_front.log', 'cleanLog')"
|
||||
}
|
||||
],
|
||||
"fileName": "app_front.log",
|
||||
"filePath": "/app/front/log/app_front.log",
|
||||
"textAreaCssClass": "logs logs-small"
|
||||
},
|
||||
{
|
||||
"buttons": [
|
||||
],
|
||||
"fileName": "db_is_locked.log",
|
||||
"filePath": "/app/front/log/db_is_locked.log",
|
||||
"textAreaCssClass": "logs logs-small"
|
||||
},
|
||||
{
|
||||
"buttons": [
|
||||
{
|
||||
"labelStringCode": "Maint_PurgeLog",
|
||||
"event": "logManage('stdout.log', 'cleanLog')"
|
||||
}
|
||||
],
|
||||
"fileName": "stdout.log",
|
||||
"filePath": "/app/front/log/stdout.log",
|
||||
"textAreaCssClass": "logs logs-small"
|
||||
},
|
||||
{
|
||||
"buttons": [
|
||||
{
|
||||
"labelStringCode": "Maint_PurgeLog",
|
||||
"event": "logManage('stderr.log', 'cleanLog')"
|
||||
}
|
||||
],
|
||||
"fileName": "stderr.log",
|
||||
"filePath": "/app/front/log/stderr.log",
|
||||
"textAreaCssClass": "logs logs-small"
|
||||
}
|
||||
]
|
||||
43
front/php/components/tile_cards.php
Executable file
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
function renderInfobox($params) {
|
||||
$onclickEvent = isset($params['onclickEvent']) ? $params['onclickEvent'] : '';
|
||||
$color = isset($params['color']) ? $params['color'] : '';
|
||||
$title = isset($params['title']) ? $params['title'] : '';
|
||||
$label = isset($params['label']) ? $params['label'] : '';
|
||||
$icon = isset($params['icon']) ? $params['icon'] : '';
|
||||
|
||||
return '
|
||||
<div class="tile col-lg-2 col-sm-4 col-xs-6">
|
||||
<a href="#" onclick="javascript: ' . htmlspecialchars($onclickEvent) . ';">
|
||||
<div class="small-box ' . htmlspecialchars($color) . '">
|
||||
<div class="inner">
|
||||
<h3>' . htmlspecialchars($title) . '</h3>
|
||||
<p class="infobox_label">' . htmlspecialchars($label) . '</p>
|
||||
</div>
|
||||
<div class="icon">
|
||||
<i class="fa ' . htmlspecialchars($icon) . ' text-aqua-40"></i>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
</div>';
|
||||
}
|
||||
|
||||
// Load default data from JSON file
|
||||
$defaultDataFile = 'tile_cards_defaults.json';
|
||||
$defaultData = file_exists($defaultDataFile) ? json_decode(file_get_contents($defaultDataFile), true) : [];
|
||||
|
||||
// Check if 'items' parameter exists and is valid JSON
|
||||
$items = isset($_POST['items']) ? json_decode($_POST['items'], true) : [];
|
||||
|
||||
// Use default data if 'items' is not provided or cannot be decoded
|
||||
if (empty($items)) {
|
||||
$items = $defaultData;
|
||||
}
|
||||
|
||||
$html = '';
|
||||
foreach ($items as $item) {
|
||||
$html .= renderInfobox($item);
|
||||
}
|
||||
echo $html;
|
||||
exit();
|
||||
?>
|
||||
17
front/php/components/tile_cards_defaults.json
Executable file
@@ -0,0 +1,17 @@
|
||||
[
|
||||
{
|
||||
"onclickEvent": "handleClick(1)",
|
||||
"color": "bg-primary",
|
||||
"title": "Default Infobox 1",
|
||||
"label": "Default label for Infobox 1",
|
||||
"icon": "fa-bell"
|
||||
},
|
||||
{
|
||||
"onclickEvent": "handleClick(2)",
|
||||
"color": "bg-success",
|
||||
"title": "Default Infobox 2",
|
||||
"label": "Default label for Infobox 2",
|
||||
"icon": "fa-envelope"
|
||||
}
|
||||
]
|
||||
|
||||
@@ -5,70 +5,190 @@
|
||||
//
|
||||
// db.php - Front module. Server side. DB common file
|
||||
//------------------------------------------------------------------------------
|
||||
# Puche 2021 / 2022+ jokob jokob@duck.com GNU GPLv3
|
||||
# 2022 jokob jokob@duck.com GNU GPLv3
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// DB File Path
|
||||
$DBFILE = dirname(__FILE__).'/../../../db/app.db';
|
||||
$DBFILE_LOCKED_FILE = dirname(__FILE__).'/../../../front/log/db_is_locked.log';
|
||||
|
||||
$db_locked = false;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Connect DB
|
||||
//------------------------------------------------------------------------------
|
||||
function SQLite3_connect ($trytoreconnect) {
|
||||
global $DBFILE;
|
||||
try
|
||||
{
|
||||
// connect to database
|
||||
// return new SQLite3($DBFILE, SQLITE3_OPEN_READONLY);
|
||||
return new SQLite3($DBFILE, SQLITE3_OPEN_READWRITE);
|
||||
}
|
||||
catch (Exception $exception)
|
||||
{
|
||||
// sqlite3 throws an exception when it is unable to connect
|
||||
// try to reconnect one time after 3 seconds
|
||||
|
||||
if($trytoreconnect)
|
||||
{
|
||||
echo '<script>alert("Error connecting to database, will try in 3s")</script>';
|
||||
sleep(3);
|
||||
return SQLite3_connect(false);
|
||||
function SQLite3_connect($trytoreconnect = true, $retryCount = 0) {
|
||||
global $DBFILE, $DBFILE_LOCKED_FILE;
|
||||
$maxRetries = 5; // Maximum number of retries
|
||||
$baseDelay = 1; // Base delay in seconds
|
||||
|
||||
try {
|
||||
// Connect to database
|
||||
global $db_locked;
|
||||
$db_locked = false;
|
||||
|
||||
// Write unlock status to the locked file
|
||||
file_put_contents($DBFILE_LOCKED_FILE, '0');
|
||||
|
||||
return new SQLite3($DBFILE, SQLITE3_OPEN_READWRITE);
|
||||
} catch (Exception $exception) {
|
||||
// sqlite3 throws an exception when it is unable to connect
|
||||
global $db_locked;
|
||||
$db_locked = true;
|
||||
|
||||
// Write lock status to the locked file
|
||||
file_put_contents($DBFILE_LOCKED_FILE, '1');
|
||||
error_log("Failed to connect to database: " . $exception->getMessage());
|
||||
|
||||
// Connection failed, check if we should retry
|
||||
if ($trytoreconnect && $retryCount < $maxRetries) {
|
||||
// Calculate exponential backoff delay
|
||||
$delay = $baseDelay * pow(2, $retryCount);
|
||||
sleep($delay);
|
||||
|
||||
// Retry the connection with an increased retry count
|
||||
return SQLite3_connect(true, $retryCount + 1);
|
||||
} else {
|
||||
// Maximum retries reached, hide loading spinner and show failure alert
|
||||
$message = 'Failed to connect to database after ' . $retryCount . ' retries.';
|
||||
write_notification($message);
|
||||
return false; // Or handle the failure appropriately
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// ->query override to handle retries
|
||||
//------------------------------------------------------------------------------
|
||||
class CustomDatabaseWrapper {
|
||||
private $sqlite;
|
||||
private $maxRetries;
|
||||
private $retryDelay;
|
||||
|
||||
public function __construct($filename, $flags = SQLITE3_OPEN_READWRITE | SQLITE3_OPEN_CREATE, $maxRetries = 10, $retryDelay = 1000, $encryptionKey = null) {
|
||||
$this->sqlite = new SQLite3($filename, $flags, $encryptionKey);
|
||||
$this->maxRetries = $maxRetries;
|
||||
$this->retryDelay = $retryDelay;
|
||||
}
|
||||
|
||||
public function query(string $query): SQLite3Result|bool {
|
||||
global $DBFILE_LOCKED_FILE;
|
||||
|
||||
// Check if the query is an UPDATE, DELETE, or INSERT
|
||||
$queryType = strtoupper(substr(trim($query), 0, strpos(trim($query), ' ')));
|
||||
$isModificationQuery = in_array($queryType, ['UPDATE', 'DELETE', 'INSERT']);
|
||||
|
||||
$attempts = 0;
|
||||
while ($attempts < $this->maxRetries) {
|
||||
|
||||
$result = false;
|
||||
|
||||
try {
|
||||
$result = $this->sqlite->query($query);
|
||||
} catch (Exception $exception) {
|
||||
// continue unless maxRetries reached
|
||||
if($attempts > $this->maxRetries)
|
||||
{
|
||||
throw $exception;
|
||||
}
|
||||
}
|
||||
|
||||
if ($result !== false and $result !== null) {
|
||||
|
||||
$this->query_log_remove($query);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
$this->query_log_add($query);
|
||||
|
||||
$attempts++;
|
||||
usleep($this->retryDelay * 1000 * $attempts); // Retry delay in milliseconds
|
||||
}
|
||||
|
||||
// If all retries failed, throw an exception or handle the error as needed
|
||||
// Add '0' to indicate that the database is not locked/execution failed
|
||||
file_put_contents($DBFILE_LOCKED_FILE, '0');
|
||||
|
||||
$message = 'Error executing query (attempts: ' . $attempts . '), query: ' . $query;
|
||||
write_notification($message);
|
||||
error_log("Query failed after {$this->maxRetries} attempts: " . $this->sqlite->lastErrorMsg());
|
||||
}
|
||||
|
||||
public function query_log_add($query)
|
||||
{
|
||||
global $DBFILE_LOCKED_FILE;
|
||||
|
||||
// Remove new lines from the query
|
||||
$query = str_replace(array("\r", "\n"), ' ', $query);
|
||||
|
||||
// Generate a hash of the query
|
||||
$queryHash = md5($query);
|
||||
|
||||
// Log the query being attempted along with timestamp and query hash
|
||||
$executionLog = "1|" . date('Y-m-d H:i:s') . "|$queryHash|$query";
|
||||
error_log("Attempting to write '$executionLog' to execution log file after failed query: $query");
|
||||
file_put_contents($DBFILE_LOCKED_FILE, $executionLog . PHP_EOL, FILE_APPEND);
|
||||
error_log("Execution log file content after failed query attempt: " . file_get_contents($DBFILE_LOCKED_FILE));
|
||||
}
|
||||
|
||||
public function query_log_remove($query)
|
||||
{
|
||||
global $DBFILE_LOCKED_FILE;
|
||||
|
||||
// Remove new lines from the query
|
||||
$query = str_replace(array("\r", "\n"), ' ', $query);
|
||||
|
||||
// Generate a hash of the query
|
||||
$queryHash = md5($query);
|
||||
|
||||
// Remove the entry corresponding to the finished query from the execution log based on query hash
|
||||
$executionLogs = file($DBFILE_LOCKED_FILE, FILE_IGNORE_NEW_LINES);
|
||||
$executionLogs = array_filter($executionLogs, function($log) use ($queryHash) {
|
||||
return strpos($log, $queryHash) === false;
|
||||
});
|
||||
file_put_contents($DBFILE_LOCKED_FILE, implode(PHP_EOL, $executionLogs));
|
||||
}
|
||||
|
||||
// Delegate other SQLite3 methods to the $sqlite instance
|
||||
public function __call($name, $arguments) {
|
||||
return call_user_func_array([$this->sqlite, $name], $arguments);
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Open DB
|
||||
//------------------------------------------------------------------------------
|
||||
function OpenDB($DBPath = null) {
|
||||
global $DBFILE;
|
||||
global $db;
|
||||
|
||||
function OpenDB (...$DBPath) {
|
||||
global $DBFILE;
|
||||
global $db;
|
||||
// Use custom path if supplied
|
||||
if ($DBPath !== null) {
|
||||
$DBFILE = $DBPath;
|
||||
}
|
||||
|
||||
// use custom path if supplied
|
||||
foreach ($DBPath as $path) {
|
||||
$DBFILE = $path;
|
||||
}
|
||||
|
||||
if(strlen($DBFILE) == 0)
|
||||
{
|
||||
echo '<script>alert("Database not available")</script>';
|
||||
die ('<div style="padding-left:150px">Database not available</div>');
|
||||
}
|
||||
if (strlen($DBFILE) == 0) {
|
||||
$message = 'Database not available';
|
||||
echo '<script>alert('.$message.')</script>';
|
||||
write_notification($message);
|
||||
|
||||
die('<div style="padding-left:150px">'.$message.'</div>');
|
||||
}
|
||||
|
||||
$db = SQLite3_connect(true);
|
||||
|
||||
if(!$db)
|
||||
{
|
||||
echo '<script>alert("Error connecting to the database")</script>';
|
||||
die ('<div style="padding-left:150px">Error connecting to the database</div>');
|
||||
}
|
||||
try {
|
||||
$db = new CustomDatabaseWrapper($DBFILE);
|
||||
} catch (Exception $e) {
|
||||
$message = "Error connecting to the database";
|
||||
echo '<script>alert('.$message.'": ' . $e->getMessage() . '")</script>';
|
||||
write_notification($message);
|
||||
die('<div style="padding-left:150px">'.$message.'</div>');
|
||||
}
|
||||
|
||||
$db->exec('PRAGMA journal_mode = wal;');
|
||||
$db->exec('PRAGMA journal_mode = wal;');
|
||||
}
|
||||
|
||||
|
||||
// # Open DB once and keep open
|
||||
// # Opening / closing DB frequently actually casues more issues
|
||||
OpenDB (); // main
|
||||
// Open DB once and keep open
|
||||
OpenDB(); // main
|
||||
?>
|
||||
|
||||
@@ -48,6 +48,10 @@
|
||||
$id = $_REQUEST['id'];
|
||||
}
|
||||
|
||||
if (isset ($_REQUEST['delay'])) {
|
||||
$delay = $_REQUEST['delay'];
|
||||
}
|
||||
|
||||
if (isset ($_REQUEST['values'])) {
|
||||
$values = $_REQUEST['values'];
|
||||
}
|
||||
@@ -72,6 +76,7 @@
|
||||
case 'read' : read($rawSql); break;
|
||||
case 'update': update($columnName, $id, $defaultValue, $expireMinutes, $dbtable, $columns, $values); break;
|
||||
case 'delete': delete($columnName, $id, $dbtable); break;
|
||||
case 'lockDatabase': lockDatabase($delay); break;
|
||||
default: logServerConsole ('Action: '. $action); break;
|
||||
}
|
||||
}
|
||||
@@ -263,5 +268,11 @@ function delete($columnName, $id, $dbtable)
|
||||
}
|
||||
|
||||
|
||||
// Simulate database locking by starting a transaction
|
||||
function lockDatabase($delay) {
|
||||
$db = new SQLite3($GLOBALS['DBFILE']);
|
||||
$db->exec('BEGIN EXCLUSIVE;');
|
||||
sleep($delay); // Sleep for N seconds to simulate long-running transaction
|
||||
}
|
||||
|
||||
?>
|
||||
|
||||
@@ -26,11 +26,8 @@
|
||||
case 'setDeviceData': setDeviceData(); break;
|
||||
case 'deleteDevice': deleteDevice(); break;
|
||||
case 'deleteAllWithEmptyMACs': deleteAllWithEmptyMACs(); break;
|
||||
case 'createBackupDB': createBackupDB(); break;
|
||||
|
||||
case 'deleteAllDevices': deleteAllDevices(); break;
|
||||
case 'runScan15min': runScan15min(); break;
|
||||
case 'runScan1min': runScan1min(); break;
|
||||
case 'deleteUnknownDevices': deleteUnknownDevices(); break;
|
||||
case 'deleteEvents': deleteEvents(); break;
|
||||
case 'deleteEvents30': deleteEvents30(); break;
|
||||
@@ -155,6 +152,8 @@ function setDeviceData() {
|
||||
dev_Comments = "'. quotes($_REQUEST['comments']) .'",
|
||||
dev_Network_Node_MAC_ADDR = "'. quotes($_REQUEST['networknode']).'",
|
||||
dev_Network_Node_port = "'. quotes($_REQUEST['networknodeport']).'",
|
||||
dev_SSID = "'. quotes($_REQUEST['ssid']).'",
|
||||
dev_NetworkSite = "'. quotes($_REQUEST['networksite']).'",
|
||||
dev_StaticIP = "'. quotes($_REQUEST['staticIP']) .'",
|
||||
dev_ScanCycle = "'. quotes($_REQUEST['scancycle']) .'",
|
||||
dev_AlertEvents = "'. quotes($_REQUEST['alertevents']) .'",
|
||||
@@ -464,15 +463,29 @@ function ExportCSV() {
|
||||
// Import CSV of devices
|
||||
//------------------------------------------------------------------------------
|
||||
function ImportCSV() {
|
||||
$file = '../../../config/devices.csv';
|
||||
|
||||
if (file_exists($file)) {
|
||||
global $db;
|
||||
$skipped = "";
|
||||
$error = "";
|
||||
global $db;
|
||||
$file = '../../../config/devices.csv';
|
||||
$data = "";
|
||||
$skipped = "";
|
||||
$error = "";
|
||||
|
||||
// check if content passed in query string
|
||||
if(isset ($_POST['content']) && !empty ($_POST['content']))
|
||||
{
|
||||
// Decode the Base64 string
|
||||
$data = base64_decode($_POST['content']);
|
||||
|
||||
} else if (file_exists($file)) { // try to get the data form the file
|
||||
|
||||
// Read the CSV file
|
||||
$data = file_get_contents($file);
|
||||
} else {
|
||||
echo lang('BackDevices_DBTools_ImportCSVMissing');
|
||||
}
|
||||
|
||||
if($data != "")
|
||||
{
|
||||
$lines = explode("\n", $data);
|
||||
|
||||
// Get the column headers from the first line of the CSV
|
||||
@@ -518,8 +531,6 @@ function ImportCSV() {
|
||||
// An error occurred while writing to the DB, display the last error message
|
||||
echo lang('BackDevices_DBTools_ImportCSVError') . "\n" . $error . "\n" . $sql . "\n\n" . $result;
|
||||
}
|
||||
} else {
|
||||
echo lang('BackDevices_DBTools_ImportCSVMissing');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -593,7 +604,11 @@ function getDevicesList() {
|
||||
array("connected_devices", 15, 15),
|
||||
array("dev_Location", 16, 16),
|
||||
array("dev_Vendor", 17, 17),
|
||||
array("dev_Network_Node_port", 18, 18)
|
||||
array("dev_Network_Node_port", 18, 18),
|
||||
array("dev_GUID", 19, 19),
|
||||
array("dev_SyncHubNodeName", 20, 20),
|
||||
array("dev_NetworkSite", 21, 21),
|
||||
array("dev_SSID", 22, 22)
|
||||
);
|
||||
|
||||
if($forceDefaultOrder == FALSE)
|
||||
@@ -662,7 +677,11 @@ function getDevicesList() {
|
||||
handleNull($row['connected_devices']),
|
||||
handleNull($row['dev_Location']),
|
||||
handleNull($row['dev_Vendor']),
|
||||
handleNull($row['dev_Network_Node_port'])
|
||||
handleNull($row['dev_Network_Node_port']),
|
||||
handleNull($row['dev_GUID']),
|
||||
handleNull($row['dev_SyncHubNodeName']),
|
||||
handleNull($row['dev_NetworkSite']),
|
||||
handleNull($row['dev_SSID'])
|
||||
);
|
||||
|
||||
$newOrder = array();
|
||||
@@ -894,18 +913,11 @@ function copyFromDevice() {
|
||||
|
||||
$MAC_FROM = $_REQUEST['macFrom'];
|
||||
$MAC_TO = $_REQUEST['macTo'];
|
||||
|
||||
if ((false === filter_var($MAC_FROM , FILTER_VALIDATE_MAC) && $MAC_FROM != "Internet" && $MAC_FROM != "") ) {
|
||||
throw new Exception('Invalid mac address');
|
||||
}
|
||||
if ((false === filter_var($MAC_TO , FILTER_VALIDATE_MAC) && $MAC_TO != "Internet" && $MAC_TO != "") ) {
|
||||
throw new Exception('Invalid mac address');
|
||||
}
|
||||
|
||||
global $db;
|
||||
|
||||
// clean-up temporary table
|
||||
$sql = "DROP TABLE temp_devices ";
|
||||
$sql = "DROP TABLE IF EXISTS temp_devices ";
|
||||
$result = $db->query($sql);
|
||||
|
||||
// create temporary table with the source data
|
||||
|
||||
@@ -105,7 +105,7 @@ function getEvents() {
|
||||
$periodDate = getDateFromPeriod();
|
||||
|
||||
// SQL
|
||||
$SQL1 = 'SELECT eve_DateTime AS eve_DateTimeOrder, dev_name, dev_owner, eve_DateTime, eve_EventType, NULL, NULL, NULL, NULL, eve_IP, NULL, eve_AdditionalInfo, NULL, Dev_MAC
|
||||
$SQL1 = 'SELECT eve_DateTime AS eve_DateTimeOrder, dev_name, dev_owner, eve_DateTime, eve_EventType, NULL, NULL, NULL, NULL, eve_IP, NULL, eve_AdditionalInfo, NULL, Dev_MAC, eve_PendingAlertEmail
|
||||
FROM Events_Devices
|
||||
WHERE eve_DateTime >= '. $periodDate;
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
<?php
|
||||
ini_set('error_log', '../../log/app.php_errors.log'); // initializing the app.php_errors.log file for the maintenance section
|
||||
require dirname(__FILE__).'/../templates/timezone.php';
|
||||
require dirname(__FILE__).'/db.php';
|
||||
require dirname(__FILE__).'/util.php';
|
||||
require dirname(__FILE__).'/../templates/language/lang.php';
|
||||
require dirname(__FILE__).'/utilNotification.php';
|
||||
?>
|
||||
|
||||
@@ -28,6 +28,7 @@ if(array_key_exists('settings', $_REQUEST) != FALSE)
|
||||
|
||||
// call functions based on requested params
|
||||
switch ($FUNCTION) {
|
||||
|
||||
case 'savesettings':
|
||||
|
||||
saveSettings();
|
||||
@@ -224,6 +225,8 @@ function displayMessage($message, $logAlert = FALSE, $logConsole = TRUE, $logFil
|
||||
|
||||
}
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------------------
|
||||
// Adds an action to perform into the execution_queue.log file
|
||||
function addToExecutionQueue($action)
|
||||
{
|
||||
@@ -243,6 +246,7 @@ function addToExecutionQueue($action)
|
||||
}
|
||||
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------------------
|
||||
function cleanLog($logFile)
|
||||
{
|
||||
@@ -250,7 +254,7 @@ function cleanLog($logFile)
|
||||
|
||||
$path = "";
|
||||
|
||||
$allowedFiles = ['app.log', 'app_front.log', 'IP_changes.log', 'stdout.log', 'stderr.log', "pholus_lastrun.log", 'app.php_errors.log'];
|
||||
$allowedFiles = ['app.log', 'app_front.log', 'IP_changes.log', 'stdout.log', 'stderr.log', "pholus_lastrun.log", 'app.php_errors.log', 'execution_queue.log'];
|
||||
|
||||
if(in_array($logFile, $allowedFiles))
|
||||
{
|
||||
@@ -302,7 +306,7 @@ function saveSettings()
|
||||
$txt = $txt."# Generated: ".$timestamp." #\n";
|
||||
$txt = $txt."# #\n";
|
||||
$txt = $txt."# Config file for the LAN intruder detection app: #\n";
|
||||
$txt = $txt."# https://github.com/jokob-sk/NetAlertX #\n";
|
||||
$txt = $txt."# https://github.com/jokob-sk/NetAlertX #\n";
|
||||
$txt = $txt."# #\n";
|
||||
$txt = $txt."#-----------------AUTOGENERATED FILE-----------------#\n";
|
||||
|
||||
@@ -322,49 +326,57 @@ function saveSettings()
|
||||
$txt .= "\n#---------------------------\n";
|
||||
|
||||
foreach ($decodedSettings as $setting) {
|
||||
$settingGroup = $setting[0];
|
||||
$settingKey = $setting[1];
|
||||
$settingType = $setting[2];
|
||||
$settingValue = $setting[3];
|
||||
$settingGroup = $setting[0];
|
||||
$settingKey = $setting[1];
|
||||
$dataType = $setting[2];
|
||||
$settingValue = $setting[3];
|
||||
|
||||
// // Parse the settingType JSON
|
||||
// $settingType = json_decode($settingTypeJson, true);
|
||||
|
||||
// Sanity check
|
||||
if($settingKey == "UI_LANG" && $settingValue == "") {
|
||||
echo "🔴 Error: important settings missing. Refresh the page with 🔃 on the top and try again.";
|
||||
return;
|
||||
}
|
||||
|
||||
if ($group == $settingGroup) {
|
||||
|
||||
if ($dataType == 'string' ) {
|
||||
$val = encode_single_quotes($settingValue);
|
||||
$txt .= $settingKey . "='" . $val . "'\n";
|
||||
} elseif ($dataType == 'integer') {
|
||||
$txt .= $settingKey . "=" . $settingValue . "\n";
|
||||
} elseif ($dataType == 'json') {
|
||||
$txt .= $settingKey . "=" . $settingValue . "\n";
|
||||
} elseif ($dataType == 'boolean') {
|
||||
$val = ($settingValue === true || $settingValue === 1 || strtolower($settingValue) === 'true') ? "True" : "False";
|
||||
$txt .= $settingKey . "=" . $val . "\n";
|
||||
} elseif ($dataType == 'array' ) {
|
||||
$temp = '';
|
||||
|
||||
if(is_array($settingValue) == FALSE)
|
||||
{
|
||||
$settingValue = json_decode($settingValue);
|
||||
}
|
||||
// skipping __metadata entries (?)
|
||||
if (count($setting) > 3 && is_array($settingValue) == true) {
|
||||
foreach ($settingValue as $val) {
|
||||
$temp .= "'" . encode_single_quotes($val) . "',";
|
||||
}
|
||||
|
||||
if ($group == $settingGroup) {
|
||||
if ($settingType == 'text' || $settingType == 'password' || $settingType == 'readonly' || $settingType == 'text.select') {
|
||||
$val = encode_single_quotes($settingValue);
|
||||
$txt .= $settingKey . "='" . $val . "'\n";
|
||||
} elseif ($settingType == 'integer' || $settingType == 'integer.select') {
|
||||
$txt .= $settingKey . "=" . $settingValue . "\n";
|
||||
} elseif ($settingType == 'boolean' || $settingType == 'integer.checkbox') {
|
||||
$temp = substr_replace($temp, "", -1); // remove last comma ','
|
||||
}
|
||||
|
||||
if ($settingValue === true || $settingValue === 1 || strtolower($settingValue) === 'true') {
|
||||
$val = "True";
|
||||
} else {
|
||||
$val = "False";
|
||||
}
|
||||
|
||||
$txt .= $settingKey . "=" . $val . "\n";
|
||||
} elseif ($settingType == 'text.multiselect' || $settingType == 'subnets' || $settingType == 'list') {
|
||||
$temp = '';
|
||||
|
||||
if(is_array($settingValue) == FALSE)
|
||||
{
|
||||
$settingValue = json_decode($settingValue);
|
||||
}
|
||||
|
||||
if (count($setting) > 3 && is_array($settingValue) == true) {
|
||||
foreach ($settingValue as $val) {
|
||||
$temp .= "'" . encode_single_quotes($val) . "',";
|
||||
}
|
||||
|
||||
$temp = substr_replace($temp, "", -1); // remove last comma ','
|
||||
}
|
||||
|
||||
$temp = '['.$temp.']'; // wrap brackets
|
||||
$txt .= $settingKey . "=" . $temp . "\n";
|
||||
} elseif ($settingType == 'json') {
|
||||
$txt .= $settingKey . "=" . $settingValue . "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
$temp = '['.$temp.']'; // wrap brackets
|
||||
$txt .= $settingKey . "=" . $temp . "\n";
|
||||
|
||||
} else {
|
||||
$txt .= $settingKey . "='⭕Not handled⭕'\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -387,8 +399,10 @@ function saveSettings()
|
||||
// Replace the original file with the temporary file
|
||||
rename($tempConfPath, $fullConfPath);
|
||||
|
||||
displayMessage("<br/>Settings saved to the <code>app.conf</code> file.<br/><br/>A time-stamped backup of the previous file created. <br/><br/> Reloading...<br/>",
|
||||
FALSE, TRUE, TRUE, TRUE);
|
||||
// displayMessage(lang('settings_saved'),
|
||||
// FALSE, TRUE, TRUE, TRUE);
|
||||
|
||||
echo "OK";
|
||||
|
||||
}
|
||||
|
||||
@@ -535,7 +549,12 @@ function getDevicesColumns(){
|
||||
"dev_Archived",
|
||||
"dev_Network_Node_port",
|
||||
"dev_Network_Node_MAC_ADDR",
|
||||
"dev_Icon"];
|
||||
"dev_Icon",
|
||||
"dev_GUID",
|
||||
"dev_SyncHubNodeName",
|
||||
"dev_NetworkSite",
|
||||
"dev_SSID"
|
||||
];
|
||||
|
||||
return $columns;
|
||||
}
|
||||
|
||||
196
front/php/server/utilNotification.php
Executable file
@@ -0,0 +1,196 @@
|
||||
<?php
|
||||
|
||||
|
||||
require dirname(__FILE__).'/../templates/timezone.php';
|
||||
|
||||
// ----------------------------------------------------------------------------------------
|
||||
// Check if the action parameter is set in the GET request
|
||||
if (isset($_GET['action'])) {
|
||||
// Collect GUID if provided
|
||||
$guid = isset($_GET['guid']) ? $_GET['guid'] : null;
|
||||
|
||||
// Perform the appropriate action based on the action parameter
|
||||
switch ($_GET['action']) {
|
||||
case 'write_notification':
|
||||
// Call the write_notification function with content and level parameters
|
||||
if (isset($_GET['content'])) {
|
||||
$content = $_GET['content'];
|
||||
$level = isset($_GET['level']) ? $_GET['level'] : "interrupt";
|
||||
write_notification($content, $level);
|
||||
}
|
||||
break;
|
||||
case 'remove_notification':
|
||||
// Call the remove_notification function with guid parameter
|
||||
if ($guid) {
|
||||
remove_notification($guid);
|
||||
}
|
||||
break;
|
||||
case 'mark_notification_as_read':
|
||||
// Call the mark_notification_as_read function with guid parameter
|
||||
if ($guid) {
|
||||
mark_notification_as_read($guid);
|
||||
}
|
||||
break;
|
||||
case 'notifications_clear':
|
||||
// Call the notifications_clear function
|
||||
notifications_clear();
|
||||
break;
|
||||
case 'notifications_mark_all_read':
|
||||
// Call the notifications_mark_all_read function
|
||||
notifications_mark_all_read();
|
||||
break;
|
||||
case 'get_unread_notifications':
|
||||
// Call the get_unread_notifications function
|
||||
get_unread_notifications();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------------------
|
||||
// Generates a random GUID
|
||||
function generate_guid() {
|
||||
if (function_exists('com_create_guid') === true) {
|
||||
return trim(com_create_guid(), '{}');
|
||||
}
|
||||
return sprintf('%04X%04X-%04X-%04X-%04X-%04X%04X%04X',
|
||||
mt_rand(0, 65535), mt_rand(0, 65535), mt_rand(0, 65535),
|
||||
mt_rand(16384, 20479), mt_rand(32768, 49151), mt_rand(0, 65535),
|
||||
mt_rand(0, 65535), mt_rand(0, 65535));
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------------------
|
||||
// Logs a notification in in-app notification system
|
||||
function write_notification($content, $level = "interrupt") {
|
||||
$NOTIFICATION_API_FILE = '/app/front/api/user_notifications.json';
|
||||
|
||||
// Generate GUID
|
||||
$guid = generate_guid();
|
||||
|
||||
// Generate timestamp
|
||||
$timestamp = (new DateTime('now'))->format('Y-m-d H:i:s');
|
||||
|
||||
// Escape content to prevent breaking JSON
|
||||
$escaped_content = json_encode($content);
|
||||
|
||||
// Prepare notification array
|
||||
$notification = array(
|
||||
'timestamp' => $timestamp,
|
||||
'guid' => $guid,
|
||||
'read' => 0,
|
||||
'level'=> $level,
|
||||
'content' => $escaped_content,
|
||||
);
|
||||
|
||||
// Read existing notifications
|
||||
$notifications = json_decode(file_get_contents($NOTIFICATION_API_FILE), true);
|
||||
|
||||
// Add new notification
|
||||
$notifications[] = $notification;
|
||||
|
||||
// Write notifications to file
|
||||
file_put_contents($NOTIFICATION_API_FILE, json_encode($notifications));
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------------------
|
||||
// Removes a notification based on GUID
|
||||
function remove_notification($guid) {
|
||||
$NOTIFICATION_API_FILE = '/app/front/api/user_notifications.json';
|
||||
|
||||
// Read existing notifications
|
||||
$notifications = json_decode(file_get_contents($NOTIFICATION_API_FILE), true);
|
||||
|
||||
// Filter out the notification with the specified GUID
|
||||
$filtered_notifications = array_filter($notifications, function($notification) use ($guid) {
|
||||
return $notification['guid'] !== $guid;
|
||||
});
|
||||
|
||||
// Write filtered notifications back to file
|
||||
file_put_contents($NOTIFICATION_API_FILE, json_encode(array_values($filtered_notifications)));
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------------------
|
||||
// Deletes all notifications
|
||||
function notifications_clear() {
|
||||
$NOTIFICATION_API_FILE = '/app/front/api/user_notifications.json';
|
||||
|
||||
// Clear notifications by writing an empty array to the file
|
||||
file_put_contents($NOTIFICATION_API_FILE, json_encode(array()));
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------------------
|
||||
// Mark a notification read based on GUID
|
||||
function mark_notification_as_read($guid) {
|
||||
$NOTIFICATION_API_FILE = '/app/front/api/user_notifications.json';
|
||||
$max_attempts = 3;
|
||||
$attempts = 0;
|
||||
|
||||
do {
|
||||
// Check if the file exists and is readable
|
||||
if (file_exists($NOTIFICATION_API_FILE) && is_readable($NOTIFICATION_API_FILE)) {
|
||||
// Attempt to read existing notifications
|
||||
$notifications = json_decode(file_get_contents($NOTIFICATION_API_FILE), true);
|
||||
|
||||
// Check if reading was successful
|
||||
if ($notifications !== null) {
|
||||
// Iterate over notifications to find the one with the specified GUID
|
||||
foreach ($notifications as &$notification) {
|
||||
if ($notification['guid'] === $guid) {
|
||||
// Mark the notification as read
|
||||
$notification['read'] = 1;
|
||||
break;
|
||||
} elseif ($guid == null) // no guid given, mark all read
|
||||
{
|
||||
$notification['read'] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Write updated notifications back to file
|
||||
file_put_contents($NOTIFICATION_API_FILE, json_encode($notifications));
|
||||
return; // Exit the function after successful operation
|
||||
}
|
||||
}
|
||||
|
||||
// Increment the attempt count
|
||||
$attempts++;
|
||||
|
||||
// Sleep for a short duration before retrying
|
||||
usleep(500000); // Sleep for 0.5 seconds (500,000 microseconds) before retrying
|
||||
|
||||
} while ($attempts < $max_attempts);
|
||||
|
||||
// If maximum attempts reached or file reading failed, handle the error
|
||||
echo "Failed to read notification file after $max_attempts attempts.";
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------------------
|
||||
function notifications_mark_all_read() {
|
||||
mark_notification_as_read(null);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------------------
|
||||
function get_unread_notifications() {
|
||||
$NOTIFICATION_API_FILE = '/app/front/api/user_notifications.json';
|
||||
|
||||
// Read existing notifications
|
||||
if (file_exists($NOTIFICATION_API_FILE) && is_readable($NOTIFICATION_API_FILE)) {
|
||||
$notifications = json_decode(file_get_contents($NOTIFICATION_API_FILE), true);
|
||||
|
||||
if ($notifications !== null) {
|
||||
// Filter unread notifications
|
||||
$unread_notifications = array_filter($notifications, function($notification) {
|
||||
return $notification['read'] === 0;
|
||||
});
|
||||
|
||||
// Return unread notifications as JSON
|
||||
header('Content-Type: application/json');
|
||||
echo json_encode(array_values($unread_notifications));
|
||||
} else {
|
||||
echo json_encode([]);
|
||||
}
|
||||
} else {
|
||||
echo json_encode([]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
?>
|
||||
35
front/php/templates/auth.php
Executable file
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
session_start();
|
||||
|
||||
$isAuthenticated = false;
|
||||
|
||||
// Check if the user is logged in
|
||||
if (isset($_SESSION["login"]) && $_SESSION["login"] == 1) {
|
||||
$isAuthenticated = true;
|
||||
}
|
||||
|
||||
// $current_directory = __DIR__;
|
||||
// echo "Current directory: " . $current_directory;
|
||||
|
||||
// Check if a valid cookie is present
|
||||
$CookieSaveLoginName = "NetAlertX_SaveLogin";
|
||||
$config_file = "../../../config/app.conf"; // depends on where this file is called from
|
||||
$config_file_lines = file($config_file);
|
||||
$config_file_lines = array_values(preg_grep('/^SETPWD_password.*=/', $config_file_lines));
|
||||
$password_line = explode("'", $config_file_lines[0]);
|
||||
$Pia_Password = $password_line[1];
|
||||
|
||||
if (isset($_COOKIE[$CookieSaveLoginName]) && $Pia_Password == $_COOKIE[$CookieSaveLoginName]) {
|
||||
$isAuthenticated = true;
|
||||
}
|
||||
|
||||
if ($isAuthenticated) {
|
||||
echo 'Authorized 200';
|
||||
http_response_code(200);
|
||||
exit; // Important: Ensure script exits after successful authentication
|
||||
} else {
|
||||
http_response_code(401);
|
||||
echo 'Unauthorized 401';
|
||||
exit; // Ensure script exits after failed authentication
|
||||
}
|
||||
?>
|
||||
@@ -17,12 +17,12 @@
|
||||
<!-- Default to the left -->
|
||||
|
||||
<!-- NetAlertX footer with url -->
|
||||
<a href="https://github.com/jokob-sk/NetAlertX" target="_blank">Net <b>Alert</b><sup>x</sup></a>
|
||||
<a href="https://github.com/jokob-sk/NetAlertX" target="_blank">Net<b>Alert</b><sup>x</sup></a>
|
||||
|
||||
|
||||
<!-- To the right -->
|
||||
<div class="pull-right no-hidden-xs">
|
||||
| <a href="https://github.com/jokob-sk/NetAlertX/tree/main/docs" target="_blank">Docs <i class="fa fa-circle-question"></i></a>
|
||||
| <a href="https://github.com/jokob-sk/NetAlertX/tree/main/docs#documentation-overview" target="_blank">Docs <i class="fa fa-circle-question"></i></a>
|
||||
| <a href="https://github.com/jokob-sk/NetAlertX/issues"><i class="fa-solid fa-bug"></i></a>
|
||||
| <a href="https://github.com/jokob-sk/NetAlertX/"><i class="fa-brands fa-github"></i></a>
|
||||
| <a href="mailto:jokob@duck.com?subject=NetAlertX"><i class="fa-solid fa-envelope"></i></a>
|
||||
@@ -41,23 +41,25 @@
|
||||
<!-- ./wrapper -->
|
||||
|
||||
<!-- Bootstrap 3.3.7 -->
|
||||
<script src="lib/AdminLTE/bower_components/bootstrap/dist/js/bootstrap.min.js"></script>
|
||||
<script src="lib/AdminLTE/bower_components/bootstrap/dist/js/bootstrap.min.js"></script>
|
||||
|
||||
<!-- jQuery UI -->
|
||||
<script src="lib/AdminLTE/bower_components/jquery-ui/jquery-ui.min.js"></script>
|
||||
|
||||
<!-- AdminLTE App -->
|
||||
<script src="lib/AdminLTE/dist/js/adminlte.min.js"></script>
|
||||
<script src="lib/AdminLTE/dist/js/adminlte.min.js"></script>
|
||||
|
||||
<!-- Optionally, you can add Slimscroll and FastClick plugins.
|
||||
Both of these plugins are recommended to enhance the
|
||||
user experience. -->
|
||||
<!-- Select2 CSS -->
|
||||
<link rel="stylesheet" href="lib/AdminLTE/bower_components/select2/dist/css/select2.min.css">
|
||||
|
||||
<!-- SlimScroll -->
|
||||
<!-- <script src="lib/AdminLTE/bower_components/jquery-slimscroll/jquery.slimscroll.min.js"></script> -->
|
||||
<!-- FastClick -->
|
||||
<!-- <script src="lib/AdminLTE/bower_components/fastclick/lib/fastclick.js"></script> -->
|
||||
<!-- NetAlertX -->
|
||||
<script src="js/handle_version.js"></script>
|
||||
<script src="js/ui_components.js?v=<?php include 'php/templates/version.php'; ?>"></script>
|
||||
|
||||
<!-- NetAlertX -------------------------------------------------------------- -->
|
||||
|
||||
<script src="js/handle_version.js"></script>
|
||||
<!-- Select2 JavaScript -->
|
||||
<script src="lib/AdminLTE/bower_components/select2/dist/js/select2.full.min.js" defer></script>
|
||||
|
||||
|
||||
<?php
|
||||
require 'migrationCheck.php';
|
||||
|
||||
@@ -21,7 +21,7 @@ require dirname(__FILE__).'/security.php';
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="apple-mobile-web-app-capable" content="yes" />
|
||||
<title>Net Alert ✖ - <?php echo gethostname();?></title>
|
||||
<title>NetAlertX - <?php echo gethostname();?></title>
|
||||
|
||||
<!-- 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">
|
||||
@@ -30,7 +30,11 @@ require dirname(__FILE__).'/security.php';
|
||||
<!-- jQuery 3 -->
|
||||
<script src="lib/AdminLTE/bower_components/jquery/dist/jquery.min.js"></script>
|
||||
|
||||
<script src="js/common.js"></script>
|
||||
<script src="js/common.js?v=<?php include 'php/templates/version.php'; ?>"></script>
|
||||
<script src="js/modal.js?v=<?php include 'php/templates/version.php'; ?>"></script>
|
||||
<script src="js/tests.js?v=<?php include 'php/templates/version.php'; ?>"></script>
|
||||
<script src="js/db_methods.js?v=<?php include 'php/templates/version.php'; ?>"></script>
|
||||
<script src="js/settings_utils.js?v=<?php include 'php/templates/version.php'; ?>"></script>
|
||||
|
||||
<!-- Bootstrap 3.3.7 -->
|
||||
<link rel="stylesheet" href="lib/AdminLTE/bower_components/bootstrap/dist/css/bootstrap.min.css">
|
||||
@@ -56,17 +60,11 @@ require dirname(__FILE__).'/security.php';
|
||||
<!-- NetAlertX CSS -->
|
||||
<link rel="stylesheet" href="css/app.css">
|
||||
|
||||
<!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries -->
|
||||
<!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
|
||||
<!--[if lt IE 9]>
|
||||
<script src="https://oss.maxcdn.com/html5shiv/3.7.3/html5shiv.min.js"></script>
|
||||
<script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
|
||||
<![endif]-->
|
||||
|
||||
<!-- Google Font -->
|
||||
<!-- <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:300,400,600,700,300italic,400italic,600italic"> -->
|
||||
<link rel="stylesheet" href="css/offline-font.css">
|
||||
<link rel="icon" type="image/x-icon" href="img/NetAlertX_logo.png">
|
||||
<link id="favicon" rel="icon" type="image/x-icon" href="img/NetAlertX_logo.png">
|
||||
|
||||
<!-- For better UX on Mobile Devices using the Shortcut on the Homescreen -->
|
||||
<link rel="manifest" href="img/manifest.json">
|
||||
@@ -130,7 +128,7 @@ if ($ENABLED_DARKMODE === True) {
|
||||
<img src="img/NetAlertX_logo.png" class="pia-top-left-logo" alt="NetAlertX Logo"/>
|
||||
</span>
|
||||
<!-- logo for regular state and mobile devices -->
|
||||
<span class="logo-lg">Net <b>Alert</b><sup>x</sup>
|
||||
<span class="logo-lg">Net<b>Alert</b><sup>x</sup>
|
||||
|
||||
</span>
|
||||
|
||||
@@ -152,19 +150,24 @@ if ($ENABLED_DARKMODE === True) {
|
||||
<ul class="nav navbar-nav">
|
||||
<!-- Back Button -->
|
||||
<li>
|
||||
<a id="back-button" href="javascript:history.go(-1);" role="button" span class='of-bt-icon'><i class='fa fa-arrow-left'></i></a>
|
||||
<a id="back-button" href="javascript:history.go(-1);" role="button" span class='fa fa-arrow-left'></a>
|
||||
</li>
|
||||
<!-- Next Button -->
|
||||
<li>
|
||||
<a id="next-button" href="javascript:history.go(1);" role="button" span class='of-bt-icon'><i class='fa fa-arrow-right'></i></a>
|
||||
<a id="next-button" href="javascript:history.go(1);" role="button" span class='fa fa-arrow-right'></a>
|
||||
</li>
|
||||
<!-- Clear cache & Reload -->
|
||||
<li>
|
||||
<a id="reload-button" href='#' role="button" span class='of-bt-icon' onclick='clearCache()'><i class='fa fa-repeat'></i></a>
|
||||
<a id="reload-button" href='#' role="button" span class='fa fa-repeat' onclick='clearCache()'></a>
|
||||
</li>
|
||||
<!-- Full Screen -->
|
||||
<li>
|
||||
<a id="fullscreen-button" href='#' role="button" span class='of-bt-icon' onclick='toggleFullscreen()'><i class='fa fa-arrows-alt'></i></a>
|
||||
<a id="fullscreen-button" href='#' role="button" span class='fa fa-arrows-alt' onclick='toggleFullscreen()'></a>
|
||||
</li>
|
||||
<!-- Full Screen -->
|
||||
<li>
|
||||
<a id="notifications-button" href='userNotifications.php' role="button" span class='fa-solid fa-bell'></a>
|
||||
<span id="unread-notifications-bell-count" title="" class="badge bg-red unread-notifications-bell" >0</span>
|
||||
</li>
|
||||
<!-- Server Status -->
|
||||
<li>
|
||||
@@ -172,6 +175,9 @@ if ($ENABLED_DARKMODE === True) {
|
||||
<div class="header-status">
|
||||
<code id="state"></code>
|
||||
</div>
|
||||
<div class="header-status-locked-db">
|
||||
<i class="fa-solid fa-database fa-fade"></i>
|
||||
</div>
|
||||
</a>
|
||||
</li>
|
||||
<!-- Server Name -->
|
||||
@@ -184,11 +190,11 @@ if ($ENABLED_DARKMODE === True) {
|
||||
<!-- Header right info -->
|
||||
<li class="dropdown user user-menu">
|
||||
<!-- Menu Toggle Button -->
|
||||
<a href="#" class="dropdown-toggle" data-toggle="dropdown">
|
||||
<!-- The user image in the navbar-->
|
||||
<a href="#" class="dropdown-toggle" style=" height: 50px;" data-toggle="dropdown">
|
||||
|
||||
<span class="hidden-xs" ><!-- The user image in the navbar-->
|
||||
<img src="img/NetAlertX_logo.png" class="user-image" style="border-radius: initial" alt="NetAlertX Logo">
|
||||
<!-- hidden-xs hides the username on small devices so only the image appears. -->
|
||||
<span class="hidden-xs">Net <b>Alert</b><sup>x</sup></span>
|
||||
<!-- hidden-xs hides the username on small devices so only the image appears. --></span>
|
||||
</a>
|
||||
<ul class="dropdown-menu">
|
||||
<!-- The user image in the menu -->
|
||||
@@ -253,10 +259,10 @@ if ($ENABLED_DARKMODE === True) {
|
||||
<a href="devices.php#new" onclick="initializeDatatable('new')" > <?= lang("Device_Shortcut_NewDevices");?> </a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="devices.php#down" onclick="initializeDatatable('down')" > <?= lang("Device_Shortcut_DownAlerts");?> </a>
|
||||
<a href="devices.php#down" onclick="initializeDatatable('down')" > <?= lang("Device_Shortcut_DownOnly");?> </a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="devices.php#down_only" onclick="initializeDatatable('down_only')" > <?= lang("Device_Shortcut_DownOnly");?> </a>
|
||||
<a href="devices.php#offline" onclick="initializeDatatable('offline')" > <?= lang("Gen_Offline");?> </a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="devices.php#archived" onclick="initializeDatatable('archived')" > <?= lang("Device_Shortcut_Archived");?> </a>
|
||||
@@ -267,14 +273,14 @@ if ($ENABLED_DARKMODE === True) {
|
||||
|
||||
<!-- Monitoring menu item -->
|
||||
|
||||
<li class=" treeview <?php if (in_array (basename($_SERVER['SCRIPT_NAME']), array('presence.php', 'report.php', 'events.php' ) ) ){ echo 'active menu-open'; } ?>">
|
||||
<li class=" treeview <?php if (in_array (basename($_SERVER['SCRIPT_NAME']), array('presence.php', 'report.php', 'events.php', 'userNotifications.php' ) ) ){ echo 'active menu-open'; } ?>">
|
||||
<a href="#">
|
||||
<i class="fa fa-fw fa-chart-bar"></i> <span><?= lang('Navigation_Monitoring');?></span>
|
||||
<span class="pull-right-container">
|
||||
<i class="fa fa-angle-left pull-right"></i>
|
||||
</span>
|
||||
</a>
|
||||
<ul class="treeview-menu " style="display: <?php if (in_array (basename($_SERVER['SCRIPT_NAME']), array('presence.php', 'report.php', 'events.php' ) ) ){ echo 'block'; } else {echo 'none';} ?>;">
|
||||
<ul class="treeview-menu " style="display: <?php if (in_array (basename($_SERVER['SCRIPT_NAME']), array('presence.php', 'report.php', 'events.php', 'userNotifications.php' ) ) ){ echo 'block'; } else {echo 'none';} ?>;">
|
||||
<li>
|
||||
<a href="presence.php"> <?= lang("Navigation_Presence");?> </a>
|
||||
</li>
|
||||
@@ -284,6 +290,9 @@ if ($ENABLED_DARKMODE === True) {
|
||||
<li>
|
||||
<a href="report.php"> <?= lang("Navigation_Report");?> </a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="userNotifications.php"> <?= lang("Navigation_Notifications");?> </a>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
</li>
|
||||
@@ -332,7 +341,7 @@ if ($ENABLED_DARKMODE === True) {
|
||||
</a>
|
||||
<ul class="treeview-menu" style="display: <?php if (in_array (basename($_SERVER['SCRIPT_NAME']), array('settings.php') ) ){ echo 'block'; } else {echo 'none';} ?>;">
|
||||
<li>
|
||||
<a href="settings.php#pageTitle"> <?= lang("settings_enabled");?> </a>
|
||||
<a href="settings.php#settingsOverview"> <?= lang("settings_enabled");?> </a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="settings.php#core_content_header"> <?= lang("settings_core_label");?> </a>
|
||||
@@ -341,13 +350,13 @@ if ($ENABLED_DARKMODE === True) {
|
||||
<a href="settings.php#system_content_header"> <?= lang("settings_system_label");?> </a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="settings.php#device_scanner_content_header"> <?= lang("settings_device_scanners_label");?> </a>
|
||||
<a href="settings.php#device_scanners_content_header"> <?= lang("settings_device_scanners_label");?> </a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="settings.php#other_content_header"> <?= lang("settings_other_scanners_label");?> </a>
|
||||
<a href="settings.php#other_scanners_content_header"> <?= lang("settings_other_scanners_label");?> </a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="settings.php#publisher_content_header"> <?= lang("settings_publishers_label");?> </a>
|
||||
<a href="settings.php#publishers_content_header"> <?= lang("settings_publishers_label");?> </a>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
@@ -363,7 +372,7 @@ if ($ENABLED_DARKMODE === True) {
|
||||
</a>
|
||||
<ul class="treeview-menu " style="display: <?php if (in_array (basename($_SERVER['SCRIPT_NAME']), array('plugins.php', 'workflows.php' ) ) ){ echo 'block'; } else {echo 'none';} ?>;">
|
||||
<li>
|
||||
<div class="info-icon-nav work-in-progress"> </div>
|
||||
<div class="info-icon-nav"> </div>
|
||||
<a href="workflows.php"><?= lang('Navigation_Workflows');?></a>
|
||||
</li>
|
||||
<li>
|
||||
@@ -407,7 +416,7 @@ if ($ENABLED_DARKMODE === True) {
|
||||
// Generate work-in-progress icons
|
||||
function workInProgress() {
|
||||
|
||||
if($(".work-in-progress").html().trim() == "")
|
||||
if($(".work-in-progress").length > 0 && $(".work-in-progress").html().trim() == "")
|
||||
{
|
||||
$(".work-in-progress").append(`
|
||||
<a href="https://github.com/jokob-sk/NetAlertX/issues" target="_blank">
|
||||
|
||||
554
front/php/templates/language/de_de.json
Executable file → Normal file
@@ -1,11 +1,11 @@
|
||||
{
|
||||
"API_CUSTOM_SQL_description": "Benutzerdefinierte SQL-Abfrage, welche eine JSON-Datei generiert und diese mit dem <a href=\"/api/table_custom_endpoint.json\" target=\"_blank\">Dateiendpunkt <code>table_custom_endpoint.json</code></a> zur Verf\u00fcgung stellt.",
|
||||
"API_CUSTOM_SQL_description": "Benutzerdefinierte SQL-Abfrage, welche eine JSON-Datei generiert und diese mit dem <a href=\"/api/table_custom_endpoint.json\" target=\"_blank\">Dateiendpunkt <code>table_custom_endpoint.json</code></a> zur Verfügung stellt.",
|
||||
"API_CUSTOM_SQL_name": "Benutzerdefinierte SQL-Abfrage",
|
||||
"API_display_name": "API",
|
||||
"API_icon": "<i class=\"fa fa-arrow-down-up-across-line\"></i>",
|
||||
"APPRISE_HOST_description": "Apprise host URL starting with <code>http://</code> or <code>https://</code>. (do not forget to include <code>/notify</code> at the end)",
|
||||
"APPRISE_HOST_name": "Apprise host URL",
|
||||
"APPRISE_PAYLOAD_description": "Select the payoad type sent to Apprise. For example <code>html</code> works well with emails, <code>text</code> with chat apps, such as Telegram.",
|
||||
"APPRISE_PAYLOAD_description": "Select the payload type sent to Apprise. For example <code>html</code> works well with emails, <code>text</code> with chat apps, such as Telegram.",
|
||||
"APPRISE_PAYLOAD_name": "Payload type",
|
||||
"APPRISE_SIZE_description": "The maximum size of the apprise payload as number of characters in the passed string. If above limit, it will be truncated and a <code>(text was truncated)</code> message is appended.",
|
||||
"APPRISE_SIZE_name": "Max payload size",
|
||||
@@ -13,25 +13,25 @@
|
||||
"APPRISE_URL_name": "Apprise notification URL",
|
||||
"About_Design": "Designed for:",
|
||||
"About_Exit": "Abmelden",
|
||||
"About_Title": "Open Source Network Guard",
|
||||
"About_Title": "Netzwerksicherheitsscanner und Benachrichtigungsframework",
|
||||
"AppEvents_DateTimeCreated": "protokolliert",
|
||||
"AppEvents_Extra": "Extra",
|
||||
"AppEvents_GUID": "Anwendungsereignis-GUID",
|
||||
"AppEvents_Helper1": "Helfer 1",
|
||||
"AppEvents_Helper2": "Helfer 2",
|
||||
"AppEvents_Helper3": "Helfer 3",
|
||||
"AppEvents_ObjectForeignKey": "Unbekannter Schl\u00fcssel",
|
||||
"AppEvents_ObjectForeignKey": "Unbekannter Schlüssel",
|
||||
"AppEvents_ObjectIndex": "Index",
|
||||
"AppEvents_ObjectIsArchived": "Ist archiviert (Zum Protokoll Zeitpunkt)",
|
||||
"AppEvents_ObjectIsNew": "Ist neu (Zum Protokoll Zeitpunkt)",
|
||||
"AppEvents_ObjectPlugin": "Verkn\u00fcpfte Plugins",
|
||||
"AppEvents_ObjectPrimaryID": "",
|
||||
"AppEvents_ObjectSecondaryID": "",
|
||||
"AppEvents_ObjectPlugin": "Verknüpfte Plugins",
|
||||
"AppEvents_ObjectPrimaryID": "Primär ID",
|
||||
"AppEvents_ObjectSecondaryID": "Sekundär ID",
|
||||
"AppEvents_ObjectStatus": "",
|
||||
"AppEvents_ObjectStatusColumn": "",
|
||||
"AppEvents_ObjectType": "",
|
||||
"AppEvents_Plugin": "",
|
||||
"AppEvents_Type": "",
|
||||
"AppEvents_ObjectType": "Objekttyp",
|
||||
"AppEvents_Plugin": "Plugin",
|
||||
"AppEvents_Type": "Typ",
|
||||
"Apprise_display_name": "Apprise",
|
||||
"Apprise_icon": "<i class=\"fa fa-bullhorn\"></i>",
|
||||
"BackDevDetail_Actions_Ask_Run": "Do you want to execute the action?",
|
||||
@@ -39,49 +39,49 @@
|
||||
"BackDevDetail_Actions_Title_Run": "Run action",
|
||||
"BackDevDetail_Copy_Ask": "Copy details from device from the dropdown list (Everything on this page will be overwritten)?",
|
||||
"BackDevDetail_Copy_Title": "Copy details",
|
||||
"BackDevDetail_Tools_WOL_error": "Befehl wurde NICHT ausgef\u00fchrt.",
|
||||
"BackDevDetail_Tools_WOL_okay": "Befehl wurde ausgef\u00fchrt.",
|
||||
"BackDevDetail_Tools_WOL_error": "Befehl wurde NICHT ausgeführt.",
|
||||
"BackDevDetail_Tools_WOL_okay": "Befehl wurde ausgeführt.",
|
||||
"BackDevices_Arpscan_disabled": "Automatischer Arp-Scan deaktiviert.",
|
||||
"BackDevices_Arpscan_enabled": "Automatischer Arp-Scan aktiviert.",
|
||||
"BackDevices_Backup_CopError": "Die originale Datenbank konnte nicht gesichert werden.",
|
||||
"BackDevices_Backup_Failed": "Das Backup wurde teilweise ausgef\u00fchrt. Das Archiv ist entweder leer oder nicht vorhanden.",
|
||||
"BackDevices_Backup_Failed": "Das Backup wurde teilweise ausgeführt. Das Archiv ist entweder leer oder nicht vorhanden.",
|
||||
"BackDevices_Backup_okay": "Das Backup wurde erfolgreich beendet.",
|
||||
"BackDevices_DBTools_DelActHistory": "Die Anzeige der Netzwerkaktivit\u00e4t wurde zur\u00fcckgesetzt.",
|
||||
"BackDevices_DBTools_DelActHistoryError": "Fehler beim Zur\u00fccksetzen der Netzwerkaktivit\u00e4tsanzeige.",
|
||||
"BackDevices_DBTools_DelDevError_a": "Fehler beim L\u00f6schen des Ger\u00e4tes.",
|
||||
"BackDevices_DBTools_DelDevError_b": "Fehler beim L\u00f6schen der Ger\u00e4te.",
|
||||
"BackDevices_DBTools_DelDev_a": "Ger\u00e4t erfolgreich gel\u00f6scht.",
|
||||
"BackDevices_DBTools_DelDev_b": "Ger\u00e4te erfolgreich gel\u00f6scht.",
|
||||
"BackDevices_DBTools_DelEvents": "Events erfolgreich gel\u00f6scht.",
|
||||
"BackDevices_DBTools_DelEventsError": "Fehler beim L\u00f6schen der Ereignisse.",
|
||||
"BackDevices_DBTools_ImportCSV": "Die Ger\u00e4te aus der CSV-Datei wurden erfolgreich importiert.",
|
||||
"BackDevices_DBTools_DelActHistory": "Die Anzeige der Netzwerkaktivität wurde zurückgesetzt.",
|
||||
"BackDevices_DBTools_DelActHistoryError": "Fehler beim Zurücksetzen der Netzwerkaktivitätsanzeige.",
|
||||
"BackDevices_DBTools_DelDevError_a": "Fehler beim Löschen des Gerätes.",
|
||||
"BackDevices_DBTools_DelDevError_b": "Fehler beim Löschen der Geräte.",
|
||||
"BackDevices_DBTools_DelDev_a": "Gerät gelöscht.",
|
||||
"BackDevices_DBTools_DelDev_b": "Geräte gelöscht.",
|
||||
"BackDevices_DBTools_DelEvents": "Events gelöscht.",
|
||||
"BackDevices_DBTools_DelEventsError": "Fehler beim Löschen der Ereignisse.",
|
||||
"BackDevices_DBTools_ImportCSV": "Die Geräte aus der CSV-Datei wurden erfolgreich importiert.",
|
||||
"BackDevices_DBTools_ImportCSVError": "Die CSV-Datei konnte nicht importiert werden. Stellen Sie sicher, dass das Format korrekt ist.",
|
||||
"BackDevices_DBTools_ImportCSVMissing": "Die CSV-Datei konnte nicht in <b>/config/devices.csv</b> gefunden werden.",
|
||||
"BackDevices_DBTools_Purge": "Die \u00e4ltesten Backups wurden gel\u00f6scht.",
|
||||
"BackDevices_DBTools_UpdDev": "Ger\u00e4t erfolgreich aktualisiert.",
|
||||
"BackDevices_DBTools_UpdDevError": "Fehler beim Aktualisieren des Ger\u00e4tes.",
|
||||
"BackDevices_DBTools_Purge": "Die ältesten Backups wurden gelöscht.",
|
||||
"BackDevices_DBTools_UpdDev": "Gerät erfolgreich aktualisiert.",
|
||||
"BackDevices_DBTools_UpdDevError": "Fehler beim Aktualisieren des Gerätes.",
|
||||
"BackDevices_DBTools_Upgrade": "Datenbank erfolgreich aktualisiert.",
|
||||
"BackDevices_DBTools_UpgradeError": "Fehler beim Aktualisieren der Datenbank.",
|
||||
"BackDevices_Device_UpdDevError": "Konnte Ger\u00e4te nicht aktualisieren, versuchen Sie es sp\u00e4ter erneut. Die Datenbank ist wahrscheinlich wegen einer laufenden Aufgabe gesperrt.",
|
||||
"BackDevices_Device_UpdDevError": "Konnte Geräte nicht aktualisieren, versuchen Sie es später erneut. Die Datenbank ist wahrscheinlich wegen einer laufenden Aufgabe gesperrt.",
|
||||
"BackDevices_Restore_CopError": "Die originale Datenbank konnte nicht kopiert werden.",
|
||||
"BackDevices_Restore_Failed": "Die Wiederherstellung ist fehlgeschlagen. Stellen Sie das Backup manuell her.",
|
||||
"BackDevices_Restore_okay": "Die Wiederherstellung wurde erfolgreich ausgef\u00fchrt.",
|
||||
"BackDevices_Restore_okay": "Die Wiederherstellung wurde erfolgreich ausgeführt.",
|
||||
"BackDevices_darkmode_disabled": "Heller Modus aktiviert.",
|
||||
"BackDevices_darkmode_enabled": "Dunkler Modus aktiviert.",
|
||||
"DAYS_TO_KEEP_EVENTS_description": "Dies ist eine Wartungseinstellung. Spezifiziert wie viele Tage Events gespeichert bleiben. Alle \u00e4lteren Events werden periodisch gel\u00f6scht. Wird auch auf die Plugins History angewendet.",
|
||||
"DAYS_TO_KEEP_EVENTS_name": "L\u00f6sche Events \u00e4lter als",
|
||||
"DevDetail_Copy_Device_Title": "<i class=\"fa fa-copy\"></i> Details von Ger\u00e4t kopieren",
|
||||
"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",
|
||||
"DevDetail_Copy_Device_Title": "<i class=\"fa fa-copy\"></i> Details von Gerät kopieren",
|
||||
"DevDetail_Copy_Device_Tooltip": "Copy details from device from the dropdown list. Everything on this page will be overwritten",
|
||||
"DevDetail_EveandAl_AlertAllEvents": "Melde alle Ereignisse",
|
||||
"DevDetail_EveandAl_AlertDown": "Melde Down",
|
||||
"DevDetail_EveandAl_Archived": "Archivierung",
|
||||
"DevDetail_EveandAl_NewDevice": "Neues Ger\u00e4t",
|
||||
"DevDetail_EveandAl_NewDevice": "Neues Gerät",
|
||||
"DevDetail_EveandAl_NewDevice_Tooltip": "",
|
||||
"DevDetail_EveandAl_RandomMAC": "Zuf\u00e4llige MAC",
|
||||
"DevDetail_EveandAl_RandomMAC": "Zufällige MAC",
|
||||
"DevDetail_EveandAl_ScanCycle": "Scan Abstand",
|
||||
"DevDetail_EveandAl_ScanCycle_a": "Ger\u00e4t scannen",
|
||||
"DevDetail_EveandAl_ScanCycle_z": "Ger\u00e4t nicht scannen",
|
||||
"DevDetail_EveandAl_Skip": "pausiere wiederhol. Meldungen f\u00fcr",
|
||||
"DevDetail_EveandAl_ScanCycle_a": "Gerät scannen",
|
||||
"DevDetail_EveandAl_ScanCycle_z": "Gerät nicht scannen",
|
||||
"DevDetail_EveandAl_Skip": "pausiere wiederhol. Meldungen für",
|
||||
"DevDetail_EveandAl_Title": "Ereignisse & Alarme einstellen",
|
||||
"DevDetail_Events_CheckBox": "Blende Verbindungs-Ereignisse aus",
|
||||
"DevDetail_GoToNetworkNode": "Navigate to the Network page of the given node.",
|
||||
@@ -93,42 +93,44 @@
|
||||
"DevDetail_MainInfo_Group": "Gruppe",
|
||||
"DevDetail_MainInfo_Location": "Standort",
|
||||
"DevDetail_MainInfo_Name": "Name",
|
||||
"DevDetail_MainInfo_Network": "Netzwerk Knoten",
|
||||
"DevDetail_MainInfo_Network_Port": "Netzwerk Knoten Port",
|
||||
"DevDetail_MainInfo_Network": "<i class=\"fa fa-server\"></i> Knoten (MAC)",
|
||||
"DevDetail_MainInfo_Network_Port": "<i class=\"fa fa-ethernet\"></i> Port",
|
||||
"DevDetail_MainInfo_Network_Site": "",
|
||||
"DevDetail_MainInfo_Network_Title": "<i class=\"fa fa-network-wired\"></i> Network",
|
||||
"DevDetail_MainInfo_Owner": "Eigen­t\u00fcmer",
|
||||
"DevDetail_MainInfo_Title": "Haupt Infos",
|
||||
"DevDetail_MainInfo_Owner": "Eigen­tümer",
|
||||
"DevDetail_MainInfo_SSID": "",
|
||||
"DevDetail_MainInfo_Title": "<i class=\"fa fa-pencil\"></i> Hauptinformation",
|
||||
"DevDetail_MainInfo_Type": "Typ",
|
||||
"DevDetail_MainInfo_Vendor": "Hersteller",
|
||||
"DevDetail_MainInfo_mac": "MAC",
|
||||
"DevDetail_Network_Node_hover": "Select the parent network device the current device is connected to to populate the Network tree.",
|
||||
"DevDetail_Network_Node_hover": "Wählen Sie das Elternnetzgerät aus, an das das aktuelle Gerät angeschlossen ist, um den Netzwerkbaum zu erstellen.",
|
||||
"DevDetail_Network_Port_hover": "The port this device is connected to on the parent network device. If left empty a wifi icon is displayed in the Network tree.",
|
||||
"DevDetail_Nmap_Scans": "Nmap Scans",
|
||||
"DevDetail_Nmap_Scans_desc": "Hier kannst du manuelle NMAP Scans starten. Regul\u00e4re automatische NMAP Scans k\u00f6nnen mit dem Services & Ports (NMAP) Plugin geplant werden. Gehe zu den <a href='/settings.php' target='_blank'>Einstellungen</a> um mehr herauszufinden.",
|
||||
"DevDetail_Nmap_Scans_desc": "Hier kannst du manuelle NMAP Scans starten. Reguläre automatische NMAP Scans können mit dem Services & Ports (NMAP) Plugin geplant werden. Gehe zu den <a href='/settings.php' target='_blank'>Einstellungen</a> um mehr herauszufinden.",
|
||||
"DevDetail_Nmap_buttonDefault": "Standard Scan",
|
||||
"DevDetail_Nmap_buttonDefault_text": "Standard Scan: Nmap scannt die ersten 1.000 Ports f\u00fcr jedes angeforderte Scan-Protokoll. Damit werden etwa 93 % der TCP-Ports und 49 % der UDP-Ports erfasst. (ca. 5-10 Sekunden)",
|
||||
"DevDetail_Nmap_buttonDefault_text": "Standard Scan: Nmap scannt die ersten 1.000 Ports für jedes angeforderte Scan-Protokoll. Damit werden etwa 93 % der TCP-Ports und 49 % der UDP-Ports erfasst. (ca. 5-10 Sekunden)",
|
||||
"DevDetail_Nmap_buttonDetail": "Detailierter Scan",
|
||||
"DevDetail_Nmap_buttonDetail_text": "Detailierter Scan: Standardscan mit aktivierter Betriebssystemerkennung, Versionserkennung, Skript-Scan und Traceroute (bis zu 30 oder mehr Sekunden)",
|
||||
"DevDetail_Nmap_buttonFast": "Schneller Scan",
|
||||
"DevDetail_Nmap_buttonFast_text": "Schneller Scan: \u00dcberpr\u00fcft nur die wichtigsten 100 Ports (wenige Sekunden)",
|
||||
"DevDetail_Nmap_buttonSkipDiscovery": "Ohne Erreichbarkeitspr\u00fcfung",
|
||||
"DevDetail_Nmap_buttonSkipDiscovery_text": "Ohne Erreichbarkeitspr\u00fcfung (-Pn Parameter): Standard Scan bei dem nmap annimmt, dass der Host erreichbar ist.",
|
||||
"DevDetail_Nmap_resultsLink": "Nachdem ein Scan gestartet wurde, kann diese Seite verlassen werden. Resultate sind auch in der Datei <code>app_front.log</code> verf\u00fcgbar.",
|
||||
"DevDetail_Owner_hover": "Der Eigent\u00fcmer des Ger\u00e4tes. Freies Textfeld.",
|
||||
"DevDetail_Nmap_buttonFast_text": "Schneller Scan: Überprüft nur die wichtigsten 100 Ports (wenige Sekunden)",
|
||||
"DevDetail_Nmap_buttonSkipDiscovery": "Ohne Erreichbarkeitsprüfung",
|
||||
"DevDetail_Nmap_buttonSkipDiscovery_text": "Ohne Erreichbarkeitsprüfung (-Pn Parameter): Standard Scan bei dem nmap annimmt, dass der Host erreichbar ist.",
|
||||
"DevDetail_Nmap_resultsLink": "Nachdem ein Scan gestartet wurde, kann diese Seite verlassen werden. Resultate sind auch in der Datei <code>app_front.log</code> verfügbar.",
|
||||
"DevDetail_Owner_hover": "Der Eigentümer des Gerätes. Freies Textfeld.",
|
||||
"DevDetail_Periodselect_All": "Alle Infos",
|
||||
"DevDetail_Periodselect_LastMonth": "Letzter Monat",
|
||||
"DevDetail_Periodselect_LastWeek": "Letzte Woche",
|
||||
"DevDetail_Periodselect_LastYear": "Letztes Jahr",
|
||||
"DevDetail_Periodselect_today": "Heute",
|
||||
"DevDetail_Run_Actions_Title": "<i class=\"fa fa-play\"></i> Aktion auf Ger\u00e4t ausf\u00fchren",
|
||||
"DevDetail_Run_Actions_Tooltip": "Eine Aktion aus der Dropdown-Liste auf dem aktuellen Ger\u00e4t ausf\u00fchren.",
|
||||
"DevDetail_Run_Actions_Title": "<i class=\"fa fa-play\"></i> Aktion auf Gerät ausführen",
|
||||
"DevDetail_Run_Actions_Tooltip": "Eine Aktion aus der Dropdown-Liste auf dem aktuellen Gerät ausführen.",
|
||||
"DevDetail_SessionInfo_FirstSession": "Erste Sitzung",
|
||||
"DevDetail_SessionInfo_LastIP": "Letzte IP",
|
||||
"DevDetail_SessionInfo_LastSession": "Letzte Sitzung",
|
||||
"DevDetail_SessionInfo_StaticIP": "Statische IP",
|
||||
"DevDetail_SessionInfo_Status": "Status",
|
||||
"DevDetail_SessionInfo_Title": "Sitzungsinfos",
|
||||
"DevDetail_SessionTable_Additionalinfo": "Zus\u00e4tzliche Info",
|
||||
"DevDetail_SessionInfo_Title": "<i class=\"fa fa-calendar\"></i> Sitzungsinformation",
|
||||
"DevDetail_SessionTable_Additionalinfo": "Zusätzliche Info",
|
||||
"DevDetail_SessionTable_Connection": "Verbindung",
|
||||
"DevDetail_SessionTable_Disconnection": "Trennung",
|
||||
"DevDetail_SessionTable_Duration": "Dauer",
|
||||
@@ -138,76 +140,77 @@
|
||||
"DevDetail_Shortcut_DownAlerts": "Down Meldungen",
|
||||
"DevDetail_Shortcut_Presence": "Anwesenheit",
|
||||
"DevDetail_Shortcut_Sessions": "Sitzungen",
|
||||
"DevDetail_Tab_Details": "Details",
|
||||
"DevDetail_Tab_Events": "Ereignisse",
|
||||
"DevDetail_Tab_Details": "<i class=\"fa fa-info-circle\"></i> Details",
|
||||
"DevDetail_Tab_Events": "<i class=\"fa fa-bolt\"></i> Ereignisse",
|
||||
"DevDetail_Tab_EventsTableDate": "Datum",
|
||||
"DevDetail_Tab_EventsTableEvent": "Ereignistype",
|
||||
"DevDetail_Tab_EventsTableIP": "IP",
|
||||
"DevDetail_Tab_EventsTableInfo": "Zus\u00e4tzliche Informationen",
|
||||
"DevDetail_Tab_Nmap": "Nmap",
|
||||
"DevDetail_Tab_NmapEmpty": "An diesem Ger\u00e4t wurden keine offenen Ports mit Nmap gefunden.",
|
||||
"DevDetail_Tab_EventsTableInfo": "Zusätzliche Informationen",
|
||||
"DevDetail_Tab_Nmap": "<i class=\"fa fa-ethernet\"></i> Nmap",
|
||||
"DevDetail_Tab_NmapEmpty": "An diesem Gerät wurden keine offenen Ports mit Nmap gefunden.",
|
||||
"DevDetail_Tab_NmapTableExtra": "Extra",
|
||||
"DevDetail_Tab_NmapTableHeader": "Ergebnisse geplanter Scans",
|
||||
"DevDetail_Tab_NmapTableIndex": "Index",
|
||||
"DevDetail_Tab_NmapTablePort": "Port",
|
||||
"DevDetail_Tab_NmapTableService": "Dienst",
|
||||
"DevDetail_Tab_NmapTableState": "Status",
|
||||
"DevDetail_Tab_NmapTableText": "Erstelle einen Plan \u00fcber die<a href=\"/settings.php#NMAP_ACTIVE\">Einstellungen</a>",
|
||||
"DevDetail_Tab_NmapTableText": "Erstelle einen Plan über die<a href=\"/settings.php#NMAP_ACTIVE\">Einstellungen</a>",
|
||||
"DevDetail_Tab_NmapTableTime": "Zeit",
|
||||
"DevDetail_Tab_Plugins": "<i class=\"fa fa-plug\"></i> Plugins",
|
||||
"DevDetail_Tab_Presence": "Anwesenheit",
|
||||
"DevDetail_Tab_Sessions": "Sitzungen",
|
||||
"DevDetail_Tab_Presence": "<i class=\"fa fa-calendar\"></i> Anwesenheit",
|
||||
"DevDetail_Tab_Sessions": "<i class=\"fa fa-list-ol\"></i> Sitzungen",
|
||||
"DevDetail_Tab_Tools": "<i class=\"fa fa-screwdriver-wrench\"></i> Tools",
|
||||
"DevDetail_Tab_Tools_Internet_Info_Description": "Das Internet-Info-Tool zeigt Informationen \u00fcber die Internetverbindung an, wie z. B. IP-Adresse, Stadt, Land, Ortsvorwahl und Zeitzone.",
|
||||
"DevDetail_Tab_Tools_Internet_Info_Description": "Das Internet-Info-Tool zeigt Informationen über die Internetverbindung an, wie z. B. IP-Adresse, Stadt, Land, Ortsvorwahl und Zeitzone.",
|
||||
"DevDetail_Tab_Tools_Internet_Info_Error": "Es ist ein Fehler aufgetreten",
|
||||
"DevDetail_Tab_Tools_Internet_Info_Start": "Internet-Info starten",
|
||||
"DevDetail_Tab_Tools_Internet_Info_Title": "Internetinformationen",
|
||||
"DevDetail_Tab_Tools_Nslookup_Description": "Nslookup ist ein Befehlszeilentool zur Abfrage des Domain Name System (DNS). DNS ist ein System, das Domainnamen wie www.google.com in IP-Adressen wie 172.217.0.142 \u00fcbersetzt. ",
|
||||
"DevDetail_Tab_Tools_Nslookup_Error": "Fehler: IP-Adresse ist ung\u00fcltig",
|
||||
"DevDetail_Tab_Tools_Nslookup_Description": "Nslookup ist ein Befehlszeilentool zur Abfrage des Domain Name System (DNS). DNS ist ein System, das Domainnamen wie www.google.com in IP-Adressen wie 172.217.0.142 übersetzt. ",
|
||||
"DevDetail_Tab_Tools_Nslookup_Error": "Fehler: IP-Adresse ist ungültig",
|
||||
"DevDetail_Tab_Tools_Nslookup_Start": "Nslookup starten",
|
||||
"DevDetail_Tab_Tools_Nslookup_Title": "Nslookup",
|
||||
"DevDetail_Tab_Tools_Speedtest_Description": "Das Speedtest-Tool misst die Download-Geschwindigkeit, Upload-Geschwindigkeit und Latenz der Internetverbindung.",
|
||||
"DevDetail_Tab_Tools_Speedtest_Start": "Speedtest starten",
|
||||
"DevDetail_Tab_Tools_Speedtest_Title": "Speedtest test",
|
||||
"DevDetail_Tab_Tools_Traceroute_Description": "Traceroute ist ein Netzwerkdiagnosebefehl, mit dem der Pfad verfolgt wird, den Datenpakete von einem Host zu einem anderen nehmen.<br><br>Der Befehl verwendet das Internet Control Message Protocol (ICMP), um Pakete an Zwischenknoten auf der Route zu senden, jeden Zwischenknoten Der Knoten antwortet mit einem ICMP-Timeout-Paket (TTL-Zeit\u00fcberschreitung).<br><br>Die Ausgabe des Traceroute-Befehls zeigt die IP-Adresse jedes Zwischenknotens auf der Route an.<br><br>Die Ausgabe der Traceroute Der Befehl zeigt die IP-Adresse jedes Zwischenknotens auf der Route an.<br><br>Der Befehl \u201etraceroute\u201c kann zur Diagnose von Netzwerkproblemen wie Verz\u00f6gerungen, Paketverlust und blockierten Routen verwendet werden.<br><br>Das ist auch m\u00f6glich kann verwendet werden, um den Standort eines Zwischenknotens in einem Netzwerk zu identifizieren.",
|
||||
"DevDetail_Tab_Tools_Traceroute_Error": "Fehler: IP-Adresse ist ung\u00fcltig",
|
||||
"DevDetail_Tab_Tools_Traceroute_Description": "Traceroute ist ein Netzwerkdiagnosebefehl, mit dem der Pfad verfolgt wird, den Datenpakete von einem Host zu einem anderen nehmen.<br><br>Der Befehl verwendet das Internet Control Message Protocol (ICMP), um Pakete an Zwischenknoten auf der Route zu senden, jeden Zwischenknoten Der Knoten antwortet mit einem ICMP-Timeout-Paket (TTL-Zeitüberschreitung).<br><br>Die Ausgabe des Traceroute-Befehls zeigt die IP-Adresse jedes Zwischenknotens auf der Route an.<br><br>Die Ausgabe der Traceroute Der Befehl zeigt die IP-Adresse jedes Zwischenknotens auf der Route an.<br><br>Der Befehl „traceroute“ kann zur Diagnose von Netzwerkproblemen wie Verzögerungen, Paketverlust und blockierten Routen verwendet werden.<br><br>Das ist auch möglich kann verwendet werden, um den Standort eines Zwischenknotens in einem Netzwerk zu identifizieren.",
|
||||
"DevDetail_Tab_Tools_Traceroute_Error": "Fehler: IP-Adresse ist ungültig",
|
||||
"DevDetail_Tab_Tools_Traceroute_Start": "Traceroute starten",
|
||||
"DevDetail_Tab_Tools_Traceroute_Title": "Traceroute",
|
||||
"DevDetail_Tools_WOL": "Sende Wol Befehl an ",
|
||||
"DevDetail_Tools_WOL_noti": "Wake-on-LAN",
|
||||
"DevDetail_Tools_WOL_noti_text": "Der Wake-on-LAN Befehl wurd and die Broadcast Adresse gesendet. Wenn sich das zu startende Ger\u00e4t nicht im gleichen Subnet/vlan wie Pi.Alert befindet, wird das Ger\u00e4t nicht reagieren.",
|
||||
"DevDetail_Type_hover": "Der Type des Ger\u00e4tes. If you select any of the pre-defined network devices (e.g.: AP, Firewall, Router, Switch...) they will show up in the Network tree configuration as possible parent network nodes.",
|
||||
"DevDetail_Tools_WOL_noti_text": "Der Wake-on-LAN Befehl wurde and die Broadcast Adresse gesendet. Wenn sich das zu startende Gerät nicht im gleichen Subnet / VLan wie NetAlertX befindet, wird das Gerät nicht reagieren.",
|
||||
"DevDetail_Type_hover": "Der Type des Gerätes. If you select any of the pre-defined network devices (e.g.: AP, Firewall, Router, Switch...) they will show up in the Network tree configuration as possible parent network nodes.",
|
||||
"DevDetail_Vendor_hover": "Vendor should be auto-detected. You can overwrite or add your custom value.",
|
||||
"DevDetail_WOL_Title": "<i class=\"fa fa-power-off\"></i> Wake-on-LAN",
|
||||
"DevDetail_button_AddIcon": "",
|
||||
"DevDetail_button_AddIcon_Help": "",
|
||||
"DevDetail_button_AddIcon_Tooltip": "",
|
||||
"DevDetail_button_Delete": "L\u00f6sche Ger\u00e4t",
|
||||
"DevDetail_button_DeleteEvents": "L\u00f6sche Events",
|
||||
"DevDetail_button_DeleteEvents_Warning": "Sind Sie sicher, dass Sie alle Ereignisse dieses Ger\u00e4ts l\u00f6schen m\u00f6chten? (dies l\u00f6scht den Ereignisverlauf und die Sitzungen und k\u00f6nnte bei st\u00e4ndigen (anhaltenden) Benachrichtigungen helfen)",
|
||||
"DevDetail_button_AddIcon": "Neues Symbol Hinzufügen",
|
||||
"DevDetail_button_AddIcon_Help": "Füge ein HTML SVG Tag oder Font Awesome HTML Tag ein. Siehe <a href=\"https://github.com/jokob-sk/NetAlertX/blob/main/docs/ICONS.md\" target=\"_blank\">Icon Dokumentation</a> für details.",
|
||||
"DevDetail_button_AddIcon_Tooltip": "Neues Icon zu diesem Gerät hinzufügen, welches es noch nicht im dropdown gibt.",
|
||||
"DevDetail_button_Delete": "Lösche Gerät",
|
||||
"DevDetail_button_DeleteEvents": "Lösche Events",
|
||||
"DevDetail_button_DeleteEvents_Warning": "Sind Sie sicher, dass Sie alle Ereignisse dieses Geräts löschen möchten? (dies löscht den Ereignisverlauf und die Sitzungen und könnte bei ständigen (anhaltenden) Benachrichtigungen helfen)",
|
||||
"DevDetail_button_OverwriteIcons": "Overwrite Icons",
|
||||
"DevDetail_button_OverwriteIcons_Tooltip": "Overwrite icons of all devices with the same device type",
|
||||
"DevDetail_button_OverwriteIcons_Warning": "Are you sure you want to overwrite all icons of all devices with the same device type as the current device type?",
|
||||
"DevDetail_button_Reset": "Verwerfen",
|
||||
"DevDetail_button_Save": "Speichern",
|
||||
"Device_MultiEdit": "",
|
||||
"Device_MultiEdit": "Mehrfach-bearbeiten",
|
||||
"Device_MultiEdit_Backup": "",
|
||||
"Device_MultiEdit_Fields": "",
|
||||
"Device_MultiEdit_MassActions": "",
|
||||
"Device_MultiEdit_Fields": "Felder bearbeiten:",
|
||||
"Device_MultiEdit_MassActions": "Massen aktionen:",
|
||||
"Device_MultiEdit_Tooltip": "",
|
||||
"Device_Searchbox": "Suche",
|
||||
"Device_Shortcut_AllDevices": "Alle Ger\u00e4te",
|
||||
"Device_Shortcut_AllDevices": "Meine Geräte",
|
||||
"Device_Shortcut_Archived": "Archiviert",
|
||||
"Device_Shortcut_Connected": "Verbunden",
|
||||
"Device_Shortcut_Devices": "Ger\u00e4te",
|
||||
"Device_Shortcut_DownAlerts": "Down Meldungen",
|
||||
"Device_Shortcut_DownOnly": "",
|
||||
"Device_Shortcut_Devices": "Geräte",
|
||||
"Device_Shortcut_DownAlerts": "Offline & Nicht erreichbar",
|
||||
"Device_Shortcut_DownOnly": "Offline",
|
||||
"Device_Shortcut_Favorites": "Favoriten",
|
||||
"Device_Shortcut_NewDevices": "Neue Ger\u00e4te",
|
||||
"Device_Shortcut_OnlineChart": "Ger\u00e4tepr\u00e4senz im Laufe der Zeit",
|
||||
"Device_TableHead_Connected_Devices": "Verbundene Ger\u00e4te",
|
||||
"Device_Shortcut_NewDevices": "Neue Geräte",
|
||||
"Device_Shortcut_OnlineChart": "Gerätepräsenz im Laufe der Zeit",
|
||||
"Device_TableHead_Connected_Devices": "Verbundene Geräte",
|
||||
"Device_TableHead_Favorite": "Favorit",
|
||||
"Device_TableHead_FirstSession": "Erste Sitzung",
|
||||
"Device_TableHead_GUID": "",
|
||||
"Device_TableHead_Group": "Gruppe",
|
||||
"Device_TableHead_Icon": "Icon",
|
||||
"Device_TableHead_LastIP": "Letzte IP",
|
||||
@@ -217,24 +220,27 @@
|
||||
"Device_TableHead_MAC": "MAC",
|
||||
"Device_TableHead_MAC_full": "Gesamte MAC",
|
||||
"Device_TableHead_Name": "Name",
|
||||
"Device_TableHead_Owner": "Eigent\u00fcmer",
|
||||
"Device_TableHead_Parent_MAC": "\u00dcbergeordnete MAC",
|
||||
"Device_TableHead_NetworkSite": "",
|
||||
"Device_TableHead_Owner": "Eigentümer",
|
||||
"Device_TableHead_Parent_MAC": "Übergeordnete MAC",
|
||||
"Device_TableHead_Port": "Port",
|
||||
"Device_TableHead_RowID": "",
|
||||
"Device_TableHead_RowID": "Zeilen ID",
|
||||
"Device_TableHead_Rowid": "Zeilennummer",
|
||||
"Device_TableHead_SSID": "",
|
||||
"Device_TableHead_Status": "Status",
|
||||
"Device_TableHead_SyncHubNodeName": "",
|
||||
"Device_TableHead_Type": "Typ",
|
||||
"Device_TableHead_Vendor": "Hersteller",
|
||||
"Device_Table_Not_Network_Device": "Nicht konfiguriert als Netzwerkger\u00e4t",
|
||||
"Device_Table_info": "Zeige _START_ bis _END_ von _TOTAL_ Eintr\u00e4gen",
|
||||
"Device_Table_nav_next": "N\u00e4chste",
|
||||
"Device_Table_nav_prev": "Zur\u00fcck",
|
||||
"Device_Tablelenght": "Zeige _MENU_ Eintr\u00e4ge",
|
||||
"Device_Table_Not_Network_Device": "Nicht konfiguriert als Netzwerkgerät",
|
||||
"Device_Table_info": "Zeige _START_ bis _END_ von _TOTAL_ Einträgen",
|
||||
"Device_Table_nav_next": "Nächste",
|
||||
"Device_Table_nav_prev": "Zurück",
|
||||
"Device_Tablelenght": "Zeige _MENU_ Einträge",
|
||||
"Device_Tablelenght_all": "Alle",
|
||||
"Device_Title": "Ger\u00e4te",
|
||||
"Device_Title": "Geräte",
|
||||
"Donations_Others": "Others",
|
||||
"Donations_Platforms": "Sponsor platforms",
|
||||
"Donations_Text": "Hey \ud83d\udc4b! </br> Thanks for clicking on this menu item \ud83d\ude05 </br> </br> I'm trying to collect some donations to make you better software. Also, it would help me not to get burned out. Me burning out might mean end of support for this app. Any small (recurring or not) sponsorship makes me want ot put more effort into this app. I don't want to lock features (new plugins) behind paywalls \ud83d\udd10. </br> Currently, I'm waking up 2h before work so I contribute to the app a bit. If I had some recurring income I could shorten my workweek and in the remaining time fully focus on NetAlertX. You'd get more functionality, a more polished app and less bugs. </br> </br> Thanks for reading - I'm super grateful for any support \u2764\ud83d\ude4f </br> </br> TL;DR: By supporting me you get: </br> </br> <ul><li>Regular updates to keep your data and family safe \ud83d\udd04</li><li>Less bugs \ud83d\udc1b\ud83d\udd2b</li><li>Better and more functionality\u2795</li><li>I don't get burned out \ud83d\udd25\ud83e\udd2f</li><li>Less rushed releases \ud83d\udca8</li><li>Better docs\ud83d\udcda</li><li>Quicker and better support with issues \ud83c\udd98</li><li>Less grumpy me \ud83d\ude04</li></ul> </br> \ud83d\udce7Email me to <a href='mailto:jokob@duck.com?subject=NetAlertX'>jokob@duck.com</a> if you want to get in touch or if I should add other sponsorship platforms. </br>",
|
||||
"Donations_Text": "Hey 👋! </br> Thanks for clicking on this menu item 😅 </br> </br> I'm trying to collect some donations to make you better software. Also, it would help me not to get burned out. Me burning out might mean end of support for this app. Any small (recurring or not) sponsorship makes me want ot put more effort into this app. I don't want to lock features (new plugins) behind paywalls 🔐. </br> Currently, I'm waking up 2h before work so I contribute to the app a bit. If I had some recurring income I could shorten my workweek and in the remaining time fully focus on NetAlertX. You'd get more functionality, a more polished app and less bugs. </br> </br> Thanks for reading - I'm super grateful for any support ❤🙏 </br> </br> TL;DR: By supporting me you get: </br> </br> <ul><li>Regular updates to keep your data and family safe 🔄</li><li>Less bugs 🐛🔫</li><li>Better and more functionality➕</li><li>I don't get burned out 🔥🤯</li><li>Less rushed releases 💨</li><li>Better docs📚</li><li>Quicker and better support with issues 🆘</li><li>Less grumpy me 😄</li></ul> </br> 📧Email me to <a href='mailto:jokob@duck.com?subject=NetAlertX'>jokob@duck.com</a> if you want to get in touch or if I should add other sponsorship platforms. </br>",
|
||||
"Donations_Title": "Donations",
|
||||
"ENABLE_PLUGINS_description": "NOTUSED Enables the <a target=\"_blank\" href=\"https://github.com/jokob-sk/NetAlertX/tree/main/front/plugins\">plugins</a> functionality. Loading plugins requires more hardware resources so you might want to disable them on low-powered system.",
|
||||
"ENABLE_PLUGINS_name": "NOTUSED Enable Plugins",
|
||||
@@ -251,13 +257,13 @@
|
||||
"Events_Shortcut_DownAlerts": "Down Meldungen",
|
||||
"Events_Shortcut_Events": "Ereignisse",
|
||||
"Events_Shortcut_MissSessions": "fehlende Sitzungen",
|
||||
"Events_Shortcut_NewDevices": "Neue Ger\u00e4te",
|
||||
"Events_Shortcut_NewDevices": "Neue Geräte",
|
||||
"Events_Shortcut_Sessions": "Sitzungen",
|
||||
"Events_Shortcut_VoidSessions": "beendete Sitzungen",
|
||||
"Events_TableHead_AdditionalInfo": "Zus\u00e4tzliche Info",
|
||||
"Events_TableHead_AdditionalInfo": "Zusätzliche Info",
|
||||
"Events_TableHead_Connection": "Verbindung",
|
||||
"Events_TableHead_Date": "Datum",
|
||||
"Events_TableHead_Device": "Ger\u00e4t",
|
||||
"Events_TableHead_Device": "Gerät",
|
||||
"Events_TableHead_Disconnection": "Trennung",
|
||||
"Events_TableHead_Duration": "Dauer",
|
||||
"Events_TableHead_DurationOrder": "Duration Order",
|
||||
@@ -265,77 +271,90 @@
|
||||
"Events_TableHead_IP": "IP",
|
||||
"Events_TableHead_IPOrder": "IP Order",
|
||||
"Events_TableHead_Order": "Order",
|
||||
"Events_TableHead_Owner": "Eigent\u00fcmer",
|
||||
"Events_Table_info": "Zeige _START_ bis _END_ von _TOTAL_ Eintr\u00e4gen",
|
||||
"Events_Table_nav_next": "N\u00e4chste",
|
||||
"Events_Table_nav_prev": "Zur\u00fcck",
|
||||
"Events_Tablelenght": "Zeige _MENU_ Eintr\u00e4ge",
|
||||
"Events_TableHead_Owner": "Eigentümer",
|
||||
"Events_TableHead_PendingAlert": "",
|
||||
"Events_Table_info": "Zeige _START_ bis _END_ von _TOTAL_ Einträgen",
|
||||
"Events_Table_nav_next": "Nächste",
|
||||
"Events_Table_nav_prev": "Zurück",
|
||||
"Events_Tablelenght": "Zeige _MENU_ Einträge",
|
||||
"Events_Tablelenght_all": "Alle",
|
||||
"Events_Title": "Ereignisse",
|
||||
"Gen_Action": "Action",
|
||||
"Gen_Add": "",
|
||||
"Gen_Add_All": "",
|
||||
"Gen_All_Devices": "",
|
||||
"Gen_AreYouSure": "Sind Sie sich sicher?",
|
||||
"Gen_Backup": "Sichern",
|
||||
"Gen_Cancel": "Abbrechen",
|
||||
"Gen_Copy": "Run",
|
||||
"Gen_DataUpdatedUITakesTime": "OK - It may take a while for the UI to update if a scan is runnig",
|
||||
"Gen_Delete": "L\u00f6schen",
|
||||
"Gen_Delete": "Löschen",
|
||||
"Gen_DeleteAll": "Delete all",
|
||||
"Gen_Error": "",
|
||||
"Gen_LockedDB": "ERROR - DB eventuell gesperrt - Nutze die Konsole in den Entwickler Werkzeugen (F12) zur \u00dcberpr\u00fcfung oder probiere es sp\u00e4ter erneut.",
|
||||
"Gen_Error": "Fehler",
|
||||
"Gen_Filter": "",
|
||||
"Gen_LockedDB": "ERROR - DB eventuell gesperrt - Nutze die Konsole in den Entwickler Werkzeugen (F12) zur Überprüfung oder probiere es später erneut.",
|
||||
"Gen_Offline": "",
|
||||
"Gen_Okay": "Ok",
|
||||
"Gen_Purge": "Aufr\u00e4umen",
|
||||
"Gen_Purge": "Aufräumen",
|
||||
"Gen_ReadDocs": "Mehr in der Dokumentation",
|
||||
"Gen_Remove_All": "",
|
||||
"Gen_Remove_Last": "",
|
||||
"Gen_Restore": "Wiederherstellen",
|
||||
"Gen_Run": "Run",
|
||||
"Gen_Save": "Speichern",
|
||||
"Gen_Saved": "Gespeichert",
|
||||
"Gen_Selected_Devices": "",
|
||||
"Gen_Search": "",
|
||||
"Gen_Selected_Devices": "Ausgewählte Geräte:",
|
||||
"Gen_Switch": "Umschalten",
|
||||
"Gen_Upd": "Aktualisierung erfolgreich",
|
||||
"Gen_Upd_Fail": "Aktualisierung fehlgeschlagen",
|
||||
"Gen_Warning": "",
|
||||
"Gen_Work_In_Progress": "",
|
||||
"Gen_Update": "",
|
||||
"Gen_Update_Value": "",
|
||||
"Gen_Warning": "Warnung",
|
||||
"Gen_Work_In_Progress": "Keine Finalversion, feedback bitte unter: https://github.com/jokob-sk/NetAlertX/issues",
|
||||
"General_display_name": "Allgemein",
|
||||
"General_icon": "<i class=\"fa fa-gears\"></i>",
|
||||
"HRS_TO_KEEP_NEWDEV_description": "Dies ist eine Wartungseinstellung. Ger\u00e4te markiert als <b>Neues Ger\u00e4t</b> werden gel\u00f6scht, wenn ihre <b>Erste Sitzung</b> l\u00e4nger her ist als die angegebenen Stunden in dieser Einstellung. <code>0</code> deaktiviert diese Funktion. Nutzen Sie diese Einstellung, um <b>Neue Ger\u00e4te</b> automatisch nach <code>X</code> Stunden zu l\u00f6schen.",
|
||||
"HRS_TO_KEEP_NEWDEV_name": "Neue Ger\u00e4te speichern f\u00fcr",
|
||||
"HRS_TO_KEEP_NEWDEV_description": "Dies ist eine Wartungseinstellung. Wenn aktiviert (<code>0</code> bedeutet deaktiviert), werden als <b>\"Neues Gerät\"</b> markierte Geräte gelöscht, wenn ihre <b>erste Sitzung</b> länger her ist als in dieser Einstellung angegeben. Verwenden Sie diese Einstellung, wenn Sie <b>Neue Geräte</b> nach <code>X</code> Stunden automatisch löschen wollen.",
|
||||
"HRS_TO_KEEP_NEWDEV_name": "Neue Geräte speichern für",
|
||||
"HelpFAQ_Cat_Detail": "Detailansicht",
|
||||
"HelpFAQ_Cat_Detail_300_head": "Was bedeutet ",
|
||||
"HelpFAQ_Cat_Detail_300_text_a": "meint ein Netzwerkger\u00e4t (welches den typ AP, Gateway, Firewall, Hypervisor, Powerline, Switch, WLAN, PLC, Router,USB LAN Adapter, USB WIFI Adapter, or Internet eingestellt hat)",
|
||||
"HelpFAQ_Cat_Detail_300_text_b": "bezeichnet die Anschlussnummer/Portnummer, an der das gerade bearbeitete Ger\u00e4t mit diesem Netzwerkger\u00e4t verbunden ist.",
|
||||
"HelpFAQ_Cat_Detail_300_text_a": "meint ein Netzwerkgerät (ein Gerät vom Typ AP, Gateway, Firewall, Hypervisor, Powerline, Switch, WLAN, PLC, Router, USB-LAN-Adapter oder Internet). Benutzerdefinierte Typen können über die <code>NETWORK_DEVICE_TYPES</code> Einstellung hinzugefügt werden.",
|
||||
"HelpFAQ_Cat_Detail_300_text_b": "bezeichnet die Anschlussnummer/Portnummer, an der das gerade bearbeitete Gerät mit diesem Netzwerkgerät verbunden ist. Siehe <a target=\"_blank\" href=\"https://github.com/jokob-sk/NetAlertX/blob/main/docs/NETWORK_TREE.md\">diese Dokumentation</a> für mehr informationen.",
|
||||
"HelpFAQ_Cat_Detail_301_head_a": "Wann wird nun gescannt? Bei ",
|
||||
"HelpFAQ_Cat_Detail_301_head_b": " steht 1min aber der Graph zeigt 5min - Abst\u00e4nde an.",
|
||||
"HelpFAQ_Cat_Detail_301_text": "Den zeitlichen Abstand zwischen den Scans legt der \"Cronjob\" fest, welcher standardm\u00e4\u00dfig auf 5min eingestellt ist. Die Benennung \"1min\" bezieht sich auf die zu erwartende Dauer des Scans. Abh\u00e4ngig vor der Netzwerkkonfiguration kann diese Zeitangabe variieren. Um den Cronjob zu bearbeiten, kannst du im Terminal/der Konsole <span class=\"text-danger help_faq_code\">crontab -e</span> eingeben und den Intervall \u00e4ndern.",
|
||||
"HelpFAQ_Cat_Detail_301_head_b": " steht 1min aber der Graph zeigt 5min - Abstände an.",
|
||||
"HelpFAQ_Cat_Detail_301_text": "Den zeitlichen Abstand zwischen den Scans legt der \"Cronjob\" fest, welcher standardmäßig auf 5min eingestellt ist. Die Benennung \"1min\" bezieht sich auf die zu erwartende Dauer des Scans. Abhängig vor der Netzwerkkonfiguration kann diese Zeitangabe variieren. Um den Cronjob zu bearbeiten, kannst du im Terminal/der Konsole <span class=\"text-danger help_faq_code\">crontab -e</span> eingeben und den Intervall ändern.",
|
||||
"HelpFAQ_Cat_Detail_302_head_a": "Was bedeutet ",
|
||||
"HelpFAQ_Cat_Detail_302_head_b": " und warum kann ich das nicht ausw\u00e4hlen?",
|
||||
"HelpFAQ_Cat_Detail_302_text": "Einige moderne Ger\u00e4te generieren aus Datenschutzgr\u00fcnden zuf\u00e4llige MAC-Adressen, die keinem Hersteller mehr zugeordnet werden k\u00f6nnen und welche sich mit jeder neuen Verbindung \u00e4ndern. Pi.Alert erkennt, ob es sich um eine solche zuf\u00e4llige MAC-Adresse handelt und aktiviert dieses \"Feld\" automatisch. Um das Verhalten abzustellen, musst du in deinem Endger\u00e4t schauen, wie du die MAC-Adressen-Generierung deaktivierst.",
|
||||
"HelpFAQ_Cat_Detail_302_head_b": "und warum kann ich das nicht auswählen?",
|
||||
"HelpFAQ_Cat_Detail_302_text": "Einige moderne Geräte generieren aus Datenschutzgründen zufällige MAC-Adressen, die keinem Hersteller mehr zugeordnet werden können und welche sich mit jeder neuen Verbindung ändern. NetAlertX erkennt, ob es sich um eine solche zufällige MAC-Adresse handelt und aktiviert dieses \"Feld\" automatisch. Um das Verhalten abzustellen, musst du in deinem Endgerät schauen, wie du die MAC-Adressen-Generierung deaktivierst.",
|
||||
"HelpFAQ_Cat_Detail_303_head": "Was ist Nmap und wozu dient es?",
|
||||
"HelpFAQ_Cat_Detail_303_text": "Nmap ist ein Netzwerkscanner mit vielf\u00e4ltigen M\u00f6glichkeiten.<br> Wenn ein neues Ger\u00e4t in deiner Liste auftaucht, hast du die M\u00f6glichkeit \u00fcber den Nmap-Scan genauere Informationen \u00fcber das Ger\u00e4t zu erhalten.",
|
||||
"HelpFAQ_Cat_Device_200_head": "Ich habe, mir nicht bekannte, Ger\u00e4te in meiner Liste. Nach dem L\u00f6schen tauchen diese immer wieder auf.",
|
||||
"HelpFAQ_Cat_Device_200_text": "Wenn du Pi-hole verwendest, beachte bitte, dass Pi.Alert Informationen von Pi-hole abruft. Pausiere Pi.Alert, gehe in Pi-hole auf die Settings-Seite und l\u00f6sche ggf. die betreffende DHCP-Lease. Anschlie\u00dfend schaue, ebenfalls in Pi-hole, unter Tools -> Network, ob sich dort die immer wiederkehrenden Hosts finden lassen. Wenn ja, l\u00f6sche diese dort ebenfalls. Nun kannst du Pi.Alert wieder starten. Jetzt sollte das Ger\u00e4t/die Ger\u00e4te nicht mehr auftauchen.",
|
||||
"HelpFAQ_Cat_Detail_303_text": "Nmap ist ein Netzwerkscanner mit vielfältigen Möglichkeiten.<br> Wenn ein neues Gerät in deiner Liste auftaucht, hast du die Möglichkeit über den Nmap-Scan genauere Informationen über das Gerät zu erhalten.",
|
||||
"HelpFAQ_Cat_Device_200_head": "Ich habe, mir nicht bekannte, Geräte in meiner Liste. Nach dem Löschen tauchen diese immer wieder auf.",
|
||||
"HelpFAQ_Cat_Device_200_text": "Wenn du Pi-hole verwendest, beachte bitte, dass NetAlertX Informationen von Pi-hole abruft. Pausiere NetAlertX, gehe in Pi-hole auf die Settings-Seite und lösche ggf. die betreffende DHCP-Lease. Anschließend schaue, ebenfalls in Pi-hole, unter Tools -> Network, ob sich dort die immer wiederkehrenden Hosts finden lassen. Wenn ja, lösche diese dort ebenfalls. Nun kannst du NetAlertX wieder starten. Jetzt sollte das Gerät/die Geräte nicht mehr auftauchen.",
|
||||
"HelpFAQ_Cat_General": "Allgemein",
|
||||
"HelpFAQ_Cat_General_100_head": "Die Uhr oben rechts und die Zeiten der Events/Anwesenheit stimmen nicht \u00fcberein (Zeitverschiebung).",
|
||||
"HelpFAQ_Cat_General_100_text_a": "Auf deinem PC ist f\u00fcr die PHP Umgebung folgende Zeitzone voreingestellt:",
|
||||
"HelpFAQ_Cat_General_100_text_b": "Sollte dies nicht die Zeitzone sein, in der du dich aufh\u00e4ltst, solltest du die Zeitzone in der PHP Konfigurationsdatei anpassen. Diese findest du in diesem Verzeichnis:",
|
||||
"HelpFAQ_Cat_General_100_text_c": "Suche in dieser Datei nach dem Eintrag 'date.timezone', entferne ggf. das f\u00fchrende ';' und trage die gew\u00fcnschte Zeitzone ein. Eine Liste mit den unterst\u00fctzten Zeitzonen findest du hier (<a href=\"https://www.php.net/manual/de/timezones.php\" target=\"blank\">Link</a>).",
|
||||
"HelpFAQ_Cat_General_100_head": "Die Uhr oben rechts und die Zeiten der Events/Anwesenheit stimmen nicht überein (Zeitverschiebung).",
|
||||
"HelpFAQ_Cat_General_100_text_a": "Auf deinem PC ist für die PHP Umgebung folgende Zeitzone voreingestellt:",
|
||||
"HelpFAQ_Cat_General_100_text_b": "Sollte dies nicht die Zeitzone sein, in der du dich aufhältst, solltest du die Zeitzone in der PHP Konfigurationsdatei anpassen. Diese findest du in diesem Verzeichnis:",
|
||||
"HelpFAQ_Cat_General_100_text_c": "Suche in dieser Datei nach dem Eintrag 'date.timezone', entferne ggf. das führende ';' und trage die gewünschte Zeitzone ein. Eine Liste mit den unterstützten Zeitzonen findest du hier (<a href=\"https://www.php.net/manual/de/timezones.php\" target=\"blank\">Link</a>).",
|
||||
"HelpFAQ_Cat_General_101_head": "Mein Netzwerk scheint langsamer zu werden, Streaming ruckelt.",
|
||||
"HelpFAQ_Cat_General_101_text": "Es kann durchaus sein, das leistungsschwache Ger\u00e4te mit der Art und Weise, wie NetAlertX neue Ger\u00e4te im Netzwerk erkennt, an ihre Leistungsgrenzen kommen. Dies verst\u00e4rkt sich noch einmal, <br/> wenn diese Ger\u00e4te per WLAN mit dem Netzwerk kommunizieren. L\u00f6sungen w\u00e4ren hier, wenn m\u00f6glich ein Wechsel auf eine Kabelverbindung oder, falls das Ger\u00e4te nur einen begrenzten Zeitraum genutzt <br/> werden soll, den arp-Scan auf der Wartungsseite zu pausieren.",
|
||||
"HelpFAQ_Cat_General_102_head": "Ich bekomme die Meldung, dass die Datenbank schreibgesch\u00fctzt (read only) ist.",
|
||||
"HelpFAQ_Cat_General_102_text": "Pr\u00fcfe im NetAlertX verzeichnis ob der Ordner der Datenbank (db) die richtigen Rechte zugewiesen bekommen hat:<br> <span class=\"text-danger help_faq_code\">drwxrwx--- 2 (dein Username) www-data</span><br> Sollte die Berechtigung nicht stimmen, kannst du sie mit folgenden Befehlen im Terminal oder der Konsole wieder setzen:<br> <span class=\"text-danger help_faq_code\"> sudo chgrp -R www-data /app/db<br> chmod -R 770 /app/db </span><br> Wenn die Datenbank danach noch immer schreibgesch\u00fctzt ist, versuche eine erneute Installation, oder das Zu\u00fcckspielen eines Datenbank-Backups \u00fcber die Wartungsseite.",
|
||||
"HelpFAQ_Cat_General_102docker_head": "(\ud83d\udc33 Docker only) Database issues (AJAX errors, read-only, not found)",
|
||||
"HelpFAQ_Cat_General_102docker_text": "Double-check you have followed the <a href=\"https://github.com/jokob-sk/NetAlertX/tree/main/dockerfiles\">dockerfile readme (most up-to-date info)</a>. <br/> <br/> <ul data-sourcepos=\"49:4-52:146\" dir=\"auto\"> <li data-sourcepos=\"49:4-49:106\">Download the <a href=\"https://github.com/jokob-sk/NetAlertX/blob/main/db/app.db\">original DB from GitHub</a>.</li> <li data-sourcepos=\"50:4-50:195\">Map the <code>app.db</code> file (<g-emoji class=\"g-emoji\" alias=\"warning\" fallback-src=\"https://github.githubassets.com/images/icons/emoji/unicode/26a0.png\">\u26a0</g-emoji> not folder) from above to <code>/app/db/app.db</code> (see <a href=\"https://github.com/jokob-sk/NetAlertX/tree/main/dockerfiles#-examples\">Examples</a> for details).</li><li data-sourcepos=\"51:4-51:161\">If facing issues (AJAX errors, can not write to DB, etc,) make sure permissions are set correctly, alternatively check the logs under <code>/app/front/log</code>.</li> <li data-sourcepos=\"52:4-52:146\">To solve permission issues you can also try to create a DB backup and then run a DB Restore via the <strong>Maintenance > Backup/Restore</strong> section.</li> <li data-sourcepos=\"53:4-53:228\">If the database is in read-only mode you can solve this by setting the owner and group by executing the following command on the host system: <code>docker exec netalertx chown -R www-data:www-data /app/db/app.db</code>.</li></ul>",
|
||||
"HelpFAQ_Cat_General_103_head": "Die Login-Seite erscheint nicht, auch nicht nach der Passwort\u00e4nderung.",
|
||||
"HelpFAQ_Cat_General_101_text": "Es kann durchaus sein, das leistungsschwache Geräte mit der Art und Weise, wie NetAlertX neue Geräte im Netzwerk erkennt, an ihre Leistungsgrenzen kommen. Dies verstärkt sich noch einmal, <br/> wenn diese Geräte per WLAN mit dem Netzwerk kommunizieren. Lösungen wären hier, wenn möglich ein Wechsel auf eine Kabelverbindung oder, falls das Geräte nur einen begrenzten Zeitraum genutzt <br/> werden soll, den arp-Scan auf der Wartungsseite zu pausieren.",
|
||||
"HelpFAQ_Cat_General_102_head": "Ich bekomme die Meldung, dass die Datenbank schreibgeschützt (read only) ist.",
|
||||
"HelpFAQ_Cat_General_102_text": "Prüfe im NetAlertX verzeichnis ob der Ordner der Datenbank (db) die richtigen Rechte zugewiesen bekommen hat:<br> <span class=\"text-danger help_faq_code\">drwxrwx--- 2 (dein Username) www-data</span><br> Sollte die Berechtigung nicht stimmen, kannst du sie mit folgenden Befehlen im Terminal oder der Konsole wieder setzen:<br> <span class=\"text-danger help_faq_code\"> sudo chgrp -R www-data /app/db<br> chmod -R 770 /app/db </span><br> Wenn die Datenbank danach noch immer schreibgeschützt ist, versuche eine erneute Installation, oder das Zuückspielen eines Datenbank-Backups über die Wartungsseite.",
|
||||
"HelpFAQ_Cat_General_102docker_head": "(🐳 Docker only) Database issues (AJAX errors, read-only, not found)",
|
||||
"HelpFAQ_Cat_General_102docker_text": "Double-check you have followed the <a href=\"https://github.com/jokob-sk/NetAlertX/tree/main/dockerfiles\">dockerfile readme (most up-to-date info)</a>. <br/> <br/> <ul data-sourcepos=\"49:4-52:146\" dir=\"auto\"> <li data-sourcepos=\"49:4-49:106\">Download the <a href=\"https://github.com/jokob-sk/NetAlertX/blob/main/db/app.db\">original DB from GitHub</a>.</li> <li data-sourcepos=\"50:4-50:195\">Map the <code>app.db</code> file (<g-emoji class=\"g-emoji\" alias=\"warning\" fallback-src=\"https://github.githubassets.com/images/icons/emoji/unicode/26a0.png\">⚠</g-emoji> not folder) from above to <code>/app/db/app.db</code> (see <a href=\"https://github.com/jokob-sk/NetAlertX/tree/main/dockerfiles#-examples\">Examples</a> for details).</li><li data-sourcepos=\"51:4-51:161\">If facing issues (AJAX errors, can not write to DB, etc,) make sure permissions are set correctly, alternatively check the logs under <code>/app/front/log</code>.</li> <li data-sourcepos=\"52:4-52:146\">To solve permission issues you can also try to create a DB backup and then run a DB Restore via the <strong>Maintenance > Backup/Restore</strong> section.</li> <li data-sourcepos=\"53:4-53:228\">If the database is in read-only mode you can solve this by setting the owner and group by executing the following command on the host system: <code>docker exec netalertx chown -R www-data:www-data /app/db/app.db</code>.</li></ul>",
|
||||
"HelpFAQ_Cat_General_103_head": "Die Login-Seite erscheint nicht, auch nicht nach der Passwortänderung.",
|
||||
"HelpFAQ_Cat_General_103_text": "Neben dem Passwort, muss in der Konfigurationsdatei <span class=\"text-danger help_faq_code\">/app/config/app.conf</span> auch der Parameter <span class=\"text-danger help_faq_code\">PIALERT_WEB_PROTECTION</span> auf <span class=\"text-danger help_faq_code\">True</span> gesetzt sein.",
|
||||
"HelpFAQ_Cat_Network_600_head": "Was bringt mir diese Seite?",
|
||||
"HelpFAQ_Cat_Network_600_text": "Diese Seite soll dir die M\u00f6glichkeit bieten, die Belegung deiner Netzwerkger\u00e4te abzubilden. Dazu kannst du einen oder mehrere Switches, WLANs, Router, etc. erstellen, sie ggf. mit einer Portanzahl versehen und bereits erkannte Ger\u00e4te diesen zuordnen. Diese Zuordnung erfolgt in der Detailansicht, des zuzuordnenden Ger\u00e4tes. So ist es dir m\u00f6glich, schnell festzustellen an welchem Port ein Host angeschlossen und ob er online ist.",
|
||||
"HelpFAQ_Cat_Network_600_text": "Diese Seite soll dir die Möglichkeit bieten, die Belegung deiner Netzwerkgeräte abzubilden. Dazu kannst du einen oder mehrere Switches, WLANs, Router, etc. erstellen, sie ggf. mit einer Portanzahl versehen und bereits erkannte Geräte diesen zuordnen. Diese Zuordnung erfolgt in der Detailansicht, des zuzuordnenden Gerätes. So ist es dir möglich, schnell festzustellen an welchem Port ein Host angeschlossen und ob er online ist. Siehe <a target=\"_blank\" href=\"https://github.com/jokob-sk/Pi.NetAlertX/blob/main/docs/NETWORK_TREE.md\">diese Dokumentation </a> für mehr Infos.",
|
||||
"HelpFAQ_Cat_Network_601_head": "Gibt es mehr Dokumentation?",
|
||||
"HelpFAQ_Cat_Network_601_text": "Ja, gibt es! Siehe <a target=\"_blank\" href=\"https://github.com/jokob-sk/NetAlertX/blob/main/docs/\">alle Dokumentationen</a> f\u00fcr mehr Infos.",
|
||||
"HelpFAQ_Cat_Presence_400_head": "Ger\u00e4te werden mit einer gelben Markierung und dem Hinweis \"missing Event\" angezeigt.",
|
||||
"HelpFAQ_Cat_Presence_400_text": "Wenn dies geschieht hast du die M\u00f6glickeit, bei dem betreffenden Ger\u00e4t (Detailsansicht) die Events zu l\u00f6schen. Eine andere M\u00f6glichkeit w\u00e4re, das Ger\u00e4t einzuschalten und zu warten, bis NetAlertX mit dem n\u00e4chsten Scan das Ger\u00e4t als \"Online\" erkennt und anschlie\u00dfend das Ger\u00e4t einfach wieder ausschalten. Nun sollte NetAlertX mit dem n\u00e4chsten Scan den Zustand des Ger\u00e4tes ordentlich in der Datenbank vermerken.",
|
||||
"HelpFAQ_Cat_Presence_401_head": "Ein Ger\u00e4t wird als Anwesend angezeigt, obwohl es \"Offline\" ist.",
|
||||
"HelpFAQ_Cat_Presence_401_text": "Wenn dies geschieht hast du die M\u00f6glickeit, bei dem betreffenden Ger\u00e4t (Detailsansicht) die Events zu l\u00f6schen. Eine andere M\u00f6glichkeit w\u00e4re, das Ger\u00e4t einzuschalten und zu warten, bis NetAlertX mit dem n\u00e4chsten Scan das Ger\u00e4t als \"Online\" erkennt und anschlie\u00dfend das Ger\u00e4t einfach wieder ausschalten. Nun sollte NetAlertX mit dem n\u00e4chsten Scan den Zustand des Ger\u00e4tes ordentlich in der Datenbank vermerken.",
|
||||
"HelpFAQ_Cat_Network_601_text": "Ja, gibt es! Siehe <a target=\"_blank\" href=\"https://github.com/jokob-sk/NetAlertX/blob/main/docs/\">alle Dokumentationen</a> für mehr Infos.",
|
||||
"HelpFAQ_Cat_Presence_400_head": "Geräte werden mit einer gelben Markierung und dem Hinweis \"missing Event\" angezeigt.",
|
||||
"HelpFAQ_Cat_Presence_400_text": "Wenn dies geschieht hast du die Möglickeit, bei dem betreffenden Gerät (Detailsansicht) die Events zu löschen. Eine andere Möglichkeit wäre, das Gerät einzuschalten und zu warten, bis NetAlertX mit dem nächsten Scan das Gerät als \"Online\" erkennt und anschließend das Gerät einfach wieder ausschalten. Nun sollte NetAlertX mit dem nächsten Scan den Zustand des Gerätes ordentlich in der Datenbank vermerken.",
|
||||
"HelpFAQ_Cat_Presence_401_head": "Ein Gerät wird als Anwesend angezeigt, obwohl es \"Offline\" ist.",
|
||||
"HelpFAQ_Cat_Presence_401_text": "Wenn dies geschieht hast du die Möglickeit, bei dem betreffenden Gerät (Detailsansicht) die Events zu löschen. Eine andere Möglichkeit wäre, das Gerät einzuschalten und zu warten, bis NetAlertX mit dem nächsten Scan das Gerät als \"Online\" erkennt und anschließend das Gerät einfach wieder ausschalten. Nun sollte NetAlertX mit dem nächsten Scan den Zustand des Gerätes ordentlich in der Datenbank vermerken.",
|
||||
"HelpFAQ_Title": "Hilfe / FAQ",
|
||||
"LOG_LEVEL_description": "Diese Einstellung aktiviert die erweiterte Protokollierung. N\u00fctzlich f\u00fcrs Debuggen von in die Datenbank geschriebenen Events.",
|
||||
"LOADED_PLUGINS_description": "",
|
||||
"LOADED_PLUGINS_name": "",
|
||||
"LOG_LEVEL_description": "Diese Einstellung aktiviert die erweiterte Protokollierung. Nützlich fürs Debuggen von in die Datenbank geschriebenen Events.",
|
||||
"LOG_LEVEL_name": "Erweiterte Protokollierung",
|
||||
"Loading": "Laden...",
|
||||
"Login_Box": "Passwort eingeben",
|
||||
@@ -344,9 +363,9 @@
|
||||
"Login_Psw_alert": "Sicherheitshinweis!",
|
||||
"Login_Psw_folder": "im Ordner /app/config",
|
||||
"Login_Psw_new": "neues_passwort",
|
||||
"Login_Psw_run": "Um das Passwort zu \u00e4ndern nutze:",
|
||||
"Login_Psw_run": "Um das Passwort zu ändern nutze:",
|
||||
"Login_Remember": "Passwort speichern",
|
||||
"Login_Remember_small": "(f\u00fcr 7 Tage g\u00fcltig)",
|
||||
"Login_Remember_small": "(für 7 Tage gültig)",
|
||||
"Login_Submit": "Anmelden",
|
||||
"Login_Toggle_Alert_headline": "Passwort Warnung!",
|
||||
"Login_Toggle_Info": "Passwort Informationen",
|
||||
@@ -365,70 +384,76 @@
|
||||
"MQTT_USER_name": "MQTT user",
|
||||
"MQTT_display_name": "MQTT",
|
||||
"MQTT_icon": "<i class=\"fa fa-square-rss\"></i>",
|
||||
"Maint_PurgeLog": "",
|
||||
"Maint_RestartServer": "",
|
||||
"Maint_Restart_Server_noti_text": "",
|
||||
"Maintenance_Running_Version": "Installierte Version",
|
||||
"Maintenance_Status": "Status",
|
||||
"Maintenance_Title": "Wartungswerkzeuge",
|
||||
"Maintenance_Tool_ExportCSV": "CSV Export",
|
||||
"Maintenance_Tool_ExportCSV_noti": "CSV Export",
|
||||
"Maintenance_Tool_ExportCSV_noti_text": "Sind Sie sich sicher, dass Sie die CSV-Datei erstellen wollen?",
|
||||
"Maintenance_Tool_ExportCSV_text": "Generiere eine CSV-Datei (comma separated values) mit einer Liste aller Ger\u00e4te und deren Beziehungen zwischen Netzwerkknoten und verbundenen Ger\u00e4ten. Dies kann auch durch das Besuchen dieser URL <code>your NetAlertX url/php/server/devices.php?action=ExportCSV</code> ausgel\u00f6st werden.",
|
||||
"Maintenance_Tool_ExportCSV_text": "Generiere eine CSV-Datei (comma separated values) mit einer Liste aller Geräte und deren Beziehungen zwischen Netzwerkknoten und verbundenen Geräten. Dies kann auch durch das Besuchen der URL <code>your NetAlertX url/php/server/devices.php?action=ExportCSV</code> oder durch Aktivieren des <a href=\"settings.php#CSVBCKP_header\">CSV-Backups</a> ausgelöst werden.",
|
||||
"Maintenance_Tool_ImportCSV": "CSV Import",
|
||||
"Maintenance_Tool_ImportCSV_noti": "CSV Import",
|
||||
"Maintenance_Tool_ImportCSV_noti_text": "Sind Sie sich sicher, dass Sie die CSV-Datei importieren wollen? Dies wird alle Ger\u00e4te in der Datenbank \u00fcberschreiben.",
|
||||
"Maintenance_Tool_ImportCSV_text": "Machen Sie ein Backup, bevor Sie diese Funk\u00adtion nutzen. Importiere eine CSV-Datei (comma separated values) mit einer Liste aller Ger\u00e4te und deren Beziehungen zwischen Netzwerkknoten und verbundenen Ger\u00e4ten. Um dies zu tun platziere die <b>devices.csv</b> benannte CSV-Datei in deinen <b>/config</b> Ordner.",
|
||||
"Maintenance_Tool_ImportCSV_noti_text": "Sind Sie sich sicher, dass Sie die CSV-Datei importieren wollen? Dies wird <b>alle Geräte in der Datenbank überschreiben</b>.",
|
||||
"Maintenance_Tool_ImportCSV_text": "Machen Sie ein Backup, bevor Sie diese Funktion nutzen. Importiere eine CSV-Datei (comma separated values) mit einer Liste aller Geräte und deren Beziehungen zwischen Netzwerkknoten und verbundenen Geräten. Um dies zu tun platziere die <b>devices.csv</b> benannte CSV-Datei in deinen <b>/config</b> Ordner.",
|
||||
"Maintenance_Tool_ImportPastedCSV": "",
|
||||
"Maintenance_Tool_ImportPastedCSV_noti_text": "",
|
||||
"Maintenance_Tool_ImportPastedCSV_text": "",
|
||||
"Maintenance_Tool_arpscansw": "arp-Scan stoppen/starten",
|
||||
"Maintenance_Tool_arpscansw_noti": "arp-Scan stoppen/starten",
|
||||
"Maintenance_Tool_arpscansw_noti_text": "Wenn der Scan aus ist, bleibt er so lange aus bis er wieder aktiviert wird.",
|
||||
"Maintenance_Tool_arpscansw_text": "Schaltet den arp-Scan an oder aus. Wenn der Scan aus ist, bleibt er so lange aus bis er wieder aktiviert wird. Bereits laufende Scans werden dabei nicht beendet.",
|
||||
"Maintenance_Tool_backup": "DB Sicherung",
|
||||
"Maintenance_Tool_backup_noti": "DB Sicherung",
|
||||
"Maintenance_Tool_backup_noti_text": "Sind Sie sicher, dass Sie die Datenbank jetzt sichern m\u00f6chten. Pr\u00fcfen Sie, dass gerade keine Scans stattfinden.",
|
||||
"Maintenance_Tool_backup_noti_text": "Sind Sie sicher, dass Sie die Datenbank jetzt sichern möchten. Prüfen Sie, dass gerade keine Scans stattfinden.",
|
||||
"Maintenance_Tool_backup_text": "Die Datenbank-Sicher­ungen befinden sich im Datenbank-Ver­zeich­nis, gepackt als zip-Archive, benannt mit dem Erstellungs­datum. Es gibt keine maximale Anzahl von Backups.",
|
||||
"Maintenance_Tool_check_visible": "Abw\u00e4hlen um die Spalte auszublenden.",
|
||||
"Maintenance_Tool_check_visible": "Abwählen um die Spalte auszublenden.",
|
||||
"Maintenance_Tool_darkmode": "Darstellungswechsel (Dunkel/Hell)",
|
||||
"Maintenance_Tool_darkmode_noti": "Darstellungswechsel",
|
||||
"Maintenance_Tool_darkmode_noti_text": "Wechselt zwischen der hellen und der dunklen Darstellung. Wenn die Umschaltung nicht ordentlich funktionieren sollte, versuchen Sie den Browsercache zu l\u00f6schen.",
|
||||
"Maintenance_Tool_darkmode_text": "Wechselt zwischen der hellen und der dunklen Darstellung. Wenn der Wechsel nicht richtig funktionieren sollte, versuchen Sie den Browsercache zu l\u00f6schen. Die \u00c4nderung findet serverseitig statt, betrifft also alle verwendeten Ger\u00e4te.",
|
||||
"Maintenance_Tool_del_ActHistory": "L\u00f6schen der Netzwerkaktivit\u00e4t",
|
||||
"Maintenance_Tool_del_ActHistory_noti": "Netzwerkaktivit\u00e4t l\u00f6schen",
|
||||
"Maintenance_Tool_del_ActHistory_noti_text": "Sind Sie sicher, dass Sie die Netzwerkaktivit\u00e4t zur\u00fccksetzen m\u00f6chten?",
|
||||
"Maintenance_Tool_del_ActHistory_text": "Der Graph f\u00fcr die Netzwerkaktivit\u00e4t wird zur\u00fcckgesetzt. Hierbei werden die Events nicht beeinflusst.",
|
||||
"Maintenance_Tool_del_alldev": "Alle Ger\u00e4te l\u00f6schen",
|
||||
"Maintenance_Tool_del_alldev_noti": "Ger\u00e4te l\u00f6schen",
|
||||
"Maintenance_Tool_del_alldev_noti_text": "Sind Sie sich sicher, dass Sie alle Ger\u00e4te l\u00f6schen wollen?",
|
||||
"Maintenance_Tool_del_alldev_text": "Machen Sie ein Backup, bevor Sie diese Funk­tion nutzen. Der Vor­gang kann ohne Back­up nicht r\u00fcck­g\u00e4ngig gemacht werden. Alle Ger\u00e4te werden in der Datenbank ge­l\u00f6scht.",
|
||||
"Maintenance_Tool_del_allevents": "Alle Ereignisse l\u00f6schen",
|
||||
"Maintenance_Tool_del_allevents30": "Alle Ereignisse \u00e4lter als 30 Tage l\u00f6schen",
|
||||
"Maintenance_Tool_del_allevents30_noti": "Ereignisse l\u00f6schen",
|
||||
"Maintenance_Tool_del_allevents30_noti_text": "Sind Sie sich sicher, dass Sie alle Ereignisse \u00e4lter als 30 Tage l\u00f6schen wollen? Dies setzt die Pr\u00e4senz aller Ger\u00e4te zur\u00fcck.",
|
||||
"Maintenance_Tool_del_allevents30_text": "Machen Sie ein Backup, bevor Sie diese Funk\u00adtion nutzen. Der Vor\u00adgang kann ohne Back\u00adup nicht r\u00fcck\u00adg\u00e4ngig gemacht werden. Alle Ereignisse \u00e4lter als 30 Tage werden aus der Datenbank ge\u00adl\u00f6scht. Dies setzt auch die Anwesenheit zu\u00adr\u00fcck. Es kann ab dem Moment zu ung\u00fcltigen Sitzungen kommen. Ein Scan, w\u00e4hrend das betreffende Ger\u00e4t online ist, sollte das Problem l\u00f6sen.",
|
||||
"Maintenance_Tool_del_allevents_noti": "Alle Ereignisse l\u00f6schen",
|
||||
"Maintenance_Tool_del_allevents_noti_text": "Sind Sie sicher, dass Sie alle Ereignisse aus der Datenbank l\u00f6schen wollen. Dies setzt die Anwesenheit aller Ger\u00e4te zur\u00fcck.",
|
||||
"Maintenance_Tool_del_allevents_text": "Machen Sie ein Backup, bevor Sie diese Funk­tion nutzen. Der Vor­gang kann ohne Back­up nicht r\u00fcck­g\u00e4ngig gemacht werden. Alle Ereignisse werden aus der Datenbank ge­l\u00f6scht. Dies setzt auch die Anwesenheit zu­r\u00fcck. Es kann ab dem Moment zu ung\u00fcltigen Sitzungen kommen. Ein Scan, w\u00e4hrend das betreffende Ger\u00e4t online ist, sollte das Problem l\u00f6sen.",
|
||||
"Maintenance_Tool_del_empty_macs": "Alle Ger\u00e4te ohne MAC l\u00f6schen",
|
||||
"Maintenance_Tool_del_empty_macs_noti": "Ger\u00e4te l\u00f6schen",
|
||||
"Maintenance_Tool_del_empty_macs_noti_text": "Sind Sie sicher, dass Sie alle Ger\u00e4te ohne MAC-Adresse l\u00f6schen wollen?<br>(Vielleicht bevorzugen Sie eine Archivierung)",
|
||||
"Maintenance_Tool_del_empty_macs_text": "Machen Sie ein Backup, bevor Sie diese Funk­tion nutzen. Der Vor­gang kann ohne Back­up nicht r\u00fcck­g\u00e4ngig gemacht werden. Alle Ge\u00e4te ohne MAC-Adresse werden aus der Datenbank ge­l\u00f6scht.",
|
||||
"Maintenance_Tool_del_selecteddev": "",
|
||||
"Maintenance_Tool_darkmode_noti_text": "Wechselt zwischen der hellen und der dunklen Darstellung. Wenn die Umschaltung nicht ordentlich funktionieren sollte, versuchen Sie den Browsercache zu löschen.",
|
||||
"Maintenance_Tool_darkmode_text": "Wechselt zwischen der hellen und der dunklen Darstellung. Wenn der Wechsel nicht richtig funktionieren sollte, versuchen Sie den Browsercache zu löschen. Die Änderung findet serverseitig statt, betrifft also alle verwendeten Geräte.",
|
||||
"Maintenance_Tool_del_ActHistory": "Löschen der Netzwerkaktivität",
|
||||
"Maintenance_Tool_del_ActHistory_noti": "Netzwerkaktivität löschen",
|
||||
"Maintenance_Tool_del_ActHistory_noti_text": "Sind Sie sicher, dass Sie die Netzwerkaktivität zurücksetzen möchten?",
|
||||
"Maintenance_Tool_del_ActHistory_text": "Der Graph für die Netzwerkaktivität wird zurückgesetzt. Hierbei werden die Events nicht beeinflusst.",
|
||||
"Maintenance_Tool_del_alldev": "Alle Geräte löschen",
|
||||
"Maintenance_Tool_del_alldev_noti": "Geräte löschen",
|
||||
"Maintenance_Tool_del_alldev_noti_text": "Sind Sie sich sicher, dass Sie alle Geräte löschen wollen?",
|
||||
"Maintenance_Tool_del_alldev_text": "Machen Sie ein Backup, bevor Sie diese Funk­tion nutzen. Der Vor­gang kann ohne Back­up nicht rück­gängig gemacht werden. Alle Geräte werden in der Datenbank ge­löscht.",
|
||||
"Maintenance_Tool_del_allevents": "Alle Ereignisse löschen",
|
||||
"Maintenance_Tool_del_allevents30": "Alle Ereignisse älter als 30 Tage löschen",
|
||||
"Maintenance_Tool_del_allevents30_noti": "Ereignisse löschen",
|
||||
"Maintenance_Tool_del_allevents30_noti_text": "Sind Sie sich sicher, dass Sie alle Ereignisse älter als 30 Tage löschen wollen? Dies setzt die Präsenz aller Geräte zurück.",
|
||||
"Maintenance_Tool_del_allevents30_text": "Machen Sie ein Backup, bevor Sie diese Funktion nutzen. Der Vorgang kann ohne Backup nicht rückgängig gemacht werden. Alle Ereignisse älter als 30 Tage werden aus der Datenbank gelöscht. Dies setzt auch die Anwesenheit zurück. Es kann ab dem Moment zu ungültigen Sitzungen kommen. Ein Scan, während das betreffende Gerät online ist, sollte das Problem lösen.",
|
||||
"Maintenance_Tool_del_allevents_noti": "Alle Ereignisse löschen",
|
||||
"Maintenance_Tool_del_allevents_noti_text": "Sind Sie sicher, dass Sie alle Ereignisse aus der Datenbank löschen wollen. Dies setzt die Anwesenheit aller Geräte zurück.",
|
||||
"Maintenance_Tool_del_allevents_text": "Machen Sie ein Backup, bevor Sie diese Funk­tion nutzen. Der Vor­gang kann ohne Back­up nicht rück­gängig gemacht werden. Alle Ereignisse werden aus der Datenbank ge­löscht. Dies setzt auch die Anwesenheit zu­rück. Es kann ab dem Moment zu ungültigen Sitzungen kommen. Ein Scan, während das betreffende Gerät online ist, sollte das Problem lösen.",
|
||||
"Maintenance_Tool_del_empty_macs": "Alle Geräte ohne MAC löschen",
|
||||
"Maintenance_Tool_del_empty_macs_noti": "Geräte löschen",
|
||||
"Maintenance_Tool_del_empty_macs_noti_text": "Sind Sie sicher, dass Sie alle Geräte ohne MAC-Adresse löschen wollen?<br>(Vielleicht bevorzugen Sie eine Archivierung)",
|
||||
"Maintenance_Tool_del_empty_macs_text": "Machen Sie ein Backup, bevor Sie diese Funk­tion nutzen. Der Vor­gang kann ohne Back­up nicht rück­gängig gemacht werden. Alle Geäte ohne MAC-Adresse werden aus der Datenbank ge­löscht.",
|
||||
"Maintenance_Tool_del_selecteddev": "Ausgewählte Geräte löschen",
|
||||
"Maintenance_Tool_del_selecteddev_text": "",
|
||||
"Maintenance_Tool_del_unknowndev": "L\u00f6schen der (unknown) Ger\u00e4te",
|
||||
"Maintenance_Tool_del_unknowndev_noti": "L\u00f6sche (unknown) Ger\u00e4te",
|
||||
"Maintenance_Tool_del_unknowndev_noti_text": "Sind Sie sicher, dass Sie alle (unknown) Ger\u00e4te aus der Datenbank l\u00f6schen wollen?",
|
||||
"Maintenance_Tool_del_unknowndev_text": "Machen Sie ein Backup, bevor Sie diese Funk­tion nutzen. Der Vor­gang kann ohne Back­up nicht r\u00fcck­g\u00e4ngig gemacht werden. Alle Gr\u00e4te mit dem Namen (unknown) werden aus der Datenbank ge­l\u00f6scht.",
|
||||
"Maintenance_Tool_displayed_columns_text": "\u00c4ndere die Sichtbarkeit und Anordnung der Spalten in der <a href=\"devices.php\"><b> <i class=\"fa fa-laptop\"></i> Ger\u00e4te</b></a>-Seite. (Drag-and-Drop funktioniert nicht einwandfrei, ist aber verwendbar. Ich habe <a href=\"https://github.com/jokob-sk/NetAlertX/commit/94b32f0f7332879f5a7d2af05dafa2e5d5cfa5da\">3 Stunden</a> versucht das zu beheben, werde es aber nicht weiter verfolgen. \u00dcber einen PR mit einem Fix w\u00fcrde ich mich freuen :) ).",
|
||||
"Maintenance_Tool_drag_me": "Zieh mich um die Anordnung der Spalten zu \u00e4ndern.",
|
||||
"Maintenance_Tool_del_unknowndev": "Löschen der (unknown) Geräte",
|
||||
"Maintenance_Tool_del_unknowndev_noti": "Lösche (unknown) Geräte",
|
||||
"Maintenance_Tool_del_unknowndev_noti_text": "Sind Sie sicher, dass Sie alle (unknown) Geräte aus der Datenbank löschen wollen?",
|
||||
"Maintenance_Tool_del_unknowndev_text": "Machen Sie ein Backup, bevor Sie diese Funk­tion nutzen. Der Vor­gang kann ohne Back­up nicht rück­gängig gemacht werden. Alle Gräte mit dem Namen (unknown) werden aus der Datenbank ge­löscht.",
|
||||
"Maintenance_Tool_displayed_columns_text": "Ändere die Sichtbarkeit und Anordnung der Spalten in der <a href=\"devices.php\"><b> <i class=\"fa fa-laptop\"></i> Geräte</b></a>-Seite. (Drag-and-Drop funktioniert nicht einwandfrei, ist aber verwendbar. Ich habe <a href=\"https://github.com/jokob-sk/NetAlertX/commit/94b32f0f7332879f5a7d2af05dafa2e5d5cfa5da\">3 Stunden</a> versucht das zu beheben, werde es aber nicht weiter verfolgen. Über einen PR mit einem Fix würde ich mich freuen :) ).",
|
||||
"Maintenance_Tool_drag_me": "Zieh mich um die Anordnung der Spalten zu ändern.",
|
||||
"Maintenance_Tool_order_columns_text": "",
|
||||
"Maintenance_Tool_purgebackup": "Sicherungen aufr\u00e4umen",
|
||||
"Maintenance_Tool_purgebackup_noti": "Sicherungen aufr\u00e4umen",
|
||||
"Maintenance_Tool_purgebackup_noti_text": "Sind Sie sicher, alle Backups, bis auf die letzten 3 l\u00f6schen m\u00f6chten?",
|
||||
"Maintenance_Tool_purgebackup_text": "Es werden, bis auf die letzten 3 Backups, alle \u00fcbrigen Backups gel\u00f6scht.",
|
||||
"Maintenance_Tool_purgebackup": "Sicherungen aufräumen",
|
||||
"Maintenance_Tool_purgebackup_noti": "Sicherungen aufräumen",
|
||||
"Maintenance_Tool_purgebackup_noti_text": "Sind Sie sicher, alle Backups, bis auf die letzten 3 löschen möchten?",
|
||||
"Maintenance_Tool_purgebackup_text": "Es werden, bis auf die letzten 3 Backups, alle übrigen Backups gelöscht.",
|
||||
"Maintenance_Tool_restore": "DB Wiederherstellung",
|
||||
"Maintenance_Tool_restore_noti": "DB Wiederherstellung",
|
||||
"Maintenance_Tool_restore_noti_text": "Sind Sie sicher, dass Sie die Datenbank aus der neusten Sicherung wiederherstellen m\u00f6chten? Pr\u00fcfen Sie, dass gerade keine Scans stattfinden.",
|
||||
"Maintenance_Tool_restore_text": "Das neuste Backup kann \u00fcber diese Funk­tion wiederhergestellt werden. \u00c4ltere Sicher­ungen m\u00fcssen manuell wieder­hergestellt wer­den. Es empfiehlt sich eine Integrit\u00e4ts­pr\u00fcfung nach der Wieder­her­stellung zu machen, falls die Datenbank bei der Sicherung ge\u00f6ffnet war.",
|
||||
"Maintenance_Tool_restore_noti_text": "Sind Sie sicher, dass Sie die Datenbank aus der neusten Sicherung wiederherstellen möchten? Prüfen Sie, dass gerade keine Scans stattfinden.",
|
||||
"Maintenance_Tool_restore_text": "Das neuste Backup kann über diese Funk­tion wiederhergestellt werden. Ältere Sicher­ungen müssen manuell wieder­hergestellt wer­den. Es empfiehlt sich eine Integritäts­prüfung nach der Wieder­her­stellung zu machen, falls die Datenbank bei der Sicherung geöffnet war.",
|
||||
"Maintenance_Tool_upgrade_database_noti": "Aktualisiere Datenbank",
|
||||
"Maintenance_Tool_upgrade_database_noti_text": "Machen Sie ein Backup, bevor Sie diese Funk­tion nutzen.",
|
||||
"Maintenance_Tool_upgrade_database_text": "Mit dieser Schaltfl\u00e4che wird die Datenbank aktualisiert, um das Diagramm der Netzwerkaktivit\u00e4ten der letzten 12 Stunden zu aktivieren. Bitte sichern Sie Ihre Datenbank, falls Probleme auftreten.",
|
||||
"Maintenance_Tool_upgrade_database_text": "Mit dieser Schaltfläche wird die Datenbank aktualisiert, um das Diagramm der Netzwerkaktivitäten der letzten 12 Stunden zu aktivieren. Bitte sichern Sie Ihre Datenbank, falls Probleme auftreten.",
|
||||
"Maintenance_Tools_Tab_BackupRestore": "Sicherg. / Wiederherstellg.",
|
||||
"Maintenance_Tools_Tab_Logging": "Logs",
|
||||
"Maintenance_Tools_Tab_Settings": "Einstellungen",
|
||||
@@ -440,24 +465,24 @@
|
||||
"Maintenance_built_on": "Erstellt am",
|
||||
"Maintenance_current_version": "Du bist up-to-date. Sieh dir an, <a href=\"https://github.com/jokob-sk/NetAlertX/issues/138\" target=\"_blank\">woran ich gerade arbeite</a>.",
|
||||
"Maintenance_database_backup": "DB Sicherungen",
|
||||
"Maintenance_database_backup_found": "Sicherungen verf\u00fcgbar",
|
||||
"Maintenance_database_backup_found": "Sicherungen verfügbar",
|
||||
"Maintenance_database_backup_total": "Speicherplatz insgesamt",
|
||||
"Maintenance_database_lastmod": "Letzte \u00c4nderung",
|
||||
"Maintenance_database_lastmod": "Letzte Änderung",
|
||||
"Maintenance_database_path": "Datenbank-Pfad",
|
||||
"Maintenance_database_rows": "Tabelle (Reihen)",
|
||||
"Maintenance_database_size": "Datenbank-Gr\u00f6\u00dfe",
|
||||
"Maintenance_lang_selector_apply": "\u00dcbernehmen",
|
||||
"Maintenance_lang_selector_empty": "Sprache w\u00e4hlen",
|
||||
"Maintenance_database_size": "Datenbank-Größe",
|
||||
"Maintenance_lang_selector_apply": "Übernehmen",
|
||||
"Maintenance_lang_selector_empty": "Sprache wählen",
|
||||
"Maintenance_lang_selector_lable": "Sprachauswahl",
|
||||
"Maintenance_lang_selector_text": "Die \u00c4nderung findet serverseitig statt, betrifft also alle verwendeten Ger\u00e4te.",
|
||||
"Maintenance_new_version": "\ud83c\udd95 Eine neue Version ist vef\u00fcgbar. Sieh dir die <a href=\"https://github.com/jokob-sk/NetAlertX/releases\" target=\"_blank\">Versionshinweise</a> an.",
|
||||
"Maintenance_themeselector_apply": "\u00dcbernehmen",
|
||||
"Maintenance_themeselector_empty": "Skin w\u00e4hlen",
|
||||
"Maintenance_lang_selector_text": "Die Änderung findet serverseitig statt, betrifft also alle verwendeten Geräte.",
|
||||
"Maintenance_new_version": "🆕 Eine neue Version ist vefügbar. Sieh dir die <a href=\"https://github.com/jokob-sk/NetAlertX/releases\" target=\"_blank\">Versionshinweise</a> an.",
|
||||
"Maintenance_themeselector_apply": "Übernehmen",
|
||||
"Maintenance_themeselector_empty": "Skin wählen",
|
||||
"Maintenance_themeselector_lable": "Skin Auswahl",
|
||||
"Maintenance_themeselector_text": "Die \u00c4nderung findet serverseitig statt, betrifft also alle verwendeten Ger\u00e4te.",
|
||||
"Maintenance_themeselector_text": "Die Änderung findet serverseitig statt, betrifft also alle verwendeten Geräte.",
|
||||
"Maintenance_version": "App Updates",
|
||||
"NETWORK_DEVICE_TYPES_description": "Welche Ger\u00e4tetypen als Netzwerkger\u00e4te in der Netzwerkansicht verwendet werden k\u00f6nnen. Der Ger\u00e4tetyp muss genau der <code>Typ</code>-Einstellung eines spezifischen Ger\u00e4ts in den Ger\u00e4tedetails \u00fcbereinstimmen. Entfernen Sie keine existierenden Typen, sondern f\u00fcgen Sie nur neue ein.",
|
||||
"NETWORK_DEVICE_TYPES_name": "Netzwerkger\u00e4te-Typen",
|
||||
"NETWORK_DEVICE_TYPES_description": "Welche Gerätetypen als Netzwerkgeräte in der Netzwerkansicht verwendet werden können. Der Gerätetyp muss genau der <code>Typ</code>-Einstellung eines spezifischen Geräts in den Gerätedetails übereinstimmen. Entfernen Sie keine existierenden Typen, sondern fügen Sie nur neue ein.",
|
||||
"NETWORK_DEVICE_TYPES_name": "Netzwerkgeräte-Typen",
|
||||
"NTFY_HOST_description": "NTFY host URL starting with <code>http://</code> or <code>https://</code>. You can use the hosted instance on <a target=\"_blank\" href=\"https://ntfy.sh/\">https://ntfy.sh</a> by simply entering <code>https://ntfy.sh</code>.",
|
||||
"NTFY_HOST_name": "NTFY host URL",
|
||||
"NTFY_PASSWORD_description": "Enter password if you need (host) an instance with enabled authetication.",
|
||||
@@ -469,69 +494,72 @@
|
||||
"NTFY_display_name": "NTFY",
|
||||
"NTFY_icon": "<i class=\"fa fa-terminal\"></i>",
|
||||
"Navigation_About": "",
|
||||
"Navigation_Devices": "Ger\u00e4te",
|
||||
"Navigation_Devices": "Geräte",
|
||||
"Navigation_Donations": "Donations",
|
||||
"Navigation_Events": "Ereignisse",
|
||||
"Navigation_Flows": "Flows",
|
||||
"Navigation_HelpFAQ": "Hilfe / FAQ",
|
||||
"Navigation_Integrations": "",
|
||||
"Navigation_Integrations": "Integrationen",
|
||||
"Navigation_Maintenance": "Wartung",
|
||||
"Navigation_Monitoring": "",
|
||||
"Navigation_Monitoring": "Überwachung",
|
||||
"Navigation_Network": "Netzwerk",
|
||||
"Navigation_Notifications": "",
|
||||
"Navigation_Plugins": "Plugins",
|
||||
"Navigation_Presence": "Anwesenheit",
|
||||
"Navigation_Report": "Bericht",
|
||||
"Navigation_Settings": "Einstellungen",
|
||||
"Navigation_SystemInfo": "Systeminfo",
|
||||
"Navigation_Workflows": "",
|
||||
"Navigation_Workflows": "Arbeitsabläufe",
|
||||
"Network_Assign": "Zum obigen <i class=\"fa fa-server\"></i> Netzwerkknoten zuweisen",
|
||||
"Network_Cant_Assign": "Internet-Wurzelknoten kann nicht als \u00e4u\u00dferer Kindknoten zugewiesen werden.",
|
||||
"Network_Configuration_Error": "",
|
||||
"Network_Connected": "Verbundene Ger\u00e4te",
|
||||
"Network_ManageAdd": "Ger\u00e4t hinzuf\u00fcgen",
|
||||
"Network_ManageAdd_Name": "Name des Ger\u00e4tes",
|
||||
"Network_Cant_Assign": "Internet-Wurzelknoten kann nicht als äußerer Kindknoten zugewiesen werden.",
|
||||
"Network_Configuration_Error": "Konfigurationsfehler",
|
||||
"Network_Connected": "Verbundene Geräte",
|
||||
"Network_ManageAdd": "Gerät hinzufügen",
|
||||
"Network_ManageAdd_Name": "Name des Gerätes",
|
||||
"Network_ManageAdd_Name_text": "Name ohne Sonderzeichen",
|
||||
"Network_ManageAdd_Port": "Portanzahl",
|
||||
"Network_ManageAdd_Port_text": "bei WLAN oder Powerline leer lassen",
|
||||
"Network_ManageAdd_Submit": "Hinzuf\u00fcgen",
|
||||
"Network_ManageAdd_Type": "Ger\u00e4tetyp",
|
||||
"Network_ManageAdd_Type_text": "-- Typ w\u00e4hlen --",
|
||||
"Network_ManageAdd_Submit": "Hinzufügen",
|
||||
"Network_ManageAdd_Type": "Gerätetyp",
|
||||
"Network_ManageAdd_Type_text": "-- Typ wählen --",
|
||||
"Network_ManageAssign": "Zuweisen",
|
||||
"Network_ManageDel": "Ger\u00e4t l\u00f6schen",
|
||||
"Network_ManageDel_Name": "Ger\u00e4t zum L\u00f6schen ausw\u00e4hlen",
|
||||
"Network_ManageDel_Name_text": "-- Ger\u00e4t w\u00e4hlen --",
|
||||
"Network_ManageDel_Submit": "L\u00f6schen",
|
||||
"Network_ManageDevices": "Ger\u00e4te verwalten",
|
||||
"Network_ManageEdit": "Ger\u00e4t bearbeiten",
|
||||
"Network_ManageEdit_ID": "Ger\u00e4t zum Bearbeiten ausw\u00e4hlen",
|
||||
"Network_ManageEdit_ID_text": "-- Ger\u00e4t w\u00e4hlen --",
|
||||
"Network_ManageDel": "Gerät löschen",
|
||||
"Network_ManageDel_Name": "Gerät zum Löschen auswählen",
|
||||
"Network_ManageDel_Name_text": "-- Gerät wählen --",
|
||||
"Network_ManageDel_Submit": "Löschen",
|
||||
"Network_ManageDevices": "Geräte verwalten",
|
||||
"Network_ManageEdit": "Gerät bearbeiten",
|
||||
"Network_ManageEdit_ID": "Gerät zum Bearbeiten auswählen",
|
||||
"Network_ManageEdit_ID_text": "-- Gerät wählen --",
|
||||
"Network_ManageEdit_Name": "Neuer Name",
|
||||
"Network_ManageEdit_Name_text": "Name ohne Sonderzeichen",
|
||||
"Network_ManageEdit_Port": "Neue Portanzahl",
|
||||
"Network_ManageEdit_Port_text": "bei WLAN oder Powerline leer lassen",
|
||||
"Network_ManageEdit_Submit": "Speichern",
|
||||
"Network_ManageEdit_Type": "Neuer Typ",
|
||||
"Network_ManageEdit_Type_text": "-- Typ w\u00e4hlen --",
|
||||
"Network_ManageEdit_Type_text": "-- Typ wählen --",
|
||||
"Network_ManageLeaf": "Zuweisungen verwalten",
|
||||
"Network_ManageUnassign": "Zuweisung aufheben",
|
||||
"Network_NoAssignedDevices": "Dieser Netzwerkknoten hat keine zugewiesenen Ger\u00e4te (Kindknoten). Weise eins von unten zu oder gehe in den <b><i class=\"fa fa-info-circle\"></i> Details</b> Tab eines Ger\u00e4tes in <a href=\"devices.php\"><b> <i class=\"fa fa-laptop\"></i> Ger\u00e4te</b></a>, und weise dort das Ger\u00e4t einem <b><i class=\"fa fa-server\"></i> Netzwerk Knoten</b> und einem <b><i class=\"fa fa-ethernet\"></i> Netzwerk Knoten Port</b> zu.",
|
||||
"Network_NoDevices": "",
|
||||
"Network_NoAssignedDevices": "Dieser Netzwerkknoten hat keine zugewiesenen Geräte (Kindknoten). Weise eins von unten zu oder gehe in den <b><i class=\"fa fa-info-circle\"></i> Details</b> Tab eines Gerätes in <a href=\"devices.php\"><b> <i class=\"fa fa-laptop\"></i> Geräte</b></a>, und weise dort das Gerät einem <b><i class=\"fa fa-server\"></i> Netzwerk Knoten</b> und einem <b><i class=\"fa fa-ethernet\"></i> Netzwerk Knoten Port</b> zu.",
|
||||
"Network_NoDevices": "Keine Geräte zum konfigurieren",
|
||||
"Network_Node": "Netzwerkknoten",
|
||||
"Network_Node_Name": "Knotenname",
|
||||
"Network_Parent": "\u00dcbergeordnetes Netzwerkger\u00e4t",
|
||||
"Network_Parent": "Übergeordnetes Netzwerkgerät",
|
||||
"Network_Root": "",
|
||||
"Network_Root_Not_Configured": "",
|
||||
"Network_Root_Unconfigurable": "Nicht konfigurierbare Wurzel",
|
||||
"Network_Table_Hostname": "Ger\u00e4tename",
|
||||
"Network_Table_Hostname": "Gerätename",
|
||||
"Network_Table_IP": "IP",
|
||||
"Network_Table_State": "Status",
|
||||
"Network_Title": "Netzwerk\u00fcbersicht",
|
||||
"Network_UnassignedDevices": "Nicht zugewiesene Ger\u00e4te",
|
||||
"PIALERT_WEB_PASSWORD_description": "Das Standardpasswort ist <code>123456</code>. Um das Passwort zu \u00e4ndern, entweder <code>/app/back/pialert-cli</code> im Container starten oder <a onclick=\"toggleAllSettings()\" href=\"#SETPWD_RUN\"><code>SETPWD_RUN</code> Set password plugin</a> nutzen.",
|
||||
"Network_Title": "Netzwerkübersicht",
|
||||
"Network_UnassignedDevices": "Nicht zugewiesene Geräte",
|
||||
"Notifications_All": "",
|
||||
"Notifications_Mark_All_Read": "",
|
||||
"PIALERT_WEB_PASSWORD_description": "Das Standardpasswort ist <code>123456</code>. Um das Passwort zu ändern, entweder <code>/app/back/pialert-cli</code> im Container starten oder <a onclick=\"toggleAllSettings()\" href=\"#SETPWD_RUN\"><code>SETPWD_RUN</code> Set password plugin</a> nutzen.",
|
||||
"PIALERT_WEB_PASSWORD_name": "Login-Passwort",
|
||||
"PIALERT_WEB_PROTECTION_description": "Ein Loginfenster wird angezeigt wenn aktiviert. Untere Beschreibung genau durchlesen falls Sie sich aus Ihrer Instanz aussperren.",
|
||||
"PIALERT_WEB_PROTECTION_name": "Login aktivieren",
|
||||
"PLUGINS_KEEP_HIST_description": "Wie viele Plugin Scanresultate behalten werden (pro Plugin, nicht ger\u00e4tespezifisch).",
|
||||
"PLUGINS_KEEP_HIST_description": "Wie viele Plugin Scanresultate behalten werden (pro Plugin, nicht gerätespezifisch).",
|
||||
"PLUGINS_KEEP_HIST_name": "Plugins Verlauf",
|
||||
"PUSHSAFER_TOKEN_description": "Your secret Pushsafer API key (token).",
|
||||
"PUSHSAFER_TOKEN_name": "Pushsafer token",
|
||||
@@ -550,21 +578,21 @@
|
||||
"Presence_CalHead_quarter": "Quartal",
|
||||
"Presence_CalHead_week": "Woche",
|
||||
"Presence_CalHead_year": "Jahr",
|
||||
"Presence_CallHead_Devices": "Ger\u00e4te",
|
||||
"Presence_CallHead_Devices": "Geräte",
|
||||
"Presence_Loading": "Laden...",
|
||||
"Presence_Shortcut_AllDevices": "Alle Ger\u00e4te",
|
||||
"Presence_Shortcut_AllDevices": "Meine Geräte",
|
||||
"Presence_Shortcut_Archived": "Archiviert",
|
||||
"Presence_Shortcut_Connected": "Verbunden",
|
||||
"Presence_Shortcut_Devices": "Ger\u00e4te",
|
||||
"Presence_Shortcut_Devices": "Geräte",
|
||||
"Presence_Shortcut_DownAlerts": "Down Meldungen",
|
||||
"Presence_Shortcut_Favorites": "Favoriten",
|
||||
"Presence_Shortcut_NewDevices": "Neue Ger\u00e4te",
|
||||
"Presence_Title": "Anwesenheit pro Ger\u00e4t",
|
||||
"Presence_Shortcut_NewDevices": "Neue Geräte",
|
||||
"Presence_Title": "Anwesenheit pro Gerät",
|
||||
"REPORT_APPRISE_description": "Enable sending notifications via <a target=\"_blank\" href=\"https://hub.docker.com/r/caronc/apprise\">Apprise</a>.",
|
||||
"REPORT_APPRISE_name": "Enable Apprise",
|
||||
"REPORT_DASHBOARD_URL_description": "Diese URL wird als Basis f\u00fcrs Erstellen von Links in E-Mails genutzt. Geben Sie die gesamte URL startend mit <code>http://</code> inklusive der genutzten Portnummer ein (keinen nachfolgenden Schr\u00e4gstrich <code>/</code> nutzen).",
|
||||
"REPORT_DASHBOARD_URL_description": "Diese URL wird als Basis fürs Erstellen von Links in E-Mails genutzt. Geben Sie die gesamte URL startend mit <code>http://</code> inklusive der genutzten Portnummer ein (keinen nachfolgenden Schrägstrich <code>/</code> nutzen).",
|
||||
"REPORT_DASHBOARD_URL_name": "NetAlertX URL",
|
||||
"REPORT_ERROR": "Die gesuchte Seite ist vor\u00fcbergehend nicht verf\u00fcgbar. Bitte versuchen Sie es nach ein paar Sekunden erneut",
|
||||
"REPORT_ERROR": "Die gesuchte Seite ist vorübergehend nicht verfügbar. Bitte versuchen Sie es nach ein paar Sekunden erneut",
|
||||
"REPORT_FROM_description": "Notification email subject line. Some SMTP servers need this to be an email.",
|
||||
"REPORT_FROM_name": "Email subject",
|
||||
"REPORT_MAIL_description": "If enabled an email is sent out with a list of changes you nove subscribed to. Please also fill out all remaining settings related to the SMTP setup below. If facing issues, set <code>LOG_LEVEL</code> to <code>debug</code> and check the <a href=\"/maintenance.php#tab_Logging\">error log</a>.",
|
||||
@@ -581,6 +609,7 @@
|
||||
"REPORT_WEBHOOK_description": "Enable webhooks for notifications. Webhooks help you to connect to a lot of 3rd party tools, such as IFTTT, Zapier or <a href=\"https://n8n.io/\" target=\"_blank\">n8n</a> to name a few. Check out this simple <a href=\"https://github.com/jokob-sk/NetAlertX/blob/main/docs/WEBHOOK_N8N.md\" target=\"_blank\">n8n guide here</a> to get started. If enabled, configure related settings below.",
|
||||
"REPORT_WEBHOOK_name": "Enable Webhooks",
|
||||
"RandomMAC_hover": "Autodetected - indicates if the device randomizes it's MAC address.",
|
||||
"Reports_Sent_Log": "",
|
||||
"SCAN_SUBNETS_description": "Arp-scan is a command-line tool that uses the ARP protocol to discover and fingerprint IP hosts on the local network. An alternative to ARP scan is to enable the <a onclick=\"toggleAllSettings()\" href=\"#PIHOLE_RUN\"><code>PIHOLE_RUN</code>PiHole integration settings</a>. The arp-scan time itself depends on the number of IP addresses to check so set this up carefully with the appropriate network mask and interface. Check the <a href=\"https://github.com/jokob-sk/NetAlertX/blob/main/docs/SUBNETS.md\" target=\"_blank\">subnets documentation</a> for help on setting up VLANs, what VLANs are supported, or how to figure out the network mask and your interface.",
|
||||
"SMTP_FORCE_SSL_description": "Force SSL when connecting to your SMTP server.",
|
||||
"SMTP_FORCE_SSL_name": "Force SSL",
|
||||
@@ -609,12 +638,12 @@
|
||||
"Systeminfo_CPU_Speed": "CPU-Geschwindigkeit:",
|
||||
"Systeminfo_CPU_Temp": "CPU-Temp:",
|
||||
"Systeminfo_CPU_Vendor": "CPU-Anbieter:",
|
||||
"Systeminfo_Client_Resolution": "Browseraufl\u00f6sung:",
|
||||
"Systeminfo_Client_Resolution": "Browserauflösung:",
|
||||
"Systeminfo_Client_User_Agent": "Browser-Bezeichnung:",
|
||||
"Systeminfo_General": "Allgemein",
|
||||
"Systeminfo_General_Date": "Datum:",
|
||||
"Systeminfo_General_Date2": "Datum2:",
|
||||
"Systeminfo_General_Full_Date": "Vollst\u00e4ndiges Datum:",
|
||||
"Systeminfo_General_Full_Date": "Vollständiges Datum:",
|
||||
"Systeminfo_General_TimeZone": "Zeitzone:",
|
||||
"Systeminfo_Memory": "Arbeitsspeicher",
|
||||
"Systeminfo_Memory_Total_Memory": "Gesamtspeicher:",
|
||||
@@ -635,10 +664,10 @@
|
||||
"Systeminfo_Network_HTTP_Referer": "HTTP-Referer:",
|
||||
"Systeminfo_Network_HTTP_Referer_String": "Kein HTTP-Referer",
|
||||
"Systeminfo_Network_Hardware": "Netzwerk Hardware",
|
||||
"Systeminfo_Network_Hardware_Interface_Mask": "",
|
||||
"Systeminfo_Network_Hardware_Interface_Name": "",
|
||||
"Systeminfo_Network_Hardware_Interface_RX": "",
|
||||
"Systeminfo_Network_Hardware_Interface_TX": "",
|
||||
"Systeminfo_Network_Hardware_Interface_Mask": "Netzwerkmaske",
|
||||
"Systeminfo_Network_Hardware_Interface_Name": "Interfacename",
|
||||
"Systeminfo_Network_Hardware_Interface_RX": "Empfangen",
|
||||
"Systeminfo_Network_Hardware_Interface_TX": "Gesendet",
|
||||
"Systeminfo_Network_IP": "IP Internet:",
|
||||
"Systeminfo_Network_IP_Connection": "IP-Verbindung:",
|
||||
"Systeminfo_Network_IP_Server": "Server-IP:",
|
||||
@@ -657,9 +686,9 @@
|
||||
"Systeminfo_Services_Description": "Dienstbeschreibung",
|
||||
"Systeminfo_Services_Name": "Dienstname",
|
||||
"Systeminfo_Storage": "Speicher",
|
||||
"Systeminfo_Storage_Device": "Ger\u00e4t:",
|
||||
"Systeminfo_Storage_Device": "Gerät:",
|
||||
"Systeminfo_Storage_Mount": "Mountpunkt:",
|
||||
"Systeminfo_Storage_Size": "Gr\u00f6\u00dfe:",
|
||||
"Systeminfo_Storage_Size": "Größe:",
|
||||
"Systeminfo_Storage_Type": "Typ:",
|
||||
"Systeminfo_Storage_Usage": "Speicherverwendung",
|
||||
"Systeminfo_Storage_Usage_Free": "Frei:",
|
||||
@@ -675,25 +704,25 @@
|
||||
"Systeminfo_System_System": "System:",
|
||||
"Systeminfo_System_Uname": "Uname:",
|
||||
"Systeminfo_System_Uptime": "Betriebszeit:",
|
||||
"Systeminfo_This_Client": "Dieses Ger\u00e4t",
|
||||
"Systeminfo_USB_Devices": "USB-Ger\u00e4te",
|
||||
"Systeminfo_This_Client": "Dieses Gerät",
|
||||
"Systeminfo_USB_Devices": "USB-Geräte",
|
||||
"TICKER_MIGRATE_TO_NETALERTX": "",
|
||||
"TIMEZONE_description": "Zeitzone um Statistiken korrekt darzustellen. Finde deine Zeitzone <a target=\"_blank\" href=\"https://en.wikipedia.org/wiki/List_of_tz_database_time_zones\" rel=\"nofollow\">hier</a>.",
|
||||
"TIMEZONE_name": "Zeitzone",
|
||||
"UI_DEV_SECTIONS_description": "",
|
||||
"UI_DEV_SECTIONS_name": "",
|
||||
"UI_DEV_SECTIONS_description": "Auswählen, welche Elemente in der Geräteseite versteckt werden sollen.",
|
||||
"UI_DEV_SECTIONS_name": "Geräteauswahl verstecken",
|
||||
"UI_ICONS_description": "",
|
||||
"UI_ICONS_name": "",
|
||||
"UI_LANG_description": "Bevorzugte Oberfl\u00e4chensprache ausw\u00e4hlen.",
|
||||
"UI_ICONS_name": "Vorgefertigte Icons",
|
||||
"UI_LANG_description": "Bevorzugte Oberflächensprache auswählen. Du möchtest beim Übersetzen helfen? Gerne hier: <a href=\"https://hosted.weblate.org/projects/pialert/core/\" target=\"_blank\">Weblate</a>.",
|
||||
"UI_LANG_name": "UI Sprache",
|
||||
"UI_MY_DEVICES_description": "",
|
||||
"UI_MY_DEVICES_name": "",
|
||||
"UI_MY_DEVICES_name": "In \"Meine Geräte\" anzeigen",
|
||||
"UI_NOT_RANDOM_MAC_description": "",
|
||||
"UI_NOT_RANDOM_MAC_name": "",
|
||||
"UI_PRESENCE_description": "Ausw\u00e4hlen, welche Status im <b>Ger\u00e4tepr\u00e4senz im Laufe der Zeit</b>-Diagramm in der <a href=\"/devices.php\" target=\"_blank\">Ger\u00e4te</a>-Seite angzeigt werden sollen. (<code>STRG + klicken</code> zum aus-/abw\u00e4hlen).",
|
||||
"UI_PRESENCE_name": "Anzeige im Pr\u00e4senzdiagramm",
|
||||
"UI_PRESENCE_description": "Auswählen, welche Status im <b>Gerätepräsenz im Laufe der Zeit</b>-Diagramm in der <a href=\"/devices.php\" target=\"_blank\">Geräte</a>-Seite angzeigt werden sollen. (<code>STRG + klicken</code> zum aus-/abwählen).",
|
||||
"UI_PRESENCE_name": "Anzeige im Präsenzdiagramm",
|
||||
"UI_REFRESH_description": "",
|
||||
"UI_REFRESH_name": "",
|
||||
"UI_REFRESH_name": "Automatisch Aktualisieren",
|
||||
"WEBHOOK_PAYLOAD_description": "The Webhook payload data format for the <code>body</code> > <code>attachments</code> > <code>text</code> attribute in the payload json. See an example of the payload <a target=\"_blank\" href=\"https://github.com/jokob-sk/NetAlertX/blob/main/front/report_templates/webhook_json_sample.json\">here</a>. (e.g.: for discord use <code>text</code>)",
|
||||
"WEBHOOK_PAYLOAD_name": "Payload type",
|
||||
"WEBHOOK_REQUEST_METHOD_description": "The HTTP request method to be used for the webhook call.",
|
||||
@@ -706,37 +735,40 @@
|
||||
"WEBHOOK_URL_name": "Target URL",
|
||||
"Webhooks_display_name": "Webhooks",
|
||||
"Webhooks_icon": "<i class=\"fa fa-circle-nodes\"></i>",
|
||||
"devices_old": "",
|
||||
"general_event_description": " The event you have triggered might take a while until background processes finish. The execution ended once you see <code>finished</code> below. Check the <a href='/maintenance.php#tab_Logging'>error log</a> if you didn not get the expected result. <br/> <br/> Status: ",
|
||||
"devices_old": "Aktualisiert...",
|
||||
"general_event_description": "Das Ereignis, das Sie ausgelöst haben, könnte eine Weile dauern, bis Hintergrundprozesse abgeschlossen sind. Die Ausführung endet, wenn die unten ausgeführte Warteschlangen abgearbeitet ist. (Siehe <a href='/maintenance.php#tab_Logging'>error log</a>, wenn Probleme auftreten.)<br/> <br/> Ausführungsschlange:",
|
||||
"general_event_title": "Executing an ad-hoc event",
|
||||
"report_guid": "",
|
||||
"report_guid_missing": "",
|
||||
"report_select_format": "",
|
||||
"report_time": "",
|
||||
"report_select_format": "Format auswählen:",
|
||||
"report_time": "Benachrichtigungszeit:",
|
||||
"run_event_icon": "fa-play",
|
||||
"run_event_tooltip": "Enable the setting and save your changes at first before you run it.",
|
||||
"settings_core_icon": "",
|
||||
"settings_core_icon": "fa-solid fa-gem",
|
||||
"settings_core_label": "",
|
||||
"settings_device_scanners": "",
|
||||
"settings_device_scanners_icon": "",
|
||||
"settings_device_scanners_label": "",
|
||||
"settings_enabled": "",
|
||||
"settings_enabled_icon": "",
|
||||
"settings_device_scanners_icon": "fa-solid fa-magnifying-glass-plus",
|
||||
"settings_device_scanners_info": "",
|
||||
"settings_device_scanners_label": "Gerätescanner",
|
||||
"settings_enabled": "Aktive Einstellungen",
|
||||
"settings_enabled_icon": "fa-solid fa-toggle-on",
|
||||
"settings_expand_all": "Expand all",
|
||||
"settings_imported": "Last time settings were imported from the app.conf file:",
|
||||
"settings_imported_label": "",
|
||||
"settings_imported_label": "Einstellungen importiert",
|
||||
"settings_missing": "Not all settings loaded, refresh the page! This is probably caused by a high load on the database or app startup sequence.",
|
||||
"settings_missing_block": "You can not save your settings without specifying all setting keys. Refresh the page. This is probably caused by a high load on the database.",
|
||||
"settings_old": "Importing settings and re-initializing...",
|
||||
"settings_other_scanners": "",
|
||||
"settings_other_scanners_icon": "",
|
||||
"settings_other_scanners_label": "",
|
||||
"settings_other_scanners_icon": "fa-solid fa-recycle",
|
||||
"settings_other_scanners_label": "Andere Scanner",
|
||||
"settings_publishers": "",
|
||||
"settings_publishers_icon": "",
|
||||
"settings_publishers_label": "",
|
||||
"settings_publishers_icon": "fa-solid fa-paper-plane",
|
||||
"settings_publishers_info": "",
|
||||
"settings_publishers_label": "Veröffentlicher",
|
||||
"settings_saved": "<br/>Settings saved to the <code>app.conf</code> file.<br/><br/>A time-stamped backup of the previous file created. <br/><br/> Reloading...<br/>",
|
||||
"settings_system_icon": "",
|
||||
"settings_system_label": "",
|
||||
"settings_system_icon": "fa-solid fa-gear",
|
||||
"settings_system_label": "System",
|
||||
"settings_update_item_warning": "",
|
||||
"test_event_icon": "fa-vial-circle-check",
|
||||
"test_event_tooltip": "Save your changes at first before you test your settings."
|
||||
}
|
||||
}
|
||||
|
||||