Compare commits

...

231 Commits

Author SHA1 Message Date
jokob-sk
4363e083d5 prevent 401 in ingress #961 2025-01-19 16:05:58 +11:00
jokob-sk
e766b19d8c css, pluginsCore, less verbose debug fixes
Some checks are pending
docker / docker_dev (push) Waiting to run
2025-01-19 15:05:26 +11:00
jokob-sk
b18ee70b8a Merge branch 'main' of https://github.com/jokob-sk/NetAlertX 2025-01-19 13:41:52 +11:00
jokob-sk
f70bb40ef4 INTERNET ip check curl #937 2025-01-19 13:41:48 +11:00
Hosted Weblate
844a408ff4 Merge branch 'origin/main' into Weblate. 2025-01-19 01:56:31 +00:00
Максим Горпиніч
a21cc0db85 Translated using Weblate (Ukrainian)
Currently translated at 100.0% (751 of 751 strings)

Translation: NetAlertX/core
Translate-URL: https://hosted.weblate.org/projects/pialert/core/uk/
2025-01-19 01:56:30 +00:00
Sylvain Pichon
a5427f795b Translated using Weblate (French)
Currently translated at 99.3% (746 of 751 strings)

Translation: NetAlertX/core
Translate-URL: https://hosted.weblate.org/projects/pialert/core/fr/
2025-01-19 01:56:29 +00:00
jokob-sk
6b390b66de report cleanup + #960 2025-01-19 12:56:00 +11:00
jokob-sk
af1e9b921b Merge branch 'main' of https://github.com/jokob-sk/NetAlertX
Some checks are pending
docker / docker_dev (push) Waiting to run
2025-01-18 10:07:13 +11:00
jokob-sk
9503cc6397 HA addon ingress #960 2025-01-18 10:07:03 +11:00
Jokob @NetAlertX
a16c2dfed6 Update README.md
Some checks failed
docker / docker_dev (push) Has been cancelled
2025-01-16 22:53:25 +11:00
Jokob @NetAlertX
b4fc05d1e8 Merge pull request #958 from labmonkey/asuswrt-import
Added AsusWRT device import plugin
2025-01-16 22:47:09 +11:00
labmonkey
adb99e5f1a Revert leftover code 2025-01-16 03:41:26 +01:00
Pawel Derehajlo
fbce3e18c2 Added AsusWRT device import plugin 2025-01-16 03:37:37 +01:00
anton garcias
0fcda5ff0a Translated using Weblate (Catalan)
Some checks are pending
docker / docker_dev (push) Waiting to run
Currently translated at 100.0% (751 of 751 strings)

Translation: NetAlertX/core
Translate-URL: https://hosted.weblate.org/projects/pialert/core/ca/
2025-01-15 15:13:13 +01:00
jokob-sk
a5c6510654 less verbose AVAHISCAN logs
Some checks are pending
docker / docker_dev (push) Waiting to run
2025-01-15 12:39:03 +11:00
jokob-sk
6d44ed1bba Merge branch 'main' of https://github.com/jokob-sk/NetAlertX
Some checks are pending
docker / docker_dev (push) Waiting to run
2025-01-14 13:28:38 +11:00
jokob-sk
314372a0f2 server time format #954 2025-01-14 13:28:34 +11:00
Sylvain Pichon
5a84cb5cc2 Translated using Weblate (French)
Some checks are pending
docker / docker_dev (push) Waiting to run
Currently translated at 98.0% (736 of 751 strings)

Translation: NetAlertX/core
Translate-URL: https://hosted.weblate.org/projects/pialert/core/fr/
2025-01-13 16:58:25 +01:00
jokob-sk
12cc71552c #951, small fixes
Some checks are pending
docker / docker_dev (push) Waiting to run
2025-01-13 08:16:20 +11:00
jokob-sk
6752b7fc40 gpc.json #952 2025-01-13 07:54:44 +11:00
jokob-sk
f371515258 issue template
Some checks are pending
docker / docker_dev (push) Waiting to run
2025-01-12 21:21:08 +11:00
jokob-sk
f1d73f6ad4 Merge branch 'main' of https://github.com/jokob-sk/NetAlertX 2025-01-12 21:18:50 +11:00
jokob-sk
9fee846436 HA addon docs 2025-01-12 21:18:44 +11:00
Massimo Pissarello
b4ebd640e5 Translated using Weblate (Italian)
Some checks are pending
docker / docker_dev (push) Waiting to run
Currently translated at 100.0% (751 of 751 strings)

Translation: NetAlertX/core
Translate-URL: https://hosted.weblate.org/projects/pialert/core/it/
2025-01-12 08:22:16 +01:00
jokob-sk
e94953b9af Merge branch 'main' of https://github.com/jokob-sk/NetAlertX
Some checks are pending
docker / docker_dev (push) Waiting to run
2025-01-12 11:46:35 +11:00
jokob-sk
6718d054dc #951, small fixes 2025-01-12 11:46:32 +11:00
anton garcias
88e4dbf12e Translated using Weblate (Catalan)
Some checks are pending
docker / docker_dev (push) Waiting to run
Currently translated at 100.0% (751 of 751 strings)

Translation: NetAlertX/core
Translate-URL: https://hosted.weblate.org/projects/pialert/core/ca/
2025-01-11 13:09:29 +01:00
Anonymous
9f1db5ca1a Translated using Weblate (Portuguese (Brazil))
Currently translated at 32.7% (246 of 751 strings)

Translation: NetAlertX/core
Translate-URL: https://hosted.weblate.org/projects/pialert/core/pt_BR/
2025-01-11 10:47:32 +01:00
jokob-sk
5e7bb207c8 Merge branch 'main' of https://github.com/jokob-sk/NetAlertX 2025-01-11 20:20:30 +11:00
jokob-sk
6ad90610ea Docs, small fixes 2025-01-11 20:20:09 +11:00
Anonymous
c8ff0d79d1 Translated using Weblate (Ukrainian)
Some checks are pending
docker / docker_dev (push) Waiting to run
Currently translated at 97.7% (734 of 751 strings)

Translation: NetAlertX/core
Translate-URL: https://hosted.weblate.org/projects/pialert/core/uk/
2025-01-11 04:36:15 +01:00
Anonymous
61cf87f0d6 Translated using Weblate (Catalan)
Currently translated at 98.1% (737 of 751 strings)

Translation: NetAlertX/core
Translate-URL: https://hosted.weblate.org/projects/pialert/core/ca/
2025-01-11 04:36:14 +01:00
Anonymous
75bbc8bcf3 Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 88.2% (663 of 751 strings)

Translation: NetAlertX/core
Translate-URL: https://hosted.weblate.org/projects/pialert/core/zh_Hans/
2025-01-11 04:36:14 +01:00
Anonymous
5582c0bf5e Translated using Weblate (Polish)
Currently translated at 88.9% (668 of 751 strings)

Translation: NetAlertX/core
Translate-URL: https://hosted.weblate.org/projects/pialert/core/pl/
2025-01-11 04:36:14 +01:00
Anonymous
8278a92b11 Translated using Weblate (Italian)
Currently translated at 98.2% (738 of 751 strings)

Translation: NetAlertX/core
Translate-URL: https://hosted.weblate.org/projects/pialert/core/it/
2025-01-11 04:36:14 +01:00
Anonymous
d5ac00a307 Translated using Weblate (Russian)
Currently translated at 98.2% (738 of 751 strings)

Translation: NetAlertX/core
Translate-URL: https://hosted.weblate.org/projects/pialert/core/ru/
2025-01-11 04:36:13 +01:00
Anonymous
2b02596b17 Translated using Weblate (Norwegian Bokmål)
Currently translated at 88.0% (661 of 751 strings)

Translation: NetAlertX/core
Translate-URL: https://hosted.weblate.org/projects/pialert/core/nb_NO/
2025-01-11 04:36:13 +01:00
Anonymous
2a736f3c19 Translated using Weblate (French)
Currently translated at 97.7% (734 of 751 strings)

Translation: NetAlertX/core
Translate-URL: https://hosted.weblate.org/projects/pialert/core/fr/
2025-01-11 04:36:13 +01:00
Anonymous
0b5c0b198a Translated using Weblate (Spanish)
Currently translated at 96.9% (728 of 751 strings)

Translation: NetAlertX/core
Translate-URL: https://hosted.weblate.org/projects/pialert/core/es/
2025-01-11 04:36:12 +01:00
Anonymous
2d0a461724 Translated using Weblate (German)
Currently translated at 89.7% (674 of 751 strings)

Translation: NetAlertX/core
Translate-URL: https://hosted.weblate.org/projects/pialert/core/de/
2025-01-11 04:36:12 +01:00
jokob-sk
74c6faccc7 Merge branch 'main' of https://github.com/jokob-sk/NetAlertX
Some checks are pending
docker / docker_dev (push) Waiting to run
2025-01-11 11:29:17 +11:00
jokob-sk
89a154e224 Docs 2025-01-11 11:29:02 +11:00
Hosted Weblate
8b84e4b325 Merge branch 'origin/main' into Weblate. 2025-01-11 01:24:14 +01:00
Massimo Pissarello
e55002a9b0 Translated using Weblate (Italian)
Currently translated at 100.0% (745 of 745 strings)

Translation: NetAlertX/core
Translate-URL: https://hosted.weblate.org/projects/pialert/core/it/
2025-01-11 01:24:12 +01:00
jokob-sk
b4e741568b Extending Maintenance w/ app.conf (im/ex)port 2025-01-11 11:23:32 +11:00
jokob-sk
711e0012cb sort by port #949 2025-01-11 07:54:51 +11:00
jokob-sk
ea2e8459b5 sort by port #949 2025-01-11 07:51:43 +11:00
jokob-sk
16d06e8a74 Merge branch 'main' of https://github.com/jokob-sk/NetAlertX
Some checks failed
docker / docker_dev (push) Has been cancelled
2025-01-10 07:49:04 +11:00
jokob-sk
9625c36d12 devStaticIp to multi edit #947 2025-01-10 07:48:35 +11:00
Safeguard
f47858d773 Translated using Weblate (Russian)
Some checks are pending
docker / docker_dev (push) Waiting to run
Currently translated at 100.0% (745 of 745 strings)

Translation: NetAlertX/core
Translate-URL: https://hosted.weblate.org/projects/pialert/core/ru/
2025-01-09 15:53:40 +01:00
jokob-sk
ac2ce85f33 Merge branch 'main' of https://github.com/jokob-sk/NetAlertX 2025-01-09 22:16:08 +11:00
jokob-sk
e887a11755 docs, cleanup 2025-01-09 22:15:58 +11:00
Anonymous
73f432c786 Translated using Weblate (Ukrainian)
Some checks are pending
docker / docker_dev (push) Waiting to run
Currently translated at 99.3% (740 of 745 strings)

Translation: NetAlertX/core
Translate-URL: https://hosted.weblate.org/projects/pialert/core/uk/
2025-01-09 08:26:03 +01:00
Anonymous
9c95a79e07 Translated using Weblate (Catalan)
Currently translated at 99.8% (744 of 745 strings)

Translation: NetAlertX/core
Translate-URL: https://hosted.weblate.org/projects/pialert/core/ca/
2025-01-09 08:26:03 +01:00
Anonymous
f0bbd37812 Translated using Weblate (Portuguese (Brazil))
Currently translated at 33.0% (246 of 745 strings)

Translation: NetAlertX/core
Translate-URL: https://hosted.weblate.org/projects/pialert/core/pt_BR/
2025-01-09 08:26:03 +01:00
Anonymous
7b0777b805 Translated using Weblate (Italian)
Currently translated at 99.8% (744 of 745 strings)

Translation: NetAlertX/core
Translate-URL: https://hosted.weblate.org/projects/pialert/core/it/
2025-01-09 08:26:03 +01:00
Safeguard
b8dce59138 Translated using Weblate (Russian)
Currently translated at 99.5% (742 of 745 strings)

Translation: NetAlertX/core
Translate-URL: https://hosted.weblate.org/projects/pialert/core/ru/
2025-01-09 08:26:03 +01:00
Anonymous
f1d79074ec Translated using Weblate (Russian)
Currently translated at 99.5% (742 of 745 strings)

Translation: NetAlertX/core
Translate-URL: https://hosted.weblate.org/projects/pialert/core/ru/
2025-01-09 08:26:03 +01:00
Anonymous
4156bc1669 Translated using Weblate (French)
Currently translated at 99.3% (740 of 745 strings)

Translation: NetAlertX/core
Translate-URL: https://hosted.weblate.org/projects/pialert/core/fr/
2025-01-09 08:26:02 +01:00
Anonymous
00c4dd86c6 Translated using Weblate (Spanish)
Currently translated at 98.5% (734 of 745 strings)

Translation: NetAlertX/core
Translate-URL: https://hosted.weblate.org/projects/pialert/core/es/
2025-01-09 08:26:02 +01:00
Anonymous
979ce8fd75 Translated using Weblate (German)
Currently translated at 91.2% (680 of 745 strings)

Translation: NetAlertX/core
Translate-URL: https://hosted.weblate.org/projects/pialert/core/de/
2025-01-09 08:26:02 +01:00
jokob-sk
ede926beee docs, cleanup 2025-01-09 14:41:56 +11:00
jokob-sk
e1f91ddf17 docs, social posts, s6 setup improvements #945 2025-01-09 14:22:39 +11:00
jokob-sk
e9539962c9 cron s6 rollback #945
Some checks are pending
docker / docker_dev (push) Waiting to run
2025-01-09 09:23:29 +11:00
jokob-sk
87d18a9067 Merge branch 'main' of https://github.com/jokob-sk/NetAlertX 2025-01-09 08:35:14 +11:00
jokob-sk
2302cd9a31 s6 setup improvements #945 2025-01-09 08:35:09 +11:00
jokob-sk
babe0eab53 Merge pull request #944 from kpolszewski/mtscan-readme-update
better readme for the MikroTik plugin
2025-01-09 08:16:22 +11:00
kpolszewski
7c878690ef better readme for the MikroTik plugin 2025-01-08 19:15:32 +01:00
jokob-sk
b29dcbfa98 CHECKMK script
Some checks failed
docker / docker_dev (push) Waiting to run
🤖Automation - Update Sponsors Table / update-table (push) Has been cancelled
2025-01-08 23:32:14 +11:00
jokob-sk
8d9c48166b Merge branch 'main' of https://github.com/jokob-sk/NetAlertX 2025-01-08 23:00:07 +11:00
jokob-sk
540f8e850b DUMMY as new device source plugin 2025-01-08 23:00:03 +11:00
github-actions[bot]
8316d8e741 [🤖Automation] Update README with sponsors information 2025-01-08 11:53:43 +00:00
jokob-sk
ad32e76a55 Merge branch 'main' of https://github.com/jokob-sk/NetAlertX
Some checks are pending
docker / docker_dev (push) Waiting to run
2025-01-08 08:12:54 +11:00
jokob-sk
190ffd3237 missing start.sh in final image v0.2 2025-01-08 08:12:50 +11:00
jokob-sk
c8280184dc Merge pull request #940 from kpolszewski/mtscan-names-from-comments
Restore manual naming of static leases using comments for MKSCAN + cleanups
2025-01-08 07:01:52 +11:00
Kamil Olszewski
076d8bbcc2 Restore manual naming of static leases using comments for MKSCAN + cleanups 2025-01-07 18:23:53 +01:00
jokob-sk
b21d57c524 Merge branch 'main' of https://github.com/jokob-sk/NetAlertX
Some checks are pending
docker / docker_dev (push) Waiting to run
2025-01-07 23:17:27 +11:00
jokob-sk
064e0cb0ff missing pre-setup.sh in final image v0.1 2025-01-07 23:17:00 +11:00
github-actions[bot]
ad9d61aa16 [🤖Automation] Update README with sponsors information 2025-01-07 11:53:54 +00:00
jokob-sk
64ff9710d1 Merge branch 'main' of https://github.com/jokob-sk/NetAlertX 2025-01-07 21:03:24 +11:00
jokob-sk
0cc87e3cfc cleanup, docs, devSyncHubNode #931 2025-01-07 21:03:20 +11:00
github-actions[bot]
c40af37ca1 [🤖Automation] Update README with sponsors information
Some checks are pending
docker / docker_dev (push) Waiting to run
2025-01-06 11:53:45 +00:00
jokob-sk
07a7ace5fc devSyncHubNode on dev details #931 + ICMP not mapped to CurrentScan #934
Some checks are pending
docker / docker_dev (push) Waiting to run
2025-01-06 10:50:54 +11:00
jokob-sk
4a82ea87f7 devSite to dropdown #930 2025-01-06 09:56:12 +11:00
jokob-sk
db47571424 Merge branch 'main' of https://github.com/jokob-sk/NetAlertX
Some checks are pending
docker / docker_dev (push) Waiting to run
2025-01-06 09:22:54 +11:00
jokob-sk
58eaa33f39 GraphQL and API docs #933 + create /plugins log folder #921 2025-01-06 09:22:34 +11:00
github-actions[bot]
6e4d34b93a [🤖Automation] Update README with sponsors information
Some checks are pending
docker / docker_dev (push) Waiting to run
2025-01-05 11:53:45 +00:00
jokob-sk
b2c445e39d Merge branch 'main' of https://github.com/jokob-sk/NetAlertX
Some checks are pending
docker / docker_dev (push) Waiting to run
2025-01-05 09:50:23 +11:00
jokob-sk
90ff2efbfb devSite to dropdown #930 2025-01-05 09:50:19 +11:00
github-actions[bot]
b49f1ab335 [🤖Automation] Update README with sponsors information
Some checks are pending
docker / docker_dev (push) Waiting to run
2025-01-04 11:53:43 +00:00
jokob-sk
3da5af1b7c cleanup
Some checks are pending
docker / docker_dev (push) Waiting to run
2025-01-04 14:46:59 +11:00
jokob-sk
90dd8e3198 cleanup 2025-01-04 13:40:08 +11:00
jokob-sk
c31966bdd0 Merge branch 'main' of https://github.com/jokob-sk/NetAlertX 2025-01-04 13:20:27 +11:00
jokob-sk
f2a0018982 docs + cleanup 2025-01-04 13:20:18 +11:00
github-actions[bot]
48ac108ca0 [🤖Automation] Update README with sponsors information
Some checks are pending
docker / docker_dev (push) Waiting to run
2025-01-03 11:53:49 +00:00
jokob-sk
19441a4431 graphql_schema logging too noisy #917 2025-01-03 22:34:19 +11:00
jokob-sk
5541d6c9d2 Merge branch 'main' of https://github.com/jokob-sk/NetAlertX 2025-01-03 22:28:17 +11:00
jokob-sk
93886cec8a better defaults for freebox parsing issue #927 2025-01-03 22:27:56 +11:00
Hosted Weblate
8401ecda5e Merge branch 'origin/main' into Weblate. 2025-01-03 12:21:23 +01:00
anton garcias
09aba51a33 Translated using Weblate (Catalan)
Currently translated at 100.0% (745 of 745 strings)

Translation: NetAlertX/core
Translate-URL: https://hosted.weblate.org/projects/pialert/core/ca/
2025-01-03 12:21:23 +01:00
Massimo Pissarello
06946e4fea Translated using Weblate (Italian)
Currently translated at 100.0% (745 of 745 strings)

Translation: NetAlertX/core
Translate-URL: https://hosted.weblate.org/projects/pialert/core/it/
2025-01-03 12:21:23 +01:00
Ettore Atalan
f96ca3e08b Translated using Weblate (German)
Currently translated at 91.4% (681 of 745 strings)

Translation: NetAlertX/core
Translate-URL: https://hosted.weblate.org/projects/pialert/core/de/
2025-01-03 12:21:23 +01:00
jokob-sk
4aa18691f4 Merge branch 'main' of https://github.com/jokob-sk/NetAlertX 2025-01-03 22:20:50 +11:00
jokob-sk
bd198587cd docs + freebox parsing issue #927 2025-01-03 22:20:45 +11:00
Anonymous
e8a4cb1d51 Translated using Weblate (Ukrainian)
Some checks are pending
docker / docker_dev (push) Waiting to run
Currently translated at 99.4% (741 of 745 strings)

Translation: NetAlertX/core
Translate-URL: https://hosted.weblate.org/projects/pialert/core/uk/
2025-01-02 18:08:41 +01:00
Anonymous
75e25867e1 Translated using Weblate (Catalan)
Currently translated at 98.6% (735 of 745 strings)

Translation: NetAlertX/core
Translate-URL: https://hosted.weblate.org/projects/pialert/core/ca/
2025-01-02 18:08:41 +01:00
Anonymous
7dadb735c2 Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 89.7% (669 of 745 strings)

Translation: NetAlertX/core
Translate-URL: https://hosted.weblate.org/projects/pialert/core/zh_Hans/
2025-01-02 18:08:41 +01:00
Anonymous
b444f98e2d Translated using Weblate (Polish)
Currently translated at 90.3% (673 of 745 strings)

Translation: NetAlertX/core
Translate-URL: https://hosted.weblate.org/projects/pialert/core/pl/
2025-01-02 18:08:40 +01:00
Anonymous
8ca2bff456 Translated using Weblate (Russian)
Currently translated at 99.4% (741 of 745 strings)

Translation: NetAlertX/core
Translate-URL: https://hosted.weblate.org/projects/pialert/core/ru/
2025-01-02 18:08:40 +01:00
Anonymous
308285f808 Translated using Weblate (Norwegian Bokmål)
Currently translated at 89.3% (666 of 745 strings)

Translation: NetAlertX/core
Translate-URL: https://hosted.weblate.org/projects/pialert/core/nb_NO/
2025-01-02 18:08:40 +01:00
Anonymous
1a03b2ccc3 Translated using Weblate (French)
Currently translated at 99.4% (741 of 745 strings)

Translation: NetAlertX/core
Translate-URL: https://hosted.weblate.org/projects/pialert/core/fr/
2025-01-02 18:08:40 +01:00
Anonymous
f6a6c3684c Translated using Weblate (Spanish)
Currently translated at 98.6% (735 of 745 strings)

Translation: NetAlertX/core
Translate-URL: https://hosted.weblate.org/projects/pialert/core/es/
2025-01-02 18:08:40 +01:00
Anonymous
f2bf379597 Translated using Weblate (German)
Currently translated at 90.2% (672 of 745 strings)

Translation: NetAlertX/core
Translate-URL: https://hosted.weblate.org/projects/pialert/core/de/
2025-01-02 18:08:40 +01:00
github-actions[bot]
802002a5f0 [🤖Automation] Update README with sponsors information
Some checks are pending
docker / docker_dev (push) Waiting to run
2025-01-02 11:53:48 +00:00
jokob-sk
e6d2a1c138 Merge branch 'main' of https://github.com/jokob-sk/NetAlertX
Some checks are pending
docker / docker_dev (push) Waiting to run
2025-01-02 10:16:06 +11:00
jokob-sk
729c24029f docs + fixes to CustomProps 2025-01-02 10:15:58 +11:00
github-actions[bot]
3260a67bf4 [🤖Automation] Update README with sponsors information
Some checks are pending
docker / docker_dev (push) Waiting to run
2025-01-01 11:53:36 +00:00
github-actions[bot]
5621f13c6e [🤖Automation] Update README with sponsors information
Some checks are pending
docker / docker_dev (push) Waiting to run
2024-12-31 11:53:38 +00:00
Hosted Weblate
aae08594bb Merge branch 'origin/main' into Weblate.
Some checks are pending
docker / docker_dev (push) Waiting to run
2024-12-31 05:25:17 +01:00
Massimo Pissarello
ba83884b9f Translated using Weblate (Italian)
Currently translated at 100.0% (745 of 745 strings)

Translation: NetAlertX/core
Translate-URL: https://hosted.weblate.org/projects/pialert/core/it/
2024-12-31 05:25:16 +01:00
jokob-sk
2799d3598b docs 2024-12-31 15:24:51 +11:00
jokob-sk
1ee746a625 Merge branch 'main' of https://github.com/jokob-sk/NetAlertX
Some checks are pending
docker / docker_dev (push) Waiting to run
2024-12-31 10:14:21 +11:00
jokob-sk
e52601e062 ENCRYPTION_KEY, docs 2024-12-31 10:14:01 +11:00
Safeguard
2dad079979 Translated using Weblate (Russian)
Currently translated at 100.0% (743 of 743 strings)

Translation: NetAlertX/core
Translate-URL: https://hosted.weblate.org/projects/pialert/core/ru/
2024-12-30 20:49:35 +01:00
github-actions[bot]
47bdb60c85 [🤖Automation] Update README with sponsors information
Some checks are pending
docker / docker_dev (push) Waiting to run
2024-12-30 11:53:52 +00:00
github-actions[bot]
6b9df66b02 [🤖Automation] Update README with sponsors information
Some checks are pending
docker / docker_dev (push) Waiting to run
2024-12-29 11:53:50 +00:00
github-actions[bot]
b188afab44 [🤖Automation] Update README with sponsors information
Some checks failed
docker / docker_dev (push) Has been cancelled
2024-12-28 11:53:33 +00:00
Максим Горпиніч
5fe6ac9816 Translated using Weblate (Ukrainian)
Some checks are pending
docker / docker_dev (push) Waiting to run
Currently translated at 100.0% (743 of 743 strings)

Translation: NetAlertX/core
Translate-URL: https://hosted.weblate.org/projects/pialert/core/uk/
2024-12-28 09:51:45 +01:00
Sylvain Pichon
d354acbcb2 Translated using Weblate (French)
Currently translated at 100.0% (743 of 743 strings)

Translation: NetAlertX/core
Translate-URL: https://hosted.weblate.org/projects/pialert/core/fr/
2024-12-28 09:51:44 +01:00
Hosted Weblate
846e15d518 Merge branch 'origin/main' into Weblate.
Some checks are pending
docker / docker_dev (push) Waiting to run
2024-12-27 22:20:28 +00:00
Максим Горпиніч
067467da53 Translated using Weblate (Ukrainian)
Currently translated at 100.0% (742 of 742 strings)

Translation: NetAlertX/core
Translate-URL: https://hosted.weblate.org/projects/pialert/core/uk/
2024-12-27 22:20:27 +00:00
jokob-sk
8ac4112ab9 Archived devices were shown #920 2024-12-28 09:19:55 +11:00
jokob-sk
ae32b0dd42 Merge branch 'main' of https://github.com/jokob-sk/NetAlertX 2024-12-28 09:19:34 +11:00
jokob-sk
4fef6e156b Archived devices were shown #920 2024-12-28 09:19:25 +11:00
github-actions[bot]
161a74dee0 [🤖Automation] Update README with sponsors information
Some checks are pending
docker / docker_dev (push) Waiting to run
2024-12-27 11:53:49 +00:00
jokob-sk
fa570b9bc9 🔌LUCIRPC - OpenWRT import - import fix
Some checks are pending
docker / docker_dev (push) Waiting to run
2024-12-27 14:18:52 +11:00
jokob-sk
9cfbc7d140 Merge branch 'main' of https://github.com/jokob-sk/NetAlertX 2024-12-27 14:08:08 +11:00
jokob-sk
dda440eb53 🔌LUCIRPC - OpenWRT import 2024-12-27 14:05:49 +11:00
Hosted Weblate
efe161494e Merge branch 'origin/main' into Weblate. 2024-12-27 01:43:02 +00:00
gallegonovato
35f1624804 Translated using Weblate (Spanish)
Currently translated at 100.0% (737 of 737 strings)

Translation: NetAlertX/core
Translate-URL: https://hosted.weblate.org/projects/pialert/core/es/
2024-12-27 01:43:00 +00:00
jokob-sk
74ec75f105 Merge branch 'main' of https://github.com/jokob-sk/NetAlertX
Some checks are pending
docker / docker_dev (push) Waiting to run
2024-12-27 12:42:26 +11:00
jokob-sk
0f474fb884 Custom Device Properties v0.1 #876 2024-12-27 12:42:15 +11:00
Sylvain Pichon
f54ffcbbc3 Translated using Weblate (French)
Some checks are pending
docker / docker_dev (push) Waiting to run
Currently translated at 100.0% (737 of 737 strings)

Translation: NetAlertX/core
Translate-URL: https://hosted.weblate.org/projects/pialert/core/fr/
2024-12-26 16:00:37 +01:00
github-actions[bot]
21fd9866fe [🤖Automation] Update README with sponsors information 2024-12-26 11:53:51 +00:00
github-actions[bot]
3732416616 [🤖Automation] Update README with sponsors information
Some checks are pending
docker / docker_dev (push) Waiting to run
2024-12-25 11:53:41 +00:00
github-actions[bot]
29396ad6bd [🤖Automation] Update README with sponsors information
Some checks are pending
docker / docker_dev (push) Waiting to run
2024-12-24 11:53:56 +00:00
jokob-sk
37ae75ed9a Merge branch 'main' of https://github.com/jokob-sk/NetAlertX
Some checks are pending
docker / docker_dev (push) Waiting to run
2024-12-24 11:06:46 +11:00
jokob-sk
7cdcf95300 Docs and simngle quote removal when editing device #918 2024-12-24 11:06:43 +11:00
github-actions[bot]
e26f7d42e9 [🤖Automation] Update README with sponsors information
Some checks are pending
docker / docker_dev (push) Waiting to run
2024-12-23 11:53:46 +00:00
jokob-sk
8f8264c6fa WOL plugin #887
Some checks are pending
docker / docker_dev (push) Waiting to run
2024-12-23 13:10:27 +11:00
jokob-sk
a4c34140bf Merge branch 'main' of https://github.com/jokob-sk/NetAlertX 2024-12-23 11:28:57 +11:00
jokob-sk
466aa62a02 DB cleanup script + docs 2024-12-23 11:28:46 +11:00
github-actions[bot]
febc26b187 [🤖Automation] Update README with sponsors information
Some checks are pending
docker / docker_dev (push) Waiting to run
2024-12-22 11:53:40 +00:00
jokob-sk
7248e73e03 Respecting LOG_LEVEL in plugins
Some checks are pending
docker / docker_dev (push) Waiting to run
2024-12-22 13:18:08 +11:00
jokob-sk
96e62468fc lang files 2024-12-22 11:25:23 +11:00
jokob-sk
c8c95b22a1 Merge branch 'main' of https://github.com/jokob-sk/NetAlertX 2024-12-22 11:24:48 +11:00
jokob-sk
b63b00f30c Revert DataTable upgrade, new default docker-compose, performance tip 2024-12-22 11:24:44 +11:00
github-actions[bot]
6e07032c15 [🤖Automation] Update README with sponsors information
Some checks are pending
docker / docker_dev (push) Waiting to run
2024-12-21 11:53:47 +00:00
Максим Горпиніч
3663e617e0 Translated using Weblate (Ukrainian)
Currently translated at 100.0% (737 of 737 strings)

Translation: NetAlertX/core
Translate-URL: https://hosted.weblate.org/projects/pialert/core/uk/
2024-12-21 07:00:35 +00:00
anton garcias
5a9ff3f07f Translated using Weblate (Catalan)
Currently translated at 100.0% (737 of 737 strings)

Translation: NetAlertX/core
Translate-URL: https://hosted.weblate.org/projects/pialert/core/ca/
2024-12-21 07:00:34 +00:00
github-actions[bot]
5af2b283c9 [🤖Automation] Update README with sponsors information
Some checks are pending
docker / docker_dev (push) Waiting to run
2024-12-20 11:53:48 +00:00
Massimo Pissarello
b88cfeda4c Translated using Weblate (Italian)
Some checks are pending
docker / docker_dev (push) Waiting to run
Currently translated at 100.0% (737 of 737 strings)

Translation: NetAlertX/core
Translate-URL: https://hosted.weblate.org/projects/pialert/core/it/
2024-12-20 05:58:22 +01:00
Anonymous
ec9466c562 Translated using Weblate (Ukrainian)
Currently translated at 99.4% (733 of 737 strings)

Translation: NetAlertX/core
Translate-URL: https://hosted.weblate.org/projects/pialert/core/uk/
2024-12-20 04:15:52 +01:00
Anonymous
e3039b5f7c Translated using Weblate (Catalan)
Currently translated at 99.1% (731 of 737 strings)

Translation: NetAlertX/core
Translate-URL: https://hosted.weblate.org/projects/pialert/core/ca/
2024-12-20 04:15:52 +01:00
Massimo Pissarello
46bb490384 Translated using Weblate (Italian)
Currently translated at 99.7% (735 of 737 strings)

Translation: NetAlertX/core
Translate-URL: https://hosted.weblate.org/projects/pialert/core/it/
2024-12-20 04:15:52 +01:00
Anonymous
a174eb58d7 Translated using Weblate (Italian)
Currently translated at 99.7% (735 of 737 strings)

Translation: NetAlertX/core
Translate-URL: https://hosted.weblate.org/projects/pialert/core/it/
2024-12-20 04:15:52 +01:00
Anonymous
43df53f1c1 Translated using Weblate (Russian)
Currently translated at 99.3% (732 of 737 strings)

Translation: NetAlertX/core
Translate-URL: https://hosted.weblate.org/projects/pialert/core/ru/
2024-12-20 04:15:51 +01:00
Anonymous
4ec803f4c4 Translated using Weblate (French)
Currently translated at 99.4% (733 of 737 strings)

Translation: NetAlertX/core
Translate-URL: https://hosted.weblate.org/projects/pialert/core/fr/
2024-12-20 04:15:51 +01:00
Anonymous
cfbbe83b7a Translated using Weblate (Spanish)
Currently translated at 99.4% (733 of 737 strings)

Translation: NetAlertX/core
Translate-URL: https://hosted.weblate.org/projects/pialert/core/es/
2024-12-20 04:15:51 +01:00
jokob-sk
7913d42699 Bootstrap + DataTables cleanup 2024-12-20 14:11:16 +11:00
jokob-sk
40e4502f29 Merge branch 'main' of https://github.com/jokob-sk/NetAlertX 2024-12-20 11:15:07 +11:00
jokob-sk
b80718d409 📚Docs + DISCOVER_PLUGINS 2024-12-20 11:14:44 +11:00
github-actions[bot]
147d8833f7 [🤖Automation] Update README with sponsors information
Some checks are pending
docker / docker_dev (push) Waiting to run
2024-12-19 11:53:49 +00:00
jokob-sk
8877e7a528 Merge branch 'main' of https://github.com/jokob-sk/NetAlertX 2024-12-19 20:15:36 +11:00
jokob-sk
f38d72a690 Fix debounce of api points to address Disk IO #914 + NMAPDEV_FAKE_MAC 2024-12-19 20:15:15 +11:00
Hosted Weblate
be673315d4 Merge branch 'origin/main' into Weblate. 2024-12-19 08:34:50 +01:00
Максим Горпиніч
07f3eabea3 Translated using Weblate (Ukrainian)
Currently translated at 100.0% (734 of 734 strings)

Translation: NetAlertX/core
Translate-URL: https://hosted.weblate.org/projects/pialert/core/uk/
2024-12-19 08:34:49 +01:00
Massimo Pissarello
d51373dcab Translated using Weblate (Italian)
Currently translated at 100.0% (734 of 734 strings)

Translation: NetAlertX/core
Translate-URL: https://hosted.weblate.org/projects/pialert/core/it/
2024-12-19 08:34:46 +01:00
jokob-sk
773b49a1b4 Merge branch 'main' of https://github.com/jokob-sk/NetAlertX 2024-12-19 18:33:02 +11:00
jokob-sk
f4d215c843 Fix debounce of api points to address Disk IO #914 2024-12-19 18:32:57 +11:00
jokob-sk
30d9d4b8fc Override init fix of schedules 2024-12-19 12:12:20 +11:00
Максим Горпиніч
d82d76a1c7 Translated using Weblate (Ukrainian)
Some checks are pending
docker / docker_dev (push) Waiting to run
Currently translated at 45.5% (334 of 734 strings)

Translation: NetAlertX/core
Translate-URL: https://hosted.weblate.org/projects/pialert/core/uk/
2024-12-19 01:05:58 +01:00
Hosted Weblate
743f97e194 Merge branch 'origin/main' into Weblate.
Some checks are pending
docker / docker_dev (push) Waiting to run
2024-12-18 21:41:19 +01:00
Sylvain Pichon
31e4743caa Translated using Weblate (French)
Currently translated at 100.0% (734 of 734 strings)

Translation: NetAlertX/core
Translate-URL: https://hosted.weblate.org/projects/pialert/core/fr/
2024-12-18 21:41:17 +01:00
jokob-sk
0a7356f9be Merge branch 'main' of https://github.com/jokob-sk/NetAlertX 2024-12-19 07:40:45 +11:00
jokob-sk
498ca227fd Override init fix of schedules + uk_ua 2024-12-19 07:40:33 +11:00
github-actions[bot]
55fc068dda [🤖Automation] Update README with sponsors information
Some checks are pending
docker / docker_dev (push) Waiting to run
2024-12-18 11:53:50 +00:00
gallegonovato
0d030ef355 Translated using Weblate (Spanish)
Some checks are pending
docker / docker_dev (push) Waiting to run
Currently translated at 100.0% (734 of 734 strings)

Translation: NetAlertX/core
Translate-URL: https://hosted.weblate.org/projects/pialert/core/es/
2024-12-17 14:18:46 +01:00
github-actions[bot]
0e381c6592 [🤖Automation] Update README with sponsors information
Some checks are pending
docker / docker_dev (push) Waiting to run
2024-12-17 11:54:01 +00:00
jokob-sk
4976324c50 Override init fix of schedules 2024-12-17 22:46:31 +11:00
jokob-sk
48f86e91f7 Override init fix of schedules 2024-12-17 22:25:45 +11:00
jokob-sk
3cc4caa34c Override init fix of schedules 2024-12-17 22:00:41 +11:00
jokob-sk
191afdf857 Override init fix of schedules + #913 2024-12-17 21:16:34 +11:00
jokob-sk
721a275fd8 Merge branch 'main' of https://github.com/jokob-sk/NetAlertX
Some checks are pending
docker / docker_dev (push) Waiting to run
2024-12-17 07:04:05 +11:00
jokob-sk
caf5a5347c chore: Try to reduce disk IO 2024-12-17 07:04:00 +11:00
github-actions[bot]
688944bda1 [🤖Automation] Update README with sponsors information
Some checks are pending
docker / docker_dev (push) Waiting to run
2024-12-16 11:53:44 +00:00
jokob-sk
c132374421 Merge branch 'main' of https://github.com/jokob-sk/NetAlertX 2024-12-16 20:21:59 +11:00
jokob-sk
953534724c chore: Try to reduce disk IO 2024-12-16 20:21:52 +11:00
github-actions[bot]
6115c12a6c [🤖Automation] Update README with sponsors information
Some checks are pending
docker / docker_dev (push) Waiting to run
2024-12-15 11:53:52 +00:00
github-actions[bot]
07d3a3fede [🤖Automation] Update README with sponsors information
Some checks are pending
docker / docker_dev (push) Waiting to run
2024-12-14 11:53:40 +00:00
jokob-sk
2e47af7b63 sync plugin fix - remove devStatus 2024-12-14 09:15:06 +11:00
jokob-sk
e7843e6e73 Merge branch 'main' of https://github.com/jokob-sk/NetAlertX
Some checks are pending
docker / docker_dev (push) Waiting to run
2024-12-14 08:45:44 +11:00
jokob-sk
f247e4aabc disk io:logging improvements 2024-12-14 08:45:39 +11:00
github-actions[bot]
4f02232ee3 [🤖Automation] Update README with sponsors information
Some checks are pending
docker / docker_dev (push) Waiting to run
2024-12-13 11:53:36 +00:00
Thales
c1c8352274 Translated using Weblate (Portuguese (Brazil))
Some checks are pending
docker / docker_dev (push) Waiting to run
Currently translated at 33.6% (247 of 733 strings)

Translation: NetAlertX/core
Translate-URL: https://hosted.weblate.org/projects/pialert/core/pt_BR/
2024-12-13 02:00:28 +01:00
Safeguard
c3157f6ef4 Translated using Weblate (Russian)
Currently translated at 100.0% (733 of 733 strings)

Translation: NetAlertX/core
Translate-URL: https://hosted.weblate.org/projects/pialert/core/ru/
2024-12-13 02:00:27 +01:00
jokob-sk
0a200afebc /log/plugins 2024-12-13 10:43:04 +11:00
jokob-sk
c4c1f9e345 Merge branch 'main' of https://github.com/jokob-sk/NetAlertX 2024-12-13 10:18:56 +11:00
jokob-sk
7b15efa913 last_result.log -> /log/plugins/last_result.PLUGPREF.log 2024-12-13 10:18:35 +11:00
github-actions[bot]
a0db36cb2d [🤖Automation] Update README with sponsors information
Some checks are pending
docker / docker_dev (push) Waiting to run
2024-12-12 11:53:47 +00:00
jokob-sk
796b4596ce Merge pull request #912 from ytorres/patch-3
Some checks are pending
docker / docker_dev (push) Waiting to run
Fix typo
2024-12-12 07:18:37 +11:00
Yannick Torrès
2625f2f96e Fix typo 2024-12-11 19:07:42 +01:00
github-actions[bot]
90f4abc037 [🤖Automation] Update README with sponsors information
Some checks are pending
docker / docker_dev (push) Waiting to run
2024-12-11 11:53:47 +00:00
github-actions[bot]
8345bda25d [🤖Automation] Update README with sponsors information
Some checks are pending
docker / docker_dev (push) Waiting to run
2024-12-10 11:53:46 +00:00
jokob-sk
7656ef99e8 Merge pull request #910 from ytorres/patch-1
Some checks are pending
docker / docker_dev (push) Waiting to run
Update install_dependencies.debian.sh
2024-12-10 07:35:00 +11:00
jokob-sk
9c2841167c Merge pull request #911 from ytorres/patch-2
Update start.debian.sh
2024-12-10 07:34:03 +11:00
Yannick Torrès
03056f7823 Update start.debian.sh
1/ Fix {} arround user_notifications.json file => if there is just one file, this create a file named "{user_notifications.json} ;)

2/ Fix group for the above file
2024-12-09 19:06:18 +01:00
Yannick Torrès
2275bf3114 Update install_dependencies.debian.sh
Work on a fresh installation of Debian Bookworm
2024-12-09 19:00:23 +01:00
github-actions[bot]
ac0c13dced [🤖Automation] Update README with sponsors information
Some checks are pending
docker / docker_dev (push) Waiting to run
2024-12-09 11:53:39 +00:00
github-actions[bot]
74df660145 [🤖Automation] Update README with sponsors information
Some checks are pending
docker / docker_dev (push) Waiting to run
2024-12-08 11:53:53 +00:00
jokob-sk
a7e35c4697 Move of LOG folder from /app/front/log to app/log 2024-12-08 21:06:44 +11:00
jokob-sk
cd9c4a2176 Merge branch 'main' of https://github.com/jokob-sk/NetAlertX
Some checks are pending
docker / docker_dev (push) Waiting to run
2024-12-08 19:17:17 +11:00
jokob-sk
f7160f0843 Move of API folder from /app/front/api to app/api 2024-12-08 19:17:10 +11:00
github-actions[bot]
2ad80f5ba5 [🤖Automation] Update README with sponsors information
Some checks are pending
docker / docker_dev (push) Waiting to run
2024-12-07 11:53:34 +00:00
jokob-sk
d7858c6042 GraphQL Settings 2024-12-07 19:25:07 +11:00
jokob-sk
79c4574e21 Merge branch 'main' of https://github.com/jokob-sk/NetAlertX
Some checks are pending
docker / docker_dev (push) Waiting to run
2024-12-07 10:38:38 +11:00
jokob-sk
daff0ee7f2 📚Docs 2024-12-07 10:38:34 +11:00
github-actions[bot]
a22df52725 [🤖Automation] Update README with sponsors information
Some checks are pending
docker / docker_dev (push) Waiting to run
2024-12-06 11:53:32 +00:00
jokob-sk
89b4b9b98e Merge branch 'main' of https://github.com/jokob-sk/NetAlertX
Some checks are pending
docker / docker_dev (push) Waiting to run
2024-12-06 07:39:00 +11:00
jokob-sk
80e8cb3f74 Icon cleanup 2024-12-06 07:38:51 +11:00
233 changed files with 50796 additions and 3162 deletions

View File

@@ -6,14 +6,10 @@
docker-compose.yml docker-compose.yml
Dockerfile Dockerfile
Dockerfile.debian Dockerfile.debian
dockerfiles/LICENSE
dockerfiles/README.md
dockerfiles/README_ES.md
docs docs
LICENSE.txt LICENSE.txt
README.md README.md
CONTRIBUTING CONTRIBUTING
FUNDING.yml FUNDING.yml
config/.gitignore config/.gitignore
db/.gitignore db/.gitignore

View File

@@ -60,10 +60,11 @@ body:
required: false required: false
- type: dropdown - type: dropdown
attributes: attributes:
label: What branch are you running? label: What installation are you running?
options: options:
- Production - Production (netalertx)
- Dev - Dev (netalertx-dev)
- Home Assistant (addon)
validations: validations:
required: true required: true
- type: textarea - type: textarea
@@ -74,7 +75,7 @@ body:
***Generally speaking, all bug reports should have logs provided.*** ***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. 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! 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 trouble getting to the log files. You can use `tail -100 /app/log/app.log` in the container if you have trouble getting to the log files.
validations: validations:
required: false required: false
- type: checkboxes - type: checkboxes

32
.github/workflows/social_post_on_release.yml vendored Executable file
View File

@@ -0,0 +1,32 @@
name: 📧 Twitter and Discord Posts
on:
release:
types: [published]
jobs:
post-release:
runs-on: ubuntu-latest
steps:
- name: Wait for 15 minutes
run: sleep 900 # 15 minutes delay
# Post to Twitter
- name: Post to Twitter
uses: gr2m/twitter-together@v2
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TWITTER_API_KEY: ${{ secrets.TWITTER_API_KEY }}
TWITTER_API_SECRET: ${{ secrets.TWITTER_API_SECRET }}
TWITTER_ACCESS_TOKEN: ${{ secrets.TWITTER_ACCESS_TOKEN }}
TWITTER_ACCESS_TOKEN_SECRET: ${{ secrets.TWITTER_ACCESS_TOKEN_SECRET }}
with:
tweet: |
🎉 New release: **${{ github.event.release.name }}** is live! 🚀
Check it out here: ${{ github.event.release.html_url }}
# Post to Discord
- name: Post to Discord
run: |
curl -X POST -H "Content-Type: application/json" \
-d '{"content": "🎉 New release: **${{ github.event.release.name }}** is live! 🚀\nCheck it out here: ${{ github.event.release.html_url }}"}' \
${{ secrets.DISCORD_WEBHOOK_URL }}

View File

@@ -1,29 +0,0 @@
name: 🤖Automation - Update Sponsors Table
on:
schedule:
- cron: '50 11 * * *' # Set your preferred schedule (UTC)
jobs:
update-table:
runs-on: ubuntu-latest
steps:
- name: Checkout Repository
uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: 3.8
- name: Install Dependencies
run: |
python -m pip install --upgrade pip
pip install -r update_sponsors_requirements.txt # If you have any Python dependencies
- name: Update Sponsors Table
run: |
python update_sponsors.py
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}

11
.gitignore vendored
View File

@@ -7,8 +7,11 @@ db/*
db/pialert.db db/pialert.db
db/app.db db/app.db
front/log/* front/log/*
/log/*
front/api/* front/api/*
/api/*
**/plugins/**/*.log **/plugins/**/*.log
**/plugins/cloud_services/*
**/%40eaDir/ **/%40eaDir/
**/@eaDir/ **/@eaDir/
@@ -22,10 +25,10 @@ __pycache__/
**/pialert.db_bak **/pialert.db_bak
.*.swp .*.swp
front/img/account/* front/img/cloud_services/*
**/account.php **/cloud_services.php
**/account.js **/cloud_services.js
front/css/account.css front/css/cloud_services.css
docker-compose.yml.ffsb42 docker-compose.yml.ffsb42
.env.omada.ffsb42 .env.omada.ffsb42

View File

@@ -14,7 +14,7 @@ ENV PATH="/opt/venv/bin:$PATH"
COPY . ${INSTALL_DIR}/ COPY . ${INSTALL_DIR}/
RUN pip install graphene flask netifaces tplink-omada-client pycryptodome requests paho-mqtt scapy cron-converter pytz json2table dhcp-leases pyunifi speedtest-cli chardet python-nmap dnspython librouteros git+https://github.com/foreign-sub/aiofreepybox.git \ RUN pip install openwrt-luci-rpc asusrouter asyncio aiohttp graphene flask netifaces tplink-omada-client wakeonlan pycryptodome requests paho-mqtt scapy cron-converter pytz json2table dhcp-leases pyunifi speedtest-cli chardet python-nmap dnspython librouteros git+https://github.com/foreign-sub/aiofreepybox.git \
&& bash -c "find ${INSTALL_DIR} -type d -exec chmod 750 {} \;" \ && 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 -exec chmod 640 {} \;" \
&& bash -c "find ${INSTALL_DIR} -type f \( -name '*.sh' -o -name '*.py' -o -name 'speedtest-cli' \) -exec chmod 750 {} \;" && bash -c "find ${INSTALL_DIR} -type f \( -name '*.sh' -o -name '*.py' -o -name 'speedtest-cli' \) -exec chmod 750 {} \;"
@@ -69,9 +69,9 @@ COPY --from=builder --chown=nginx:www-data ${INSTALL_DIR}/ ${INSTALL_DIR}/
COPY install/crontab /etc/crontabs/root COPY install/crontab /etc/crontabs/root
# Start all required services # Start all required services
RUN ${INSTALL_DIR}/dockerfiles/pre-setup.sh RUN ${INSTALL_DIR}/dockerfiles/start.sh
HEALTHCHECK --interval=30s --timeout=5s --start-period=15s --retries=2 \ HEALTHCHECK --interval=30s --timeout=5s --start-period=15s --retries=2 \
CMD curl -sf -o /dev/null ${LISTEN_ADDR}:${PORT}/api/app_state.json CMD curl -sf -o /dev/null ${LISTEN_ADDR}:${PORT}/php/server/query_json.php?file=app_state.json
ENTRYPOINT ["/init"] ENTRYPOINT ["/init"]

View File

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

122
README.md
View File

@@ -1,104 +1,80 @@
[![GitHub Committed](https://img.shields.io/github/last-commit/jokob-sk/NetAlertX?color=40ba12&label=Pushed&logo=GitHub&logoColor=fff&style=for-the-badge)](https://github.com/jokob-sk/NetAlertX)
[![Docker Size](https://img.shields.io/docker/image-size/jokobsk/netalertx?label=Size&logo=Docker&color=0aa8d2&logoColor=fff&style=for-the-badge)](https://hub.docker.com/r/jokobsk/netalertx) [![Docker Size](https://img.shields.io/docker/image-size/jokobsk/netalertx?label=Size&logo=Docker&color=0aa8d2&logoColor=fff&style=for-the-badge)](https://hub.docker.com/r/jokobsk/netalertx)
[![Docker Pulls](https://img.shields.io/docker/pulls/jokobsk/netalertx?label=Pulls&logo=docker&color=0aa8d2&logoColor=fff&style=for-the-badge)](https://hub.docker.com/r/jokobsk/netalertx) [![Docker Pulls](https://img.shields.io/docker/pulls/jokobsk/netalertx?label=Pulls&logo=docker&color=0aa8d2&logoColor=fff&style=for-the-badge)](https://hub.docker.com/r/jokobsk/netalertx)
[![GitHub Release](https://img.shields.io/github/v/release/jokob-sk/NetAlertX?color=0aa8d2&logoColor=fff&logo=GitHub&style=for-the-badge)](https://github.com/jokob-sk/NetAlertX/releases) [![GitHub Release](https://img.shields.io/github/v/release/jokob-sk/NetAlertX?color=0aa8d2&logoColor=fff&logo=GitHub&style=for-the-badge)](https://github.com/jokob-sk/NetAlertX/releases)
[![Discord](https://img.shields.io/discord/1274490466481602755?color=0aa8d2&logoColor=fff&logo=Discord&style=for-the-badge)](https://discord.gg/NczTUTWyRr) [![Discord](https://img.shields.io/discord/1274490466481602755?color=0aa8d2&logoColor=fff&logo=Discord&style=for-the-badge)](https://discord.gg/NczTUTWyRr)
[![Home Assistant](https://img.shields.io/badge/Repo-blue?logo=home-assistant&style=for-the-badge&color=0aa8d2&logoColor=fff&label=Add)](https://my.home-assistant.io/redirect/supervisor_add_addon_repository/?repository_url=https%3A%2F%2Fgithub.com%2Falexbelgium%2Fhassio-addons)
# 🖧🔍 Network scanner & notification framework # NetAlertX - Network, presence scanner and alert 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). Get visibility of what's going on on your WIFI/LAN network and enable presence detection of important devices. 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).
| 🐳 [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) | | [📑 Docker guide](https://github.com/jokob-sk/NetAlertX/blob/main/dockerfiles/README.md) | [🚀 Releases](https://github.com/jokob-sk/NetAlertX/releases) | [📚 Docs](https://github.com/jokob-sk/NetAlertX/tree/main/docs) | [🔌 Plugins](https://github.com/jokob-sk/NetAlertX/blob/main/front/plugins/README.md) | [🤖 Ask AI](https://gurubase.io/g/netalertx)
|----------------------|----------------------| ----------------------| ----------------------| |----------------------| ----------------------| ----------------------| ----------------------| ----------------------|
![showcase][showcase]
| ![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> <details>
<summary>📷 Click for more screenshots</summary> <summary>📷 Click for more screenshots</summary>
| ![presence][presence] | ![maintenance][maintenance] | ![settings][settings] | | ![Main screen][main] | ![device_details 1][device_details] | ![Screen network][network] |
|----------------------|----------------------|----------------------| |----------------------|----------------------|----------------------|
| ![presence][presence] | ![maintenance][maintenance] | ![settings][settings] |
| ![sync_hub][sync_hub] | ![report1][report1] | ![device_nmap][device_nmap] | | ![sync_hub][sync_hub] | ![report1][report1] | ![device_nmap][device_nmap] |
</details> Head to [https://netalertx.com/](https://netalertx.com/) for even more gifs and screenshots 📷.
<details>
<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> 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>.
Looking forward to your contributions if you decide to share your work with the community ❤.
</details> </details>
## Scan Methods, Notifications, Integration, Extension system ## 📦 Features
| Features | Details | ### Scanners
|-------------|-------------|
| 🔍 | 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 a full lits of avaliable plugins.
|📧 | 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. | ### Notification gateways
| | Build your own scanners with the [Plugin system](https://github.com/jokob-sk/NetAlertX/tree/main/front/plugins#readme) |
Send notifications to more than 80+ services, including Telegram via [Apprise](https://hub.docker.com/r/caronc/apprise), or use native [Pushsafer](https://www.pushsafer.com/), [Pushover](https://www.pushover.net/), or [NTFY](https://ntfy.sh/) publishers.
### Integrations and Plugins
Feed your data and device changes into [Home Assistant](https://github.com/jokob-sk/NetAlertX/blob/main/docs/HOME_ASSISTANT.md), read [API endpoints](https://github.com/jokob-sk/NetAlertX/blob/main/docs/API.md), or use [Webhooks](https://github.com/jokob-sk/NetAlertX/blob/main/docs/WEBHOOK_N8N.md) to setup custom automation flows. You can also
build your own scanners with the [Plugin system](https://github.com/jokob-sk/NetAlertX/tree/main/front/plugins#readme) in as little as [15 minutes](https://www.youtube.com/watch?v=cdbxlwiWhv8).
## Installation & Documentation ## 📚 Documentation
<!--- --------------------------------------------------------------------- ---> <!--- --------------------------------------------------------------------- --->
Supported browsers: Chrome, Firefox Supported browsers: Chrome, Firefox
| Docs | Link | - [[Installation] Docker](https://github.com/jokob-sk/NetAlertX/blob/main/dockerfiles/README.md)
|-------------|-------------| - [[Installation] Home Assistant](https://github.com/alexbelgium/hassio-addons/tree/master/netalertx)
| 📥🐳 | [Docker instructions](https://github.com/jokob-sk/NetAlertX/blob/main/dockerfiles/README.md) - [[Installation] Bare metal](https://github.com/jokob-sk/NetAlertX/blob/main/docs/HW_INSTALL.md)
| 📥🗄️ | [HW install (experimental 🧪)](https://github.com/jokob-sk/NetAlertX/blob/main/docs/HW_INSTALL.md) | - [[Installation] Unraid App](https://unraid.net/community/apps)
| 📥🟧 | [Unraid App](https://unraid.net/community/apps) | - [[Setup] Usage and Configuration](https://github.com/jokob-sk/NetAlertX/blob/main/docs/README.md)
| 📚 | [All Documentation](https://github.com/jokob-sk/NetAlertX/blob/main/docs/README.md) (App Usage and Configuration) | - [[Development] API docs](https://github.com/jokob-sk/NetAlertX/blob/main/docs/API.md)
- [[Development] Custom Plugins](https://github.com/jokob-sk/NetAlertX/blob/main/docs/PLUGINS_DEV.md)
> Other Alternatives
>
> - Check out [leiweibau's on HW installed fork](https://github.com/leiweibau/Pi.Alert/) (maintained)
> - [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
## 📃 Everything else
<!--- --------------------------------------------------------------------- --->
### 📧 Get notified what's new
Get notified about a new release, what new functionality you can use and about breaking changes. Get notified about a new release, what new functionality you can use and about breaking changes.
![Follow and star][follow_star] ![Follow and star][follow_star]
### ⭐ Sponsors ### 🔀 Other Alternative Apps
[![GitHub Sponsors](https://img.shields.io/github/sponsors/jokob-sk?style=social)](https://github.com/sponsors/jokob-sk) - [PiAlert by leiweibau](https://github.com/leiweibau/Pi.Alert/) (maintained, bare-metal install)
- [WatchYourLAN](https://github.com/aceberg/WatchYourLAN) - Lightweight network IP scanner with web GUI (Open source)
Thank you to all the wonderful people who are sponsoring this project. - [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)
> preventing my burnout😅 are:
<!-- SPONSORS-LIST DO NOT MODIFY BELOW -->
| All Sponsors |
|---|
<!-- SPONSORS-LIST DO NOT MODIFY ABOVE -->
### 💙 Donations
Thank you to everyone who appreciates this tool and donates.
<details> <details>
<summary>Click for more ways to donate</summary> <summary>Click for more ways to donate</summary>
@@ -115,26 +91,20 @@ Thank you to all the wonderful people who are sponsoring this project.
</details> </details>
### 🙏Contributors ### 🏗 Contributors
This project would be nothing without the amazing work of the community, with special thanks to: This project would be nothing without the amazing work of the community, with special thanks to:
> [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... > [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), [vladaurosh](https://github.com/vladaurosh) for Alpine re-base help, [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. Check out all the [amazing contributors](https://github.com/jokob-sk/NetAlertX/graphs/contributors).
## Everything else
<!--- --------------------------------------------------------------------- --->
### 🌍 Translations ### 🌍 Translations
Proudly using [Weblate](https://hosted.weblate.org/projects/pialert/). Proudly using [Weblate](https://hosted.weblate.org/projects/pialert/). Help out and suggest languages in the [online portal of Weblate](https://hosted.weblate.org/projects/pialert/core/).
<a href="https://hosted.weblate.org/engage/pialert/"> <a href="https://hosted.weblate.org/engage/pialert/">
<img src="https://hosted.weblate.org/widget/pialert/core/multi-auto.svg" alt="Translation status" /> <img src="https://hosted.weblate.org/widget/pialert/core/multi-auto.svg" alt="Translation status" />
</a> </a>
Help out and suggest languages in the [online portal of Weblate](https://hosted.weblate.org/projects/pialert/core/).
### License ### License
> GPL 3.0 | [Read more here](LICENSE.txt) | Source of the [animated GIF (Loading Animation)](https://commons.wikimedia.org/wiki/File:Loading_Animation.gif) | Source of the [selfhosted Fonts](https://github.com/adobe-fonts/source-sans) > GPL 3.0 | [Read more here](LICENSE.txt) | Source of the [animated GIF (Loading Animation)](https://commons.wikimedia.org/wiki/File:Loading_Animation.gif) | Source of the [selfhosted Fonts](https://github.com/adobe-fonts/source-sans)
@@ -148,7 +118,7 @@ Help out and suggest languages in the [online portal of Weblate](https://hosted.
[maintenance]: ./docs/img/maintenance.png "Screen 4" [maintenance]: ./docs/img/maintenance.png "Screen 4"
[network]: ./docs/img/network.png "Screen 5" [network]: ./docs/img/network.png "Screen 5"
[settings]: ./docs/img/settings.png "Screen 6" [settings]: ./docs/img/settings.png "Screen 6"
[network_setup]: ./docs/img/network_setup.gif "Screen 6" [showcase]: ./docs/img/showcase.gif "Screen 6"
[help_faq]: ./docs/img/help_faq.png "Screen 7" [help_faq]: ./docs/img/help_faq.png "Screen 7"
[sync_hub]: ./docs/img/sync_hub.png "Screen 8" [sync_hub]: ./docs/img/sync_hub.png "Screen 8"
[notification_center]: ./docs/img/notification_center.png "Screen 8" [notification_center]: ./docs/img/notification_center.png "Screen 8"

View File

@@ -17,9 +17,10 @@
# Scan multiple interfaces (eth1 and eth0): # Scan multiple interfaces (eth1 and eth0):
# 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', '192.168.1.0/24 --interface=eth0' ]
DISCOVER_PLUGINS=True
SCAN_SUBNETS=['192.168.1.0/24 --interface=eth0'] SCAN_SUBNETS=['192.168.1.0/24 --interface=eth0']
TIMEZONE='Europe/Berlin' TIMEZONE='Europe/Berlin'
LOADED_PLUGINS = ['ARPSCAN','CSVBCKP','DBCLNP', 'INTRNT','MAINT','NEWDEV','NSLOOKUP','NTFPRCS', 'AVAHISCAN', 'SETPWD','SMTP', 'SYNC', 'VNDRPDT', 'WORKFLOWS'] LOADED_PLUGINS = ['ARPSCAN','CSVBCKP','DBCLNP', 'INTRNT','MAINT','NEWDEV','NSLOOKUP','NTFPRCS', 'AVAHISCAN', 'SETPWD','SMTP', 'SYNC', 'VNDRPDT', 'WORKFLOWS', 'UI']
DAYS_TO_KEEP_EVENTS=90 DAYS_TO_KEEP_EVENTS=90
# Used for generating links in emails. Make sure not to add a trailing slash! # Used for generating links in emails. Make sure not to add a trailing slash!

View File

@@ -1,12 +1,12 @@
#!/bin/bash #!/bin/bash
export INSTALL_DIR=/app export INSTALL_DIR=/app
LOG_FILE="${INSTALL_DIR}/front/log/execution_queue.log" LOG_FILE="${INSTALL_DIR}/log/execution_queue.log"
# Check if there are any entries with cron_restart_backend # Check if there are any entries with cron_restart_backend
if grep -q "cron_restart_backend" "$LOG_FILE"; then if grep -q "cron_restart_backend" "$LOG_FILE"; then
# Kill all python processes and restart the server # Kill all python processes (restart handled by s6 overlay)
pkill -f "python " && (python ${INSTALL_DIR}/server > /dev/null 2>&1 &) && echo 'done' pkill -f "python " && echo 'done'
# Remove all lines containing cron_restart_backend from the log file # Remove all lines containing cron_restart_backend from the log file
sed -i '/cron_restart_backend/d' "$LOG_FILE" sed -i '/cron_restart_backend/d' "$LOG_FILE"

View File

@@ -1,4 +1,3 @@
version: "3"
services: services:
netalertx: netalertx:
privileged: true privileged: true
@@ -16,7 +15,12 @@ services:
# - ${APP_DATA_LOCATION}/netalertx_dev/db:/app/db # - ${APP_DATA_LOCATION}/netalertx_dev/db:/app/db
- ${APP_DATA_LOCATION}/netalertx/db:/app/db - ${APP_DATA_LOCATION}/netalertx/db:/app/db
# (optional) useful for debugging if you have issues setting up the container # (optional) useful for debugging if you have issues setting up the container
# - ${LOGS_LOCATION}:/app/front/log - ${APP_DATA_LOCATION}/netalertx/log:/app/log
# (API: OPTION 1) use for performance
- type: tmpfs
target: /app/api
# (API: OPTION 2) use when debugging issues
# - ${DEV_LOCATION}/api:/app/api
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
# DELETE START anyone trying to use this file: comment out / delete BELOW lines, they are only for development purposes # DELETE START anyone trying to use this file: comment out / delete BELOW lines, they are only for development purposes
- ${APP_DATA_LOCATION}/netalertx/dhcp_samples/dhcp1.leases:/mnt/dhcp1.leases - ${APP_DATA_LOCATION}/netalertx/dhcp_samples/dhcp1.leases:/mnt/dhcp1.leases
@@ -34,17 +38,14 @@ services:
- ${DEV_LOCATION}/back/update_vendors.sh:/app/back/update_vendors.sh - ${DEV_LOCATION}/back/update_vendors.sh:/app/back/update_vendors.sh
- ${DEV_LOCATION}/front/lib:/app/front/lib - ${DEV_LOCATION}/front/lib:/app/front/lib
- ${DEV_LOCATION}/front/js:/app/front/js - ${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
- ${DEV_LOCATION}/install/install_dependencies.debian.sh:/app/install/install_dependencies.debian.sh
- ${DEV_LOCATION}/front/api:/app/front/api
- ${DEV_LOCATION}/front/php:/app/front/php - ${DEV_LOCATION}/front/php:/app/front/php
- ${DEV_LOCATION}/front/deviceDetails.php:/app/front/deviceDetails.php - ${DEV_LOCATION}/front/deviceDetails.php:/app/front/deviceDetails.php
- ${DEV_LOCATION}/front/deviceDetailsEdit.php:/app/front/deviceDetailsEdit.php - ${DEV_LOCATION}/front/deviceDetailsEdit.php:/app/front/deviceDetailsEdit.php
- ${DEV_LOCATION}/front/userNotifications.php:/app/front/userNotifications.php - ${DEV_LOCATION}/front/userNotifications.php:/app/front/userNotifications.php
- ${DEV_LOCATION}/front/deviceDetailsTools.php:/app/front/deviceDetailsTools.php - ${DEV_LOCATION}/front/deviceDetailsTools.php:/app/front/deviceDetailsTools.php
- ${DEV_LOCATION}/front/deviceDetailsPresence.php:/app/front/deviceDetailsPresence.php
- ${DEV_LOCATION}/front/deviceDetailsSessions.php:/app/front/deviceDetailsSessions.php
- ${DEV_LOCATION}/front/deviceDetailsEvents.php:/app/front/deviceDetailsEvents.php
- ${DEV_LOCATION}/front/devices.php:/app/front/devices.php - ${DEV_LOCATION}/front/devices.php:/app/front/devices.php
- ${DEV_LOCATION}/front/events.php:/app/front/events.php - ${DEV_LOCATION}/front/events.php:/app/front/events.php
- ${DEV_LOCATION}/front/plugins.php:/app/front/plugins.php - ${DEV_LOCATION}/front/plugins.php:/app/front/plugins.php
@@ -56,7 +57,7 @@ services:
- ${DEV_LOCATION}/front/presence.php:/app/front/presence.php - ${DEV_LOCATION}/front/presence.php:/app/front/presence.php
- ${DEV_LOCATION}/front/settings.php:/app/front/settings.php - ${DEV_LOCATION}/front/settings.php:/app/front/settings.php
- ${DEV_LOCATION}/front/systeminfo.php:/app/front/systeminfo.php - ${DEV_LOCATION}/front/systeminfo.php:/app/front/systeminfo.php
- ${DEV_LOCATION}/front/account.php:/app/front/account.php - ${DEV_LOCATION}/front/cloud_services.php:/app/front/cloud_services.php
- ${DEV_LOCATION}/front/report.php:/app/front/report.php - ${DEV_LOCATION}/front/report.php:/app/front/report.php
- ${DEV_LOCATION}/front/workflows.php:/app/front/workflows.php - ${DEV_LOCATION}/front/workflows.php:/app/front/workflows.php
- ${DEV_LOCATION}/front/appEventsCore.php:/app/front/appEventsCore.php - ${DEV_LOCATION}/front/appEventsCore.php:/app/front/appEventsCore.php
@@ -65,8 +66,8 @@ services:
- ${DEV_LOCATION}/front/plugins:/app/front/plugins - ${DEV_LOCATION}/front/plugins:/app/front/plugins
# DELETE END anyone trying to use this file: comment out / delete ABOVE lines, they are only for development purposes # DELETE END anyone trying to use this file: comment out / delete ABOVE lines, they are only for development purposes
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
environment: environment:
# - APP_CONF_OVERRIDE={"SCAN_SUBNETS":"['192.168.1.0/24 --interface=eth1']","UI_theme":"Dark"} # - APP_CONF_OVERRIDE={"SCAN_SUBNETS":"['192.168.1.0/24 --interface=eth1']","GRAPHQL_PORT":"20223","UI_theme":"Light"}
- TZ=${TZ} - TZ=${TZ}
- PORT=${PORT} - PORT=${PORT}
# ❗ DANGER ZONE BELOW - Setting ALWAYS_FRESH_INSTALL=true will delete the content of the /db & /config folders # ❗ DANGER ZONE BELOW - Setting ALWAYS_FRESH_INSTALL=true will delete the content of the /db & /config folders

View File

@@ -1,14 +1,13 @@
[![GitHub Committed](https://img.shields.io/github/last-commit/jokob-sk/NetAlertX?color=40ba12&label=Committed&logo=GitHub&logoColor=fff&style=for-the-badge)](https://github.com/jokob-sk/NetAlertX)
[![Docker Size](https://img.shields.io/docker/image-size/jokobsk/netalertx?label=Size&logo=Docker&color=0aa8d2&logoColor=fff&style=for-the-badge)](https://hub.docker.com/r/jokobsk/netalertx) [![Docker Size](https://img.shields.io/docker/image-size/jokobsk/netalertx?label=Size&logo=Docker&color=0aa8d2&logoColor=fff&style=for-the-badge)](https://hub.docker.com/r/jokobsk/netalertx)
[![Docker Pulls](https://img.shields.io/docker/pulls/jokobsk/netalertx?label=Pulls&logo=docker&color=0aa8d2&logoColor=fff&style=for-the-badge)](https://hub.docker.com/r/jokobsk/netalertx) [![Docker Pulls](https://img.shields.io/docker/pulls/jokobsk/netalertx?label=Pulls&logo=docker&color=0aa8d2&logoColor=fff&style=for-the-badge)](https://hub.docker.com/r/jokobsk/netalertx)
[![GitHub Release](https://img.shields.io/github/v/release/jokob-sk/NetAlertX?color=0aa8d2&logoColor=fff&logo=GitHub&style=for-the-badge)](https://github.com/jokob-sk/NetAlertX/releases) [![GitHub Release](https://img.shields.io/github/v/release/jokob-sk/NetAlertX?color=0aa8d2&logoColor=fff&logo=GitHub&style=for-the-badge)](https://github.com/jokob-sk/NetAlertX/releases)
[![Discord](https://img.shields.io/discord/1274490466481602755?color=0aa8d2&logoColor=fff&logo=Discord&style=for-the-badge)](https://discord.gg/NczTUTWyRr) [![Discord](https://img.shields.io/discord/1274490466481602755?color=0aa8d2&logoColor=fff&logo=Discord&style=for-the-badge)](https://discord.gg/NczTUTWyRr)
[![Home Assistant](https://img.shields.io/badge/Repo-blue?logo=home-assistant&style=for-the-badge&color=0aa8d2&logoColor=fff&label=Add)](https://my.home-assistant.io/redirect/supervisor_add_addon_repository/?repository_url=https%3A%2F%2Fgithub.com%2Falexbelgium%2Fhassio-addons)
# NetAlertX - Network scanner & notification framework
# NetAlertX 🖧🔍 Network scanner & notification framework | [📑 Docker guide](https://github.com/jokob-sk/NetAlertX/blob/main/dockerfiles/README.md) | [🚀 Releases](https://github.com/jokob-sk/NetAlertX/releases) | [📚 Docs](https://github.com/jokob-sk/NetAlertX/tree/main/docs) | [🔌 Plugins](https://github.com/jokob-sk/NetAlertX/blob/main/front/plugins/README.md) | [🤖 Ask AI](https://gurubase.io/g/netalertx)
|----------------------| ----------------------| ----------------------| ----------------------| ----------------------|
| 🐳 [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/GENERAL/github_social_image.jpg" target="_blank"> <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" /> <img src="https://raw.githubusercontent.com/jokob-sk/NetAlertX/main/docs/img/GENERAL/github_social_image.jpg" width="1000px" />
@@ -22,17 +21,19 @@ Head to [https://netalertx.com/](https://netalertx.com/) for more gifs and scree
## 📕 Basic Usage ## 📕 Basic Usage
> [!WARNING] > [!WARNING]
> You will have to run the container on the `host` network. > You will have to run the container on the `host` network and specify `SCAN_SUBNETS` unless you use other [plugin scanners](https://github.com/jokob-sk/NetAlertX/blob/main/front/plugins/README.md). The initial scan can take a few minutes, so please wait 5-10 minutes for the initial discovery to finish.
```yaml ```yaml
docker run -d --rm --network=host \ docker run -d --rm --network=host \
-v local/path/config:/app/config \ -v local_path/config:/app/config \
-v local/path/db:/app/db \ -v local_path/db:/app/db \
--mount type=tmpfs,target=/app/api \
-e TZ=Europe/Berlin \ -e TZ=Europe/Berlin \
-e PORT=20211 \ -e PORT=20211 \
jokobsk/netalertx:latest jokobsk/netalertx:latest
``` ```
- The initial scan can take up to 15min (with 50 devices and MQTT). Subsequent ones 3 and 5 minutes so wait that long for all of the scans to run.
See alternative [docked-compose examples](https://github.com/jokob-sk/NetAlertX/blob/main/docs/DOCKER_COMPOSE.md).
### Docker environment variables ### Docker environment variables
@@ -41,7 +42,7 @@ docker run -d --rm --network=host \
| `PORT` |Port of the web interface | `20211` | | `PORT` |Port of the web interface | `20211` |
| `LISTEN_ADDR` |Set the specific IP Address for the listener address for the nginx webserver (web interface). This could be useful when using multiple subnets to hide the web interface from all untrusted networks. | `0.0.0.0` | | `LISTEN_ADDR` |Set the specific IP Address for the listener address for the nginx webserver (web interface). This could be useful when using multiple subnets to hide the web interface from all untrusted networks. | `0.0.0.0` |
|`TZ` |Time zone to display stats correctly. Find your time zone [here](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones) | `Europe/Berlin` | |`TZ` |Time zone to display stats correctly. Find your time zone [here](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones) | `Europe/Berlin` |
|`APP_CONF_OVERRIDE` | JSON override for settings, e.g. `{"SCAN_SUBNETS":"['192.168.1.0/24 --interface=eth1']","UI_theme":"Dark"}` (Experimental 🧪) | `N/A` | |`APP_CONF_OVERRIDE` | JSON override for settings, e.g. `{"SCAN_SUBNETS":"['192.168.1.0/24 --interface=eth1']","GRAPHQL_PORT":"20212"}` | `N/A` |
|`ALWAYS_FRESH_INSTALL` | If `true` will delete the content of the `/db` & `/config` folders. For testing purposes. Can be coupled with [watchtower](https://github.com/containrrr/watchtower) to have an always freshly installed `netalertx`/`netalertx-dev` image. | `N/A` | |`ALWAYS_FRESH_INSTALL` | If `true` will delete the content of the `/db` & `/config` folders. For testing purposes. Can be coupled with [watchtower](https://github.com/containrrr/watchtower) to have an always freshly installed `netalertx`/`netalertx-dev` image. | `N/A` |
> You can override the default GraphQL port setting `GRAPHQL_PORT` (set to `20212`) by using the `APP_CONF_OVERRIDE` env variable. > You can override the default GraphQL port setting `GRAPHQL_PORT` (set to `20212`) by using the `APP_CONF_OVERRIDE` env variable.
@@ -53,54 +54,34 @@ docker run -d --rm --network=host \
| Required | Path | Description | | Required | Path | Description |
| :------------- | :------------- | :-------------| | :------------- | :------------- | :-------------|
| ✅ | `:/app/config` | Folder which will contain the `app.conf` & `devices.csv` ([read about devices.csv](https://github.com/jokob-sk/NetAlertX/blob/main/docs/DEVICES_BULK_EDITING.md)) files (see below for details). | | ✅ | `:/app/config` | Folder which will contain the `app.conf` & `devices.csv` ([read about devices.csv](https://github.com/jokob-sk/NetAlertX/blob/main/docs/DEVICES_BULK_EDITING.md)) files |
| ✅ | `:/app/db` | Folder which will contain the `app.db` file | | ✅ | `:/app/db` | Folder which will contain the `app.db` database file |
| | `:/app/front/log` | Logs folder useful for debugging if you have issues setting up the container | | | `:/app/log` | Logs folder useful for debugging if you have issues setting up the container |
| | `:/etc/pihole/pihole-FTL.db` | PiHole's `pihole-FTL.db` database file. Required if you want to use PiHole DB mapping. | | | `:/app/api` | A simple [API endpoint](https://github.com/jokob-sk/NetAlertX/blob/main/docs/API.md) containing static (but regularly updated) json and other files. |
| | `:/etc/pihole/dhcp.leases` | PiHole's `dhcp.leases` file. Required if you want to use PiHole `dhcp.leases` file. This has to be matched with a corresponding `DHCPLSS_paths_to_check` setting entry (the path in the container must contain `pihole`)|
| | `:/app/front/api` | A simple [API endpoint](https://github.com/jokob-sk/NetAlertX/blob/main/docs/API.md) containing static (but regularly updated) json and other files. |
| | `:/app/front/plugins/<plugin>/ignore_plugin` | Map a file `ignore_plugin` to ignore a plugin. Plugins can be soft-disabled via settings. More in the [Plugin docs](https://github.com/jokob-sk/NetAlertX/blob/main/front/plugins/README.md). | | | `:/app/front/plugins/<plugin>/ignore_plugin` | Map a file `ignore_plugin` to ignore a plugin. Plugins can be soft-disabled via settings. More in the [Plugin docs](https://github.com/jokob-sk/NetAlertX/blob/main/front/plugins/README.md). |
| | `:/etc/resolv.conf` | Use a custom `resolv.conf` file for [better name resolution](https://github.com/jokob-sk/NetAlertX/blob/main/docs/REVERSE_DNS.md). | | | `:/etc/resolv.conf` | Use a custom `resolv.conf` file for [better name resolution](https://github.com/jokob-sk/NetAlertX/blob/main/docs/REVERSE_DNS.md). |
> Use separate `db` and `config` directories, don't nest them. > Use separate `db` and `config` directories, do not nest them.
### (If UI is not available) Modify the config (`app.conf`) ### Initial setup
- The preferred way is to manage the configuration via the Settings section in the UI.
- 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. - If unavailable, the app generates a default `app.conf` and `app.db` file on the first run.
- The preferred way is to manage the configuration via the Settings section in the UI, if UI is inaccessible you can modify [app.conf](https://github.com/jokob-sk/NetAlertX/tree/main/back) in the `/app/config/` folder directly
### ⚙ Important settings ### Setting up scanners
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. You have to specify which network(s) should be scanned. This is done by entering subnets that are accessible from the host. If you use the default `ARPSCAN` plugin, you have to specify at least one valid subnet and interface in the `SCAN_SUBNETS` setting. See the documentation on [How to set up multiple SUBNETS, VLANs and what are limitations](https://github.com/jokob-sk/NetAlertX/blob/main/docs/SUBNETS.md) for troubleshooting and more advanced scenarios.
| 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). |
If you are running PiHole you can synchronize devices directly. Check the [PiHole configuration guide](https://github.com/jokob-sk/NetAlertX/blob/main/docs/PIHOLE_GUIDE.md) for details.
> [!NOTE] > [!NOTE]
> It's recommended to use the same schedule interval for all plugins responsible for discovering new devices. > You can bulk-import devices via the [CSV import method](https://github.com/jokob-sk/NetAlertX/blob/main/docs/DEVICES_BULK_EDITING.md).
#### 🧭 Community guides #### 🧭 Community guides
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 🙏 You can read or watch several [community configuration guides](https://github.com/jokob-sk/NetAlertX/blob/main/docs/COMMUNITY_GUIDES.md) in Chinese, Korean, German, or French.
- ▶ [Home Lab Network Monitoring - Scotti-BYTE Enterprise Consulting Services](https://www.youtube.com/watch?v=0DryhzrQSJA) (July 2024) > Please note these might be outdated. Rely on official documentation first.
- 📄 [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)
- 📄 [防蹭网神器,网络安全助手 | 极空间部署网络扫描和通知系统『NetAlertX』](https://blog.csdn.net/qq_63499861/article/details/141105273)
- 📄 [시놀/헤놀에서 네트워크 스캐너 Pi.Alert Docker로 설치 및 사용하기](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](https://www.youtube.com/watch?v=-ouvA2UNu-A) (March 2023)
- ▶ [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.
### **Common issues** ### **Common issues**
@@ -108,129 +89,6 @@ Use the official installation guides at first and use community content as suppl
⚠ Check also common issues and [debugging tips](https://github.com/jokob-sk/NetAlertX/blob/main/docs/DEBUG_TIPS.md). ⚠ Check also common issues and [debugging tips](https://github.com/jokob-sk/NetAlertX/blob/main/docs/DEBUG_TIPS.md).
> [!NOTE]
> You can bulk-update devices via the [CSV import method](https://github.com/jokob-sk/NetAlertX/blob/main/docs/DEVICES_BULK_EDITING.md).
## 📄 docker-compose.yml Examples
### Example 1
```yaml
version: "3"
services:
netalertx:
container_name: netalertx
# use the below line if you want to test the latest dev image
# image: "jokobsk/netalertx-dev:latest"
image: "jokobsk/netalertx:latest"
network_mode: "host"
restart: unless-stopped
volumes:
- local/path/config:/app/config
- local/path/db:/app/db
# (optional) useful for debugging if you have issues setting up the container
- local/path/logs:/app/front/log
environment:
- TZ=Europe/Berlin
- PORT=20211
```
To run the container execute: `sudo docker-compose up -d`
### Example 2
Example by [SeimuS](https://github.com/SeimusS).
```yaml
netalertx:
container_name: NetAlertX
hostname: NetAlertX
privileged: true
# use the below line if you want to test the latest dev image
# image: "jokobsk/netalertx-dev:latest"
image: jokobsk/netalertx:latest
environment:
- TZ=Europe/Bratislava
restart: always
volumes:
- ./netalertx/db:/app/db
- ./netalertx/config:/app/config
network_mode: host
```
To run the container execute: `sudo docker-compose up -d`
### Example 3
`docker-compose.yml`
```yaml
version: "3"
services:
netalertx:
container_name: netalertx
# use the below line if you want to test the latest dev image
# image: "jokobsk/netalertx-dev:latest"
image: "jokobsk/netalertx:latest"
network_mode: "host"
restart: unless-stopped
volumes:
- ${APP_DATA_LOCATION}/netalertx/config:/app/config
- ${APP_DATA_LOCATION}/netalertx/db/:/app/db/
# (optional) useful for debugging if you have issues setting up the container
- ${LOGS_LOCATION}:/app/front/log
environment:
- TZ=${TZ}
- PORT=${PORT}
```
`.env` file
```yaml
#GLOBAL PATH VARIABLES
APP_DATA_LOCATION=/path/to/docker_appdata
APP_CONFIG_LOCATION=/path/to/docker_config
LOGS_LOCATION=/path/to/docker_logs
#ENVIRONMENT VARIABLES
TZ=Europe/Paris
PORT=20211
#DEVELOPMENT VARIABLES
DEV_LOCATION=/path/to/local/source/code
```
To run the container execute: `sudo docker-compose --env-file /path/to/.env up`
### Example 4
Courtesy of [pbek](https://github.com/pbek). The volume `netalertx_db` is used by the db directory. The two config files are mounted directly from a local folder to their places in the config folder. You can backup the `docker-compose.yaml` folder and the docker volumes folder.
```yaml
netalertx:
# use the below line if you want to test the latest dev image
# image: "jokobsk/netalertx-dev:latest"
image: jokobsk/netalertx
ports:
- "80:20211/tcp"
environment:
- TZ=Europe/Vienna
networks:
local:
ipv4_address: 192.168.1.2
restart: unless-stopped
volumes:
- netalertx_db:/app/db
- ./netalertx/:/app/config/
```
## 🏅 Recognitions
Big thanks to <a href="https://github.com/Macleykun">@Macleykun</a> & for help and tips & tricks for Dockerfile(s) and <a href="https://github.com/vladaurosh">@vladaurosh</a> for Alpine re-base help.
## ❤ Support me ## ❤ Support me
| [![GitHub](https://i.imgur.com/emsRCPh.png)](https://github.com/sponsors/jokob-sk) | [![Buy Me A Coffee](https://i.imgur.com/pIM6YXL.png)](https://www.buymeacoffee.com/jokobsk) | [![Patreon](https://i.imgur.com/MuYsrq1.png)](https://www.patreon.com/user?u=84385063) | | [![GitHub](https://i.imgur.com/emsRCPh.png)](https://github.com/sponsors/jokob-sk) | [![Buy Me A Coffee](https://i.imgur.com/pIM6YXL.png)](https://www.buymeacoffee.com/jokobsk) | [![Patreon](https://i.imgur.com/MuYsrq1.png)](https://www.patreon.com/user?u=84385063) |
@@ -239,4 +97,4 @@ Big thanks to <a href="https://github.com/Macleykun">@Macleykun</a> & for help a
- Bitcoin: `1N8tupjeCK12qRVU2XrV17WvKK7LCawyZM` - Bitcoin: `1N8tupjeCK12qRVU2XrV17WvKK7LCawyZM`
- Ethereum: `0x6e2749Cb42F4411bc98501406BdcD82244e3f9C7` - Ethereum: `0x6e2749Cb42F4411bc98501406BdcD82244e3f9C7`
> 📧 Email me at [jokob@duck.com](mailto:jokob@duck.com?subject=NetAlertX) if you want to get in touch or if I should add other sponsorship platforms. > 📧 Email me at [netalertx@gmail.com](mailto:netalertx@gmail.com?subject=NetAlertX Donations) if you want to get in touch or if I should add other sponsorship platforms.

View File

@@ -1,7 +1,7 @@
#!/usr/bin/with-contenv bash #!/usr/bin/with-contenv bash
echo "---------------------------------------------------------" echo "---------------------------------------------------------"
echo "[INSTALL] Run setup.sh" echo "[INSTALL] Run init.sh"
echo "---------------------------------------------------------" echo "---------------------------------------------------------"
export INSTALL_DIR=/app # Specify the installation directory here export INSTALL_DIR=/app # Specify the installation directory here
@@ -106,15 +106,17 @@ fi
# Create an empty log files # Create an empty log files
# Create the execution_queue.log and app_front.log files if they don't exist # 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,db_is_locked.log} touch "${INSTALL_DIR}"/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 touch "${INSTALL_DIR}"/api/user_notifications.json
# Create plugins sub-directory if it doesn't exist in case a custom log folder is used
mkdir -p "${INSTALL_DIR}"/log/plugins
echo "[INSTALL] Fixing permissions after copied starter config & DB" echo "[INSTALL] Fixing permissions after copied starter config & DB"
chown -R nginx:www-data "${INSTALL_DIR}"/{config,front/log,db,front/api} chown -R nginx:www-data "${INSTALL_DIR}"/{config,log,db,api}
chown -R nginx:www-data "${INSTALL_DIR}"/front/api/user_notifications.json chown -R nginx:www-data "${INSTALL_DIR}"/api/user_notifications.json
chmod 750 "${INSTALL_DIR}"/{config,front/log,db} chmod 750 "${INSTALL_DIR}"/{config,log,db}
find "${INSTALL_DIR}"/{config,front/log,db} -type f -exec chmod 640 {} \; find "${INSTALL_DIR}"/{config,log,db} -type f -exec chmod 640 {} \;
# Check if buildtimestamp.txt doesn't exist # Check if buildtimestamp.txt doesn't exist
if [ ! -f "${INSTALL_DIR}/front/buildtimestamp.txt" ]; then if [ ! -f "${INSTALL_DIR}/front/buildtimestamp.txt" ]; then

View File

@@ -13,13 +13,14 @@ 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 sed -i "/^group/c\group = www-data" /etc/php83/php-fpm.d/www.conf
# s6 overlay setup # s6 overlay setup
mkdir -p /etc/s6-overlay/s6-rc.d/{SetupOneshot,php-fpm/dependencies.d,nginx/dependencies.d} mkdir -p /etc/s6-overlay/s6-rc.d/{SetupOneshot,crond/dependencies.d,php-fpm/dependencies.d,nginx/dependencies.d,$APP_NAME/dependencies.d}
mkdir -p /etc/s6-overlay/s6-rc.d/{SetupOneshot,php-fpm/dependencies.d,nginx/dependencies.d,$APP_NAME/dependencies.d}
echo "oneshot" > /etc/s6-overlay/s6-rc.d/SetupOneshot/type echo "oneshot" > /etc/s6-overlay/s6-rc.d/SetupOneshot/type
echo "longrun" > /etc/s6-overlay/s6-rc.d/crond/type
echo "longrun" > /etc/s6-overlay/s6-rc.d/php-fpm/type 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/nginx/type
echo "longrun" > /etc/s6-overlay/s6-rc.d/$APP_NAME/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 "${INSTALL_DIR}/dockerfiles/init.sh" > /etc/s6-overlay/s6-rc.d/SetupOneshot/up
echo -e "#!/bin/execlineb -P\n/usr/sbin/crond -f -d 8" > /etc/s6-overlay/s6-rc.d/crond/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\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\nnginx -g "daemon off;"' > /etc/s6-overlay/s6-rc.d/nginx/run
echo -e '#!/bin/execlineb -P echo -e '#!/bin/execlineb -P
@@ -33,11 +34,9 @@ echo -e '#!/bin/execlineb -P
" }' > /etc/s6-overlay/s6-rc.d/$APP_NAME/run " }' > /etc/s6-overlay/s6-rc.d/$APP_NAME/run
echo -e "python ${INSTALL_DIR}/server" >> /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 touch /etc/s6-overlay/s6-rc.d/user/contents.d/{SetupOneshot,crond,php-fpm,nginx,$APP_NAME} /etc/s6-overlay/s6-rc.d/{crond,php-fpm,nginx,$APP_NAME}/dependencies.d/SetupOneshot
touch /etc/s6-overlay/s6-rc.d/user/contents.d/{SetupOneshot,php-fpm,nginx,$APP_NAME} /etc/s6-overlay/s6-rc.d/{php-fpm,nginx,$APP_NAME}/dependencies.d/SetupOneshot
touch /etc/s6-overlay/s6-rc.d/nginx/dependencies.d/php-fpm touch /etc/s6-overlay/s6-rc.d/nginx/dependencies.d/php-fpm
touch /etc/s6-overlay/s6-rc.d/$APP_NAME/dependencies.d/nginx touch /etc/s6-overlay/s6-rc.d/$APP_NAME/dependencies.d/nginx
# this removes the current file
# rm -f $0
rm -f $0

View File

@@ -1,7 +1,134 @@
## API endpoints # API endpoints
NetAlertX comes with a simple API. These API endpoints are static files, that are periodically updated based on your settings. NetAlertX comes with a couple of API endpoints. All requests need to be authorized (executed in a logged in browser session) or you have to pass the value of the `API_TOKEN` settings as authorization bearer, for example:
```graphql
curl 'http://host:GRAPHQL_PORT/graphql' \
-X POST \
-H 'Authorization: Bearer API_TOKEN' \
-H 'Content-Type: application/json' \
--data '{
"query": "query GetDevices($options: PageQueryOptionsInput) { devices(options: $options) { devices { rowid devMac devName devOwner devType devVendor devLastConnection devStatus } count } }",
"variables": {
"options": {
"page": 1,
"limit": 10,
"sort": [{ "field": "devName", "order": "asc" }],
"search": "",
"status": "connected"
}
}
}'
```
## API Endpoint: GraphQL
Endpoint URL: `php/server/query_graphql.php`
Host: `same as front end (web ui)`
Port: `20212` or as defined by the `GRAPHQL_PORT` setting
### Example Query to Fetch Devices
First, let's define the GraphQL query to fetch devices with pagination and sorting options.
```graphql
query GetDevices($options: PageQueryOptionsInput) {
devices(options: $options) {
devices {
rowid
devMac
devName
devOwner
devType
devVendor
devLastConnection
devStatus
}
count
}
}
```
### `curl` Command
You can use the following `curl` command to execute the query.
```sh
curl 'http://host:GRAPHQL_PORT/graphql' -X POST -H 'Authorization: Bearer API_TOKEN' -H 'Content-Type: application/json' --data '{
"query": "query GetDevices($options: PageQueryOptionsInput) { devices(options: $options) { devices { rowid devMac devName devOwner devType devVendor devLastConnection devStatus } count } }",
"variables": {
"options": {
"page": 1,
"limit": 10,
"sort": [{ "field": "devName", "order": "asc" }],
"search": "",
"status": "connected"
}
}
}'
```
### Explanation:
1. **GraphQL Query**:
- The `query` parameter contains the GraphQL query as a string.
- The `variables` parameter contains the input variables for the query.
2. **Query Variables**:
- `page`: Specifies the page number of results to fetch.
- `limit`: Specifies the number of results per page.
- `sort`: Specifies the sorting options, with `field` being the field to sort by and `order` being the sort order (`asc` for ascending or `desc` for descending).
- `search`: A search term to filter the devices.
- `status`: The status filter to apply (valid values are `my_devices` (determined by the `UI_MY_DEVICES` setting), `connected`, `favorites`, `new`, `down`, `archived`, `offline`).
3. **`curl` Command**:
- The `-X POST` option specifies that we are making a POST request.
- The `-H "Content-Type: application/json"` option sets the content type of the request to JSON.
- The `-d` option provides the request payload, which includes the GraphQL query and variables.
### Sample Response
The response will be in JSON format, similar to the following:
```json
{
"data": {
"devices": {
"devices": [
{
"rowid": 1,
"devMac": "00:11:22:33:44:55",
"devName": "Device 1",
"devOwner": "Owner 1",
"devType": "Type 1",
"devVendor": "Vendor 1",
"devLastConnection": "2025-01-01T00:00:00Z",
"devStatus": "connected"
},
{
"rowid": 2,
"devMac": "66:77:88:99:AA:BB",
"devName": "Device 2",
"devOwner": "Owner 2",
"devType": "Type 2",
"devVendor": "Vendor 2",
"devLastConnection": "2025-01-02T00:00:00Z",
"devStatus": "connected"
}
],
"count": 2
}
}
}
```
## API Endpoint: JSON files
This API endpoint retrieves static files, that are periodically updated.
Endpoint URL: `php/server/query_json.php?file=<file name>`
Host: `same as front end (web ui)`
Port: `20211` or as defined by the $PORT docker environment variable (same as the port for the web ui)
### When are the endpoints updated ### When are the endpoints updated
@@ -9,7 +136,7 @@ The endpoints are updated when objects in the API endpoints are changed.
### Location of the endpoints ### Location of the endpoints
In the container, these files are located under the `/app/front/api/` folder and thus on the `<netalertx_url>/api/<File name>` url. In the container, these files are located under the `/app/api/` folder. You can access them via the `/php/server/query_json.php?file=user_notifications.json` endpoint.
### Available endpoints ### Available endpoints
@@ -17,10 +144,8 @@ You can access the following files:
| File name | Description | | File name | Description |
|----------------------|----------------------| |----------------------|----------------------|
| `notification_text.txt` | The plain text version of the last notification. |
| `notification_text.html` | The full HTML of the last email notification. |
| `notification_json_final.json` | The json version of the last notification (e.g. used for webhooks - [sample JSON](https://github.com/jokob-sk/NetAlertX/blob/main/front/report_templates/webhook_json_sample.json)). | | `notification_json_final.json` | The json version of the last notification (e.g. used for webhooks - [sample JSON](https://github.com/jokob-sk/NetAlertX/blob/main/front/report_templates/webhook_json_sample.json)). |
| `table_devices.json` | The current (at the time of the last update as mentioned above on this page) state of all of the available Devices detected by the app. | | `table_devices.json` | All of the available Devices detected by the app. |
| `table_plugins_events.json` | The list of the unprocessed (pending) notification events (plugins_events DB table). | | `table_plugins_events.json` | The list of the unprocessed (pending) notification events (plugins_events DB table). |
| `table_plugins_history.json` | The list of notification events history. | | `table_plugins_history.json` | The list of notification events history. |
| `table_plugins_objects.json` | The content of the plugins_objects table. Find more info on the [Plugin system here](https://github.com/jokob-sk/NetAlertX/tree/main/front/plugins)| | `table_plugins_objects.json` | The content of the plugins_objects table. Find more info on the [Plugin system here](https://github.com/jokob-sk/NetAlertX/tree/main/front/plugins)|
@@ -29,7 +154,6 @@ You can access the following files:
| `table_settings.json` | The content of the settings table. | | `table_settings.json` | The content of the settings table. |
| `app_state.json` | Contains the current application state. | | `app_state.json` | Contains the current application state. |
Current/latest state of the aforementioned files depends on your settings.
### JSON Data format ### JSON Data format
@@ -95,3 +219,41 @@ Example JSON of the `table_devices.json` endpoint with two Devices (database row
``` ```
## API Endpoint: /log files
This API endpoint retrieves files from the `/app/log` folder.
Endpoint URL: `php/server/query_logs.php?file=<file name>`
Host: `same as front end (web ui)`
Port: `20211` or as defined by the $PORT docker environment variable (same as the port for the web ui)
| File | Description |
|--------------------------|---------------------------------------------------------------|
| `IP_changes.log` | Logs of IP address changes |
| `app.log` | Main application log |
| `app.php_errors.log` | PHP error log |
| `app_front.log` | Frontend application log |
| `app_nmap.log` | Logs of Nmap scan results |
| `db_is_locked.log` | Logs when the database is locked |
| `execution_queue.log` | Logs of execution queue activities |
| `plugins/` | Directory for temporary plugin-related files (not accessible) |
| `report_output.html` | HTML report output |
| `report_output.json` | JSON format report output |
| `report_output.txt` | Text format report output |
| `stderr.log` | Logs of standard error output |
| `stdout.log` | Logs of standard output |
## API Endpoint: /config files
To retrieve files from the `/app/config` folder.
Endpoint URL: `php/server/query_config.php?file=<file name>`
Host: `same as front end (web ui)`
Port: `20211` or as defined by the $PORT docker environment variable (same as the port for the web ui)
| File | Description |
|--------------------------|--------------------------------------------------|
| `devices.csv` | Devices csv file |
| `app.conf` | Application config file |

View File

@@ -1,7 +1,8 @@
# 💾 Backing things up # 💾 Backing things up
> [!NOTE] > [!NOTE]
> To backup 99% of your configuration backup at least the `/config` folder. Please read the whole page (or at least "Scenario 2: Corrupted database") for details. > To backup 99% of your configuration backup at least the `/app/config` folder. Please read the whole page (or at least "Scenario 2: Corrupted database") for details.
> Please also note that database definitions might change over versions. The safest way is to restore your older backups into the **same version** of the app and then gradually upgarde between releases to the latest version.
There are 3 artifacts that can be used to backup the application: There are 3 artifacts that can be used to backup the application:
@@ -22,16 +23,17 @@ The core application configuration is in the `app.conf` file (See [Settings Syst
- Notification settings - Notification settings
- Scanner settings - Scanner settings
- Scheduled maintenance settings - Scheduled maintenance settings
- UI configuration (80%) - UI configuration
### Core Device Data ### Core Device Data
The core device data is backed up to the `devices_<timestamp>.csv` file via the [CSV Backup `CSVBCKP` Plugin](https://github.com/jokob-sk/NetAlertX/tree/main/front/plugins/csv_backup). This file contains data, such as: The core device data is backed up to the `devices_<timestamp>.csv` or `devices.csv` file via the [CSV Backup `CSVBCKP` Plugin](https://github.com/jokob-sk/NetAlertX/tree/main/front/plugins/csv_backup). This file contains data, such as:
- Device names - Device names
- Device Icons - Device icons
- Device Network configuration - Device network configuration
- Device categorization - Device categorization
- Device custom properties data
### Historical data ### Historical data
@@ -40,13 +42,15 @@ Historical data is stored in the `app.db` database (See [Database overview](http
- Plugin objects - Plugin objects
- Plugin historical entries - Plugin historical entries
- History of Events, Notifications, Workflow Events - History of Events, Notifications, Workflow Events
- Presence History - Presence history
## 🧭 Backup strategies ## 🧭 Backup strategies
The safest approach to backups is to backup all of the above, by taking regular file system backups (I use [Kopia](https://github.com/kopia/kopia)). The safest approach to backups is to backup all of the above, by taking regular file system backups (I use [Kopia](https://github.com/kopia/kopia)).
Arguably, the most time is spent setting up the device list, so if only one file is kept I'd recommend to have a latest backup of the `devices_<timestamp>.csv` file, followed by the `app.conf` file. Arguably, the most time is spent setting up the device list, so if only one file is kept I'd recommend to have a latest backup of the `devices_<timestamp>.csv` or `devices.csv` file, followed by the `app.conf` file. You can also download `app.conf` and `devices.csv` file in the Maintenance section:
![Backup and Restore Section in Maintenance](/docs/img/BACKUPS/Maintenance_Backup_Restore.png)
### Scenario 1: Full backup ### Scenario 1: Full backup
@@ -54,8 +58,8 @@ End-result: Full restore
#### Source artifacts: #### Source artifacts:
- `/db/app.db` (uncorrupted) - `/app/db/app.db` (uncorrupted)
- `/config/app.conf` - `/app/config/app.conf`
#### Recovery: #### Recovery:
@@ -68,15 +72,15 @@ End-result: Partial restore (historical data & configurations from the Maintenan
#### Source artifacts: #### Source artifacts:
- `/config/app.conf` - `/app/config/app.conf`
- `/config/devices_<timestamp>.csv` or `/config/devices.csv` - `/app/config/devices_<timestamp>.csv` or `/app/config/devices.csv`
#### Recovery: #### Recovery:
Even with a corrupted database you can recover what I would argue is 99% of the configuration. Even with a corrupted database you can recover what I would argue is 99% of the configuration.
- map the `/config/app.conf` file as described in the [Setup documentation](https://github.com/jokob-sk/NetAlertX/blob/main/dockerfiles/README.md#docker-paths). - upload the `app.conf` file into the mounted `/app/config/` folder as described in the [Setup documentation](https://github.com/jokob-sk/NetAlertX/blob/main/dockerfiles/README.md#docker-paths).
- rename the `devices_<timestamp>.csv` to `devices.csv` and place it in the `/config` folder - rename the `devices_<timestamp>.csv` to `devices.csv` and place it in the `/app/config` folder
- Restore the `devices.csv` backup via the [Maintenance section](https://github.com/jokob-sk/NetAlertX/blob/main/docs/DEVICES_BULK_EDITING.md) - Restore the `devices.csv` backup via the [Maintenance section](https://github.com/jokob-sk/NetAlertX/blob/main/docs/DEVICES_BULK_EDITING.md)

14
docs/COMMUNITY_GUIDES.md Executable file
View File

@@ -0,0 +1,14 @@
# Community Guides
Use the official installation guides at first and use community content as supplementary material. Open an issue or PR if you'd like to add your link to the list 🙏 (Ordered by last update time)
- ▶ [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)
- 📄 [防蹭网神器,网络安全助手 | 极空间部署网络扫描和通知系统『NetAlertX』](https://blog.csdn.net/qq_63499861/article/details/141105273)
- 📄 [시놀/헤놀에서 네트워크 스캐너 Pi.Alert Docker로 설치 및 사용하기](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](https://www.youtube.com/watch?v=-ouvA2UNu-A) (March 2023)
- ▶ [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)

85
docs/CUSTOM_PROPERTIES.md Executable file
View File

@@ -0,0 +1,85 @@
# Custom Properties for Devices
![Custom Properties](/docs/img/CUSTOM_PROPERTIES/Device_Custom_Properties.png)
## Overview
This functionality allows you to define **custom properties** for devices, which can store and display additional information on the device listing page. By marking properties as visible, you can enhance the user interface with quick actions, notes, or external links.
### Key Features:
- **Customizable Properties**: Define specific properties for each device.
- **Visibility Control**: Choose which properties are displayed on the device listing page.
- **Interactive Elements**: Include actions like links, modals, and device management directly in the interface.
---
## Defining Custom Properties
Custom properties are structured as a list of objects, where each property includes the following fields:
| Field | Description |
|--------------------|-----------------------------------------------------------------------------|
| `CUSTPROP_icon` | The icon (Base64-encoded HTML) displayed for the property. |
| `CUSTPROP_type` | The action type (e.g., `show_notes`, `link`, `delete_dev`). |
| `CUSTPROP_name` | A short name or title for the property. |
| `CUSTPROP_args` | Arguments for the action (e.g., URL or modal text). |
| `CUSTPROP_notes` | Additional notes or details displayed when applicable. |
| `CUSTPROP_show` | A boolean to control visibility (`true` to show on the listing page). |
---
## Available Action Types
- **Show Notes**: Displays a modal with a title and additional notes.
- **Example**: Show firmware details or custom messages.
- **Link**: Redirects to a specified URL in the current browser tab. (**Arguments** Needs to contain the full URL.)
- **Link (New Tab)**: Opens a specified URL in a new browser tab. (**Arguments** Needs to contain the full URL.)
- **Delete Device**: Deletes the device using its MAC address.
- **Run Plugin**: Placeholder for executing custom plugins (not implemented yet).
---
## Usage on the Device Listing Page
![Custom Properties](/docs/img/CUSTOM_PROPERTIES/Device_Custom_Properties_vid.gif)
Visible properties (`CUSTPROP_show: true`) are displayed as interactive icons in the device listing. Each icon can perform one of the following actions based on the `CUSTPROP_type`:
1. **Modals (e.g., Show Notes)**:
- Displays detailed information in a popup modal.
- Example: Firmware version details.
2. **Links**:
- Redirect to an external or internal URL.
- Example: Open a device's documentation or external site.
3. **Device Actions**:
- Manage devices with actions like delete.
- Example: Quickly remove a device from the network.
4. **Plugins**:
- Future placeholder for running custom plugin scripts.
- **Note**: Not implemented yet.
---
## Example Scenarios
1. **Device Documentation Link**:
- Add a custom property with `CUSTPROP_type` set to `link` or `link_new_tab` to allow quick navigation to the documentation.
2. **Firmware Details**:
- Use `CUSTPROP_type: show_notes` to display firmware versions or upgrade instructions in a modal.
3. **Device Removal**:
- Enable device removal functionality using `CUSTPROP_type: delete_dev`.
---
## Notes
- **Plugin Functionality**: The `run_plugin` action type is currently not implemented and will show an alert if used.
- **Custom Icons (Experimental 🧪)**: Use Base64-encoded HTML to provide custom icons for each property. You can add your icons in Setttings via the `CUSTPROP_icon` settings
- **Visibility Control**: Only properties with `CUSTPROP_show: true` will appear on the listing page.
This feature provides a flexible way to enhance device management and display with interactive elements tailored to your needs.

View File

@@ -55,7 +55,7 @@ Input data from the plugin might cause mapping issues in specific edge cases. Lo
17:31:05 [Plugins] SQL sqlParams for mapping: [('01:01:01:01:01:01', '172.30.0.1', 0, 'aaaa', 'vvvvvvvvv', 'PIHOLE'), ('02:42:ac:1e:00:02', '172.30.0.2', 0, 'dddd', 'vvvvv2222', 'PIHOLE')] 17:31:05 [Plugins] SQL sqlParams for mapping: [('01:01:01:01:01:01', '172.30.0.1', 0, 'aaaa', 'vvvvvvvvv', 'PIHOLE'), ('02:42:ac:1e:00:02', '172.30.0.2', 0, 'dddd', 'vvvvv2222', 'PIHOLE')]
🔺 🔺
17:31:05 [API] Update API starting 17:31:05 [API] Update API starting
17:31:06 [API] Updating table_plugins_history.json file in /front/api 17:31:06 [API] Updating table_plugins_history.json file in /api
``` ```
> The debug output between the 🔻red arrows🔺 is important for debugging (arrows added only to highlight the section on this page, they are not available in the actual debug log) > The debug output between the 🔻red arrows🔺 is important for debugging (arrows added only to highlight the section on this page, they are not available in the actual debug log)

View File

@@ -64,7 +64,7 @@ Sometimes specific log sections are needed to debug issues. The Devices and Curr
### Permissions ### Permissions
* If facing issues (AJAX errors, can't write to DB, empty screen, etc,) make sure permissions are set correctly, and check the logs under `/app/front/log`. * If facing issues (AJAX errors, can't write to DB, empty screen, etc,) make sure permissions are set correctly, and check the logs under `/app/log`.
* To solve permission issues you can try setting the owner and group of the `app.db` by executing the following on the host system: `docker exec netalertx chown -R www-data:www-data /app/db/app.db`. * To solve permission issues you can try setting the owner and group of the `app.db` by executing the following on the host system: `docker exec netalertx chown -R www-data:www-data /app/db/app.db`.
* If still facing issues, try to map the app.db file (⚠ not folder) to `:/app/db/app.db` (see [docker-compose Examples](https://github.com/jokob-sk/NetAlertX/blob/main/dockerfiles/README.md#-docker-composeyml-examples) for details) * If still facing issues, try to map the app.db file (⚠ not folder) to `:/app/db/app.db` (see [docker-compose Examples](https://github.com/jokob-sk/NetAlertX/blob/main/dockerfiles/README.md#-docker-composeyml-examples) for details)
@@ -74,7 +74,7 @@ Sometimes specific log sections are needed to debug issues. The Devices and Curr
### unable to resolve host ### unable to resolve host
* Check that your `SCAN_SUBNETS` variable is using the correct mask and `--interface` as outlined in the instructions above. * Check that your `SCAN_SUBNETS` variable is using the correct mask and `--interface`. See teh [subnets docs for details](/docs/SUBNETS.md).
### Invalid JSON ### Invalid JSON
@@ -95,4 +95,4 @@ The link above will probably break in time too. Go to https://packages.debian.or
### Only Router and own device show up ### Only Router and own device show up
Make sure that the subnet and interface in SCAN_SUBNETS are the correct ones. If your device/NAS has multiple ethernet ports, you probably need to change eth0 to something else! Make sure that the subnet and interface in `SCAN_SUBNETS` are correct. If your device/NAS has multiple ethernet ports, you probably need to change `eth0` to something else.

View File

@@ -0,0 +1,6 @@
# Device Display Settings
This set of settings allows you to group Devices under different views. The Archived toggle allows you to exclude a Device from most listings and notifications.
![Display settings](/docs/img/DEVICE_MANAGEMENT/DeviceDetails_DisplaySettings.png)

View File

@@ -1,107 +1,50 @@
# NetAlertX - Device Management # NetAlertX - Device Management
<!--- --------------------------------------------------------------------- --->
To edit device information: The Main Info section is where most of the device identifiable information is stored and edited. Some of the information is autodetected via various plugins. Initial values for most of the fields can be specified in the `NEWDEV` plugin.
- Select "Devices" in the menu on the left of the screen
- Find the device you want to edit in the central table > [!NOTE]
- Go to the device page by clicking on the device name or status >
- Press "Details" tab of the device > You can multi-edit devices by selecting them in the main Devices view, from the Mainetence section, or via the CSV Export functionality under Maintenance. More info can be found in the [Devices Bulk-editing docs](https://github.com/jokob-sk/NetAlertX/blob/main/docs/DEVICES_BULK_EDITING.md).
- Edit the device data
- Press the "Save" button
![Main Info](/docs/img/DEVICE_MANAGEMENT/DeviceManagement_MainInfo.png)
## Main Info
- **MAC**: MAC addres of the device. Not editable, unless creating a new dummy device.
- **Last IP**: IP addres of the device. Not editable, unless creating a new dummy device.
- **Name**: Friendly device name. Autodetected via various 🆎 Name discovery [plugins](https://github.com/jokob-sk/NetAlertX/blob/main/front/plugins/README.md). The app attaches `(IP match)` if the name is discovered via an IP match and not MAC match which could mean the name could be incorrect as IPs might change.
- **Icon**: Partially autodetected. Select an existing or [add a custom icon](https://github.com/jokob-sk/NetAlertX/blob/main/docs/ICONS.md). You can also auto-apply the same icon on all devices of the same type.
- **Owner**: Device owner (The list is self-populated with existing owners and you can add custom values).
- **Type**: Select a device type from the dropdown list (`Smartphone`, `Tablet`,
`Laptop`, `TV`, `router`, etc.) or add a new device type. If you want the device to act as a **Network device** (and be able to be a network node in the Network view), select a type under Network Devices or add a new Network Device type in Settings. More information can be found in the [Network Setup docs](https://github.com/jokob-sk/NetAlertX/blob/main/docs/NETWORK_TREE.md).
- **Vendor**: The manufacturing vendor. Automatically updated by NetAlertX when empty or unknown, can be edited.
- **Group**: Select a group (`Always on`, `Personal`, `Friends`, etc.) or type
your own Group name.
- **Location**: Select the location, usually a room, where the device is located (`Kitchen`, `Attic`, `Living room`, etc.) or add a custom Location.
- **Comments**: Add any comments for the device, such as a serial number, or maintenance information.
> [!NOTE]
>
> Please note the above usage of the fields are only suggestions. You can use most of these fields for other purposes, such as storing the network interface, company owning a device, or similar.
## Dummy devices
You can create dummy devices from the Devices listing screen.
![Create Dummy Device](/docs/img/DEVICE_MANAGEMENT/Devices_CreateDummyDevice.png)
The **MAC** field and the **Last IP** field will then become editable.
![Save Dummy Device](/docs/img/DEVICE_MANAGEMENT/DeviceEdit_SaveDummyDevice.png)
> [!NOTE] > [!NOTE]
> >
> [Bulk-edit devices](https://github.com/jokob-sk/NetAlertX/blob/main/docs/DEVICES_BULK_EDITING.md) by using the _CSV Export_ functionality in the _Maintenance_ section. > You can couple this with the `ICMP` plugin which can be used to monitor the status of these devices, if they are actual devices reachable with the `ping` command. If not, you can use a loopback IP address so they appear online, such as `0.0.0.0` or `127.0.0.1`.
## Copying data from an existing device.
To speed up device population you can also copy data from an existing device. This can be done from the **Tools** tab on the Device details.
![Device Details][screen1]
## Main Info
- **MAC**: MAC addres of the device. Not editable.
- **Name**: Friendly device name
- **Owner**: Device owner (The list is self-populated with existing owners)
- **Type**: Select a device type from the dropdown list (Smartphone, Table,
Laptop, TV, router, ....) or type a new device type
- **Vendor**: Automatically updated by NetAlertX when empty or unknown
- **Favorite**: Mark the device as favorite and then it will appears at the
begining of the device list
- **Group**: Select a grouper ('Always on', 'Personal', Friends') or type
your own Group name
- **Comments**: Type any comments for the device
## Session Info
- **Status**: Show device status : On-line / Off-Line
- **First Session**: Date and time of the first 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
## Events & Alerts config
- **Scan Cycle**: Select the scan cycle: 0, 1', 15'
- Some devices do not respond to all ARP packets, for this cases is better
to use a 15' cycle.
- **For Apple devices I recommend using 15' cycle**
- **Alert All Events**: Send a notification in each event (connection,
disconnection, IP Changed, ...)
- **Alert Down**: Send a notification when the device is down
- *(Userful with "always connected" devices: Camera, Alexa,...)*
- **Skip repeated notifications during**: Do not send more than one
notification to this device for X hours
- *(Useful to avoid notification saturation on devices that frequently
connects and disconnects)*
# Privacy & Random MAC's
<!--- --------------------------------------------------------------------- --->
The latest versions of some operating systems (IOS and Android) incorporate a
new & interesting functionality to improve privacy: **Random MACs**.
This functionality allows you to **hide the true MAC** of the device and
**assign a random MAC** when we connect to WIFI networks.
This behavior is especially useful when connecting to WIFI's that we do not
know, but it **is totally useless when connecting to our own WIFI's** or known
networks.
**I recommend disabling this operation when connecting our devices to our own
WIFI's**, in this way, NetAlertX will be able to identify the device, and it
will not identify it as a new device every so often (every time IOS or Android
decides to change the MAC).
### IOS
![ios][ios]
- [Use private Wi-Fi addresses in iOS 14](https://support.apple.com/en-us/HT211227)
### Android
![Android][Android]
- [How to Disable MAC Randomization in Android 10](https://support.boingo.com/s/article/How-to-Disable-MAC-Randomization-in-Android-10-Android-Q)
- [How do I disable random Wi-Fi MAC address on Android 10](https://support.plume.com/hc/en-gb/articles/360052070714-How-do-I-disable-random-Wi-Fi-MAC-address-on-Android-10-)
### License
GPL 3.0
[Read more here](../LICENSE.txt)
### Contact
Always use the Issue tracker for the correct fork, for example:
[jokob-sk/NetAlertX](https://github.com/jokob-sk/NetAlertX/issues). Please also follow the guidelines on:
- [Pull Request guidelines](https://github.com/jokob-sk/NetAlertX/tree/main/docs#-pull-requests-prs)
- 🙏 [Feature request guidelines](https://github.com/jokob-sk/NetAlertX/tree/main/docs#-feature-requests)
- 🐛 [Issue guidelines](https://github.com/jokob-sk/NetAlertX/tree/main/docs#-submitting-an-issue-or-bug)
***Suggestions and comments are welcome***
<!--- --------------------------------------------------------------------- --->
[main]: ./img/1_devices.jpg "Main screen"
[screen1]: ./img/2_1_device_details.jpg "Screen 1"
[ios]: https://9to5mac.com/wp-content/uploads/sites/6/2020/08/how-to-use-private-wifi-mac-address-iphone-ipad.png?resize=2048,1009 "ios"
[Android]: ./img/android_random_mac.jpg "Android"

128
docs/DOCKER_COMPOSE.md Executable file
View File

@@ -0,0 +1,128 @@
# `docker-compose.yaml` Examples
### Example 1
```yaml
services:
netalertx:
container_name: netalertx
# use the below line if you want to test the latest dev image
# image: "jokobsk/netalertx-dev:latest"
image: "jokobsk/netalertx:latest"
network_mode: "host"
restart: unless-stopped
volumes:
- local_path/config:/app/config
- local_path/db:/app/db
# (optional) useful for debugging if you have issues setting up the container
- local_path/logs:/app/log
# (API: OPTION 1) use for performance
- type: tmpfs
target: /app/api
# (API: OPTION 2) use when debugging issues
# - local_path/api:/app/api
environment:
- TZ=Europe/Berlin
- PORT=20211
```
To run the container execute: `sudo docker-compose up -d`
### Example 2
Example by [SeimuS](https://github.com/SeimusS).
```yaml
netalertx:
container_name: NetAlertX
hostname: NetAlertX
privileged: true
# use the below line if you want to test the latest dev image
# image: "jokobsk/netalertx-dev:latest"
image: jokobsk/netalertx:latest
environment:
- TZ=Europe/Bratislava
restart: always
volumes:
- ./netalertx/db:/app/db
- ./netalertx/config:/app/config
network_mode: host
```
To run the container execute: `sudo docker-compose up -d`
### Example 3
`docker-compose.yml`
```yaml
services:
netalertx:
container_name: netalertx
# use the below line if you want to test the latest dev image
# image: "jokobsk/netalertx-dev:latest"
image: "jokobsk/netalertx:latest"
network_mode: "host"
restart: unless-stopped
volumes:
- ${APP_DATA_LOCATION}/netalertx/config:/app/config
- ${APP_DATA_LOCATION}/netalertx/db/:/app/db/
# (optional) useful for debugging if you have issues setting up the container
- ${LOGS_LOCATION}:/app/log
# (API: OPTION 1) use for performance
- type: tmpfs
target: /app/api
# (API: OPTION 2) use when debugging issues
# - local/path/api:/app/api
environment:
- TZ=${TZ}
- PORT=${PORT}
```
`.env` file
```yaml
#GLOBAL PATH VARIABLES
APP_DATA_LOCATION=/path/to/docker_appdata
APP_CONFIG_LOCATION=/path/to/docker_config
LOGS_LOCATION=/path/to/docker_logs
#ENVIRONMENT VARIABLES
TZ=Europe/Paris
PORT=20211
#DEVELOPMENT VARIABLES
DEV_LOCATION=/path/to/local/source/code
```
To run the container execute: `sudo docker-compose --env-file /path/to/.env up`
### Example 4
Courtesy of [pbek](https://github.com/pbek). The volume `netalertx_db` is used by the db directory. The two config files are mounted directly from a local folder to their places in the config folder. You can backup the `docker-compose.yaml` folder and the docker volumes folder.
```yaml
netalertx:
# use the below line if you want to test the latest dev image
# image: "jokobsk/netalertx-dev:latest"
image: jokobsk/netalertx
ports:
- "80:20211/tcp"
environment:
- TZ=Europe/Vienna
networks:
local:
ipv4_address: 192.168.1.2
restart: unless-stopped
volumes:
- netalertx_db:/app/db
- ./netalertx/:/app/config/
# (API: OPTION 1) use for performance
- type: tmpfs
target: /app/api
# (API: OPTION 2) use when debugging issues
# - local/path/api:/app/api
```

View File

@@ -2,6 +2,9 @@
NetAlertX comes with MQTT support, allowing you to show all detected devices as devices in Home Assistant. It also supplies a collection of stats, such as number of online devices. NetAlertX comes with MQTT support, allowing you to show all detected devices as devices in Home Assistant. It also supplies a collection of stats, such as number of online devices.
> [!TIP]
> You can install NetAlertX also as a Home Assistant addon [![Home Assistant](https://img.shields.io/badge/Repo-blue?logo=home-assistant&style=for-the-badge&color=0aa8d2&logoColor=fff&label=Add)](https://my.home-assistant.io/redirect/supervisor_add_addon_repository/?repository_url=https%3A%2F%2Fgithub.com%2Falexbelgium%2Fhassio-addons) via the [alexbelgium/hassio-addons](https://github.com/alexbelgium/hassio-addons/).
## ⚠ Note ## ⚠ Note
- Please note that discovery takes about ~10s per device. - Please note that discovery takes about ~10s per device.

View File

@@ -5,7 +5,7 @@ To download and install NetAlertX on the hardware/server directly use the `curl`
> [!NOTE] > [!NOTE]
> This is an Experimental feature 🧪 and it relies on community support. > This is an Experimental feature 🧪 and it relies on community support.
> >
> Looking for maintainers for this installation method 🙂 > 🙏 Looking for maintainers for this installation method 🙂
> >
> There is no guarantee that the install script or any other script will gracefully handle other installed software. > There is no guarantee that the install script or any other script will gracefully handle other installed software.
> Data loss is a possibility, **it is recommended to install NetAlertX using the supplied Docker image**. > Data loss is a possibility, **it is recommended to install NetAlertX using the supplied Docker image**.
@@ -36,6 +36,9 @@ Some facts about what and where something will be changed/installed by the HW in
## 📥 Installation via CURL ## 📥 Installation via CURL
> [!TIP]
> If the below fails try grabbing and installing one of the [previous releases](https://github.com/jokob-sk/NetAlertX/releases) and run the installation from the zip package.
```bash ```bash
curl -o install.debian.sh https://raw.githubusercontent.com/jokob-sk/NetAlertX/main/install/install.debian.sh && sudo chmod +x install.debian.sh && sudo ./install.debian.sh curl -o install.debian.sh https://raw.githubusercontent.com/jokob-sk/NetAlertX/main/install/install.debian.sh && sudo chmod +x install.debian.sh && sudo ./install.debian.sh
``` ```

View File

@@ -38,7 +38,7 @@ Copying the HTML code from [Font Awesome](https://fontawesome.com/search?o=r&m=f
> [!NOTE] > [!NOTE]
> If you want to mass-apply an icon to all devices of the same device type (Field: Type), you can click the mass-copy button (next to the "+" button). A confirmation prompt is displayed. If you proceed, icons of all devices set to the same device type as the current device, will be overwritten with the current device's icon. > If you want to mass-apply an icon to all devices of the same device type (Field: Type), you can click the mass-copy button (next to the "+" button). A confirmation prompt is displayed. If you proceed, icons of all devices set to the same device type as the current device, will be overwritten with the current device's icon.
- The blue dropdown contains all icons already used in the app for device icons. You need to navigate away or refresh the page once you add a new icon. - The dropdown contains all icons already used in the app for device icons. You might need to navigate away or refresh the page once you add a new icon.
## 🌟 Pro Font Awesome icons ## 🌟 Pro Font Awesome icons

View File

@@ -85,7 +85,7 @@ services:
- local/path/config:/app/config # ⚠ This has changed (🔺required) - local/path/config:/app/config # ⚠ This has changed (🔺required)
- local/path/db:/app/db # ⚠ This has changed (🔺required) - local/path/db:/app/db # ⚠ This has changed (🔺required)
# (optional) useful for debugging if you have issues setting up the container # (optional) useful for debugging if you have issues setting up the container
- local/path/logs:/app/front/log # ⚠ This has changed (🟡optional) - local/path/logs:/app/log # ⚠ This has changed (🟡optional)
environment: environment:
- TZ=Europe/Berlin - TZ=Europe/Berlin
- PORT=20211 - PORT=20211
@@ -135,7 +135,7 @@ services:
- local/path/config/app.conf:/app/config/app.conf # ⚠ This has changed (🔺required) - local/path/config/app.conf:/app/config/app.conf # ⚠ This has changed (🔺required)
- local/path/db/app.db:/app/db/app.db # ⚠ This has changed (🔺required) - local/path/db/app.db:/app/db/app.db # ⚠ This has changed (🔺required)
# (optional) useful for debugging if you have issues setting up the container # (optional) useful for debugging if you have issues setting up the container
- local/path/logs:/app/front/log # ⚠ This has changed (🟡optional) - local/path/logs:/app/log # ⚠ This has changed (🟡optional)
environment: environment:
- TZ=Europe/Berlin - TZ=Europe/Berlin
- PORT=20211 - PORT=20211

View File

@@ -2,7 +2,7 @@
Make sure you have a root device with the MAC `Internet` (No other MAC addresses are currently supported as the root node) set to a network device type (e.g.: **Type**:`Router`). Make sure you have a root device with the MAC `Internet` (No other MAC addresses are currently supported as the root node) set to a network device type (e.g.: **Type**:`Router`).
> 💡 Tip: You can add dummy devices via the [Undiscoverables plugin](https://github.com/jokob-sk/NetAlertX/blob/main/front/plugins/undiscoverables/README.md) > 💡 Tip: You can add dummy devices via the [Create dummy device](https://github.com/jokob-sk/NetAlertX/blob/main/docs/DEVICE_MANAGEMENT.md#dummy-devices) button in the Devices listing page.
> 💡 Tip: Export your configuration of the Network and Devices once in a while via the Export CSV feature under **Maintenance** -> **Backup/Restore** -> **CSV Export**. > 💡 Tip: Export your configuration of the Network and Devices once in a while via the Export CSV feature under **Maintenance** -> **Backup/Restore** -> **CSV Export**.

View File

@@ -29,3 +29,31 @@ The more often you scan the networks the more resources, traffic and DB read/wri
Also consider decreasing the scanned subnet, e.g. from `/16` to `/24` if need be. Also consider decreasing the scanned subnet, e.g. from `/16` to `/24` if need be.
# Store temporary files in memory
You can also store temporary files in application memory (`/app/api` and `/app/log` folders). See highlighted lines `◀` below.
```yaml
version: "3"
services:
netalertx:
container_name: netalertx
# use the below line if you want to test the latest dev image
# image: "jokobsk/netalertx-dev:latest"
image: "jokobsk/netalertx:latest"
network_mode: "host"
restart: unless-stopped
volumes:
- local/path/config:/app/config
- local/path/db:/app/db
# (optional) useful for debugging if you have issues setting up the container
- local/path/logs:/app/log
# (API: OPTION 1) use for performance
- type: tmpfs # ◀
target: /app/api # ◀
# (API: OPTION 2) use when debugging issues
# - local/path/api:/app/api
environment:
- TZ=Europe/Berlin
- PORT=20211
```

45
docs/PIHOLE_GUIDE.md Executable file
View File

@@ -0,0 +1,45 @@
# Integration with PiHole
NetAlertX comes with 2 plugins suitable for integarting with your existing PiHole instace. One plugin is using a direct SQLite DB connection, the other leverages the DHCP.leases file generated by PiHole. You can combine both approaches and also supplement it with other [plugins](/front/plugins/README.md).
## Approach 1: `DHCPLSS` Plugin - Import devices from the PiHole DHCP leases file
![DHCPLSS sample settings](/docs/img/PIHOLE_GUIDE/DHCPLSS_pihole_settings.png)
### Settings
| Setting | Description | Recommended value |
| :------------- | :------------- | :-------------|
| `DHCPLSS_RUN` | When the plugin should run. | `schedule` |
| `DHCPLSS_RUN_SCHD` | If you run multiple device scanner plugins, align the schedules of all plugins to the same value. | `*/5 * * * *` |
| `DHCPLSS_paths_to_check` | You need to map the value in this setting in the `docker-compose.yml` file. The in-container path must contain `pihole` so it's parsed correctly. | `['/etc/pihole/dhcp.leases']` |
Check the [DHCPLSS plugin readme](https://github.com/jokob-sk/NetAlertX/tree/main/front/plugins/dhcp_leases#overview) for details
### docker-compose changes
| Path | Description |
| :------------- | :------------- |
| `:/etc/pihole/dhcp.leases` | PiHole's `dhcp.leases` file. Required if you want to use PiHole `dhcp.leases` file. This has to be matched with a corresponding `DHCPLSS_paths_to_check` setting entry (the path in the container must contain `pihole`) |
## Approach 2: `PIHOLE` Plugin - Import devices directly from the PiHole database
![DHCPLSS sample settings](/docs/img/PIHOLE_GUIDE/PIHOLE_settings.png)
| Setting | Description | Recommended value |
| :------------- | :------------- | :-------------|
| `PIHOLE_RUN` | When the plugin should run. | `schedule` |
| `PIHOLE_RUN_SCHD` | If you run multiple device scanner plugins, align the schedules of all plugins to the same value. | `*/5 * * * *` |
| `PIHOLE_DB_PATH` | You need to map the value in this setting in the `docker-compose.yml` file. | `/etc/pihole/pihole-FTL.db` |
Check the [PiHole plugin readme](https://github.com/jokob-sk/NetAlertX/tree/main/front/plugins/pihole_scan) for details
### docker-compose changes
| Path | Description |
| :------------- | :------------- |
| `:/etc/pihole/pihole-FTL.db` | PiHole's `pihole-FTL.db` database file. |
Check out other [plugins](/front/plugins/README.md) that can help you discover more about your network or check how to scan [Remote networks](/docs/REMOTE_NETWORKS.md).

View File

@@ -12,6 +12,9 @@ NetAlertX comes with a plugin system to feed events from third-party scripts int
### 🎥 Watch the video: ### 🎥 Watch the video:
> [!TIP]
> Read this guide [Development environment setup guide](/docs/DEV_ENV_SETUP.md) to set up your local environment for development. 👩‍💻
[![Watch the video](/docs/img/YouTube_thumbnail.png)](https://youtu.be/cdbxlwiWhv8) [![Watch the video](/docs/img/YouTube_thumbnail.png)](https://youtu.be/cdbxlwiWhv8)
### 📸 Screenshots ### 📸 Screenshots
@@ -46,7 +49,7 @@ Please read the below carefully if you'd like to contribute with a plugin yourse
|----------------------|----------------------|----------------------| |----------------------|----------------------|----------------------|
| `config.json` | yes | Contains the plugin configuration (manifest) including the settings available to the user. | | `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. | | `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. | | `last_result.<prefix>.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. Stored in the `/api/log/plugins/` |
| `script.log` | no | Logging output (recommended) | | `script.log` | no | Logging output (recommended) |
| `README.md` | yes | Any setup considerations or overview | | `README.md` | yes | Any setup considerations or overview |
@@ -103,7 +106,7 @@ Currently, these data sources are supported (valid `data_source` value).
| 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. | | 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` ). | | 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. > * "Needs to return a "table" means that the application expects a `last_result.<prefix>.log` file with some results. It's not a blocker, however warnings in the `app.log` might be logged.
> 🔎Example > 🔎Example
>```json >```json
@@ -120,21 +123,21 @@ You can show or hide the UI on the "Plugins" page and "Plugins" tab for a plugin
### "data_source": "script" ### "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. 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.<prefix>.log` file (not required if you don't import any data into the app). The `last_result.<prefix>.log` file needs to be saved in `/api/log/plugins`.
> [!IMPORTANT] > [!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). > 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.<prefix>.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 content of the `last_result.<prefix>.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. - The format of the `last_result.<prefix>.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`. - 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". - Don't render "headers" for these "columns".
Every scan result/event entry needs to be on a new line. 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. - 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. - The order of these "columns" can't be changed.
#### 🔎 last_result.log examples #### 🔎 last_result.prefix.log examples
Valid CSV: Valid CSV:
@@ -657,7 +660,7 @@ The UI will adjust how columns are displayed in the UI based on the resolvers de
| Type Definitions | | | 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_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_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. | | `device_name_mac` | The value is considered to be a MAC address, and a link pointing to the device with the given MAC 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. | | `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. | | `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. | | `url_http_https` | Generates two links with the `https` and `http` prefix as lock icons. |

View File

@@ -29,6 +29,7 @@ There is also an in-app Help / FAQ section that should be answering frequently a
- [Synology Guide](/docs/SYNOLOGY_GUIDE.md) - [Synology Guide](/docs/SYNOLOGY_GUIDE.md)
- [Subnets and VLANs configuration for arp-scan](/docs/SUBNETS.md) - [Subnets and VLANs configuration for arp-scan](/docs/SUBNETS.md)
- [Scanning Remote Networks](/docs/REMOTE_NETWORKS.md)
- [SMTP server config](/docs/SMTP.md) - [SMTP server config](/docs/SMTP.md)
- [Custom Icon configuration and support](/docs/ICONS.md) - [Custom Icon configuration and support](/docs/ICONS.md)
- [Notifications](/docs/NOTIFICATIONS.md) - [Notifications](/docs/NOTIFICATIONS.md)
@@ -56,7 +57,6 @@ There is also an in-app Help / FAQ section that should be answering frequently a
- [Manage devices (legacy docs)](/docs/DEVICE_MANAGEMENT.md) - [Manage devices (legacy docs)](/docs/DEVICE_MANAGEMENT.md)
- [Random MAC/MAC icon meaning (legacy docs)](/docs/RANDOM_MAC.md) - [Random MAC/MAC icon meaning (legacy docs)](/docs/RANDOM_MAC.md)
#### 🔎 Examples #### 🔎 Examples
- [N8N webhook example](/docs/WEBHOOK_N8N.md) - [N8N webhook example](/docs/WEBHOOK_N8N.md)

49
docs/REMOTE_NETWORKS.md Executable file
View File

@@ -0,0 +1,49 @@
# Scanning Remote or Inaccessible Networks
By design, local network scanners such as `arp-scan` use ARP (Address Resolution Protocol) to map IP addresses to MAC addresses on the local network. Since ARP operates at Layer 2 (Data Link Layer), it typically works only within a single broadcast domain, usually limited to a single router or network segment.
To scan multiple locally accessible network segments, add them as subnets according to the [subnets](https://github.com/jokob-sk/NetAlertX/blob/main/docs/SUBNETS.md) documentation.
## Complex Use Cases
The following network setups might make some devices undetectable. Check the specific setup to understand the cause and find potential workarounds to still report on these devices.
### Wi-Fi Extenders
Wi-Fi extenders typically create a separate network or subnet, which can prevent network scanning tools like `arp-scan` from detecting devices behind the extender.
> **Possible workaround**: Scan the specific subnet that the extender uses, if it is separate from the main network.
### VPNs
ARP operates at Layer 2 (Data Link Layer) and works only within a local area network (LAN). VPNs, which operate at Layer 3 (Network Layer), route traffic between networks, preventing ARP requests from discovering devices outside the local network.
VPNs use virtual interfaces (e.g., `tun0`, `tap0`) to encapsulate traffic, bypassing ARP-based discovery. Additionally, many VPNs use NAT, which masks individual devices behind a shared IP address.
> **Possible workaround**: Configure the VPN to bridge networks instead of routing to enable ARP, though this depends on the VPN setup and security requirements.
# Other Workarounds
The following workarounds should work for most complex network setups.
## Supplementing Plugins
You can use supplementary plugins that employ alternate methods. Protocols used by the `SNMPDSC` or `DHCPLSS` plugins are widely supported on different routers and can be effective as workarounds. Check the [plugins list](https://github.com/jokob-sk/NetAlertX/blob/main/front/plugins/README.md) to find a plugin that works with your router and network setup.
## Multiple NetAlertX Instances
If you have servers in different networks, you can set up separate NetAlertX instances on those subnets and synchronize the results into one instance using the [`SYNC` plugin](https://github.com/jokob-sk/NetAlertX/tree/main/front/plugins/sync).
## Manual Entry
If you don't need to discover new devices and only need to report on their status (`online`, `offline`, `down`), you can manually enter devices and check their status using the [`ICMP` plugin](https://github.com/jokob-sk/NetAlertX/blob/main/front/plugins/icmp_scan/), which uses the `ping` command internally.
For more information on how to add devices manually (or dummy devices), refer to the [Device Management](https://github.com/jokob-sk/NetAlertX/blob/main/docs/DEVICE_MANAGEMENT.md) documentation.
To create truly dummy devices, you can use a loopback IP address (e.g., `0.0.0.0` or `127.0.0.1`) so they appear online.
## NMAP and Fake MAC Addresses
Scanning remote networks with NMAP is possible (via the `NMAPDEV` plugin), but since it cannot retrieve the MAC address, you need to enable the `NMAPDEV_FAKE_MAC` setting. This will generate a fake MAC address based on the IP address, allowing you to track devices. However, this can lead to inconsistencies, especially if the IP address changes or a previously logged device is rediscovered. If this setting is disabled, only the IP address will be discovered, and devices with missing MAC addresses will be skipped.
Check the [NMAPDEV plugin](https://github.com/jokob-sk/NetAlertX/tree/main/front/plugins/nmap_dev_scan) for details

View File

@@ -40,7 +40,7 @@ services:
volumes: volumes:
- /home/netalertx/config:/app/config - /home/netalertx/config:/app/config
- /home/netalertx/db:/app/db - /home/netalertx/db:/app/db
- /home/netalertx/log:/app/front/log - /home/netalertx/log:/app/log
environment: environment:
- TZ=Europe/Berlin - TZ=Europe/Berlin
- PORT=20211 - PORT=20211
@@ -66,7 +66,7 @@ services:
volumes: volumes:
- ./config/app.conf:/app/config/app.conf - ./config/app.conf:/app/config/app.conf
- ./db:/app/db - ./db:/app/db
- ./log:/app/front/log - ./log:/app/log
- ./config/resolv.conf:/etc/resolv.conf # Mapping the /resolv.conf file for better name resolution - ./config/resolv.conf:/etc/resolv.conf # Mapping the /resolv.conf file for better name resolution
environment: environment:
- TZ=Europe/Berlin - TZ=Europe/Berlin

62
docs/SESSION_INFO.md Executable file
View File

@@ -0,0 +1,62 @@
# Sessions Section in Device View
The **Sessions Section** provides details about a device's connection history. This data is automatically detected and cannot be edited by the user.
![Session info](/docs/img/SESSION_INFO/DeviceDetails_SessionInfo.png)
---
## Key Fields
1. **Date and Time of First Connection**
- **Description:** Displays the first detected connection time for the device.
- **Editability:** Uneditable (auto-detected).
- **Source:** Automatically captured when the device is first added to the system.
2. **Date and Time of Last Connection**
- **Description:** Shows the most recent time the device was online.
- **Editability:** Uneditable (auto-detected).
- **Source:** Updated with every new connection event.
3. **Offline Devices with Missing or Conflicting Data**
- **Description:** Handles cases where a device is offline but has incomplete or conflicting session data (e.g., missing start times).
- **Handling:** The system flags these cases for review and attempts to infer missing details.
---
## How Sessions are Discovered and Calculated
### 1. Detecting New Devices
When a device is first detected in the network, the system logs it in the events table:
`INSERT INTO Events (eve_MAC, eve_IP, eve_DateTime, eve_EventType, eve_AdditionalInfo, eve_PendingAlertEmail) SELECT cur_MAC, cur_IP, '{startTime}', 'New Device', cur_Vendor, 1 FROM CurrentScan WHERE NOT EXISTS (SELECT 1 FROM Devices WHERE devMac = cur_MAC)`
- Devices scanned in the current cycle (**CurrentScan**) are checked against the **Devices** table.
- If a device is new:
- A **New Device** event is logged.
- The devices MAC, IP, vendor, and detection time are recorded.
### 2. Logging Connection Sessions
When a new connection is detected, the system creates a session record:
`INSERT INTO Sessions (ses_MAC, ses_IP, ses_EventTypeConnection, ses_DateTimeConnection, ses_EventTypeDisconnection, ses_DateTimeDisconnection, ses_StillConnected, ses_AdditionalInfo) SELECT cur_MAC, cur_IP, 'Connected', '{startTime}', NULL, NULL, 1, cur_Vendor FROM CurrentScan WHERE NOT EXISTS (SELECT 1 FROM Sessions WHERE ses_MAC = cur_MAC)`
- A new session is logged in the **Sessions** table if no prior session exists.
- Fields like `MAC`, `IP`, `Connection Type`, and `Connection Time` are populated.
- The `Still Connected` flag is set to `1` (active connection).
### 3. Handling Missing or Conflicting Data
- Devices with incomplete or conflicting session data (e.g., missing start times) are detected.
- The system flags these records and attempts corrections by inferring details from available data.
### 4. Updating Sessions
- When a device reconnects, its session is updated with a new connection timestamp.
- When a device disconnects:
- The **Disconnection Time** is recorded.
- The `Still Connected` flag is set to `0`.
The session information is then used to display the device presence under **Monitoring** -> **Presence**.
![Monitoring Device Presence](/docs/img/SESSION_INFO/Monitoring_Presence.png)

View File

@@ -2,15 +2,13 @@
You need to specify the network interface and the network mask. You can also configure multiple subnets and specify VLANs (see VLAN exceptions below). You need to specify the network interface and the network mask. You can also configure multiple subnets and specify VLANs (see VLAN exceptions below).
`ARPSCAN` can scan multiple networks if the network allows it. To scan networks directly, the subnets must be accessible from the network where NetAlertX is running. This means NetAlertX needs to have access to the interface attached to that subnet. You can verify this by running the following command in the container: `ARPSCAN` can scan multiple networks if the network allows it. To scan networks directly, the subnets must be accessible from the network where NetAlertX is running. This means NetAlertX needs to have access to the interface attached to that subnet. You can verify this by running the following command in the container (replace the interface and ip mask):
`sudo arp-scan --interface=eth0 192.168.1.0/24` `sudo arp-scan --interface=eth0 192.168.1.0/24`
In this example, `--interface=eth0 192.168.1.0/24` represents a neighboring subnet. If this command returns no results, the network is not accessible due to your network or firewall restrictions. In this example, `--interface=eth0 192.168.1.0/24` represents a neighboring subnet. If this command returns no results, the network is not accessible due to your network or firewall restrictions.
If direct scans are not possible, you can use [supplementing plugins](https://github.com/jokob-sk/NetAlertX/blob/main/front/plugins/README.md) that use alternate methods. Protocols used by the `SNMPDSC` or `DHCPLSS` plugins have good support and usually can be used as a workaround. If direct scans are not possible (Wi-Fi Extenders, VPNs and inaccessible networks), check the [remote networks documentation](https://github.com/jokob-sk/NetAlertX/blob/main/docs/REMOTE_NETWORKS.md).
Alternatively, you can set up separate NetAlertX instances running on the subnets and synchronize the results into one instance with the [`SYNC` plugin](https://github.com/jokob-sk/NetAlertX/tree/main/front/plugins/sync).
> [!TIP] > [!TIP]
> You may need to increase the time between scans `ARPSCAN_RUN_SCHD` and the timeout `ARPSCAN_RUN_TIMEOUT` (and similar settings for related plugins) when adding more subnets. If the timeout setting is exceeded, the scan is canceled to prevent the application from hanging due to rogue plugins. > You may need to increase the time between scans `ARPSCAN_RUN_SCHD` and the timeout `ARPSCAN_RUN_TIMEOUT` (and similar settings for related plugins) when adding more subnets. If the timeout setting is exceeded, the scan is canceled to prevent the application from hanging due to rogue plugins.
@@ -24,7 +22,7 @@ Alternatively, you can set up separate NetAlertX instances running on the subnet
* **Examples for one and two subnets:** * **Examples for one and two subnets:**
* One subnet: `SCAN_SUBNETS = ['192.168.1.0/24 --interface=eth0']` * One subnet: `SCAN_SUBNETS = ['192.168.1.0/24 --interface=eth0']`
* Two subnets: `SCAN_SUBNETS = ['192.168.1.0/24 --interface=eth0','192.168.1.0/24 --interface=eth1 -vlan=107']` * Two subnets: `SCAN_SUBNETS = ['192.168.1.0/24 --interface=eth0','192.168.1.0/24 --interface=eth1 --vlan=107']`
If you get timeout messages, decrease the network mask (e.g.: from `/16` to `/24`) or increase the `TIMEOUT` setting (e.g.: `ARPSCAN_RUN_TIMEOUT` to `300` (5-minute timeout)) for the plugin and the interval between scans (e.g.: `ARPSCAN_RUN_SCHD` to `*/10 * * * *` (scans every 10 minutes)). If you get timeout messages, decrease the network mask (e.g.: from `/16` to `/24`) or increase the `TIMEOUT` setting (e.g.: `ARPSCAN_RUN_TIMEOUT` to `300` (5-minute timeout)) for the plugin and the interval between scans (e.g.: `ARPSCAN_RUN_SCHD` to `*/10 * * * *` (scans every 10 minutes)).
@@ -47,18 +45,24 @@ Specify the network filter, which **significantly** speeds up the scan process.
**Example value:** `--interface=eth0` **Example value:** `--interface=eth0`
The adapter will probably be `eth0` or `eth1`. (Check `System Info` > `Network Hardware` or run `iwconfig` in the container to find your interface name(s)). The adapter will probably be `eth0` or `eth1`. (Check `System Info` > `Network Hardware`, or run `iwconfig` in the container to find your interface name(s)).
![Network hardware](/docs/img/SUBNETS/system_info-network_hardware.png) ![Network hardware](/docs/img/SUBNETS/system_info-network_hardware.png)
> [!TIP] > [!TIP]
> As an alternative to `iwconfig`, run `ip -o link show | awk -F': ' '!/lo|vir|docker/ {print $2}'` in your container to find your interface name(s) (e.g.: `eth0`, `eth1`). > As an alternative to `iwconfig`, run `ip -o link show | awk -F': ' '!/lo|vir|docker/ {print $2}'` in your container to find your interface name(s) (e.g.: `eth0`, `eth1`):
> ```bash
> Synology-NAS:/# ip -o link show | awk -F': ' '!/lo|vir|docker/ {print $2}'
> sit0@NONE
> eth1
> eth0
> ```
### VLANs ### VLANs
**Example value:** `-vlan=107` **Example value:** `--vlan=107`
- Append `-vlan=107` to the interface field (e.g.: `eth0 -vlan=107`) for multiple VLANs. More details are available in this [comment](https://github.com/jokob-sk/NetAlertX/issues/170#issuecomment-1419902988). - Append `--vlan=107` to the `SCAN_SUBNETS` field (e.g.: `192.168.1.0/24 --interface=vmbr0 --vlan=107`) for multiple VLANs.
#### VLANs on a Hyper-V Setup #### VLANs on a Hyper-V Setup
@@ -85,7 +89,6 @@ By default, Hyper-V only allows untagged packets through to the VM interface, bl
2. Within the VM, set up sub-interfaces for each VLAN to enable scanning. On Ubuntu 22.04, Netplan can be used. In /etc/netplan/00-installer-config.yaml, add VLAN definitions: 2. Within the VM, set up sub-interfaces for each VLAN to enable scanning. On Ubuntu 22.04, Netplan can be used. In /etc/netplan/00-installer-config.yaml, add VLAN definitions:
```yaml ```yaml
network: network:
ethernets: ethernets:
eth0: eth0:
@@ -111,20 +114,3 @@ Please note the accessibility of macvlans when configured on the same computer.
- NetAlertX does not detect the macvlan container when it is running on the same computer. - NetAlertX does not detect the macvlan container when it is running on the same computer.
- NetAlertX recognizes the macvlan container when it is running on a different computer. - NetAlertX recognizes the macvlan container when it is running on a different computer.
### Wi-Fi Extenders
A Wi-Fi extender typically works by creating a separate network or subnet, which can cause certain network scanning tools, like `arp-scan`, to be unable to detect devices behind the extender.
This happens because `arp-scan` uses ARP (Address Resolution Protocol) to map IP addresses to MAC addresses on the local network. Since ARP is a Layer 2 (data link layer) protocol, it usually only works within a single broadcast domain, which is typically limited to a single router or network segment.
When you introduce a Wi-Fi extender, it may isolate devices on different segments of the network, meaning ARP packets cannot easily traverse from one segment (your main network) to another (the network behind the extender).
To scan devices behind the extender, you can try:
- Scanning the specific subnet that the extender uses, if it is separate from the main network.
- Using [supplementing plugins](https://github.com/jokob-sk/NetAlertX/blob/main/front/plugins/README.md) that use alternate methods. Protocols used by the `SNMPDSC` or `DHCPLSS` plugins have good support and usually can be used as a workaround.
Check the [plugins list](https://github.com/jokob-sk/NetAlertX/blob/main/front/plugins/README.md) to find a plugin supported by your router and your network setup.

View File

@@ -43,7 +43,7 @@ services:
- local/path/config:/app/config - local/path/config:/app/config
- local/path/db:/app/db - local/path/db:/app/db
# (optional) useful for debugging if you have issues setting up the container # (optional) useful for debugging if you have issues setting up the container
- local/path/logs:/app/front/log - local/path/logs:/app/log
environment: environment:
- TZ=Europe/Berlin - TZ=Europe/Berlin
- PORT=20211 - PORT=20211
@@ -60,7 +60,7 @@ services:
- /volume1/app_storage/netalertx/config:/app/config - /volume1/app_storage/netalertx/config:/app/config
- /volume1/app_storage/netalertx/db:/app/db - /volume1/app_storage/netalertx/db:/app/db
# (optional) useful for debugging if you have issues setting up the container # (optional) useful for debugging if you have issues setting up the container
# - local/path/logs:/app/front/log <- commented out with # ⚠ # - local/path/logs:/app/log <- commented out with # ⚠
``` ```
![Adjusting docker-compose](/docs/img/SYNOLOGY/08_Adjust_docker_compose_volumes.png) ![Adjusting docker-compose](/docs/img/SYNOLOGY/08_Adjust_docker_compose_volumes.png)

View File

@@ -1,87 +0,0 @@
# Pi.Alert Version History
<!--- --------------------------------------------------------------------- --->
| Version | Description |
| ------- | --------------------------------------------------------------- |
| v3.00 | Major set of New features & Enhancements |
| v2.70 | New features & Usability improvements in the web prontal |
| v2.61 | Bug fixing |
| v2.60 | Improved the compability of installation process (Ubuntu) |
| v2.56 | Bug fixing |
| v2.55 | Bug fixing |
| v2.52 | Bug fixing |
| v2.51 | Bug fixing |
| v2.50 | First public release |
# 🆕 2022+ [Newest Release notes](https://github.com/jokob-sk/NetAlertX/issues/138)
## Pi.Alert v3.02
<!--- --------------------------------------------------------------------- --->
**PENDING UPDATE DOC**
- Fixed: UNIQUE constraint failed with Local MAC #114
## Pi.Alert v3.01
<!--- --------------------------------------------------------------------- --->
**PENDING UPDATE DOC**
- Fixed: Problem with local MAC & IP (raspberry) #106
## Pi.Alert v3.00
<!--- --------------------------------------------------------------------- --->
**PENDING UPDATE DOC**
- `arp-scan` config options: interface, several subnets. #101 #15
- Next/previos button while editing devices #66 #37
- Internet presence/sessions monitoring #63
- Logical delete / archive / hide Device #93
- Flag to mark device with random MAC's #87
- New Device Types predefined in combobox #92
- Ask before leave the page with unsaved changes #104
- Option to don't mark devices as new during installation #94
- Uninstall script #62
- Fixed: Error updating name of devices w/o IP #97
- Fixed: Deleted devices reappear #84
- Fixed: Device running Pi.Alert must be marked as "on-line" #76
- Fixed: Incorrect calculation of presence hours #102
- Fixed: Problem redirect to homepage clicking in logo #103
## Pi.Alert v2.70
<!--- --------------------------------------------------------------------- --->
- Added Client names resolution #43
- Added Check to mark devices as "known" #16
- Remember "Show XXX entries" dropdown value #16 #26
- Remember "sorting" in devices #16
- Remember "Device panel " in device detail #16
- Added "All" option to "Show x Entries" option #16
- Added optional Location field (Door, Basement, etc.) to devices #16
- "Device updated successfully" message now is not modal #16
- Now is possible to delete Devices #16
- Added Device Type Singleboard Computer (SBC) #16
- Allowed to use " in device name #42
## Pi.Alert v2.60
<!--- --------------------------------------------------------------------- --->
- `pialert.conf` moved from `back` to `config` folder
- `pialert.conf` splitted in two files: `pialert.conf` and `version.conf`
- Added compatibility with Python 3 (default version installed with Ubuntu)
- Added compatibility in the Installation guide with Ubuntu server
- Eliminated some unnecessary packages from the installation
### License
GPL 3.0
[Read more here](../LICENSE.txt)
### Contact
Always use the Issue tracker for the correct fork, for example:
[jokob-sk/NetAlertX](https://github.com/jokob-sk/NetAlertX/issues). Please also follow the guidelines on:
- [Pull Request guidelines](https://github.com/jokob-sk/NetAlertX/tree/main/docs#-pull-requests-prs)
- 🙏 [Feature request guidelines](https://github.com/jokob-sk/NetAlertX/tree/main/docs#-feature-requests)
- 🐛 [Issue guidelines](https://github.com/jokob-sk/NetAlertX/tree/main/docs#-submitting-an-issue-or-bug)

View File

@@ -41,7 +41,7 @@ In the container execute:
`cat /var/log/nginx/error.log` `cat /var/log/nginx/error.log`
`cat /app/front/log/app.php_errors.log` `cat /app/log/app.php_errors.log`
## 8. Make sure permissions are correct ## 8. Make sure permissions are correct

Binary file not shown.

After

Width:  |  Height:  |  Size: 88 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 99 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 57 KiB

After

Width:  |  Height:  |  Size: 112 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 18 KiB

BIN
docs/img/showcase.gif Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 MiB

10
front/.well-known/gpc.json Executable file
View File

@@ -0,0 +1,10 @@
{
"gpc": true,
"compliance": [
{
"regulation": "GDPR",
"status": "not applicable"
}
]
}

View File

@@ -9,6 +9,8 @@
</div> </div>
</section> </section>
<script> <script>
// show loading dialog // show loading dialog
@@ -17,7 +19,7 @@ showSpinner()
$(document).ready(function() { $(document).ready(function() {
// Load JSON data from the provided URL // Load JSON data from the provided URL
$.getJSON('api/table_appevents.json', function(data) { $.getJSON('/php/server/query_json.php?file=table_appevents.json', function(data) {
// Process the JSON data and generate UI dynamically // Process the JSON data and generate UI dynamically
processData(data) processData(data)
@@ -89,7 +91,3 @@ function processData(data) {
</script> </script>
<!-- Datatable -->
<link rel="stylesheet" href="lib/datatables.net-bs/css/dataTables.bootstrap.min.css"/>
<script src="lib/datatables.net/js/jquery.dataTables.min.js"></script>
<script src="lib/datatables.net-bs/js/dataTables.bootstrap.min.js"></script>

View File

@@ -42,6 +42,12 @@ h5
cursor: help; cursor: help;
} }
.alignRight
{
text-align: end;
float: inline-end;
}
/* ----------------------------------------------------------------------------- /* -----------------------------------------------------------------------------
Text Classes Text Classes
----------------------------------------------------------------------------- */ ----------------------------------------------------------------------------- */
@@ -281,11 +287,6 @@ body
margin-left: 150px; margin-left: 150px;
} }
#settingsPage
{
display: grid;
}
@media (max-width: 767px) { @media (max-width: 767px) {
.main-header .logo { .main-header .logo {
@@ -614,7 +615,7 @@ body
.modal_red .modal_red
{ {
color: rgb(245, 245, 245); color: #d41001;
background-color: #e2acaa; background-color: #e2acaa;
border-color: #d41001; border-color: #d41001;
} }
@@ -887,6 +888,34 @@ height: 50px;
} */ } */
} }
/* Hide unusable buttons on the settings page for the NEWDEV plugin*/
#settingsPage #add_option_NEWDEV_devGroup,
#settingsPage #add_option_NEWDEV_devLocation,
#settingsPage #add_option_NEWDEV_devOwner,
#settingsPage #copy_icons_NEWDEV_devIcon,
#settingsPage #add_icon_NEWDEV_devIcon,
#settingsPage #add_option_NEWDEV_devSite,
#settingsPage #add_option_NEWDEV_devType
{
display: none;
}
#settingsPage
{
display: grid;
}
#settingsPage .small-box .inner .card-title {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
color: white;
}
.settingswrap .settingswrap
{ {
margin-bottom: 100px; margin-bottom: 100px;
@@ -920,16 +949,6 @@ height: 50px;
top:0px; top:0px;
} }
.overview-section
{
/* border-top: solid;
border-width: medium;
border-width: medium;
border-width: 1px;
border-radius: 15px;
margin-bottom: 3px; */
}
.settings-group i{ .settings-group i{
font-size: 16px; font-size: 16px;
@@ -1260,6 +1279,11 @@ input[readonly] {
display: flex; display: flex;
} }
.iconColumn
{
max-height: 25px;
}
.iconPreviewSelector svg .iconPreviewSelector svg
{ {
width:40px; width:40px;
@@ -1276,6 +1300,31 @@ input[readonly] {
min-width: 40px; min-width: 40px;
} }
.iconPreview svg{
min-width: 20px;
max-width: 20px;
}
.dummyDevice
{
text-align: end;
}
#tableDevices .fab
{
font-size: 1.2em;
}
#tableDevices .fa
{
font-size: 1.0em;
}
#tableDevices tbody tr
{
height: 1.5em;
}
.info-icon-nav .info-icon-nav
{ {
@@ -1344,10 +1393,93 @@ input[readonly] {
text-align: left; text-align: left;
} }
#devicePageInfoPlc
{
display: none;
padding-top: 2em;
}
/* ----------------------------------------------------------------- */
/* Device details */
/* ----------------------------------------------------------------- */
.dataTables_length label .form-control, .dataTables_filter label .form-control
{
float: none;
}
.form-inline .input-group
{
width: 100%;
}
.input-group .actionIcon
{
height: 2.4em;
}
.input-group .actionIcon:hover
{
backdrop-filter: brightness(50%);
background-color: rgba(0, 0, 0, 0.2); /* darkens the background */
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
transition: box-shadow 0.1s ease-in-out, background-color 0.1s ease-in-out;
}
.devicePropAction
{
width: 1.2em;
height: 1.2em;
display: inline-block;
padding: 0.1em;
margin: 0.1em;
/* transition: font-size 0.3s;*/
}
.devicePropAction:hover
{
font-size: larger;
padding: 0em;
margin: 0em;
}
#panDetails .dataTables_wrapper .bottom div
{
max-width: 34%;
display: block;
float:inline-end;
height: 2em;
}
#panDetails .dataTables_wrapper .bottom .dataTables_info
{
float:inline-start;
}
#panDetails .dataTables_wrapper .bottom .dataTables_length
{
padding: 0.3em;
}
/* #panDetails .dataTables_wrapper .bottom .paging_simple_numbers */
#panDetails #NEWDEV_devIcon
{
display: none;
}
#panDetails #NEWDEV_devCustomProps_label
{
display: none;
}
/* ----------------------------------------------------------------- */ /* ----------------------------------------------------------------- */
/* MODAL popups */ /* MODAL popups */
/* ----------------------------------------------------------------- */ /* ----------------------------------------------------------------- */
#modal-input-textarea #modal-input-textarea
{ {
width: 100%; width: 100%;
@@ -1491,6 +1623,7 @@ input[readonly] {
/* ----------------------------------------------------------------- */ /* ----------------------------------------------------------------- */
.plugin-filters .plugin-filters
{ {
margin: 7px; margin: 7px;
@@ -1518,6 +1651,7 @@ input[readonly] {
.integrations-plugins .content .integrations-plugins .content
{ {
display: table; display: table;
width: 100%;
} }
.plugin-content .tab-content .plugin-content .tab-content
@@ -1569,13 +1703,6 @@ input[readonly] {
} }
} }
#settingsPage .small-box .inner .card-title {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
color: white;
}
.textOverflow .textOverflow
{ {
@@ -1682,7 +1809,8 @@ input[readonly] {
table.dataTable tbody > tr.selected table.dataTable tbody > tr.selected
{ {
color:red; /* color:red; */
color: #353c42;
} }
/* ----------------------------------------------------------------------------- /* -----------------------------------------------------------------------------

View File

@@ -17,9 +17,9 @@ html {
background-color: #353c42; background-color: #353c42;
} }
body { /* body {
background-image: url('../img/boxed-bg-dark.png') !important; background-image: url('../img/boxed-bg-dark.png') !important;
} } */
body, .bg-yellow, .callout.callout-warning, .alert-warning, .label-warning, .modal-warning .modal-body { body, .bg-yellow, .callout.callout-warning, .alert-warning, .label-warning, .modal-warning .modal-body {
@@ -732,4 +732,12 @@ input[type="password"]::-webkit-caps-lock-indicator {
background-color: #000 !important; background-color: #000 !important;
} }
.callout code {
background-color: #fff !important;
color:#000 !important;
}
.thresholdFormControl
{
color:#000;
}

View File

@@ -19,9 +19,9 @@
background-color: #353c42; background-color: #353c42;
} }
body { /* body {
background-image: url('../img/boxed-bg-dark.png') !important; background-image: url('../img/boxed-bg-dark.png') !important;
} } */
body, .bg-yellow, .callout.callout-warning, .alert-warning, .label-warning, .modal-warning .modal-body { body, .bg-yellow, .callout.callout-warning, .alert-warning, .label-warning, .modal-warning .modal-body {
@@ -735,3 +735,13 @@
} }
} }
.callout code {
background-color: #fff !important;
color:#000 !important;
}
.thresholdFormControl
{
color:#000;
}

View File

@@ -17,6 +17,8 @@
?> ?>
<!-- Page ------------------------------------------------------------------ --> <!-- Page ------------------------------------------------------------------ -->
<div class="content-wrapper"> <div class="content-wrapper">
@@ -28,6 +30,14 @@
&nbsp<small>Quering device info...</small> &nbsp<small>Quering device info...</small>
</h1> </h1>
<div id="devicePageInfoPlc" class="card-body bg-light">
<div class="small-box panel rounded">
<div class="inner text-center">
</div>
</div>
</div>
<!-- period selector --> <!-- period selector -->
<span class="breadcrumb" style="top: 0px;"> <span class="breadcrumb" style="top: 0px;">
<select class="form-control" id="period" onchange="javascript: periodChanged();"> <select class="form-control" id="period" onchange="javascript: periodChanged();">
@@ -86,20 +96,10 @@
<!-- tab page 2 ------------------------------------------------------------ --> <!-- tab page 2 ------------------------------------------------------------ -->
<div class="tab-pane fade table-responsive" id="panSessions"> <div class="tab-pane fade table-responsive" id="panSessions">
<?php
<!-- Datatable Session --> require 'deviceDetailsSessions.php';
<table id="tableSessions" class="table table-bordered table-hover table-striped "> ?>
<thead>
<tr>
<th><?= lang('DevDetail_SessionTable_Order');?></th>
<th><?= lang('DevDetail_SessionTable_Connection');?></th>
<th><?= lang('DevDetail_SessionTable_Disconnection');?></th>
<th><?= lang('DevDetail_SessionTable_Duration');?></th>
<th><?= lang('DevDetail_SessionTable_IP');?></th>
<th><?= lang('DevDetail_SessionTable_Additionalinfo');?></th>
</tr>
</thead>
</table>
</div> </div>
@@ -115,35 +115,24 @@
</div> </div>
<!-- tab page 3 ------------------------------------------------------------ --> <!-- tab page 3 ------------------------------------------------------------ -->
<div class="tab-pane fade table-responsive" id="panPresence"> <div class="tab-pane fade table-responsive" id="panPresence">
<?php
// Include the other page
include 'deviceDetailsPresence.php';
?>
<!-- Calendar -->
<div id="calendar">
</div>
</div> </div>
<!-- tab page 4 ------------------------------------------------------------ --> <!-- tab page 4 ------------------------------------------------------------ -->
<div class="tab-pane fade table-responsive" id="panEvents"> <div class="tab-pane fade table-responsive" id="panEvents">
<!-- Hide Connections --> <?php
<div class="text-center"> // Include the other page
<label> include 'deviceDetailsEvents.php';
<input class="checkbox blue hidden" id="chkHideConnectionEvents" type="checkbox" checked> ?>
<?= lang('DevDetail_Events_CheckBox');?>
</label>
</div>
<!-- Datatable Events -->
<table id="tableEvents" class="table table-bordered table-hover table-striped ">
<thead>
<tr>
<th><?= lang("DevDetail_Tab_EventsTableDate");?></th>
<th><?= lang("DevDetail_Tab_EventsTableEvent");?></th>
<th><?= lang("DevDetail_Tab_EventsTableIP");?></th>
<th><?= lang("DevDetail_Tab_EventsTableInfo");?></th>
</tr>
</thead>
</table>
</div> </div>
<!-- tab page 7 ------------------------------------------------------------ --> <!-- tab page 7 ------------------------------------------------------------ -->
@@ -179,25 +168,6 @@
require 'php/templates/footer.php'; require 'php/templates/footer.php';
?> ?>
<!-- ----------------------------------------------------------------------- -->
<!-- iCkeck -->
<link rel="stylesheet" href="lib/iCheck/all.css">
<script src="lib/iCheck/icheck.min.js"></script>
<!-- Datatable -->
<link rel="stylesheet" href="lib/datatables.net-bs/css/dataTables.bootstrap.min.css">
<script src="lib/datatables.net/js/jquery.dataTables.min.js"></script>
<script src="lib/datatables.net-bs/js/dataTables.bootstrap.min.js"></script>
<!-- fullCalendar -->
<link rel="stylesheet" href="lib/fullcalendar/fullcalendar.min.css">
<link rel="stylesheet" href="lib/fullcalendar/fullcalendar.print.min.css" media="print">
<script src="lib/moment/moment.js"></script>
<script src="lib/fullcalendar/fullcalendar.min.js"></script>
<script src="lib/fullcalendar/locale-all.js"></script>
<!-- ----------------------------------------------------------------------- -->
<!-- ----------------------------------------------------------------------- --> <!-- ----------------------------------------------------------------------- -->
@@ -222,18 +192,10 @@ switch ($UI_THEME) {
mac = getMac() // can also be rowID!! not only mac mac = getMac() // can also be rowID!! not only mac
var devicesList = []; // this will contain a list the database row IDs of the devices ordered by the position displayed in the UI var devicesList = []; // this will contain a list the database row IDs of the devices ordered by the position displayed in the UI
var pos = -1; var pos = -1;
var parPeriod = 'Front_Details_Period'; var parPeriod = 'Front_Details_Period';
var parSessionsRows = 'Front_Details_Sessions_Rows';
var parEventsRows = 'Front_Details_Events_Rows';
var parEventsHide = 'Front_Details_Events_Hide';
var period = '1 month';
var tab = 'tabDetails' var tab = 'tabDetails'
var sessionsRows = 10;
var eventsRows = 10;
var eventsHide = true;
var skipRepeatedItems = ['0 h (notify all events)', '1 h', '8 h', '24 h', '168 h (one week)'];
var selectedTab = 'tabDetails'; var selectedTab = 'tabDetails';
var emptyArr = ['undefined', "", undefined, null]; var emptyArr = ['undefined', "", undefined, null];
@@ -272,22 +234,15 @@ function main () {
$('#chkHideConnectionEvents')[0].checked = eval(eventsHide == 'true'); $('#chkHideConnectionEvents')[0].checked = eval(eventsHide == 'true');
// Initialize components with parameters // Initialize components with parameters
initializeTabs();
initializeDatatables();
initializeCalendar();
// query data // Init tabs once DOM ready
$( document ).ready(function() {
getSessionsPresenceEvents(); initializeTabs();
// Force re-render calendar on tab change
// (bugfix for render error at left panel)
$(document).on('shown.bs.tab', 'a[data-toggle="tab"]', function (nav) {
if ($(nav.target).attr('href') == '#panPresence') {
$('#calendar').fullCalendar('rerenderEvents');
}
}); });
// Events tab toggle conenction events // Events tab toggle conenction events
$('input').on('ifToggled', function(event){ $('input').on('ifToggled', function(event){
@@ -308,205 +263,11 @@ function main () {
} }
// -----------------------------------------------------------------------------
function initializeDatatables () {
// Sessions datatable
$('#tableSessions').DataTable({
'paging' : true,
'lengthChange': true,
'lengthMenu' : [[10, 25, 50, 100, 500, -1], [10, 25, 50, 100, 500, 'All']],
'searching' : true,
'ordering' : true,
'info' : true,
'autoWidth' : false,
'order' : [[0,'desc'], [1,'desc']],
// Parameters
'pageLength' : sessionsRows,
'columnDefs' : [
{visible: false, targets: [0]},
// Replace HTML codes
{targets: [1,2,3,5],
'createdCell': function (td, cellData, rowData, row, col) {
$(td).html (translateHTMLcodes (cellData));
} }
],
// Processing
'processing' : true,
'language' : {
processing: '<table><td width="130px" align="middle"><?= lang("DevDetail_Loading");?></td>'+
'<td><i class="ion ion-ios-loop-strong fa-spin fa-2x fa-fw">'+
'</td></table>',
emptyTable: 'No data',
"lengthMenu": "<?= lang('Events_Tablelenght');?>",
"search": "<?= lang('Events_Searchbox');?>: ",
"paginate": {
"next": "<?= lang('Events_Table_nav_next');?>",
"previous": "<?= lang('Events_Table_nav_prev');?>"
},
"info": "<?= lang('Events_Table_info');?>",
}
});
// Events datatable
$('#tableEvents').DataTable({
'paging' : true,
'lengthChange': true,
'lengthMenu' : [[10, 25, 50, 100, 500, -1], [10, 25, 50, 100, 500, 'All']],
'searching' : true,
'ordering' : true,
'info' : true,
'autoWidth' : false,
'order' : [[0,'desc']],
// Parameters
'pageLength' : eventsRows,
'columnDefs' : [
// Replace HTML codes
{targets: [0],
'createdCell': function (td, cellData, rowData, row, col) {
$(td).html (translateHTMLcodes (cellData));
} }
],
// Processing
'processing' : true,
'language' : {
processing: '<table><td width="130px" align="middle"><?= lang("DevDetail_Loading");?></td>'+
'<td><i class="ion ion-ios-loop-strong fa-spin fa-2x fa-fw">'+
'</td></table>',
emptyTable: 'No data',
"lengthMenu": "<?= lang('Events_Tablelenght');?>",
"search": "<?= lang('Events_Searchbox');?>: ",
"paginate": {
"next": "<?= lang('Events_Table_nav_next');?>",
"previous": "<?= lang('Events_Table_nav_prev');?>"
},
"info": "<?= lang('Events_Table_info');?>",
}
});
};
// -----------------------------------------------------------------------------
function initializeCalendar () {
$('#calendar').fullCalendar({
editable : false,
droppable : false,
defaultView : 'agendaMonth',
height : 'auto',
firstDay : 1,
allDaySlot : false,
slotDuration : '02:00:00',
slotLabelInterval : '04:00:00',
slotLabelFormat : 'H:mm',
timeFormat : 'H:mm',
locale : '<?= lang('Presence_CalHead_lang');?>',
header: {
left : 'prev,next today',
center : 'title',
right : 'agendaYear,agendaMonth,agendaWeek'
},
views: {
agendaYear: {
type : 'agenda',
duration : { year: 1 },
buttonText : '<?= lang('Presence_CalHead_year');?>',
columnHeaderFormat : ''
},
agendaMonth: {
type : 'agenda',
duration : { month: 1 },
buttonText : '<?= lang('Presence_CalHead_month');?>',
columnHeaderFormat : 'D'
},
agendaWeek: {
buttonText : '<?= lang('Presence_CalHead_week');?>',
},
agendaDay: {
type : 'agenda',
duration : { day: 1 },
buttonText : '<?= lang('Presence_CalHead_day');?>',
slotLabelFormat : 'H',
slotDuration : '01:00:00'
}
},
viewRender: function(view) {
if (view.name === 'agendaYear') {
var listHeader = $('.fc-day-header')[0];
var listContent = $('.fc-widget-content')[0];
for (i=0; i < listHeader.length-2 ; i++) {
listHeader[i].style.borderColor = 'transparent';
listContent[i+2].style.borderColor = 'transparent';
if (listHeader[i].innerHTML != '<span></span>') {
if (i==0) {
listHeader[i].style.borderLeftColor = '#808080';
} else {
listHeader[i-1].style.borderRightColor = '#808080';
listContent[i+1].style.borderRightColor = '#808080';
}
listHeader[i].style.paddingLeft = '10px';
}
};
}
},
columnHeaderText: function(mom) {
switch ($('#calendar').fullCalendar('getView').name) {
case 'agendaYear':
if (mom.date() == 1) {
return mom.format('MMM');
} else {
return '';
}
break;
case 'agendaMonth':
return mom.date();
break;
case 'agendaWeek':
return mom.format ('ddd D');
break;
default:
return mom.date();
}
},
eventRender: function (event, element) {
$(element).tooltip({container: 'body', placement: 'bottom',
title: event.tooltip});
// element.attr ('title', event.tooltip); // Alternative tooltip
},
loading: function( isLoading, view ) {
if (isLoading) {
showSpinner()
} else {
// setTimeout(() => {
// updateIconPreview($('#txtIcon'))
// }, 500);
hideSpinner()
}
}
})
}
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
function periodChanged () { function periodChanged () {
getSessionsPresenceEvents(); loadSessionsData();
loadPresenceData();
loadEventsData();
} }
@@ -531,6 +292,8 @@ function performSwitch(direction)
{ {
somethingChanged = false; somethingChanged = false;
devicesList = getDevicesList()
// Update the global position in the devices list variable 'pos' // Update the global position in the devices list variable 'pos'
if (direction === "next") { if (direction === "next") {
if (pos < devicesList.length - 1) { if (pos < devicesList.length - 1) {
@@ -545,8 +308,6 @@ function performSwitch(direction)
// Get the new MAC address from devicesList // Get the new MAC address from devicesList
mac = devicesList[pos].devMac.toString(); mac = devicesList[pos].devMac.toString();
console.log(mac);
setCache("naxDeviceDetailsMac", mac); setCache("naxDeviceDetailsMac", mac);
// Update the query string with the new MAC and refresh the page // Update the query string with the new MAC and refresh the page
@@ -556,33 +317,6 @@ function performSwitch(direction)
} }
// -----------------------------------------------------------------------------
function getSessionsPresenceEvents () {
// Check MAC in url
var urlParams = new URLSearchParams(window.location.search);
mac = urlParams.get ('mac');
// Define Sessions datasource and query dada
$('#tableSessions').DataTable().ajax.url('php/server/events.php?action=getDeviceSessions&mac=' + mac +'&period='+ period).load();
// Define Presence datasource and query data
$('#calendar').fullCalendar('removeEventSources');
$('#calendar').fullCalendar('addEventSource',
{ url: 'php/server/events.php?action=getDevicePresence&mac=' + mac});
// Query events
getDeviceEvents();
}
// -----------------------------------------------------------------------------
function getDeviceEvents () {
// Define Events datasource and query dada
hideConnections = $('#chkHideConnectionEvents')[0].checked;
$('#tableEvents').DataTable().ajax.url(
'php/server/events.php?action=getDeviceEvents&mac=' + mac +'&period='+ period +'&hideConnections='+ hideConnections).load();
}
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// Activate save & restore on any value change // Activate save & restore on any value change
$(document).on('input', 'input:text', function() { $(document).on('input', 'input:text', function() {
@@ -602,7 +336,10 @@ function initializeTabs () {
{ {
selectedTab = getCache(key); selectedTab = getCache(key);
} }
$('.nav-tabs a[id='+ selectedTab +']').tab('show'); $('.nav-tabs a[id='+ selectedTab +']').tab('show');
// $('.nav-tabs a[id='+ selectedTab +']').parent().click();
// $('.nav-tabs a[id="tabPlugins"]').tab('show');
// When changed save new current tab // When changed save new current tab
$('a[data-toggle="tab"]').on('shown.bs.tab', function (e) { $('a[data-toggle="tab"]').on('shown.bs.tab', function (e) {
@@ -621,62 +358,10 @@ function initializeTabs () {
} }
//-----------------------------------------------------------------------------------
function initTable(tableId, mac){
// clear table
$("#"+tableId+" tbody").remove();
// Events datatable
$('#'+tableId).DataTable({
'paging' : true,
'lengthChange': true,
'lengthMenu' : [[10, 25, 50, 100, 500, -1], [10, 25, 50, 100, 500, 'All']],
'searching' : true,
'ordering' : true,
'info' : true,
'autoWidth' : false,
'order' : [[0,'desc']],
// Parameters
'pageLength' : 50,
'columnDefs' : [
// Replace HTML codes
{targets: [0],
'createdCell': function (td, cellData, rowData, row, col) {
$(td).html (translateHTMLcodes (cellData));
} }
],
// Processing
'processing' : true,
'language' : {
processing: '<table><td width="130px" align="middle"><?= lang("DevDetail_Loading");?></td>'+
'<td><i class="ion ion-ios-loop-strong fa-spin fa-2x fa-fw">'+
'</td></table>',
emptyTable: 'No data',
"lengthMenu": "<?= lang('Events_Tablelenght');?>",
"search": "<?= lang('Events_Searchbox');?>: ",
"paginate": {
"next": "<?= lang('Events_Table_nav_next');?>",
"previous": "<?= lang('Events_Table_nav_prev');?>"
},
"info": "<?= lang('Events_Table_info');?>",
}
});
$("#"+tableId).attr("data-mac", mac)
}
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// Render the small boxes on top // Render the small boxes on top
async function renderSmallBoxes() { async function renderSmallBoxes() {
try { try {
// Show loading dialog // Show loading dialog
showSpinner(); showSpinner();

View File

@@ -14,26 +14,21 @@
<!-- Buttons --> <!-- Buttons -->
<div class="col-xs-12"> <div class="col-xs-12">
<div class="pull-right"> <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" <button type="button"
class="btn btn-default pa-btn pa-btn-delete" class="btn btn-default pa-btn pa-btn-delete"
style="margin-left:0px;" style="margin-left:0px;"
id="btnDelete" id="btnDelete"
onclick="askDeleteDevice()"> onclick="askDeleteDevice()">
<?= lang('DevDetail_button_Delete');?> <i class="fas fa-trash-alt"></i>
<?= lang('DevDetail_button_Delete');?>
</button> </button>
<button type="button" <button type="button"
class="btn btn-primary pa-btn" class="btn btn-primary pa-btn"
style="margin-left:6px; " style="margin-left:6px; "
id="btnSave" id="btnSave"
onclick="setDeviceData()" > onclick="setDeviceData()" >
<?= lang('DevDetail_button_Save');?> <i class="fas fa-save"></i>
<?= lang('DevDetail_button_Save');?>
</button> </button>
</div> </div>
</div> </div>
@@ -68,47 +63,70 @@
// some race condition, need to implement delay // some race condition, need to implement delay
setTimeout(() => { setTimeout(() => {
$.get('api/table_settings.json?nocache=' + Date.now(), function(res) { $.get('/php/server/query_json.php', { file: 'table_settings.json', nocache: Date.now() }, function(res) {
settingsData = res["data"]; settingsData = res["data"];
// columns to hide // columns to hide
hiddenFields = ["NEWDEV_devScan", "NEWDEV_devPresentLastScan" ] hiddenFields = ["NEWDEV_devScan", "NEWDEV_devPresentLastScan" ]
// columns to disable - conditional depending if a new dummy device is created // columns to disable - conditional depending if a new dummy device is created
disabledFields = mac == "new" ? ["NEWDEV_devLastNotification", "NEWDEV_devFirstConnection", "NEWDEV_devLastConnection"] : ["NEWDEV_devLastNotification", "NEWDEV_devFirstConnection", "NEWDEV_devLastConnection", "NEWDEV_devMac", "NEWDEV_devLastIP" ]; disabledFields = mac == "new" ? ["NEWDEV_devLastNotification", "NEWDEV_devFirstConnection", "NEWDEV_devLastConnection"] : ["NEWDEV_devLastNotification", "NEWDEV_devFirstConnection", "NEWDEV_devLastConnection", "NEWDEV_devMac", "NEWDEV_devLastIP", "NEWDEV_devSyncHubNode" ];
// Grouping of fields into categories with associated documentation links // Grouping of fields into categories with associated documentation links
const fieldGroups = { const fieldGroups = {
// Group for device main information // Group for device main information
DevDetail_MainInfo_Title: { DevDetail_MainInfo_Title: {
data: ["devMac", "devLastIP", "devName", "devOwner", "devType", "devVendor", "devGroup", "devIcon", "devLocation", "devComments"], data: ["devMac", "devLastIP", "devName", "devOwner", "devType", "devVendor", "devGroup", "devIcon", "devLocation", "devComments"],
docs: "https://github.com/jokob-sk/NetAlertX/blob/main/docs/NOTIFICATIONS.md", docs: "https://github.com/jokob-sk/NetAlertX/blob/main/docs/DEVICE_MANAGEMENT.md",
iconClass: "fa fa-pencil" iconClass: "fa fa-pencil",
inputGroupClasses: "field-group main-group col-lg-4 col-sm-6 col-xs-12",
labelClasses: "col-sm-4 col-xs-12 control-label",
inputClasses: "col-sm-8 col-xs-12 input-group"
}, },
// Group for session information // Group for session information
DevDetail_SessionInfo_Title: { DevDetail_SessionInfo_Title: {
data: ["devStatus", "devLastConnection", "devFirstConnection"], data: ["devStatus", "devLastConnection", "devFirstConnection"],
docs: "https://github.com/jokob-sk/NetAlertX/blob/main/docs/NOTIFICATIONS.md", docs: "https://github.com/jokob-sk/NetAlertX/blob/main/docs/SESSION_INFO.md",
iconClass: "fa fa-calendar" iconClass: "fa fa-calendar",
inputGroupClasses: "field-group session-group col-lg-4 col-sm-6 col-xs-12",
labelClasses: "col-sm-4 col-xs-12 control-label",
inputClasses: "col-sm-8 col-xs-12 input-group"
}, },
// Group for event and alert settings // Group for event and alert settings
DevDetail_EveandAl_Title: { DevDetail_EveandAl_Title: {
data: ["devAlertEvents", "devAlertDown", "devSkipRepeated"], data: ["devAlertEvents", "devAlertDown", "devSkipRepeated"],
docs: "https://github.com/jokob-sk/NetAlertX/blob/main/docs/NOTIFICATIONS.md", docs: "https://github.com/jokob-sk/NetAlertX/blob/main/docs/NOTIFICATIONS.md",
iconClass: "fa fa-bell" iconClass: "fa fa-bell",
inputGroupClasses: "field-group alert-group col-lg-4 col-sm-6 col-xs-12",
labelClasses: "col-sm-4 col-xs-12 control-label",
inputClasses: "col-sm-8 col-xs-12 input-group"
}, },
// Group for network details // Group for network details
DevDetail_MainInfo_Network_Title: { DevDetail_MainInfo_Network_Title: {
data: ["devParentMAC", "devParentPort", "devSSID", "devSite"], data: ["devParentMAC", "devParentPort", "devSSID", "devSite", "devSyncHubNode"],
docs: "https://github.com/jokob-sk/NetAlertX/blob/main/docs/NETWORK_TREE.md", docs: "https://github.com/jokob-sk/NetAlertX/blob/main/docs/NETWORK_TREE.md",
iconClass: "fa fa-network-wired" iconClass: "fa fa-network-wired",
inputGroupClasses: "field-group network-group col-lg-4 col-sm-6 col-xs-12",
labelClasses: "col-sm-4 col-xs-12 control-label",
inputClasses: "col-sm-8 col-xs-12 input-group"
}, },
// Group for other fields like static IP, archived status, etc. // Group for other fields like static IP, archived status, etc.
DevDetail_DisplayFields_Title: { DevDetail_DisplayFields_Title: {
data: ["devStaticIP", "devIsNew", "devFavorite", "devIsArchived"], data: ["devStaticIP", "devIsNew", "devFavorite", "devIsArchived"],
docs: "https://github.com/jokob-sk/NetAlertX/blob/main/docs/NOTIFICATIONS.md", docs: "https://github.com/jokob-sk/NetAlertX/blob/main/docs/DEVICE_DISPLAY_SETTINGS.md",
iconClass: "fa fa-list-check" iconClass: "fa fa-list-check",
inputGroupClasses: "field-group display-group col-lg-4 col-sm-6 col-xs-12",
labelClasses: "col-sm-4 col-xs-12 control-label",
inputClasses: "col-sm-8 col-xs-12 input-group"
},
// Group for Custom properties.
DevDetail_CustomProperties_Title: {
data: ["devCustomProps"],
docs: "https://github.com/jokob-sk/NetAlertX/blob/main/docs/CUSTOM_PROPERTIES.md",
iconClass: "fa fa-list",
inputGroupClasses: "field-group cutprop-group col-lg-12 col-sm-12 col-xs-12",
labelClasses: "col-sm-12 col-xs-12 control-label",
inputClasses: "col-sm-12 col-xs-12 input-group"
} }
}; };
@@ -126,7 +144,7 @@
// Loop over each field group to generate sections for each category // Loop over each field group to generate sections for each category
Object.entries(fieldGroups).forEach(([groupName, obj]) => { Object.entries(fieldGroups).forEach(([groupName, obj]) => {
const groupDiv = $('<div>').addClass('field-group col-lg-4 col-sm-6 col-xs-12'); // Create a div for each group with responsive Bootstrap classes const groupDiv = $('<div>').addClass(obj.inputGroupClasses); // Create a div for each group with responsive Bootstrap classes
// Add group title and documentation link // Add group title and documentation link
groupDiv.append(`<h5><i class="${obj.iconClass}"></i> ${getString(groupName)} groupDiv.append(`<h5><i class="${obj.iconClass}"></i> ${getString(groupName)}
@@ -137,7 +155,7 @@
</span> </span>
</h5> </h5>
<hr> <hr>
`); `);
// Filter relevant settings for the current group // Filter relevant settings for the current group
const groupSettings = settings.filter(set => obj.data.includes(set.setKey.replace('NEWDEV_', ''))); const groupSettings = settings.filter(set => obj.data.includes(set.setKey.replace('NEWDEV_', '')));
@@ -156,7 +174,7 @@
// Additional form elements like the random MAC address button for devMac // Additional form elements like the random MAC address button for devMac
let inlineControl = ""; let inlineControl = "";
// handle rendom mac // handle rendom mac
if (setting.setKey == "NEWDEV_devMac" && deviceData["devRandomMAC"] == true) { if (setting.setKey == "NEWDEV_devMac" && deviceData["devIsRandomMAC"] == true) {
inlineControl += `<span class="input-group-addon pointer" inlineControl += `<span class="input-group-addon pointer"
title="${getString("RandomMAC_hover")}"> title="${getString("RandomMAC_hover")}">
<a href="https://github.com/jokob-sk/NetAlertX/blob/main/docs/RANDOM_MAC.md" target="_blank"> <a href="https://github.com/jokob-sk/NetAlertX/blob/main/docs/RANDOM_MAC.md" target="_blank">
@@ -193,15 +211,15 @@
// Generate the input field HTML // Generate the input field HTML
const inputFormHtml = `<div class="form-group col-xs-12"> const inputFormHtml = `<div class="form-group col-xs-12">
<label class="col-sm-4 col-xs-12 control-label"> ${setting.setName} <label id="${setting.setKey}_label" class="${obj.labelClasses}" > ${setting.setName}
<i my-set-key="${setting.setKey}" <i my-set-key="${setting.setKey}"
title="${getString("Settings_Show_Description")}" title="${getString("Settings_Show_Description")}"
class="fa fa-circle-info pointer helpIconSmallTopRight" class="fa fa-circle-info pointer helpIconSmallTopRight"
onclick="showDescriptionPopup(this)"> onclick="showDescriptionPopup(this)">
</i> </i>
</label> </label>
<div class="col-sm-8 col-xs-12 input-group"> <div class="${obj.inputClasses}">
${generateFormHtml(setting, fieldData.toString())} ${generateFormHtml(settingsData, setting, fieldData.toString(), null, null)}
${inlineControl} ${inlineControl}
</div> </div>
</div>`; </div>`;
@@ -214,20 +232,24 @@
}); });
// wait until everything is initialized to update icon // wait until everything is initialized to update icons
updateIconPreview(); updateAllIconPreviews();
// update readonly fields // update readonly fields
handleReadOnly(settingsData, disabledFields); handleReadOnly(settingsData, disabledFields);
// Page title - Name // Page title - Name
if (mac == "new") { if (mac == "new") {
$('#pageTitle').html(getString("Gen_AddDevice")); $('#pageTitle').html(`<i title="${getString("Gen_create_new_device")}" class="fa fa-square-plus"></i> ` + getString("Gen_create_new_device"));
$('#devicePageInfoPlc .inner').html(`<i class="fa fa-circle-info"></i> ` + getString("Gen_create_new_device_info"));
$('#devicePageInfoPlc').show();
} else if (deviceData['devOwner'] == null || deviceData['devOwner'] == '' || } else if (deviceData['devOwner'] == null || deviceData['devOwner'] == '' ||
(deviceData['devName'].toString()).indexOf(deviceData['devOwner']) != -1) { (deviceData['devName'].toString()).indexOf(deviceData['devOwner']) != -1) {
$('#pageTitle').html(deviceData['devName']); $('#pageTitle').html(deviceData['devName']);
$('#devicePageInfoPlc').hide();
} else { } else {
$('#pageTitle').html(deviceData['devName'] + ' (' + deviceData['devOwner'] + ')'); $('#pageTitle').html(deviceData['devName'] + ' (' + deviceData['devOwner'] + ')');
$('#devicePageInfoPlc').hide();
} }
}; };
@@ -313,64 +335,7 @@
showModalOK("Info", getString($(e).attr("my-set-key") + '_description')) showModalOK("Info", getString($(e).attr("my-set-key") + '_description'))
} }
// -----------------------------------------------------------------------------
function askDeleteDeviceEvents () {
// Check MAC
if (mac == '') {
return;
}
// Ask delete device Events
showModalWarning ('<?= lang('DevDetail_button_DeleteEvents');?>', '<?= lang('DevDetail_button_DeleteEvents_Warning');?>',
'<?= lang('Gen_Cancel');?>', '<?= lang('Gen_Delete');?>', 'deleteDeviceEvents');
}
function deleteDeviceEvents () {
// Check MAC
if (mac == '') {
return;
}
// Delete device events
$.get('php/server/devices.php?action=deleteDeviceEvents&mac='+ mac, function(msg) {
showMessage (msg);
});
// Deactivate controls
$('#panDetails :input').attr('disabled', true);
}
// -----------------------------------------------------------------------------
function askDeleteDevice () {
// Check MAC
if (mac == '') {
return;
}
// Ask delete device
showModalWarning ('Delete Device', 'Are you sure you want to delete this device?<br>(maybe you prefer to archive it)',
'<?= lang('Gen_Cancel');?>', '<?= lang('Gen_Delete');?>', 'deleteDevice');
}
// -----------------------------------------------------------------------------
function deleteDevice () {
// Check MAC
if (mac == '') {
return;
}
// Delete device
$.get('php/server/devices.php?action=deleteDevice&mac='+ mac, function(msg) {
showMessage (msg);
});
// Deactivate controls
$('#panDetails :input').attr('disabled', true);
// refresh API
updateApi("devices,appevents")
}
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
function setDeviceData(direction = '', refreshCallback = '') { function setDeviceData(direction = '', refreshCallback = '') {
@@ -392,51 +357,53 @@
showSpinner(); showSpinner();
// update data to server // Update data to server using POST
$.get('php/server/devices.php?action=setDeviceData&mac='+ $('#NEWDEV_devMac').val() $.post('php/server/devices.php?action=setDeviceData', {
+ '&name=' + encodeURIComponent($('#NEWDEV_devName').val().replace(/'/g, "")) mac: $('#NEWDEV_devMac').val(),
+ '&owner=' + encodeURIComponent($('#NEWDEV_devOwner').val().replace(/'/g, "")) name: encodeURIComponent($('#NEWDEV_devName').val().replace(/'/g, "")),
+ '&type=' + $('#NEWDEV_devType').val() owner: encodeURIComponent($('#NEWDEV_devOwner').val().replace(/'/g, "")),
+ '&vendor=' + encodeURIComponent($('#NEWDEV_devVendor').val().replace(/'/g, "")) type: $('#NEWDEV_devType').val().replace(/'/g, ""),
+ '&icon=' + encodeURIComponent($('#NEWDEV_devIcon').val()) vendor: encodeURIComponent($('#NEWDEV_devVendor').val().replace(/'/g, "")),
+ '&favorite=' + ($('#NEWDEV_devFavorite')[0].checked * 1) icon: encodeURIComponent($('#NEWDEV_devIcon').val()),
+ '&group=' + encodeURIComponent($('#NEWDEV_devGroup').val()) favorite: ($('#NEWDEV_devFavorite')[0].checked * 1),
+ '&location=' + encodeURIComponent($('#NEWDEV_devLocation').val()) group: encodeURIComponent($('#NEWDEV_devGroup').val().replace(/'/g, "")),
+ '&comments=' + encodeURIComponent(encodeSpecialChars($('#NEWDEV_devComments').val())) location: encodeURIComponent($('#NEWDEV_devLocation').val().replace(/'/g, "")),
+ '&networknode=' + $('#NEWDEV_devParentMAC').val() comments: encodeURIComponent(encodeSpecialChars($('#NEWDEV_devComments').val())),
+ '&networknodeport=' + $('#NEWDEV_devParentPort').val() networknode: $('#NEWDEV_devParentMAC').val(),
+ '&ssid=' + $('#NEWDEV_devSSID').val() networknodeport: $('#NEWDEV_devParentPort').val(),
+ '&networksite=' + $('#NEWDEV_devSite').val() ssid: $('#NEWDEV_devSSID').val(),
+ '&staticIP=' + ($('#NEWDEV_devStaticIP')[0].checked * 1) networksite: $('#NEWDEV_devSite').val(),
+ '&scancycle=' + "1" staticIP: ($('#NEWDEV_devStaticIP')[0].checked * 1),
+ '&alertevents=' + ($('#NEWDEV_devAlertEvents')[0].checked * 1) scancycle: "1",
+ '&alertdown=' + ($('#NEWDEV_devAlertDown')[0].checked * 1) alertevents: ($('#NEWDEV_devAlertEvents')[0].checked * 1),
+ '&skiprepeated=' + $('#NEWDEV_devSkipRepeated').val().split(' ')[0] alertdown: ($('#NEWDEV_devAlertDown')[0].checked * 1),
+ '&newdevice=' + ($('#NEWDEV_devIsNew')[0].checked * 1) skiprepeated: $('#NEWDEV_devSkipRepeated').val().split(' ')[0],
+ '&archived=' + ($('#NEWDEV_devIsArchived')[0].checked * 1) newdevice: ($('#NEWDEV_devIsNew')[0].checked * 1),
+ '&devFirstConnection=' + ($('#NEWDEV_devFirstConnection').val()) archived: ($('#NEWDEV_devIsArchived')[0].checked * 1),
+ '&devLastConnection=' + ($('#NEWDEV_devLastConnection').val()) devFirstConnection: ($('#NEWDEV_devFirstConnection').val()),
+ '&ip=' + ($('#NEWDEV_devLastIP').val()) devLastConnection: ($('#NEWDEV_devLastConnection').val()),
+ '&createNew=' + createNew devCustomProps: btoa(JSON.stringify(collectTableData("#NEWDEV_devCustomProps_table"))),
, function(msg) { ip: ($('#NEWDEV_devLastIP').val()),
createNew: createNew
showMessage (msg); }, function(msg) {
showMessage(msg);
// Remove navigation prompt "Are you sure you want to leave..." // Remove navigation prompt "Are you sure you want to leave..."
window.onbeforeunload = null; window.onbeforeunload = null;
somethingChanged = false; somethingChanged = false;
// refresh API // refresh API
updateApi("devices,appevents") updateApi("devices,appevents");
// Callback fuction // Callback function
if (typeof refreshCallback == 'function') { if (typeof refreshCallback == 'function') {
refreshCallback(direction); refreshCallback(direction);
} }
// everything loaded // Everything loaded
hideSpinner(); hideSpinner();
}); });
} }
//----------------------------------------------------------------------------------- //-----------------------------------------------------------------------------------

94
front/deviceDetailsEvents.php Executable file
View File

@@ -0,0 +1,94 @@
<?php
//------------------------------------------------------------------------------
// check if authenticated
require_once $_SERVER['DOCUMENT_ROOT'] . '/php/templates/security.php';
?>
<!-- ----------------------------------------------------------------------- -->
<!-- Hide Connections -->
<div class="text-center">
<label>
<input class="checkbox blue hidden" id="chkHideConnectionEvents" type="checkbox" checked>
<?= lang('DevDetail_Events_CheckBox');?>
</label>
</div>
<!-- Datatable Events -->
<table id="tableEvents" class="table table-bordered table-hover table-striped ">
<thead>
<tr>
<th><?= lang("DevDetail_Tab_EventsTableDate");?></th>
<th><?= lang("DevDetail_Tab_EventsTableEvent");?></th>
<th><?= lang("DevDetail_Tab_EventsTableIP");?></th>
<th><?= lang("DevDetail_Tab_EventsTableInfo");?></th>
</tr>
</thead>
</table>
<script>
var eventsRows = 10;
var eventsHide = true;
var parEventsRows = 'Front_Details_Events_Rows';
var parEventsHide = 'Front_Details_Events_Hide';
// -----------------------------------------------------------------------------
function loadEventsData() {
// Define Events datasource and query dada
hideConnections = $('#chkHideConnectionEvents')[0].checked;
$('#tableEvents').DataTable().ajax.url('php/server/events.php?action=getDeviceEvents&mac=' + mac +'&period='+ period +'&hideConnections='+ hideConnections).load();
}
function initializeSessionsDatatable () {
// Events datatable
$('#tableEvents').DataTable({
'paging' : true,
'lengthChange': true,
'lengthMenu' : [[10, 25, 50, 100, 500, -1], [10, 25, 50, 100, 500, 'All']],
'searching' : true,
'ordering' : true,
'info' : true,
'autoWidth' : false,
'order' : [[0,'desc']],
// Parameters
'pageLength' : eventsRows,
'columnDefs' : [
// Replace HTML codes
{targets: [0],
'createdCell': function (td, cellData, rowData, row, col) {
$(td).html (translateHTMLcodes (cellData));
} }
],
// Processing
'processing' : true,
'language' : {
processing: '<table><td width="130px" align="middle"><?= lang("DevDetail_Loading");?></td>'+
'<td><i class="ion ion-ios-loop-strong fa-spin fa-2x fa-fw">'+
'</td></table>',
emptyTable: 'No data',
"lengthMenu": "<?= lang('Events_Tablelenght');?>",
"search": "<?= lang('Events_Searchbox');?>: ",
"paginate": {
"next": "<?= lang('Events_Table_nav_next');?>",
"previous": "<?= lang('Events_Table_nav_prev');?>"
},
"info": "<?= lang('Events_Table_info');?>",
}
});
}
initializeSessionsDatatable();
loadEventsData();
</script>

240
front/deviceDetailsPresence.php Executable file
View File

@@ -0,0 +1,240 @@
<?php
//------------------------------------------------------------------------------
// check if authenticated
require_once $_SERVER['DOCUMENT_ROOT'] . '/php/templates/security.php';
?>
<!-- fullCalendar -->
<link rel="stylesheet" href="lib/fullcalendar/fullcalendar.min.css">
<link rel="stylesheet" href="lib/fullcalendar/fullcalendar.print.min.css" media="print">
<script src="lib/moment/moment.js"></script>
<script src="lib/fullcalendar/fullcalendar.min.js"></script>
<script src="lib/fullcalendar/locale-all.js"></script>
<!-- fullCalendar Scheduler -->
<link href="lib/fullcalendar-scheduler/scheduler.min.css" rel="stylesheet">
<script src="lib/fullcalendar-scheduler/scheduler.min.js"></script>
<!-- Calendar -->
<div id="calendar">
</div>
<script>
initializeCalendar();
loadPresenceData();
// Force re-render calendar on tab change
// (bugfix for render error at left panel)
$(document).on('shown.bs.tab', 'a[data-toggle="tab"]', function (nav) {
if ($(nav.target).attr('href') == '#panPresence') {
$('#calendar').fullCalendar('rerenderEvents');
}
});
// ---------------------------------------
// query data
function loadPresenceData()
{
// Define Presence datasource and query data
$('#calendar').fullCalendar('removeEventSources');
$('#calendar').fullCalendar('addEventSource',
{ url: 'php/server/events.php?action=getDevicePresence&mac=' + mac});
}
// ---------------------------------------
function initializeCalendar_() {
var calendarEl = document.getElementById('calendar');
var calendar = new FullCalendar.Calendar(calendarEl, {
headerToolbar: {
left: 'prev,next today',
center: 'title',
right: 'dayGridYear,dayGridMonth,timeGridWeek'
},
initialView: 'dayGridYear',
initialDate: '2023-01-12',
editable: true,
selectable: true,
dayMaxEvents: true, // allow "more" link when too many events
// businessHours: true,
// weekends: false,
events: [
{
title: 'All Day Event',
start: '2023-01-01'
},
{
title: 'Long Event',
start: '2023-01-07',
end: '2023-01-10'
},
{
groupId: 999,
title: 'Repeating Event',
start: '2023-01-09T16:00:00'
},
{
groupId: 999,
title: 'Repeating Event',
start: '2023-01-16T16:00:00'
},
{
title: 'Conference',
start: '2023-01-11',
end: '2023-01-13'
},
{
title: 'Meeting',
start: '2023-01-12T10:30:00',
end: '2023-01-12T12:30:00'
},
{
title: 'Lunch',
start: '2023-01-12T12:00:00'
},
{
title: 'Meeting',
start: '2023-01-12T14:30:00'
},
{
title: 'Happy Hour',
start: '2023-01-12T17:30:00'
},
{
title: 'Dinner',
start: '2023-01-12T20:00:00'
},
{
title: 'Birthday Party',
start: '2023-01-13T07:00:00'
},
{
title: 'Click for Google',
url: 'http://google.com/',
start: '2023-01-28'
}
]
});
calendar.render();
}
// -----------------------------------------------------------------------------
function initializeCalendar() {
$('#calendar').fullCalendar({
editable : false,
droppable : false,
defaultView : 'agendaMonth',
schedulerLicenseKey: 'GPL-My-Project-Is-Open-Source',
height : 'auto',
firstDay : 1,
allDaySlot : false,
slotDuration : '02:00:00',
slotLabelInterval : '04:00:00',
slotLabelFormat : 'H:mm',
timeFormat : 'H:mm',
locale : '<?= lang('Presence_CalHead_lang');?>',
header: {
left : 'prev,next today',
center : 'title',
right : 'agendaYear,agendaMonth,agendaWeek'
},
views: {
agendaYear: {
type : 'agenda',
duration : { year: 1 },
buttonText : '<?= lang('Presence_CalHead_year');?>',
columnHeaderFormat : ''
},
agendaMonth: {
type : 'agenda',
duration : { month: 1 },
buttonText : '<?= lang('Presence_CalHead_month');?>',
columnHeaderFormat : 'D'
},
agendaWeek: {
buttonText : '<?= lang('Presence_CalHead_week');?>',
},
agendaDay: {
type : 'agenda',
duration : { day: 1 },
buttonText : '<?= lang('Presence_CalHead_day');?>',
slotLabelFormat : 'H',
slotDuration : '01:00:00'
}
},
viewRender: function(view) {
if (view.name === 'agendaYear') {
var listHeader = $('.fc-day-header')[0];
var listContent = $('.fc-widget-content')[0];
for (i=0; i < listHeader.length-2 ; i++) {
listHeader[i].style.borderColor = 'transparent';
listContent[i+2].style.borderColor = 'transparent';
if (listHeader[i].innerHTML != '<span></span>') {
if (i==0) {
listHeader[i].style.borderLeftColor = '#808080';
} else {
listHeader[i-1].style.borderRightColor = '#808080';
listContent[i+1].style.borderRightColor = '#808080';
}
listHeader[i].style.paddingLeft = '10px';
}
};
}
},
columnHeaderText: function(mom) {
switch ($('#calendar').fullCalendar('getView').name) {
case 'agendaYear':
if (mom.date() == 1) {
return mom.format('MMM');
} else {
return '';
}
break;
case 'agendaMonth':
return mom.date();
break;
case 'agendaWeek':
return mom.format ('ddd D');
break;
default:
return mom.date();
}
},
eventRender: function (event, element) {
$(element).tooltip({container: 'body', placement: 'bottom',
title: event.tooltip});
// element.attr ('title', event.tooltip); // Alternative tooltip
},
loading: function( isLoading, view ) {
if (isLoading) {
showSpinner()
} else {
hideSpinner()
}
}
})
}
</script>

81
front/deviceDetailsSessions.php Executable file
View File

@@ -0,0 +1,81 @@
<?php
//------------------------------------------------------------------------------
// check if authenticated
require_once $_SERVER['DOCUMENT_ROOT'] . '/php/templates/security.php';
?>
<!-- ----------------------------------------------------------------------- -->
<!-- Datatable Session -->
<table id="tableSessions" class="table table-bordered table-hover table-striped ">
<thead>
<tr>
<th><?= lang('DevDetail_SessionTable_Order');?></th>
<th><?= lang('DevDetail_SessionTable_Connection');?></th>
<th><?= lang('DevDetail_SessionTable_Disconnection');?></th>
<th><?= lang('DevDetail_SessionTable_Duration');?></th>
<th><?= lang('DevDetail_SessionTable_IP');?></th>
<th><?= lang('DevDetail_SessionTable_Additionalinfo');?></th>
</tr>
</thead>
</table>
<script>
var parSessionsRows = 'Front_Details_Sessions_Rows';
var sessionsRows = 10;
var period = '1 month';
function initializeSessionsDatatable () {
// Sessions datatable
$('#tableSessions').DataTable({
'paging' : true,
'lengthChange': true,
'lengthMenu' : [[10, 25, 50, 100, 500, -1], [10, 25, 50, 100, 500, 'All']],
'searching' : true,
'ordering' : true,
'info' : true,
'autoWidth' : false,
'order' : [[0,'desc'], [1,'desc']],
// Parameters
'pageLength' : sessionsRows,
'columnDefs' : [
{visible: false, targets: [0]},
// Replace HTML codes
{targets: [1,2,3,5],
'createdCell': function (td, cellData, rowData, row, col) {
$(td).html (translateHTMLcodes (cellData));
} }
],
// Processing
'processing' : true,
'language' : {
processing: '<table><td width="130px" align="middle"><?= lang("DevDetail_Loading");?></td>'+
'<td><i class="ion ion-ios-loop-strong fa-spin fa-2x fa-fw">'+
'</td></table>',
emptyTable: 'No data',
"lengthMenu": "<?= lang('Events_Tablelenght');?>",
"search": "<?= lang('Events_Searchbox');?>: ",
"paginate": {
"next": "<?= lang('Events_Table_nav_next');?>",
"previous": "<?= lang('Events_Table_nav_prev');?>"
},
"info": "<?= lang('Events_Table_info');?>",
}
});
}
function loadSessionsData(){
$('#tableSessions').DataTable().ajax.url('php/server/events.php?action=getDeviceSessions&mac=' + getMac() +'&period='+ period).load();
}
initializeSessionsDatatable();
loadSessionsData();
</script>

View File

@@ -65,6 +65,48 @@
<?php } ?> <?php } ?>
<!-- Delete Events -->
<h4 class=""><i class="fa-solid fa-bell"></i>
<?= lang("DevDetail_button_DeleteEvents") ?>
</h4>
<h5 class="">
<?= lang("DevDetail_button_DeleteEvents_Warning") ?>
</h5>
<br>
<div style="width:100%; text-align: center; margin-bottom: 50px;">
<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>
<br>
<div id="wol_output" style="margin-top: 10px;"></div>
</div>
<!-- Reset Custom Proprties -->
<h4 class=""><i class="fa-solid fa-list"></i>
<?= lang("DevDetail_CustomProperties_Title") ?>
</h4>
<h5 class="">
<?= lang("DevDetail_CustomProps_reset_info") ?>
</h5>
<br>
<div style="width:100%; text-align: center; margin-bottom: 50px;">
<button type="button"
class="btn btn-default pa-btn pa-btn-delete"
style="margin-left:0px;"
id="btnDeleteEvents"
onclick="askResetDeviceProps()">
<?= lang("Gen_Reset") ?>
</button>
<br>
<div id="wol_output" style="margin-top: 10px;"></div>
</div>
<!-- SPEEDTEST --> <!-- SPEEDTEST -->
<?php if ($_REQUEST["mac"] == "Internet") { ?> <?php if ($_REQUEST["mac"] == "Internet") { ?>
<h4 class=""><i class="fa-solid fa-gauge-high"></i> <h4 class=""><i class="fa-solid fa-gauge-high"></i>
@@ -336,6 +378,56 @@
return devicesList; return devicesList;
} }
// ----------------------------------------------------------------
// -----------------------------------------------------------------------------
function askDeleteDeviceEvents () {
// Check MAC
if (mac == '') {
return;
}
// Ask delete device Events
showModalWarning ('<?= lang('DevDetail_button_DeleteEvents');?>', '<?= lang('DevDetail_button_DeleteEvents_Warning');?>',
'<?= lang('Gen_Cancel');?>', '<?= lang('Gen_Delete');?>', 'deleteDeviceEvents');
}
function deleteDeviceEvents () {
// Check MAC
if (mac == '') {
return;
}
// Delete device events
$.get('php/server/devices.php?action=deleteDeviceEvents&mac='+ mac, function(msg) {
showMessage (msg);
});
}
// -----------------------------------------------------------------------------
function askResetDeviceProps () {
// Check MAC
if (mac == '') {
return;
}
// Ask Resert Custom properties
showModalWarning ('<?= lang('Gen_Reset');?>', '<?= lang('DevDetail_CustomProps_reset_info');?>',
'<?= lang('Gen_Cancel');?>', '<?= lang('Gen_Delete');?>', 'resetDeviceProps');
}
function resetDeviceProps () {
// Check MAC
if (mac == '') {
return;
}
// Execute
$.get('php/server/devices.php?action=resetDeviceProps&mac='+ mac, function(msg) {
showMessage (msg);
});
}
// ---------------------------------------------------------------- // ----------------------------------------------------------------
function internetinfo() { function internetinfo() {
$( "#internetinfooutput" ).empty(); $( "#internetinfooutput" ).empty();

View File

@@ -22,6 +22,8 @@
checkPermissions([$dbPath, $confPath]); checkPermissions([$dbPath, $confPath]);
?> ?>
<!-- ----------------------------------------------------------------------- -->
<!-- Page ------------------------------------------------------------------ --> <!-- Page ------------------------------------------------------------------ -->
<div class="content-wrapper"> <div class="content-wrapper">
@@ -72,10 +74,14 @@
<!-- box-header --> <!-- box-header -->
<div class="box-header"> <div class="box-header">
<div class=" col-md-10 "> <div class=" col-md-9 ">
<h3 id="tableDevicesTitle" class="box-title text-gray "></h3> <h3 id="tableDevicesTitle" class="box-title text-gray "></h3>
</div> </div>
<div class=" col-md-2 "><a href="deviceDetails.php?mac=new"><i title="Add new dummy device" class="fa fa-square-plus"></i> <?= lang('Gen_create_new_device');?></a></div> <div class="dummyDevice col-md-3 ">
<span>
<a href="deviceDetails.php?mac=new"><i title="<?= lang('Gen_create_new_device');?>" class="fa fa-square-plus"></i> <?= lang('Gen_create_new_device');?></a>
</span>
</div>
</div> </div>
<!-- table --> <!-- table -->
@@ -111,19 +117,11 @@
?> ?>
<!-- ----------------------------------------------------------------------- -->
<!-- Datatable -->
<link rel="stylesheet" href="lib/datatables.net-bs/css/dataTables.bootstrap.min.css">
<link rel="stylesheet" href="lib/datatables.net/css/select.dataTables.min.css">
<script src="lib/datatables.net/js/jquery.dataTables.min.js"></script>
<script src="lib/datatables.net-bs/js/dataTables.bootstrap.min.js"></script>
<script src="lib/datatables.net/js/dataTables.select.min.js"></script>
<!-- page script ----------------------------------------------------------- --> <!-- page script ----------------------------------------------------------- -->
<script> <script>
var deviceStatus = 'all'; var deviceStatus = 'all';
var tableRows = getCookie ("nax_parTableRows") == "" ? 10 : getCookie ("nax_parTableRows") ; var tableRows = getCache ("nax_parTableRows") == "" ? 10 : getCache ("nax_parTableRows") ;
var tableOrder = getCookie ("nax_parTableOrder") == "" ? [[3,'desc'], [0,'asc']] : JSON.parse(getCookie ("nax_parTableOrder")) ; var tableOrder = getCache ("nax_parTableOrder") == "" ? [[3,'desc'], [0,'asc']] : JSON.parse(getCache ("nax_parTableOrder")) ;
var tableColumnHide = []; var tableColumnHide = [];
var tableColumnOrder = []; var tableColumnOrder = [];
@@ -146,6 +144,10 @@ function main () {
//initialize the table headers in the correct order //initialize the table headers in the correct order
var availableColumns = getSettingOptions("UI_device_columns").split(","); var availableColumns = getSettingOptions("UI_device_columns").split(",");
headersDefaultOrder = availableColumns.map(val => getString(val)); headersDefaultOrder = availableColumns.map(val => getString(val));
console.log(headersDefaultOrder);
var selectedColumns = JSON.parse(getSetting("UI_device_columns").replace(/'/g, '"')); var selectedColumns = JSON.parse(getSetting("UI_device_columns").replace(/'/g, '"'));
// generate default order lists of given length // generate default order lists of given length
@@ -183,6 +185,9 @@ function main () {
// mapping the default order to the user specified one // mapping the default order to the user specified one
function mapIndx(oldIndex) function mapIndx(oldIndex)
{ {
// console.log(oldIndex);
// console.log(tableColumnOrder);
for(i=0;i<tableColumnOrder.length;i++) for(i=0;i<tableColumnOrder.length;i++)
{ {
if(tableColumnOrder[i] == oldIndex) if(tableColumnOrder[i] == oldIndex)
@@ -196,30 +201,61 @@ function mapIndx(oldIndex)
// Query total numbers of Devices by status // Query total numbers of Devices by status
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
function getDevicesTotals() { function getDevicesTotals() {
maxDelay = 180; //cap at 180 seconds
// Fetch data via AJAX let maxRetries = Math.ceil(Math.log2(maxDelay)); // Calculate maximum retries to cap at maxDelay seconds
$.ajax({ let attempt = 0;
url: '/api/table_devices_tiles.json?nocache=' + Date.now(), let calledUpdateAPI = false;
type: "GET",
dataType: "json",
success: function(response) {
if (response && response.data) {
resultJSON = response.data[0]; // Assuming the structure {"data": [ ... ]}
// Save the result to cache
setCache("getDevicesTotals", JSON.stringify(resultJSON));
// Process the fetched data function fetchDataWithBackoff() {
processDeviceTotals(resultJSON); // Calculate the delay (2^attempt seconds, capped at maxDelay seconds)
} else { const delay = Math.min(2 ** attempt, maxDelay) * 1000;
console.error("Invalid response format from API");
} // Attempt to fetch data
}, $.ajax({
error: function(xhr, status, error) { url: '/php/server/query_json.php',
console.error("Failed to fetch devices data:", error); type: "GET",
dataType: "json",
data: {
file: 'table_devices_tiles.json', // Pass the file parameter
nocache: Date.now() // Prevent caching with a timestamp
},
success: function(response) {
if (response && response.data) {
const resultJSON = response.data[0]; // Assuming the structure {"data": [ ... ]}
// Save the result to cache
setCache("getDevicesTotals", JSON.stringify(resultJSON));
// Process the fetched data
processDeviceTotals(resultJSON);
} else {
console.error("Invalid response format from API");
}
},
error: function(xhr, status, error) {
console.error("Failed to fetch devices data (Attempt " + (attempt + 1) + "):", error);
// try updating the API once
if(calledUpdateAPI == false)
{
calledUpdateAPI = true;
updateApi("devices_tiles");
}
// Retry logic
if (attempt < maxRetries) {
attempt++;
setTimeout(fetchDataWithBackoff, delay);
} else {
console.error("Maximum retries reached. Unable to fetch devices data.");
}
}
});
} }
});
// Start the first fetch attempt
fetchDataWithBackoff();
} }
function processDeviceTotals(devicesData) { function processDeviceTotals(devicesData) {
@@ -271,6 +307,8 @@ function processDeviceTotals(devicesData) {
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// Render the info boxes/tiles on top // Render the info boxes/tiles on top
function renderInfoboxes(customData) { function renderInfoboxes(customData) {
if(customData.length > 0)
{
$.ajax({ $.ajax({
url: 'php/components/tile_cards.php', // PHP script URL url: 'php/components/tile_cards.php', // PHP script URL
type: 'POST', // Use POST method to send data type: 'POST', // Use POST method to send data
@@ -284,49 +322,9 @@ function renderInfoboxes(customData) {
} }
}); });
} }
// -----------------------------------------------------------------------------
// Define a function to filter data based on deviceStatus
function filterDataByStatus(data, status) {
return data.filter(function(item) {
switch (status) {
case 'my_devices':
to_display = getSetting('UI_MY_DEVICES');
let result = true;
if (!to_display.includes('down') && item.devPresentLastScan === 0 && item.devAlertDown !== 0) {
result = false;
} else if (!to_display.includes('new') && item.devIsNew === 1) {
result = false;
} else if (!to_display.includes('archived') && item.devIsArchived === 1) {
result = false;
} else if (!to_display.includes('offline') && item.devPresentLastScan === 0) {
result = false;
} else if (!to_display.includes('online') && item.devPresentLastScan === 1) {
result = false;
}
return result; // Include all items for 'my_devices' status
case 'connected':
return item.devPresentLastScan === 1;
case 'favorites':
return item.devFavorite === 1;
case 'new':
return item.devIsNew === 1;
case 'offline':
return item.devPresentLastScan === 0;
case 'down':
return (item.devPresentLastScan === 0 && item.devAlertDown !== 0);
case 'archived':
return item.devIsArchived === 1;
default:
return true; // Include all items for unknown statuses
}
});
} }
// -----------------------------------------------------------------------------
// Map column index to column name for GraphQL query // Map column index to column name for GraphQL query
function mapColumnIndexToFieldName(index, tableColumnVisible) { function mapColumnIndexToFieldName(index, tableColumnVisible) {
// the order is important, don't change it! // the order is important, don't change it!
@@ -356,7 +354,8 @@ function mapColumnIndexToFieldName(index, tableColumnVisible) {
"devSSID", "devSSID",
"devSourcePlugin", "devSourcePlugin",
"devPresentLastScan", "devPresentLastScan",
"devAlertDown" "devAlertDown",
"devCustomProps"
]; ];
// console.log("OrderBy: " + columnNames[tableColumnOrder[index]]); // console.log("OrderBy: " + columnNames[tableColumnOrder[index]]);
@@ -366,6 +365,7 @@ function mapColumnIndexToFieldName(index, tableColumnVisible) {
// --------------------------------------------------------- // ---------------------------------------------------------
// Initializes the main devices list datatable
function initializeDatatable (status) { function initializeDatatable (status) {
if(!status) if(!status)
@@ -466,6 +466,7 @@ function initializeDatatable (status) {
devStatus devStatus
devParentChildrenCount devParentChildrenCount
devIpLong devIpLong
devCustomProps
} }
count count
} }
@@ -532,7 +533,8 @@ function initializeDatatable (status) {
device.devSSID || "", device.devSSID || "",
device.devSourcePlugin || "", device.devSourcePlugin || "",
device.devPresentLastScan || "", device.devPresentLastScan || "",
device.devAlertDown || "" device.devAlertDown || "",
device.devCustomProps || ""
]; ];
const newRow = []; const newRow = [];
@@ -559,11 +561,19 @@ function initializeDatatable (status) {
'order' : tableOrder, 'order' : tableOrder,
'select' : true, // Enable selection 'select' : true, // Enable selection
'fixedHeader': true,
'fixedHeader': {
'header': true,
'footer': true
},
'columnDefs' : [ 'columnDefs' : [
{visible: false, targets: tableColumnHide }, {visible: false, targets: tableColumnHide },
{className: 'text-center', targets: [mapIndx(3), mapIndx(4), mapIndx(9), mapIndx(10), mapIndx(15), mapIndx(18)] }, {className: 'text-center', targets: [mapIndx(4), mapIndx(9), mapIndx(10), mapIndx(15), mapIndx(18)] },
{className: 'iconColumn text-center', targets: [mapIndx(3)]},
{width: '80px', targets: [mapIndx(6), mapIndx(7), mapIndx(15)] }, {width: '80px', targets: [mapIndx(6), mapIndx(7), mapIndx(15)] },
{width: '30px', targets: [mapIndx(10), mapIndx(13), mapIndx(18)] }, {width: '85px', targets: [mapIndx(9)] },
{width: '30px', targets: [mapIndx(3), mapIndx(10), mapIndx(13), mapIndx(18)] },
{orderData: [mapIndx(12)], targets: mapIndx(8) }, {orderData: [mapIndx(12)], targets: mapIndx(8) },
// Device Name // Device Name
@@ -620,9 +630,11 @@ function initializeDatatable (status) {
<a href="http://${cellData}" class="pointer" target="_blank"> <a href="http://${cellData}" class="pointer" target="_blank">
${cellData} ${cellData}
</a> </a>
<a href="https://${cellData}" class="pointer" target="_blank"> <span class="alignRight">
<a href="https://${cellData}" class="pointer" target="_blank">
<i class="fa fa-lock "></i> <i class="fa fa-lock "></i>
</a> </a>
<span>
<span>`); <span>`);
} else { } else {
$(td).html (''); $(td).html ('');
@@ -639,6 +651,17 @@ function initializeDatatable (status) {
} }
} }
}, },
// Custom Properties
{targets: [mapIndx(26)],
'createdCell': function (td, cellData, rowData, row, col) {
if (!emptyArr.includes(cellData)){
$(td).html (`<span>${renderCustomProps(cellData, rowData[mapIndx(11)])}<span>`);
} else {
$(td).html ('');
}
}
},
// Favorite // Favorite
{targets: [mapIndx(4)], {targets: [mapIndx(4)],
@@ -717,11 +740,11 @@ function initializeDatatable (status) {
// Save cookie Rows displayed, and Parameters rows & order // Save cookie Rows displayed, and Parameters rows & order
$('#tableDevices').on( 'length.dt', function ( e, settings, len ) { $('#tableDevices').on( 'length.dt', function ( e, settings, len ) {
setCookie ("nax_parTableRows", len, 129600); // save for 90 days setCache ("nax_parTableRows", len, 129600); // save for 90 days
} ); } );
$('#tableDevices').on( 'order.dt', function () { $('#tableDevices').on( 'order.dt', function () {
setCookie ("nax_parTableOrder", JSON.stringify (table.order()), 129600); // save for 90 days setCache ("nax_parTableOrder", JSON.stringify (table.order()), 129600); // save for 90 days
} ); } );
// add multi-edit button // add multi-edit button
@@ -742,9 +765,27 @@ function initializeDatatable (status) {
}); });
// search only after idle
var typingTimer; // Timer identifier
var debounceTime = 500; // Delay in milliseconds
$('input[aria-controls="tableDevices"]').off().on('keyup', function () {
clearTimeout(typingTimer); // Clear the previous timer
var searchValue = this.value;
typingTimer = setTimeout(function () {
$('#tableDevices').DataTable().search(searchValue).draw(); // Trigger the search after delay
}, debounceTime);
});
hideSpinner(); hideSpinner();
},
createdRow: function(row, data, dataIndex) {
// add devMac to the table row
$(row).attr('my-devMac', data[mapIndx(11)]);
} }
}); });
@@ -756,11 +797,9 @@ function initializeDatatable (status) {
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
function handleLoadingDialog(needsReload = false) function handleLoadingDialog(needsReload = false)
{ {
// console.log(`needsReload: ${needsReload}`);
// console.log('needsReload:'); $.get('/php/server/query_logs.php?file=execution_queue.log&nocache=' + Date.now(), function(data) {
// console.log(needsReload);
$.get('log/execution_queue.log?nocache=' + Date.now(), function(data) {
if(data.includes("update_api|devices")) if(data.includes("update_api|devices"))
{ {
@@ -785,72 +824,112 @@ function handleLoadingDialog(needsReload = false)
// the Miantenance section with a 'macs' query string identifying selected devices // the Miantenance section with a 'macs' query string identifying selected devices
function multiEditDevices() function multiEditDevices()
{ {
rows = $('#tableDevices')[0].rows // get selected devices
var selectedDevicesDataTableData = $('#tableDevices').DataTable().rows({ selected: true, page: 'current' }).data().toArray();
// Initialize an empty array to store selected rows
var selectedRows = [];
// console.log($('#tableDevices')[0].rows);
// Loop through each row in the HTML collection
for (var i = 0; i < rows.length; i++) {
var row = rows[i];
// Check if the row has the 'selected' class
if (row.classList.contains('selected')) {
// If selected, push the row's data to the selectedRows array
selectedRows.push(row);
}
}
// Now, selectedRows contains all selected rows
console.log(selectedRows);
var devicesDataTableData = $('#tableDevices').dataTable().fnGetData();
var selectedDevices = [];
for (var i = 0; i < selectedRows.length; i++) {
selectedDevices.push(devicesDataTableData[selectedRows[i]._DT_RowIndex]);
}
// Now, selectedDevices contains all selected devices
// console.log(selectedDevices);
console.log(selectedDevicesDataTableData);
macs = "" macs = ""
for (var i = 0; i < selectedDevices.length; i++) { for (var j = 0; j < selectedDevicesDataTableData.length; j++) {
macs += selectedDevices[i][mapIndx(11)] + ","; // [11] == MAC macs += selectedDevicesDataTableData[j][mapIndx(11)] + ","; // [11] == MAC
} }
// redirect to the Maintenance section // redirect to the Maintenance section
window.location.href = window.location.origin + '/maintenance.php#tab_multiEdit?macs=' + macs.slice(0, -1); window.location.href = './maintenance.php#tab_multiEdit?macs=' + macs.slice(0, -1);
} }
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// Function collects shown devices from the DataTable // Function collects shown devices from the DataTable
function getMacsOfShownDevices() { function getMacsOfShownDevices() {
rows = $('#tableDevices')[0].rows;
macs = [];
rows = $('#tableDevices')[0].rows // var devicesDataTableData = $('#tableDevices').dataTable().fnGetData();
macs = [] var devicesDataTableData = $('#tableDevices').DataTable().rows({ selected: false, page: 'current' }).data().toArray();
var devicesDataTableData = $('#tableDevices').dataTable().fnGetData(); console.log(devicesDataTableData);
var selectedDevices = []; var selectedDevices = [];
// first row is the heading, skip // first row is the heading, skip
for (var i = 1; i < rows.length; i++) { for (var i = 1; i < rows.length; i++) {
selectedDevices.push(devicesDataTableData[rows[i]._DT_RowIndex]); var rowIndex = rows[i]._DT_RowIndex;
}
// Ensure the rowIndex is valid and within bounds of devicesDataTableData
if (rowIndex >= 0 && rowIndex < devicesDataTableData.length) {
selectedDevices.push(devicesDataTableData[rowIndex]);
} else {
console.log(`Invalid rowIndex: ${rowIndex} at row ${i}`);
}
}
for (var j = 0; j < selectedDevices.length; j++) { for (var j = 0; j < selectedDevices.length; j++) {
macs.push(selectedDevices[j][mapIndx(11)]); // mapIndx(11) == MAC // Ensure that selectedDevices[j] is not undefined
if (selectedDevices[j]) {
macs.push(selectedDevices[j][mapIndx(11)]); // mapIndx(11) == MAC
} else {
console.log(`selectedDevices[${j}] is undefined`);
}
} }
return macs; return macs;
} }
// -----------------------------------------------------------------------------
// Handle custom actions/properties on a device
function renderCustomProps(custProps, mac) {
// Decode and parse the custom properties
if (!isBase64(custProps)) {
console.error(`Unable to decode CustomProps for ${mac}`);
console.error(custProps);
} else{
const props = JSON.parse(atob(custProps));
let html = "";
props.forEach((propGroup, index) => {
const propMap = Object.fromEntries(
propGroup.map(prop => Object.entries(prop)[0]) // Convert array of objects to key-value pairs
);
if (propMap["CUSTPROP_show"] === true) { // Render if visible
let onClickEvent = "";
switch (propMap["CUSTPROP_type"]) {
case "show_notes":
onClickEvent = `showModalOK('${propMap["CUSTPROP_name"]}','${propMap["CUSTPROP_notes"]}')`;
break;
case "link":
onClickEvent = `window.location.href='${propMap["CUSTPROP_args"]}';`;
break;
case "link_new_tab":
onClickEvent = `openInNewTab('${propMap["CUSTPROP_args"]}')`;
break;
case "run_plugin":
onClickEvent = `alert('Not implemented')`;
break;
case "delete_dev":
onClickEvent = `askDelDevDTInline('${mac}')`;
break;
default:
break;
}
html += `<div class="pointer devicePropAction" onclick="${onClickEvent}" title="${propMap["CUSTPROP_name"]} ${propMap["CUSTPROP_args"]}"> ${atob(propMap["CUSTPROP_icon"])} </div>`;
}
});
return html;
}
return "Error, check browser Console log"
}
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// Update cache with shown devices before navigating away // Update cache with shown devices before navigating away

View File

@@ -16,6 +16,8 @@
require 'php/templates/header.php'; require 'php/templates/header.php';
?> ?>
<!-- ----------------------------------------------------------------------- -->
<!-- Page ------------------------------------------------------------------ --> <!-- Page ------------------------------------------------------------------ -->
<div class="content-wrapper"> <div class="content-wrapper">
@@ -173,14 +175,6 @@
require 'php/templates/footer.php'; require 'php/templates/footer.php';
?> ?>
<!-- ----------------------------------------------------------------------- -->
<!-- Datatable -->
<link rel="stylesheet" href="lib/datatables.net-bs/css/dataTables.bootstrap.min.css">
<script src="lib/datatables.net/js/jquery.dataTables.min.js"></script>
<script src="lib/datatables.net-bs/js/dataTables.bootstrap.min.js"></script>
<!-- page script ----------------------------------------------------------- --> <!-- page script ----------------------------------------------------------- -->
<script> <script>
var parPeriod = 'nax_parPeriod'; var parPeriod = 'nax_parPeriod';

View File

@@ -166,8 +166,7 @@ switch ($UI_THEME) {
<!-- jQuery 3 --> <!-- jQuery 3 -->
<script src="lib/jquery/jquery.min.js"></script> <script src="lib/jquery/jquery.min.js"></script>
<!-- Bootstrap 3.3.7 -->
<script src="lib/bootstrap/bootstrap.min.js"></script>
<!-- iCheck --> <!-- iCheck -->
<script src="lib/iCheck/icheck.min.js"></script> <script src="lib/iCheck/icheck.min.js"></script>
<script> <script>

View File

@@ -12,7 +12,7 @@ var timerRefreshData = ''
var emptyArr = ['undefined', "", undefined, null, 'null']; var emptyArr = ['undefined', "", undefined, null, 'null'];
var UI_LANG = "English"; var UI_LANG = "English";
const allLanguages = ["en_us", "es_es", "de_de", "fr_fr", "it_it", "ru_ru", "nb_no", "pl_pl", "pt_br", "tr_tr", "zh_cn", "cs_cz", "ar_ar", "ca_ca"]; // needs to be same as in lang.php const allLanguages = ["en_us", "es_es", "de_de", "fr_fr", "it_it", "ru_ru", "nb_no", "pl_pl", "pt_br", "tr_tr", "zh_cn", "cs_cz", "ar_ar", "ca_ca", "uk_ua"]; // needs to be same as in lang.php
var settingsJSON = {} var settingsJSON = {}
@@ -115,10 +115,10 @@ function cacheSettings()
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
if(!getCache('completedCalls').includes('cacheSettings')) if(!getCache('completedCalls').includes('cacheSettings'))
{ {
$.get('api/table_settings.json?nocache=' + Date.now(), function(resSet) { $.get('/php/server/query_json.php', { file: 'table_settings.json', nocache: Date.now() }, function(resSet) {
$.get('api/plugins.json?nocache=' + Date.now(), function(resPlug) {
$.get('/php/server/query_json.php', { file: 'plugins.json', nocache: Date.now() }, function(resPlug) {
pluginsData = resPlug["data"]; pluginsData = resPlug["data"];
settingsData = resSet["data"]; settingsData = resSet["data"];
@@ -171,7 +171,7 @@ function cacheSettings()
} }
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// Get a setting value by key // Get a setting options value by key
function getSettingOptions (key) { function getSettingOptions (key) {
// handle initial load to make sure everything is set-up and cached // handle initial load to make sure everything is set-up and cached
@@ -225,7 +225,7 @@ function cacheStrings() {
}); });
// Fetch strings and translations from plugins // Fetch strings and translations from plugins
$.get(`api/table_plugins_language_strings.json?nocache=${Date.now()}`) $.get('/php/server/query_json.php', { file: 'table_plugins_language_strings.json', nocache: Date.now() })
.done((pluginRes) => { .done((pluginRes) => {
const data = pluginRes["data"]; const data = pluginRes["data"];
@@ -339,6 +339,9 @@ function getLangCode() {
case 'Catalan (ca_ca)': case 'Catalan (ca_ca)':
lang_code = 'ca_ca'; lang_code = 'ca_ca';
break; break;
case 'Ukrainian (uk_uk)':
lang_code = 'uk_ua';
break;
} }
return lang_code; return lang_code;
@@ -350,6 +353,30 @@ function getLangCode() {
// String utilities // String utilities
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// ----------------------------------------------------
/**
* Replaces double quotes within single-quoted strings, then converts all single quotes to double quotes,
* while preserving the intended structure.
*
* @param {string} inputString - The input string to process.
* @returns {string} - The processed string with transformations applied.
*/
function processQuotes(inputString) {
// Step 1: Replace double quotes within single-quoted strings
let tempString = inputString.replace(/'([^']*?)'/g, (match, p1) => {
const escapedContent = p1.replace(/"/g, '_escaped_double_quote_'); // Temporarily replace double quotes
return `'${escapedContent}'`;
});
// Step 2: Replace all single quotes with double quotes
tempString = tempString.replace(/'/g, '"');
// Step 3: Restore escaped double quotes
const processedString = tempString.replace(/_escaped_double_quote_/g, "'");
return processedString;
}
// ---------------------------------------------------- // ----------------------------------------------------
function jsonSyntaxHighlight(json) { function jsonSyntaxHighlight(json) {
if (typeof json != 'string') { if (typeof json != 'string') {
@@ -383,6 +410,14 @@ function isValidBase64(str) {
return invalidCharacters === ''; return invalidCharacters === '';
} }
// -------------------------------------------------------------------
// 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);
}
// ---------------------------------------------------- // ----------------------------------------------------
function isValidJSON(jsonString) { function isValidJSON(jsonString) {
try { try {
@@ -702,7 +737,7 @@ function forceLoadUrl(relativeUrl) {
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
function navigateToDeviceWithIp (ip) { function navigateToDeviceWithIp (ip) {
$.get('api/table_devices.json?nocache=' + Date.now(), function(res) { $.get('/php/server/query_json.php', { file: 'table_devices.json', nocache: Date.now() }, function(res) {
devices = res["data"]; devices = res["data"];
@@ -714,7 +749,7 @@ function navigateToDeviceWithIp (ip) {
{ {
mac = obj.devMac; mac = obj.devMac;
window.open(window.location.origin +'/deviceDetails.php?mac=' + mac , "_blank"); window.open('./deviceDetails.php?mac=' + mac , "_blank");
} }
}); });
@@ -924,7 +959,7 @@ function cacheDevices()
// if(!getCache('completedCalls').includes('cacheDevices')) // if(!getCache('completedCalls').includes('cacheDevices'))
// { // {
$.get('api/table_devices.json?nocache=' + Date.now(), function(data) { $.get('/php/server/query_json.php', { file: 'table_devices.json', nocache: Date.now() }, function(data) {
// console.log(data) // console.log(data)
@@ -1290,7 +1325,7 @@ function clearCache() {
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// Function to check if cache needs to be refreshed because of setting changes // Function to check if cache needs to be refreshed because of setting changes
function checkSettingChanges() { function checkSettingChanges() {
$.get('api/app_state.json?nocache=' + Date.now(), function(appState) { $.get('/php/server/query_json.php', { file: 'app_state.json', nocache: Date.now() }, function(appState) {
const importedMilliseconds = parseInt(appState["settingsImported"] * 1000); const importedMilliseconds = parseInt(appState["settingsImported"] * 1000);
const lastReloaded = parseInt(sessionStorage.getItem(sessionStorageKey + '_time')); const lastReloaded = parseInt(sessionStorage.getItem(sessionStorageKey + '_time'));
@@ -1345,7 +1380,7 @@ async function waitForGraphQLServer() {
// Returns 1 if running, 0 otherwise // Returns 1 if running, 0 otherwise
async function isGraphQLServerRunning() { async function isGraphQLServerRunning() {
try { try {
const response = await $.get('api/app_state.json?nocache=' + Date.now()); const response = await $.get('/php/server/query_json.php', { file: 'app_state.json', nocache: Date.now()});
console.log("graphQLServerStarted: " + response["graphQLServerStarted"]); console.log("graphQLServerStarted: " + response["graphQLServerStarted"]);
setCache("graphQLServerStarted", response["graphQLServerStarted"]); setCache("graphQLServerStarted", response["graphQLServerStarted"]);
return response["graphQLServerStarted"]; return response["graphQLServerStarted"];

View File

@@ -32,7 +32,7 @@ function renderList(
// remove first item containing the SQL query // remove first item containing the SQL query
options.shift(); options.shift();
const apiUrl = `php/server/dbHelper.php?action=read&rawSql=${encodeURIComponent(sqlQuery)}`; const apiUrl = `php/server/dbHelper.php?action=read&rawSql=${btoa(encodeURIComponent(sqlQuery))}`;
$.get(apiUrl, function (sqlOptionsData) { $.get(apiUrl, function (sqlOptionsData) {
@@ -85,7 +85,7 @@ function renderList(
// Check if database is locked // Check if database is locked
function checkDbLock() { function checkDbLock() {
$.ajax({ $.ajax({
url: "log/db_is_locked.log", // Replace with the actual path to your PHP file url: "/php/server/query_logs.php?file=db_is_locked.log",
type: "GET", type: "GET",
success: function (response) { success: function (response) {

70
front/js/device.js Executable file
View File

@@ -0,0 +1,70 @@
// -----------------------------------------------------------------------------
function askDeleteDevice() {
// Check MAC
if (mac == '') {
return;
}
// Ask delete device
showModalWarning(
getString("DevDetail_button_Delete"),
getString("DevDetail_button_Delete_ask"),
getString('Gen_Cancel'),
getString('Gen_Delete'),
'deleteDevice');
}
// -----------------------------------------------------------------------------
function askDelDevDTInline(mac) {
// Check MAC
if (mac == '') {
return;
}
showModalWarning(
getString("DevDetail_button_Delete"),
getString("DevDetail_button_Delete_ask"),
getString('Gen_Cancel'),
getString('Gen_Delete'),
() => deleteDeviceByMac(mac))
}
// -----------------------------------------------------------------------------
function deleteDevice() {
// Check MAC
if (mac == '') {
return;
}
// Delete device
$.get('php/server/devices.php?action=deleteDevice&mac=' + mac, function (msg) {
showMessage(msg);
});
// refresh API
updateApi("devices,appevents")
}
// -----------------------------------------------------------------------------
function deleteDeviceByMac(mac) {
// Check MAC
if (mac == '') {
return;
}
// alert(mac)
// return;
// Delete device
$.get('php/server/devices.php?action=deleteDevice&mac=' + mac, function (msg) {
showMessage(msg);
});
// refresh API
updateApi("devices,appevents")
}

View File

@@ -33,7 +33,7 @@ function versionUpdateUI(){
// Checks if a new version is available via the global app_state.json // Checks if a new version is available via the global app_state.json
function checkIfNewVersionAvailable() function checkIfNewVersionAvailable()
{ {
$.get('api/app_state.json?nocache=' + Date.now(), function(appState) { $.get('/php/server/query_json.php', { file: 'app_state.json', nocache: Date.now() }, function(appState) {
// console.log(appState["isNewVersionChecked"]) // console.log(appState["isNewVersionChecked"])
// console.log(appState["isNewVersion"]) // console.log(appState["isNewVersion"])

View File

@@ -65,7 +65,8 @@ function showModalWarning(
message, message,
btnCancel = getString("Gen_Cancel"), btnCancel = getString("Gen_Cancel"),
btnOK = getString("Gen_Okay"), btnOK = getString("Gen_Okay"),
callbackFunction = null callbackFunction = null,
triggeredBy = null
) { ) {
// set captions // set captions
$("#modal-warning-title").html(title); $("#modal-warning-title").html(title);
@@ -77,6 +78,10 @@ function showModalWarning(
modalCallbackFunction = callbackFunction; modalCallbackFunction = callbackFunction;
} }
if (triggeredBy != null) {
$('#'+prefix).attr("data-myparam-triggered-by", triggeredBy)
}
// Show modal // Show modal
$("#modal-warning").modal("show"); $("#modal-warning").modal("show");
} }

View File

@@ -191,14 +191,6 @@ function isSHA256(value) {
} }
} }
// -------------------------------------------------------------------
// 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);
}
// ------------------------------------------------------------------- // -------------------------------------------------------------------
// Validation // Validation
// ------------------------------------------------------------------- // -------------------------------------------------------------------
@@ -245,6 +237,68 @@ function settingsCollectedCorrectly(settingsArray, settingsJSON_DB) {
// Manipulating Editable List options // Manipulating Editable List options
// ------------------------------------------------------------------- // -------------------------------------------------------------------
// ---------------------------------------------------------
// Add row to datatable
function addDataTableRow(el)
{
alert("a")
}
// ---------------------------------------------------------
// Clone datatable row
function cloneDataTableRow(el){
console.log(el);
const id = "NEWDEV_devCustomProps_table"; // Your table ID
const table = $('#'+id).DataTable();
// Get the 'my-index' attribute from the closest tr element
const myIndex = parseInt($(el).closest("tr").attr("my-index"));
// Find the row in the table with the matching 'my-index'
const row = table.rows().nodes().to$().filter(`[my-index="${myIndex}"]`).first().get(0);
// Clone the row (including its data and controls)
let clonedRow = $(row).clone(true, true); // The true arguments copy the data and event handlers
$(clonedRow).attr("my-index",table.rows().count())
console.log(clonedRow);
// Add the cloned row to the DataTable
table.row.add(clonedRow[0]).draw();
}
// ---------------------------------------------------------
// Remove current datatable row
function removeDataTableRow(el) {
console.log(el);
const id = "NEWDEV_devCustomProps_table"; // Your table ID
const table = $('#'+id).DataTable();
if(table.rows().count() > 1)
{
// Get the 'my-index' attribute from the closest tr element
const myIndex = parseInt($(el).closest("tr").attr("my-index"));
// Find the row in the table with the matching 'my-index'
const row = table.rows().nodes().to$().filter(`[my-index="${myIndex}"]`).first().get(0);
// Remove the row from the DataTable
table.row(row).remove().draw();
}
else
{
showMessage (getString("CustProps_cant_remove"), 3000, "modal_red");
}
}
// --------------------------------------------------------- // ---------------------------------------------------------
// Add item to list // Add item to list
function addList(element, clearInput = true) { function addList(element, clearInput = true) {
@@ -551,7 +605,7 @@ function overrideToggle(element) {
} }
// ---------------------------------------------------------
// Generate options or set options based on the provided parameters // Generate options or set options based on the provided parameters
function generateOptionsOrSetOptions( function generateOptionsOrSetOptions(
setKey, setKey,
@@ -559,14 +613,21 @@ function generateOptionsOrSetOptions(
placeholder, // ID of the HTML element where dropdown should be rendered (will be replaced) placeholder, // ID of the HTML element where dropdown should be rendered (will be replaced)
processDataCallback, // Callback function to generate entries based on options processDataCallback, // Callback function to generate entries based on options
targetField, // Target field or element where selected value should be applied or updated targetField, // Target field or element where selected value should be applied or updated
transformers = [] // Transformers to be applied to the values transformers = [], // Transformers to be applied to the values
overrideOptions = null // override options if available
) { ) {
// console.log(setKey); // console.log(setKey);
// NOTE {value} options to replace with a setting or SQL value are handled in the cacheSettings() function // console.log(overrideOptions);
options = arrayToObject(createArray(getSettingOptions(setKey))) // console.log( getSettingOptions(setKey));
// console.log( setKey);
// NOTE {value} options to replace with a setting or SQL value are handled in the cacheSettings() function
options = arrayToObject(createArray(overrideOptions ? overrideOptions : getSettingOptions(setKey)))
// Call to render lists // Call to render lists
renderList( renderList(
options, options,
@@ -655,6 +716,7 @@ const handleElementOptions = (setKey, elementOptions, transformers, val) => {
let onChange = "console.log('onChange - Not implemented');"; let onChange = "console.log('onChange - Not implemented');";
let customParams = ""; let customParams = "";
let customId = ""; let customId = "";
let columns = [];
elementOptions.forEach((option) => { elementOptions.forEach((option) => {
@@ -708,6 +770,9 @@ const handleElementOptions = (setKey, elementOptions, transformers, val) => {
if (option.customId) { if (option.customId) {
customId = option.customId; customId = option.customId;
} }
if (option.columns) {
columns = option.columns;
}
}); });
if (transformers.includes("sha256")) { if (transformers.includes("sha256")) {
@@ -730,7 +795,8 @@ const handleElementOptions = (setKey, elementOptions, transformers, val) => {
onClick, onClick,
onChange, onChange,
customParams, customParams,
customId customId,
columns
}; };
}; };
@@ -863,7 +929,7 @@ function genListWithInputSet(options, valuesArray, targetField, transformers, pl
// ------------------------------------------------------------------------------ // ------------------------------------------------------------------------------
// Generate the form control for setting // Generate the form control for setting
function generateFormHtml(set, overrideValue) { function generateFormHtml(settingsData, set, overrideValue, overrideOptions, originalSetKey) {
let inputHtml = ''; let inputHtml = '';
@@ -871,14 +937,17 @@ function generateFormHtml(set, overrideValue) {
const setKey = set['setKey']; const setKey = set['setKey'];
const setType = set['setType']; const setType = set['setType'];
// if (setKey == '') {
// console.log(setType); // console.log(setType);
// console.log(setKey); // console.log(setKey);
// console.log(overrideValue); // console.log(overrideValue);
// console.log(inVal); // console.log(inVal);
// }
// Parse the setType JSON string // Parse the setType JSON string
const setTypeObject = JSON.parse(setType.replace(/'/g, '"')); const setTypeObject = JSON.parse(processQuotes(setType))
const dataType = setTypeObject.dataType; const dataType = setTypeObject.dataType;
const elements = setTypeObject.elements || []; const elements = setTypeObject.elements || [];
@@ -903,14 +972,22 @@ function generateFormHtml(set, overrideValue) {
onClick, onClick,
onChange, onChange,
customParams, customParams,
customId customId,
columns
} = handleElementOptions(setKey, elementOptions, transformers, inVal); } = handleElementOptions(setKey, elementOptions, transformers, inVal);
// Override value // Override value
const val = valRes; let val = valRes;
// console.log(val); // if (setKey == '') {
// console.log(setType);
// console.log(setKey);
// console.log(overrideValue);
// console.log(inVal);
// console.log(val);
// }
// Generate HTML based on elementType // Generate HTML based on elementType
switch (elementType) { switch (elementType) {
@@ -921,20 +998,21 @@ function generateFormHtml(set, overrideValue) {
inputHtml += `<select onChange="settingsChanged();${onChange}" inputHtml += `<select onChange="settingsChanged();${onChange}"
my-data-type="${dataType}" my-data-type="${dataType}"
my-editable="${editable}" my-editable="${editable}"
class="form-control ${addCss}" class="form-control ${addCss} ${cssClasses}"
name="${setKey}" name="${setKey}"
id="${setKey}" id="${setKey}"
my-customparams="${customParams}" my-customparams="${customParams}"
my-customid="${customId}" my-customid="${customId}"
my-originalSetKey="${originalSetKey}"
${multi}> ${multi}>
<option value="" id="${setKey + "_temp_"}"></option> <option value="" id="${setKey + "_temp_"}"></option>
</select>`; </select>`;
generateOptionsOrSetOptions(setKey, createArray(val), `${setKey}_temp_`, generateOptions, null, transformers); generateOptionsOrSetOptions(setKey, createArray(val), `${setKey}_temp_`, generateOptions, null, transformers, overrideOptions);
break; break;
case 'input': case 'input':
const checked = val === 'True' || val === '1' ? 'checked' : ''; const checked = val === 'True' || val === 'true' || val === '1' ? 'checked' : '';
const inputClass = inputType === 'checkbox' ? 'checkbox' : 'form-control'; const inputClass = inputType === 'checkbox' ? 'checkbox' : 'form-control';
inputHtml += `<input inputHtml += `<input
@@ -943,6 +1021,7 @@ function generateFormHtml(set, overrideValue) {
my-data-type="${dataType}" my-data-type="${dataType}"
my-customparams="${customParams}" my-customparams="${customParams}"
my-customid="${customId}" my-customid="${customId}"
my-originalSetKey="${originalSetKey}"
id="${setKey}${suffix}" id="${setKey}${suffix}"
type="${inputType}" type="${inputType}"
value="${val}" value="${val}"
@@ -957,6 +1036,7 @@ function generateFormHtml(set, overrideValue) {
class="btn btn-primary ${cssClasses}" class="btn btn-primary ${cssClasses}"
my-customparams="${customParams}" my-customparams="${customParams}"
my-customid="${customId}" my-customid="${customId}"
my-originalSetKey="${originalSetKey}"
my-input-from="${sourceIds}" my-input-from="${sourceIds}"
my-input-to="${setKey}" my-input-to="${setKey}"
onclick="${onClick}"> onclick="${onClick}">
@@ -969,6 +1049,7 @@ function generateFormHtml(set, overrideValue) {
class="form-control input" class="form-control input"
my-customparams="${customParams}" my-customparams="${customParams}"
my-customid="${customId}" my-customid="${customId}"
my-originalSetKey="${originalSetKey}"
my-data-type="${dataType}" my-data-type="${dataType}"
id="${setKey}" id="${setKey}"
${readOnly}>${val}</textarea>`; ${readOnly}>${val}</textarea>`;
@@ -979,10 +1060,116 @@ function generateFormHtml(set, overrideValue) {
class="${cssClasses}" class="${cssClasses}"
my-data-type="${dataType}" my-data-type="${dataType}"
my-customparams="${customParams}" my-customparams="${customParams}"
my-customid="${customId}"> my-customid="${customId}"
${getString(getStringKey)} my-originalSetKey="${originalSetKey}"
onclick="${onClick}">
${getString(getStringKey)}${placeholder}
</span>`; </span>`;
break; break;
case 'datatable':
const tableId = `${setKey}_table`;
let datatableHtml = `<table id="${tableId}" class="table table-striped">`;
// Dynamic array creation
let emptyVal = [];
let columnSettings = [];
// Generate table headers
datatableHtml += '<thead><tr>';
columns.forEach(column => {
let columnSetting = getSetObject(settingsData, column.settingKey) || {};
datatableHtml += `<th>${columnSetting.setName}</th>`;
if(column.typeOverride)
{
columnSetting["setType"] = JSON.stringify(column.typeOverride);
}
if(column.optionsOverride)
{
if (column.optionsOverride.startsWith("setting.")) {
columnSetting["setOptions"] = getSetting(column.optionsOverride.replace("setting.",""));
} else {
columnSetting["setOptions"] = column.optionsOverride;
}
}
columnSettings.push(columnSetting)
// helper for if val is empty
emptyVal.push('');
});
datatableHtml += '</tr></thead>';
// Generate table body
datatableHtml += '<tbody>';
if(val.length > 0 && isBase64(val))
{
val = atob(val)
// console.log(val);
val = JSON.parse(val)
}else{
// init empty
val = [emptyVal]
}
let index = 0;
val.forEach(rowData => {
datatableHtml += `<tr my-index="${index}">`;
let j = 0;
columnSettings.forEach(set => {
// Extract the value for the current column based on the new structure
let columnOverrideValue = rowData[j] && Object.values(rowData[j])[0];
if(columnOverrideValue == undefined)
{
columnOverrideValue = ""
}
// Create unique key to prevent dropdown data duplication
const oldKey = set["setKey"];
set["setKey"] = oldKey + "_" + index;
// Generate the cell HTML using the extracted value
const cellHtml = generateFormHtml(
settingsData,
set,
columnOverrideValue.toString(),
set["setOptions"],
oldKey
);
datatableHtml += `<td> <div class="input-group"> ${cellHtml} </div></td>`;
// Restore the original key
set["setKey"] = oldKey;
j++;
});
datatableHtml += '</tr>';
index++;
});
datatableHtml += '</tbody></table>';
inputHtml += datatableHtml;
// Initialize DataTable with jQuery
$(document).ready(() => {
$(`#${tableId}`).DataTable({
ordering: false, // Disables sorting on all columns
searching: false, // Disables the search box
dom: "<'top'rt><'bottom'ipl>", // Move length dropdown to the bottom
});
});
break;
default: default:
console.warn(`🟥 Unknown element type: ${elementType}`); console.warn(`🟥 Unknown element type: ${elementType}`);
@@ -991,10 +1178,6 @@ function generateFormHtml(set, overrideValue) {
// Generate event HTML if applicable // Generate event HTML if applicable
let eventsHtml = ''; let eventsHtml = '';
// console.log(setTypeObject);
// console.log(set);
const eventsList = createArray(set['setEvents']); const eventsList = createArray(set['setEvents']);
// inline buttons events // inline buttons events
@@ -1007,7 +1190,7 @@ function generateFormHtml(set, overrideValue) {
id="${`${event}_${setKey}`}" id="${`${event}_${setKey}`}"
data-myparam-setkey="${setKey}" data-myparam-setkey="${setKey}"
data-myparam="${setKey}" data-myparam="${setKey}"
data-myparam-plugin="${setTypeObject.prefix || ''}" data-myparam-plugin="${setKey.split('_')[0] || ''}"
data-myevent="${event}" data-myevent="${event}"
onclick="execute_settingEvent(this)"> onclick="execute_settingEvent(this)">
<i title="${getString(event + "_event_tooltip")}" class="fa ${getString(event + "_event_icon")}"></i> <i title="${getString(event + "_event_tooltip")}" class="fa ${getString(event + "_event_icon")}"></i>
@@ -1019,4 +1202,68 @@ function generateFormHtml(set, overrideValue) {
return inputHtml + eventsHtml; return inputHtml + eventsHtml;
} }
// -----------------------------------------------------
// Return the setting object by setKey
function getSetObject(settingsData, setKey) {
found = false;
result = ""
settingsData.forEach(function(set) {
if (set.setKey == setKey) {
// console.log(set);
result = set;
return;
}
});
if(result == "")
{
console.error(settingsData);
console.error(`Setting not found: ${setKey}`);
}
return result;
}
// ---------------------------------------
// Collect DataTable data
function collectTableData(tableSelector) {
const table = $(tableSelector).DataTable();
let tableData = [];
table.rows().every(function () {
const rowData = [];
const cells = $(this.node()).find('td');
cells.each((index, cell) => {
const input = $(cell).find('input, select, textarea');
if (input.length) {
if (input.attr('type') === 'checkbox') {
// For checkboxes, check if they are checked
rowData[index] = { [input.attr("my-originalsetkey")] : input.prop('checked') };
} else {
// Generic sync for other inputs (text, select, textarea)
rowData[index] = { [input.attr("my-originalsetkey")] : input.val().replace(/'/g, "").replace(/"/g, "") };
}
} else {
// Handle plain text
// rowData[index] = { [input.attr("my-originalsetkey")] : $(cell).text()};
console.log(`Nothig to collect: ${$(cell).html()}`)
}
});
tableData.push(rowData);
});
return tableData;
}

View File

@@ -97,12 +97,54 @@ function generateApiToken(elem, length) {
} }
} }
// ----------------------------------------------
// Generate a random N-byte hexadecimal key
function getRandomBytes(elem, length) {
// Retrieve and parse custom parameters from the element
let params = $(elem).attr("my-customparams")?.split(',').map(param => param.trim());
if (params && params.length >= 1) {
var targetElementID = params[0]; // Get the target element's ID
}
let targetElement = $('#' + targetElementID);
// Generate random bytes
const array = new Uint8Array(length);
window.crypto.getRandomValues(array);
// Convert bytes to hexadecimal string
let hexString = Array.from(array, byte =>
byte.toString(16).padStart(2, '0')
).join('');
// Format hexadecimal string with hyphens
let formattedHex = hexString.match(/.{1,2}/g).join('-');
console.log(formattedHex);
// console.log($(`#${targetInput}`).val());
// Set the formatted key value to the input field
targetElement.val(formattedHex);
}
// ----------------------------------------------
// Updates the icon preview
function updateAllIconPreviews() {
$(".iconInputVal").each((index, el)=>{
updateIconPreview(el)
})
}
// ---------------------------------------------- // ----------------------------------------------
// Updates the icon preview // Updates the icon preview
function updateIconPreview(elem) { function updateIconPreview(elem) {
const targetElement = $('[my-customid="NEWDEV_devIcon_preview"]');
const iconInput = $("#NEWDEV_devIcon"); const previewSpan = $(elem).parent().find(".iconPreview");
const iconInput = $(elem);
let attempts = 0; let attempts = 0;
@@ -110,10 +152,10 @@ function updateIconPreview(elem) {
let value = iconInput.val(); let value = iconInput.val();
if (value) { if (value) {
targetElement.html(atob(value)); previewSpan.html(atob(value));
iconInput.off('change input').on('change input', function () { iconInput.off('change input').on('change input', function () {
let newValue = $(this).val(); let newValue = $(elem).val();
targetElement.html(atob(newValue)); previewSpan.html(atob(newValue));
}); });
return; // Stop retrying if successful return; // Stop retrying if successful
} }
@@ -314,7 +356,7 @@ function updateModalState() {
setTimeout(function() { setTimeout(function() {
// Fetch the content from the log file using an AJAX request // Fetch the content from the log file using an AJAX request
$.ajax({ $.ajax({
url: '/log/execution_queue.log', url: '/php/server/query_logs.php?file=execution_queue.log',
type: 'GET', type: 'GET',
success: function(data) { success: function(data) {
// Update the content of the HTML element (e.g., a div with id 'logContent') // Update the content of the HTML element (e.g., a div with id 'logContent')
@@ -449,7 +491,7 @@ function showIconSelection() {
iconDiv.addEventListener('click', () => { iconDiv.addEventListener('click', () => {
selectElement.value = value; // Update the select element value selectElement.value = value; // Update the select element value
$(`#${modalId}`).modal('hide'); // Hide the modal $(`#${modalId}`).modal('hide'); // Hide the modal
updateIconPreview(); updateAllIconPreviews();
}); });
} }
}); });
@@ -528,7 +570,7 @@ window.addEventListener("load", function() {
// try to initialize // try to initialize
setTimeout(() => { setTimeout(() => {
initSelect2() initSelect2()
initializeiCheck(); // initializeiCheck();
}, 1000); }, 1000);
}); });

20
front/lib/AdminLTE/dist/css/LICENSE vendored Executable file
View File

@@ -0,0 +1,20 @@
The MIT License (MIT)
Copyright (c) 2014-2023 ColorlibHQ
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

1
front/lib/AdminLTE/dist/js/adminlte.js.map vendored Executable file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

147
front/lib/AdminLTE/dist/js/pages/dashboard3.js vendored Executable file
View File

@@ -0,0 +1,147 @@
/* global Chart:false */
$(function () {
'use strict'
var ticksStyle = {
fontColor: '#495057',
fontStyle: 'bold'
}
var mode = 'index'
var intersect = true
var $salesChart = $('#sales-chart')
// eslint-disable-next-line no-unused-vars
var salesChart = new Chart($salesChart, {
type: 'bar',
data: {
labels: ['JUN', 'JUL', 'AUG', 'SEP', 'OCT', 'NOV', 'DEC'],
datasets: [
{
backgroundColor: '#007bff',
borderColor: '#007bff',
data: [1000, 2000, 3000, 2500, 2700, 2500, 3000]
},
{
backgroundColor: '#ced4da',
borderColor: '#ced4da',
data: [700, 1700, 2700, 2000, 1800, 1500, 2000]
}
]
},
options: {
maintainAspectRatio: false,
tooltips: {
mode: mode,
intersect: intersect
},
hover: {
mode: mode,
intersect: intersect
},
legend: {
display: false
},
scales: {
yAxes: [{
// display: false,
gridLines: {
display: true,
lineWidth: '4px',
color: 'rgba(0, 0, 0, .2)',
zeroLineColor: 'transparent'
},
ticks: $.extend({
beginAtZero: true,
// Include a dollar sign in the ticks
callback: function (value) {
if (value >= 1000) {
value /= 1000
value += 'k'
}
return '$' + value
}
}, ticksStyle)
}],
xAxes: [{
display: true,
gridLines: {
display: false
},
ticks: ticksStyle
}]
}
}
})
var $visitorsChart = $('#visitors-chart')
// eslint-disable-next-line no-unused-vars
var visitorsChart = new Chart($visitorsChart, {
data: {
labels: ['18th', '20th', '22nd', '24th', '26th', '28th', '30th'],
datasets: [{
type: 'line',
data: [100, 120, 170, 167, 180, 177, 160],
backgroundColor: 'transparent',
borderColor: '#007bff',
pointBorderColor: '#007bff',
pointBackgroundColor: '#007bff',
fill: false
// pointHoverBackgroundColor: '#007bff',
// pointHoverBorderColor : '#007bff'
},
{
type: 'line',
data: [60, 80, 70, 67, 80, 77, 100],
backgroundColor: 'tansparent',
borderColor: '#ced4da',
pointBorderColor: '#ced4da',
pointBackgroundColor: '#ced4da',
fill: false
// pointHoverBackgroundColor: '#ced4da',
// pointHoverBorderColor : '#ced4da'
}]
},
options: {
maintainAspectRatio: false,
tooltips: {
mode: mode,
intersect: intersect
},
hover: {
mode: mode,
intersect: intersect
},
legend: {
display: false
},
scales: {
yAxes: [{
// display: false,
gridLines: {
display: true,
lineWidth: '4px',
color: 'rgba(0, 0, 0, .2)',
zeroLineColor: 'transparent'
},
ticks: $.extend({
beginAtZero: true,
suggestedMax: 200
}, ticksStyle)
}],
xAxes: [{
display: true,
gridLines: {
display: false
},
ticks: ticksStyle
}]
}
}
})
})
// lgtm [js/unused-local-variable]

48
front/lib/bcrypt/bcrypt.min.js vendored Executable file
View File

@@ -0,0 +1,48 @@
/*
bcrypt.js (c) 2013 Daniel Wirtz <dcode@dcode.io>
Released under the Apache License, Version 2.0
see: https://github.com/dcodeIO/bcrypt.js for details
*/
(function(u,r){"function"===typeof define&&define.amd?define([],r):"function"===typeof require&&"object"===typeof module&&module&&module.exports?module.exports=r():(u.dcodeIO=u.dcodeIO||{}).bcrypt=r()})(this,function(){function u(e){if("undefined"!==typeof module&&module&&module.exports)try{return require("crypto").randomBytes(e)}catch(d){}try{var c;(self.crypto||self.msCrypto).getRandomValues(c=new Uint32Array(e));return Array.prototype.slice.call(c)}catch(b){}if(!w)throw Error("Neither WebCryptoAPI nor a crypto module is available. Use bcrypt.setRandomFallback to set an alternative");
return w(e)}function r(e,d){for(var c=0,b=0,a=0,f=e.length;a<f;++a)e.charCodeAt(a)===d.charCodeAt(a)?++c:++b;return 0>c?!1:0===b}function H(e){var d=[],c=0;I.encodeUTF16toUTF8(function(){return c>=e.length?null:e.charCodeAt(c++)},function(b){d.push(b)});return d}function x(e,d){var c=0,b=[],a,f;if(0>=d||d>e.length)throw Error("Illegal len: "+d);for(;c<d;){a=e[c++]&255;b.push(s[a>>2&63]);a=(a&3)<<4;if(c>=d){b.push(s[a&63]);break}f=e[c++]&255;a|=f>>4&15;b.push(s[a&63]);a=(f&15)<<2;if(c>=d){b.push(s[a&
63]);break}f=e[c++]&255;a|=f>>6&3;b.push(s[a&63]);b.push(s[f&63])}return b.join("")}function B(e,d){var c=0,b=e.length,a=0,f=[],g,m,h;if(0>=d)throw Error("Illegal len: "+d);for(;c<b-1&&a<d;){h=e.charCodeAt(c++);g=h<q.length?q[h]:-1;h=e.charCodeAt(c++);m=h<q.length?q[h]:-1;if(-1==g||-1==m)break;h=g<<2>>>0;h|=(m&48)>>4;f.push(z(h));if(++a>=d||c>=b)break;h=e.charCodeAt(c++);g=h<q.length?q[h]:-1;if(-1==g)break;h=(m&15)<<4>>>0;h|=(g&60)>>2;f.push(z(h));if(++a>=d||c>=b)break;h=e.charCodeAt(c++);m=h<q.length?
q[h]:-1;h=(g&3)<<6>>>0;h|=m;f.push(z(h));++a}b=[];for(c=0;c<a;c++)b.push(f[c].charCodeAt(0));return b}function v(e,d,c,b){var a,f=e[d],g=e[d+1],f=f^c[0];a=b[f>>>24];a+=b[256|f>>16&255];a^=b[512|f>>8&255];a+=b[768|f&255];g=g^a^c[1];a=b[g>>>24];a+=b[256|g>>16&255];a^=b[512|g>>8&255];a+=b[768|g&255];f=f^a^c[2];a=b[f>>>24];a+=b[256|f>>16&255];a^=b[512|f>>8&255];a+=b[768|f&255];g=g^a^c[3];a=b[g>>>24];a+=b[256|g>>16&255];a^=b[512|g>>8&255];a+=b[768|g&255];f=f^a^c[4];a=b[f>>>24];a+=b[256|f>>16&255];a^=b[512|
f>>8&255];a+=b[768|f&255];g=g^a^c[5];a=b[g>>>24];a+=b[256|g>>16&255];a^=b[512|g>>8&255];a+=b[768|g&255];f=f^a^c[6];a=b[f>>>24];a+=b[256|f>>16&255];a^=b[512|f>>8&255];a+=b[768|f&255];g=g^a^c[7];a=b[g>>>24];a+=b[256|g>>16&255];a^=b[512|g>>8&255];a+=b[768|g&255];f=f^a^c[8];a=b[f>>>24];a+=b[256|f>>16&255];a^=b[512|f>>8&255];a+=b[768|f&255];g=g^a^c[9];a=b[g>>>24];a+=b[256|g>>16&255];a^=b[512|g>>8&255];a+=b[768|g&255];f=f^a^c[10];a=b[f>>>24];a+=b[256|f>>16&255];a^=b[512|f>>8&255];a+=b[768|f&255];g=g^a^
c[11];a=b[g>>>24];a+=b[256|g>>16&255];a^=b[512|g>>8&255];a+=b[768|g&255];f=f^a^c[12];a=b[f>>>24];a+=b[256|f>>16&255];a^=b[512|f>>8&255];a+=b[768|f&255];g=g^a^c[13];a=b[g>>>24];a+=b[256|g>>16&255];a^=b[512|g>>8&255];a+=b[768|g&255];f=f^a^c[14];a=b[f>>>24];a+=b[256|f>>16&255];a^=b[512|f>>8&255];a+=b[768|f&255];g=g^a^c[15];a=b[g>>>24];a+=b[256|g>>16&255];a^=b[512|g>>8&255];a+=b[768|g&255];f=f^a^c[16];e[d]=g^c[17];e[d+1]=f;return e}function t(e,d){for(var c=0,b=0;4>c;++c)b=b<<8|e[d]&255,d=(d+1)%e.length;
return{key:b,offp:d}}function C(e,d,c){for(var b=0,a=[0,0],f=d.length,g=c.length,m,h=0;h<f;h++)m=t(e,b),b=m.offp,d[h]^=m.key;for(h=0;h<f;h+=2)a=v(a,0,d,c),d[h]=a[0],d[h+1]=a[1];for(h=0;h<g;h+=2)a=v(a,0,d,c),c[h]=a[0],c[h+1]=a[1]}function J(e,d,c,b){for(var a=0,f=[0,0],g=c.length,m=b.length,h,l=0;l<g;l++)h=t(d,a),a=h.offp,c[l]^=h.key;for(l=a=0;l<g;l+=2)h=t(e,a),a=h.offp,f[0]^=h.key,h=t(e,a),a=h.offp,f[1]^=h.key,f=v(f,0,c,b),c[l]=f[0],c[l+1]=f[1];for(l=0;l<m;l+=2)h=t(e,a),a=h.offp,f[0]^=h.key,h=t(e,
a),a=h.offp,f[1]^=h.key,f=v(f,0,c,b),b[l]=f[0],b[l+1]=f[1]}function D(e,d,c,b,a){function f(){a&&a(n/c);if(n<c)for(var h=Date.now();n<c&&!(n+=1,C(e,l,k),C(d,l,k),100<Date.now()-h););else{for(n=0;64>n;n++)for(y=0;y<m>>1;y++)v(g,y<<1,l,k);h=[];for(n=0;n<m;n++)h.push((g[n]>>24&255)>>>0),h.push((g[n]>>16&255)>>>0),h.push((g[n]>>8&255)>>>0),h.push((g[n]&255)>>>0);if(b){b(null,h);return}return h}b&&p(f)}var g=E.slice(),m=g.length,h;if(4>c||31<c){h=Error("Illegal number of rounds (4-31): "+c);if(b){p(b.bind(this,
h));return}throw h;}if(16!==d.length){h=Error("Illegal salt length: "+d.length+" != 16");if(b){p(b.bind(this,h));return}throw h;}c=1<<c>>>0;var l,k,n=0,y;Int32Array?(l=new Int32Array(F),k=new Int32Array(G)):(l=F.slice(),k=G.slice());J(d,e,l,k);if("undefined"!==typeof b)f();else for(;;)if("undefined"!==typeof(h=f()))return h||[]}function A(e,d,c,b){function a(a){var b=[];b.push("$2");"a"<=f&&b.push(f);b.push("$");10>l&&b.push("0");b.push(l.toString());b.push("$");b.push(x(k,k.length));b.push(x(a,4*
E.length-1));return b.join("")}if("string"!==typeof e||"string"!==typeof d){b=Error("Invalid string / salt: Not a string");if(c){p(c.bind(this,b));return}throw b;}var f,g;if("$"!==d.charAt(0)||"2"!==d.charAt(1)){b=Error("Invalid salt version: "+d.substring(0,2));if(c){p(c.bind(this,b));return}throw b;}if("$"===d.charAt(2))f=String.fromCharCode(0),g=3;else{f=d.charAt(2);if("a"!==f&&"b"!==f&&"y"!==f||"$"!==d.charAt(3)){b=Error("Invalid salt revision: "+d.substring(2,4));if(c){p(c.bind(this,b));return}throw b;
}g=4}if("$"<d.charAt(g+2)){b=Error("Missing salt rounds");if(c){p(c.bind(this,b));return}throw b;}var m=10*parseInt(d.substring(g,g+1),10),h=parseInt(d.substring(g+1,g+2),10),l=m+h;d=d.substring(g+3,g+25);e=H(e+("a"<=f?"\x00":""));var k=B(d,16);if("undefined"==typeof c)return a(D(e,k,l));D(e,k,l,function(b,d){b?c(b,null):c(null,a(d))},b)}var k={},w=null;try{u(1)}catch(K){}w=null;k.setRandomFallback=function(e){w=e};k.genSaltSync=function(e,d){e=e||10;if("number"!==typeof e)throw Error("Illegal arguments: "+
typeof e+", "+typeof d);4>e?e=4:31<e&&(e=31);var c=[];c.push("$2a$");10>e&&c.push("0");c.push(e.toString());c.push("$");c.push(x(u(16),16));return c.join("")};k.genSalt=function(e,d,c){function b(a){p(function(){try{a(null,k.genSaltSync(e))}catch(b){a(b)}})}"function"===typeof d&&(c=d,d=void 0);"function"===typeof e&&(c=e,e=void 0);if("undefined"===typeof e)e=10;else if("number"!==typeof e)throw Error("illegal arguments: "+typeof e);if(c){if("function"!==typeof c)throw Error("Illegal callback: "+
typeof c);b(c)}else return new Promise(function(a,c){b(function(b,d){b?c(b):a(d)})})};k.hashSync=function(e,d){"undefined"===typeof d&&(d=10);"number"===typeof d&&(d=k.genSaltSync(d));if("string"!==typeof e||"string"!==typeof d)throw Error("Illegal arguments: "+typeof e+", "+typeof d);return A(e,d)};k.hash=function(e,d,c,b){function a(a){"string"===typeof e&&"number"===typeof d?k.genSalt(d,function(c,d){A(e,d,a,b)}):"string"===typeof e&&"string"===typeof d?A(e,d,a,b):p(a.bind(this,Error("Illegal arguments: "+
typeof e+", "+typeof d)))}if(c){if("function"!==typeof c)throw Error("Illegal callback: "+typeof c);a(c)}else return new Promise(function(b,c){a(function(a,d){a?c(a):b(d)})})};k.compareSync=function(e,d){if("string"!==typeof e||"string"!==typeof d)throw Error("Illegal arguments: "+typeof e+", "+typeof d);return 60!==d.length?!1:r(k.hashSync(e,d.substr(0,d.length-31)),d)};k.compare=function(e,d,c,b){function a(a){"string"!==typeof e||"string"!==typeof d?p(a.bind(this,Error("Illegal arguments: "+typeof e+
", "+typeof d))):60!==d.length?p(a.bind(this,null,!1)):k.hash(e,d.substr(0,29),function(b,c){b?a(b):a(null,r(c,d))},b)}if(c){if("function"!==typeof c)throw Error("Illegal callback: "+typeof c);a(c)}else return new Promise(function(b,c){a(function(a,d){a?c(a):b(d)})})};k.getRounds=function(e){if("string"!==typeof e)throw Error("Illegal arguments: "+typeof e);return parseInt(e.split("$")[2],10)};k.getSalt=function(e){if("string"!==typeof e)throw Error("Illegal arguments: "+typeof e);if(60!==e.length)throw Error("Illegal hash length: "+
e.length+" != 60");return e.substring(0,29)};var p="undefined"!==typeof process&&process&&"function"===typeof process.nextTick?"function"===typeof setImmediate?setImmediate:process.nextTick:setTimeout,s="./ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789".split(""),q=[-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,0,1,54,55,56,57,58,59,60,61,62,63,-1,-1,-1,-1,-1,-1,-1,2,3,4,5,6,7,8,9,10,11,12,
13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,-1,-1,-1,-1,-1,-1,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,-1,-1,-1,-1,-1],z=String.fromCharCode,I=function(){var e={MAX_CODEPOINT:1114111,encodeUTF8:function(d,c){var b=null;"number"===typeof d&&(b=d,d=function(){return null});for(;null!==b||null!==(b=d());)128>b?c(b&127):(2048>b?c(b>>6&31|192):(65536>b?c(b>>12&15|224):(c(b>>18&7|240),c(b>>12&63|128)),c(b>>6&63|128)),c(b&63|128)),b=null},decodeUTF8:function(d,c){for(var b,
a,f,e,k=function(a){a=a.slice(0,a.indexOf(null));var b=Error(a.toString());b.name="TruncatedError";b.bytes=a;throw b;};null!==(b=d());)if(0===(b&128))c(b);else if(192===(b&224))null===(a=d())&&k([b,a]),c((b&31)<<6|a&63);else if(224===(b&240))null!==(a=d())&&null!==(f=d())||k([b,a,f]),c((b&15)<<12|(a&63)<<6|f&63);else if(240===(b&248))null!==(a=d())&&null!==(f=d())&&null!==(e=d())||k([b,a,f,e]),c((b&7)<<18|(a&63)<<12|(f&63)<<6|e&63);else throw RangeError("Illegal starting byte: "+b);},UTF16toUTF8:function(d,
c){for(var b,a=null;null!==(b=null!==a?a:d());)55296<=b&&57343>=b&&null!==(a=d())&&56320<=a&&57343>=a?(c(1024*(b-55296)+a-56320+65536),a=null):c(b);null!==a&&c(a)},UTF8toUTF16:function(d,c){var b=null;"number"===typeof d&&(b=d,d=function(){return null});for(;null!==b||null!==(b=d());)65535>=b?c(b):(b-=65536,c((b>>10)+55296),c(b%1024+56320)),b=null},encodeUTF16toUTF8:function(d,c){e.UTF16toUTF8(d,function(b){e.encodeUTF8(b,c)})},decodeUTF8toUTF16:function(d,c){e.decodeUTF8(d,function(b){e.UTF8toUTF16(b,
c)})},calculateCodePoint:function(d){return 128>d?1:2048>d?2:65536>d?3:4},calculateUTF8:function(d){for(var c,b=0;null!==(c=d());)b+=e.calculateCodePoint(c);return b},calculateUTF16asUTF8:function(d){var c=0,b=0;e.UTF16toUTF8(d,function(a){++c;b+=e.calculateCodePoint(a)});return[c,b]}};return e}();Date.now=Date.now||function(){return+new Date};var F=[608135816,2242054355,320440878,57701188,2752067618,698298832,137296536,3964562569,1160258022,953160567,3193202383,887688300,3232508343,3380367581,1065670069,
3041331479,2450970073,2306472731],G=[3509652390,2564797868,805139163,3491422135,3101798381,1780907670,3128725573,4046225305,614570311,3012652279,134345442,2240740374,1667834072,1901547113,2757295779,4103290238,227898511,1921955416,1904987480,2182433518,2069144605,3260701109,2620446009,720527379,3318853667,677414384,3393288472,3101374703,2390351024,1614419982,1822297739,2954791486,3608508353,3174124327,2024746970,1432378464,3864339955,2857741204,1464375394,1676153920,1439316330,715854006,3033291828,
289532110,2706671279,2087905683,3018724369,1668267050,732546397,1947742710,3462151702,2609353502,2950085171,1814351708,2050118529,680887927,999245976,1800124847,3300911131,1713906067,1641548236,4213287313,1216130144,1575780402,4018429277,3917837745,3693486850,3949271944,596196993,3549867205,258830323,2213823033,772490370,2760122372,1774776394,2652871518,566650946,4142492826,1728879713,2882767088,1783734482,3629395816,2517608232,2874225571,1861159788,326777828,3124490320,2130389656,2716951837,967770486,
1724537150,2185432712,2364442137,1164943284,2105845187,998989502,3765401048,2244026483,1075463327,1455516326,1322494562,910128902,469688178,1117454909,936433444,3490320968,3675253459,1240580251,122909385,2157517691,634681816,4142456567,3825094682,3061402683,2540495037,79693498,3249098678,1084186820,1583128258,426386531,1761308591,1047286709,322548459,995290223,1845252383,2603652396,3431023940,2942221577,3202600964,3727903485,1712269319,422464435,3234572375,1170764815,3523960633,3117677531,1434042557,
442511882,3600875718,1076654713,1738483198,4213154764,2393238008,3677496056,1014306527,4251020053,793779912,2902807211,842905082,4246964064,1395751752,1040244610,2656851899,3396308128,445077038,3742853595,3577915638,679411651,2892444358,2354009459,1767581616,3150600392,3791627101,3102740896,284835224,4246832056,1258075500,768725851,2589189241,3069724005,3532540348,1274779536,3789419226,2764799539,1660621633,3471099624,4011903706,913787905,3497959166,737222580,2514213453,2928710040,3937242737,1804850592,
3499020752,2949064160,2386320175,2390070455,2415321851,4061277028,2290661394,2416832540,1336762016,1754252060,3520065937,3014181293,791618072,3188594551,3933548030,2332172193,3852520463,3043980520,413987798,3465142937,3030929376,4245938359,2093235073,3534596313,375366246,2157278981,2479649556,555357303,3870105701,2008414854,3344188149,4221384143,3956125452,2067696032,3594591187,2921233993,2428461,544322398,577241275,1471733935,610547355,4027169054,1432588573,1507829418,2025931657,3646575487,545086370,
48609733,2200306550,1653985193,298326376,1316178497,3007786442,2064951626,458293330,2589141269,3591329599,3164325604,727753846,2179363840,146436021,1461446943,4069977195,705550613,3059967265,3887724982,4281599278,3313849956,1404054877,2845806497,146425753,1854211946,1266315497,3048417604,3681880366,3289982499,290971E4,1235738493,2632868024,2414719590,3970600049,1771706367,1449415276,3266420449,422970021,1963543593,2690192192,3826793022,1062508698,1531092325,1804592342,2583117782,2714934279,4024971509,
1294809318,4028980673,1289560198,2221992742,1669523910,35572830,157838143,1052438473,1016535060,1802137761,1753167236,1386275462,3080475397,2857371447,1040679964,2145300060,2390574316,1461121720,2956646967,4031777805,4028374788,33600511,2920084762,1018524850,629373528,3691585981,3515945977,2091462646,2486323059,586499841,988145025,935516892,3367335476,2599673255,2839830854,265290510,3972581182,2759138881,3795373465,1005194799,847297441,406762289,1314163512,1332590856,1866599683,4127851711,750260880,
613907577,1450815602,3165620655,3734664991,3650291728,3012275730,3704569646,1427272223,778793252,1343938022,2676280711,2052605720,1946737175,3164576444,3914038668,3967478842,3682934266,1661551462,3294938066,4011595847,840292616,3712170807,616741398,312560963,711312465,1351876610,322626781,1910503582,271666773,2175563734,1594956187,70604529,3617834859,1007753275,1495573769,4069517037,2549218298,2663038764,504708206,2263041392,3941167025,2249088522,1514023603,1998579484,1312622330,694541497,2582060303,
2151582166,1382467621,776784248,2618340202,3323268794,2497899128,2784771155,503983604,4076293799,907881277,423175695,432175456,1378068232,4145222326,3954048622,3938656102,3820766613,2793130115,2977904593,26017576,3274890735,3194772133,1700274565,1756076034,4006520079,3677328699,720338349,1533947780,354530856,688349552,3973924725,1637815568,332179504,3949051286,53804574,2852348879,3044236432,1282449977,3583942155,3416972820,4006381244,1617046695,2628476075,3002303598,1686838959,431878346,2686675385,
1700445008,1080580658,1009431731,832498133,3223435511,2605976345,2271191193,2516031870,1648197032,4164389018,2548247927,300782431,375919233,238389289,3353747414,2531188641,2019080857,1475708069,455242339,2609103871,448939670,3451063019,1395535956,2413381860,1841049896,1491858159,885456874,4264095073,4001119347,1565136089,3898914787,1108368660,540939232,1173283510,2745871338,3681308437,4207628240,3343053890,4016749493,1699691293,1103962373,3625875870,2256883143,3830138730,1031889488,3479347698,1535977030,
4236805024,3251091107,2132092099,1774941330,1199868427,1452454533,157007616,2904115357,342012276,595725824,1480756522,206960106,497939518,591360097,863170706,2375253569,3596610801,1814182875,2094937945,3421402208,1082520231,3463918190,2785509508,435703966,3908032597,1641649973,2842273706,3305899714,1510255612,2148256476,2655287854,3276092548,4258621189,236887753,3681803219,274041037,1734335097,3815195456,3317970021,1899903192,1026095262,4050517792,356393447,2410691914,3873677099,3682840055,3913112168,
2491498743,4132185628,2489919796,1091903735,1979897079,3170134830,3567386728,3557303409,857797738,1136121015,1342202287,507115054,2535736646,337727348,3213592640,1301675037,2528481711,1895095763,1721773893,3216771564,62756741,2142006736,835421444,2531993523,1442658625,3659876326,2882144922,676362277,1392781812,170690266,3921047035,1759253602,3611846912,1745797284,664899054,1329594018,3901205900,3045908486,2062866102,2865634940,3543621612,3464012697,1080764994,553557557,3656615353,3996768171,991055499,
499776247,1265440854,648242737,3940784050,980351604,3713745714,1749149687,3396870395,4211799374,3640570775,1161844396,3125318951,1431517754,545492359,4268468663,3499529547,1437099964,2702547544,3433638243,2581715763,2787789398,1060185593,1593081372,2418618748,4260947970,69676912,2159744348,86519011,2512459080,3838209314,1220612927,3339683548,133810670,1090789135,1078426020,1569222167,845107691,3583754449,4072456591,1091646820,628848692,1613405280,3757631651,526609435,236106946,48312990,2942717905,
3402727701,1797494240,859738849,992217954,4005476642,2243076622,3870952857,3732016268,765654824,3490871365,2511836413,1685915746,3888969200,1414112111,2273134842,3281911079,4080962846,172450625,2569994100,980381355,4109958455,2819808352,2716589560,2568741196,3681446669,3329971472,1835478071,660984891,3704678404,4045999559,3422617507,3040415634,1762651403,1719377915,3470491036,2693910283,3642056355,3138596744,1364962596,2073328063,1983633131,926494387,3423689081,2150032023,4096667949,1749200295,3328846651,
309677260,2016342300,1779581495,3079819751,111262694,1274766160,443224088,298511866,1025883608,3806446537,1145181785,168956806,3641502830,3584813610,1689216846,3666258015,3200248200,1692713982,2646376535,4042768518,1618508792,1610833997,3523052358,4130873264,2001055236,3610705100,2202168115,4028541809,2961195399,1006657119,2006996926,3186142756,1430667929,3210227297,1314452623,4074634658,4101304120,2273951170,1399257539,3367210612,3027628629,1190975929,2062231137,2333990788,2221543033,2438960610,
1181637006,548689776,2362791313,3372408396,3104550113,3145860560,296247880,1970579870,3078560182,3769228297,1714227617,3291629107,3898220290,166772364,1251581989,493813264,448347421,195405023,2709975567,677966185,3703036547,1463355134,2715995803,1338867538,1343315457,2802222074,2684532164,233230375,2599980071,2000651841,3277868038,1638401717,4028070440,3237316320,6314154,819756386,300326615,590932579,1405279636,3267499572,3150704214,2428286686,3959192993,3461946742,1862657033,1266418056,963775037,
2089974820,2263052895,1917689273,448879540,3550394620,3981727096,150775221,3627908307,1303187396,508620638,2975983352,2726630617,1817252668,1876281319,1457606340,908771278,3720792119,3617206836,2455994898,1729034894,1080033504,976866871,3556439503,2881648439,1522871579,1555064734,1336096578,3548522304,2579274686,3574697629,3205460757,3593280638,3338716283,3079412587,564236357,2993598910,1781952180,1464380207,3163844217,3332601554,1699332808,1393555694,1183702653,3581086237,1288719814,691649499,2847557200,
2895455976,3193889540,2717570544,1781354906,1676643554,2592534050,3230253752,1126444790,2770207658,2633158820,2210423226,2615765581,2414155088,3127139286,673620729,2805611233,1269405062,4015350505,3341807571,4149409754,1057255273,2012875353,2162469141,2276492801,2601117357,993977747,3918593370,2654263191,753973209,36408145,2530585658,25011837,3520020182,2088578344,530523599,2918365339,1524020338,1518925132,3760827505,3759777254,1202760957,3985898139,3906192525,674977740,4174734889,2031300136,2019492241,
3983892565,4153806404,3822280332,352677332,2297720250,60907813,90501309,3286998549,1016092578,2535922412,2839152426,457141659,509813237,4120667899,652014361,1966332200,2975202805,55981186,2327461051,676427537,3255491064,2882294119,3433927263,1307055953,942726286,933058658,2468411793,3933900994,4215176142,1361170020,2001714738,2830558078,3274259782,1222529897,1679025792,2729314320,3714953764,1770335741,151462246,3013232138,1682292957,1483529935,471910574,1539241949,458788160,3436315007,1807016891,
3718408830,978976581,1043663428,3165965781,1927990952,4200891579,2372276910,3208408903,3533431907,1412390302,2931980059,4132332400,1947078029,3881505623,4168226417,2941484381,1077988104,1320477388,886195818,18198404,3786409E3,2509781533,112762804,3463356488,1866414978,891333506,18488651,661792760,1628790961,3885187036,3141171499,876946877,2693282273,1372485963,791857591,2686433993,3759982718,3167212022,3472953795,2716379847,445679433,3561995674,3504004811,3574258232,54117162,3331405415,2381918588,
3769707343,4154350007,1140177722,4074052095,668550556,3214352940,367459370,261225585,2610173221,4209349473,3468074219,3265815641,314222801,3066103646,3808782860,282218597,3406013506,3773591054,379116347,1285071038,846784868,2669647154,3771962079,3550491691,2305946142,453669953,1268987020,3317592352,3279303384,3744833421,2610507566,3859509063,266596637,3847019092,517658769,3462560207,3443424879,370717030,4247526661,2224018117,4143653529,4112773975,2788324899,2477274417,1456262402,2901442914,1517677493,
1846949527,2295493580,3734397586,2176403920,1280348187,1908823572,3871786941,846861322,1172426758,3287448474,3383383037,1655181056,3139813346,901632758,1897031941,2986607138,3066810236,3447102507,1393639104,373351379,950779232,625454576,3124240540,4148612726,2007998917,544563296,2244738638,2330496472,2058025392,1291430526,424198748,50039436,29584100,3605783033,2429876329,2791104160,1057563949,3255363231,3075367218,3463963227,1469046755,985887462],E=[1332899944,1700884034,1701343084,1684370003,1668446532,
1869963892];k.encodeBase64=x;k.decodeBase64=B;return k});

View File

@@ -0,0 +1 @@
- Presence on device details breaks

File diff suppressed because it is too large Load Diff

33679
front/lib/datatables/datatables.js Executable file

File diff suppressed because it is too large Load Diff

37
front/lib/datatables/datatables.min.css vendored Executable file

File diff suppressed because one or more lines are too long

180
front/lib/datatables/datatables.min.js vendored Executable file

File diff suppressed because one or more lines are too long

View File

@@ -16,9 +16,15 @@
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
?> ?>
<?php <?php
require 'php/templates/header.php'; require 'php/templates/header.php';
?> ?>
<!-- ----------------------------------------------------------------------- -->
<!-- Page ------------------------------------------------------------------ --> <!-- Page ------------------------------------------------------------------ -->
<div class="content-wrapper" id="maintenancePage"> <div class="content-wrapper" id="maintenancePage">
@@ -203,26 +209,38 @@ $db->close();
</div> </div>
</div> </div>
<div class="tab-pane" id="tab_BackupRestore"> <div class="tab-pane" id="tab_BackupRestore">
<div class="db_info_table"> <div class="db_info_table">
<div class="db_info_table_row"> <div class="db_info_table_row">
<div class="db_tools_table_cell_a" > <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> <button type="button" class="btn btn-default pa-btn bg-green dbtools-button" id="btnExportCSV" onclick="ExportCSV()"><?= lang('Maintenance_Tool_ExportCSV');?></button>
</div> </div>
<div class="db_tools_table_cell_b"><?= lang('Maintenance_Tool_ExportCSV_text');?></div> <div class="db_tools_table_cell_b"><?= lang('Maintenance_Tool_ExportCSV_text');?></div>
</div> </div>
<div class="db_info_table_row"> <div class="db_info_table_row">
<div class="db_tools_table_cell_a" > <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> <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>
<div class="db_tools_table_cell_b"><?= lang('Maintenance_Tool_ImportCSV_text');?></div> <div class="db_tools_table_cell_b"><?= lang('Maintenance_Tool_ImportCSV_text');?></div>
</div> </div>
<div class="db_info_table_row"> <div class="db_info_table_row">
<div class="db_tools_table_cell_a" > <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> <button type="button" class="btn btn-default pa-btn pa-btn-delete bg-red dbtools-button" id="btnImportPastedCSV" onclick="askImportPastedCSV()"><?= lang('Maintenance_Tool_ImportPastedCSV');?></button>
</div> </div>
<div class="db_tools_table_cell_b"><?= lang('Maintenance_Tool_ImportPastedCSV_text');?></div> <div class="db_tools_table_cell_b"><?= lang('Maintenance_Tool_ImportPastedCSV_text');?></div>
</div> </div>
</div> <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="btnDownloadConfig" onclick="DownloadConfig()"><?= lang('Maintenance_Tool_DownloadConfig');?></button>
</div>
<div class="db_tools_table_cell_b"><?= lang('Maintenance_Tool_DownloadConfig_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="btnImportPastedConfig" onclick="askImportPastedConfig()"><?= lang('Maintenance_Tool_ImportPastedConfig');?></button>
</div>
<div class="db_tools_table_cell_b"><?= lang('Maintenance_Tool_ImportPastedConfig_text');?></div>
</div>
</div>
</div> </div>
<!-- ---------------------------Logging-------------------------------------------- --> <!-- ---------------------------Logging-------------------------------------------- -->
<div class="tab-pane" id="tab_Logging"> <div class="tab-pane" id="tab_Logging">
@@ -439,6 +457,44 @@ function restartBackend() {
}) })
} }
// -----------------------------------------------------------
// Import pasted Config ASK
function askImportPastedConfig() {
// Add new icon as base64 string
showModalInput ('<i class="fa fa-square-plus pointer"></i> <?= lang('Maintenance_Tool_ImportConfig_noti');?>', '<?= lang('Maintenance_Tool_ImportPastedConfig_noti_text');?>',
'<?= lang('Gen_Cancel');?>', '<?= lang('Gen_Okay');?>', 'UploadConfig');
}
// -----------------------------------------------------------
// Upload Settings Config
function UploadConfig()
{
// alert("aaa")
appConf = $('#modal-input-textarea').val()
// encode for import
appConfBase64 = btoa(appConf)
// import
$.post('php/server/query_replace_config.php', { config: appConfBase64 }, function(msg) {
console.log(msg);
// showMessage(msg);
write_notification(`[Maintenance] Settings imported from backup: ${msg}`, 'interrupt');
});
}
// -----------------------------------------------------------
// Download Settings Config
function DownloadConfig()
{
// Execute
openInNewTab("php/server/query_config.php?file=app.conf&download=true")
}
// ----------------------------------------------------------- // -----------------------------------------------------------
// Export CSV // Export CSV
function ExportCSV() function ExportCSV()
@@ -476,8 +532,13 @@ function ImportPastedCSV()
{ {
var csv = $('#modal-input-textarea').val(); var csv = $('#modal-input-textarea').val();
console.log(csv);
csvBase64 = utf8ToBase64(csv); csvBase64 = utf8ToBase64(csv);
console.log(csvBase64);
$.post('php/server/devices.php?action=ImportCSV', { content: csvBase64 }, function(msg) { $.post('php/server/devices.php?action=ImportCSV', { content: csvBase64 }, function(msg) {
showMessage(msg); showMessage(msg);
write_notification(`[Maintenance] Devices imported from pasted content`, 'info'); write_notification(`[Maintenance] Devices imported from pasted content`, 'info');

View File

@@ -17,14 +17,14 @@
<h3 class="box-title"><?= lang('Gen_Selected_Devices');?></h3> <h3 class="box-title"><?= lang('Gen_Selected_Devices');?></h3>
</div> </div>
<div class="deviceSelector col-md-9 col-sm-12" style="z-index:5"></div> <div class="deviceSelector col-md-11 col-sm-11" style="z-index:5"></div>
<div class="col-md-3"> <div class="col-md-1">
<button type="button" class="btn btn-default" onclick="markAllSelected()"> <button type="button" class="btn btn-default col-md-12" onclick="markAllSelected()" title="<?= lang('Gen_Add_All');?>">
<i class="fa-solid fa-circle-check"></i> <?= lang('Gen_Add_All');?> <i class="fa-solid fa-circle-check"></i>
</button> </button>
<button type="button" class="btn btn-default" onclick="markAllNotSelected()"> <button type="button" class="btn btn-default col-md-12" onclick="markAllNotSelected()" title="<?= lang('Gen_Remove_All');?>">
<i class="fa-solid fa-circle-xmark"></i> <?= lang('Gen_Remove_All');?> <i class="fa-solid fa-circle-xmark"></i>
</button> </button>
</div> </div>
@@ -77,11 +77,11 @@
// some race condition, need to implement delay // some race condition, need to implement delay
setTimeout(() => { setTimeout(() => {
$.get('api/table_settings.json?nocache=' + Date.now(), function(res) { $.get('/php/server/query_json.php', { file: 'table_settings.json', nocache: Date.now() }, function(res) {
settingsData = res["data"]; settingsData = res["data"];
excludedColumns = ["NEWDEV_devMac", "NEWDEV_devFirstConnection", "NEWDEV_devLastConnection", "NEWDEV_devLastNotification", "NEWDEV_devLastIP", "NEWDEV_devStaticIP", "NEWDEV_devScan", "NEWDEV_devPresentLastScan" ] excludedColumns = ["NEWDEV_devMac", "NEWDEV_devFirstConnection", "NEWDEV_devLastConnection", "NEWDEV_devLastNotification", "NEWDEV_devScan", "NEWDEV_devPresentLastScan", "NEWDEV_devCustomProps" ]
const relevantColumns = settingsData.filter(set => const relevantColumns = settingsData.filter(set =>
set.setGroup === "NEWDEV" && set.setGroup === "NEWDEV" &&
@@ -90,21 +90,21 @@
!set.setKey.includes("__metadata") !set.setKey.includes("__metadata")
); );
const generateSimpleForm = columns => { const generateSimpleForm = multiEditColumns => {
const form = $('#multi-edit-form'); const form = $('#multi-edit-form');
const numColumns = 2; // Number of columns const numColumns = 2; // Number of columns
// Calculate number of elements per column // Calculate number of elements per column
const elementsPerColumn = Math.ceil(columns.length / numColumns); const elementsPerColumn = Math.ceil(multiEditColumns.length / numColumns);
// Divide columns equally // Divide columns equally
for (let i = 0; i < numColumns; i++) { for (let i = 0; i < numColumns; i++) {
const column = $('<div>').addClass('col-md-6'); const column = $('<div>').addClass('col-md-6');
// Append form groups to the column // Append form groups to the column
for (let j = i * elementsPerColumn; j < Math.min((i + 1) * elementsPerColumn, columns.length); j++) { for (let j = i * elementsPerColumn; j < Math.min((i + 1) * elementsPerColumn, multiEditColumns.length); j++) {
const setTypeObject = JSON.parse(columns[j].setType.replace(/'/g, '"')); const setTypeObject = JSON.parse(multiEditColumns[j].setType.replace(/'/g, '"'));
// get the element with the input value(s) // get the element with the input value(s)
let elements = setTypeObject.elements.filter(element => element.elementHasInputValue === 1); let elements = setTypeObject.elements.filter(element => element.elementHasInputValue === 1);
@@ -135,37 +135,38 @@
onClick, onClick,
onChange, onChange,
customParams, customParams,
customId customId,
columns
} = handleElementOptions('none', elementOptions, transformers, val = ""); } = handleElementOptions('none', elementOptions, transformers, val = "");
// render based on element type // render based on element type
if (elementType === 'select') { if (elementType === 'select') {
targetLocation = columns[j].setKey + "_generateSetOptions" targetLocation = multiEditColumns[j].setKey + "_generateSetOptions"
generateOptionsOrSetOptions(columns[j].setKey, [], targetLocation, generateOptions) generateOptionsOrSetOptions(multiEditColumns[j].setKey, [], targetLocation, generateOptions, null)
console.log(columns[j].setKey) console.log(multiEditColumns[j].setKey)
// Handle Icons as they need a preview // Handle Icons as they need a preview
if(columns[j].setKey == 'NEWDEV_devIcon') if(multiEditColumns[j].setKey == 'NEWDEV_devIcon')
{ {
input = ` input = `
<span class="input-group-addon iconPreview" my-customid="NEWDEV_devIcon_preview"></span> <span class="input-group-addon iconPreview" my-customid="NEWDEV_devIcon_preview"></span>
<select class="form-control" <select class="form-control"
onChange="updateIconPreview(this)" onChange="updateIconPreview(this)"
my-customparams="NEWDEV_devIcon,NEWDEV_devIcon_preview" my-customparams="NEWDEV_devIcon,NEWDEV_devIcon_preview"
id="${columns[j].setKey}" id="${multiEditColumns[j].setKey}"
data-my-column="${columns[j].setKey}" data-my-column="${multiEditColumns[j].setKey}"
data-my-targetColumns="${columns[j].setKey.replace('NEWDEV_','')}" > data-my-targetColumns="${multiEditColumns[j].setKey.replace('NEWDEV_','')}" >
<option id="${targetLocation}"></option> <option id="${targetLocation}"></option>
</select>` </select>`
} else{ } else{
input = `<select class="form-control" input = `<select class="form-control"
id="${columns[j].setKey}" id="${multiEditColumns[j].setKey}"
data-my-column="${columns[j].setKey}" data-my-column="${multiEditColumns[j].setKey}"
data-my-targetColumns="${columns[j].setKey.replace('NEWDEV_','')}" > data-my-targetColumns="${multiEditColumns[j].setKey.replace('NEWDEV_','')}" >
<option id="${targetLocation}"></option> <option id="${targetLocation}"></option>
</select>` </select>`
} }
@@ -178,20 +179,20 @@
input = `<input class="${inputClass}" input = `<input class="${inputClass}"
id="${columns[j].setKey}" id="${multiEditColumns[j].setKey}"
my-customid="${columns[j].setKey}" my-customid="${multiEditColumns[j].setKey}"
data-my-column="${columns[j].setKey}" data-my-column="${multiEditColumns[j].setKey}"
data-my-targetColumns="${columns[j].setKey.replace('NEWDEV_','')}" data-my-targetColumns="${multiEditColumns[j].setKey.replace('NEWDEV_','')}"
type="${inputType}">` type="${inputType}">`
} }
const inputEntry = `<div class="form-group col-sm-12" > const inputEntry = `<div class="form-group col-sm-12" >
<label class="col-sm-3 control-label">${columns[j].setName}</label> <label class="col-sm-3 control-label">${multiEditColumns[j].setName}</label>
<div class="col-sm-9"> <div class="col-sm-9">
<div class="input-group red-hover-border"> <div class="input-group red-hover-border">
${input} ${input}
<span class="input-group-addon pointer red-hover-background" onclick="massUpdateField('${columns[j].setKey}');" title="${getString('Device_MultiEdit_Tooltip')}"> <span class="input-group-addon pointer red-hover-background" onclick="massUpdateField('${multiEditColumns[j].setKey}');" title="${getString('Device_MultiEdit_Tooltip')}">
<i class="fa fa-save"></i> <i class="fa fa-save"></i>
</span> </span>
</div> </div>

View File

@@ -11,6 +11,13 @@
?> ?>
<script>
// show spinning icon
showSpinner()
</script>
<!-- Page ------------------------------------------------------------------ --> <!-- Page ------------------------------------------------------------------ -->
<div class="content-wrapper"> <div class="content-wrapper">
@@ -481,6 +488,8 @@
return; return;
} }
orderTopologyBy = createArray(getSetting("UI_TOPOLOGY_ORDER"))
devicesListnew = rawData["data"].map(item => { devicesListnew = rawData["data"].map(item => {
return { return {
"name": item[0], "name": item[0],
@@ -494,13 +503,30 @@
"port": item[18] "port": item[18]
}; };
}).sort((a, b) => { }).sort((a, b) => {
// First sort by name alphabetically // Helper to safely parse port into an integer; invalid ports become Infinity for sorting
const nameCompare = a.name.localeCompare(b.name); const parsePort = (port) => {
if (nameCompare !== 0) { const parsed = parseInt(port, 10);
return isNaN(parsed) ? Infinity : parsed;
};
switch (orderTopologyBy[0]) {
case "Name":
// First sort by name alphabetically
const nameCompare = a.name.localeCompare(b.name);
if (nameCompare !== 0) {
return nameCompare; return nameCompare;
} }
// If names are the same, sort by port numerically // If names are the same, sort by port numerically
return a.port - b.port; return parsePort(a.port) - parsePort(b.port);
case "Port":
// Sort by port numerically
return parsePort(a.port) - parsePort(b.port);
default:
// Default: Sort by rowid (as a fallback)
return a.rowid - b.rowid;
}
}); });
setCache('devicesListNew', JSON.stringify(devicesListnew)); setCache('devicesListNew', JSON.stringify(devicesListnew));
@@ -882,9 +908,6 @@
} }
} }
// show spinning icon
showSpinner()
// init device names where macs are used // init device names where macs are used
initDeviceNamesFromMACs(); initDeviceNamesFromMACs();

View File

@@ -7,8 +7,18 @@
?> ?>
<script src="js/graph_online_history.js"></script> <script src="js/graph_online_history.js"></script>
<script> <script >
$.get('api/table_online_history.json?nocache=' + Date.now(), function(res) {
if (isAppInitialized()) {
initOnlineHistoryGraph();
} else {
callAfterAppInitialized(() => initOnlineHistoryGraph());
}
function initOnlineHistoryGraph() {
$.get('/php/server/query_json.php', { file: 'table_online_history.json', nocache: Date.now() }, function(res) {
// Extracting data from the JSON response // Extracting data from the JSON response
var timeStamps = []; var timeStamps = [];
var onlineCounts = []; var onlineCounts = [];
@@ -39,6 +49,8 @@ $.get('api/table_online_history.json?nocache=' + Date.now(), function(res) {
// Handle any errors in fetching the data // Handle any errors in fetching the data
console.error('Error fetching online history data.'); console.error('Error fetching online history data.');
}); });
}
</script> </script>
<!-- <canvas id="clientsChart" width="800" height="140" class="extratooltipcanvas no-user-select"></canvas> --> <!-- <canvas id="clientsChart" width="800" height="140" class="extratooltipcanvas no-user-select"></canvas> -->
<canvas id="OnlineChart" style="width:100%; height: 150px; margin-bottom: 15px;"></canvas> <canvas id="OnlineChart" style="width:100%; height: 150px; margin-bottom: 15px;"></canvas>

View File

@@ -20,12 +20,12 @@ function renderLogArea($params) {
$content = file_get_contents($filePath); $content = file_get_contents($filePath);
} }
// Prepare the download button HTML if filePath starts with /app/front // Prepare the download button HTML if filePath starts with /app
$downloadButtonHtml = ''; $downloadButtonHtml = '';
if (strpos($filePath, '/app/front') === 0) { if (strpos($filePath, '/app') === 0) {
$downloadButtonHtml = ' $downloadButtonHtml = '
<span class="span-padding"> <span class="span-padding">
<a href="' . htmlspecialchars(str_replace('/app/front', '', $filePath)) . '" target="_blank"> <a href="' . htmlspecialchars(str_replace('/app/log/', '/php/server/query_logs.php?file=', $filePath)) . '" target="_blank">
<i class="fa fa-download"></i> <i class="fa fa-download"></i>
</a> </a>
</span>'; </span>';

View File

@@ -11,7 +11,7 @@
} }
], ],
"fileName": "app.log", "fileName": "app.log",
"filePath": "/app/front/log/app.log", "filePath": "/app/log/app.log",
"textAreaCssClass": "logs" "textAreaCssClass": "logs"
}, },
@@ -23,7 +23,7 @@
} }
], ],
"fileName": "app_front.log", "fileName": "app_front.log",
"filePath": "/app/front/log/app_front.log", "filePath": "/app/log/app_front.log",
"textAreaCssClass": "logs logs-small" "textAreaCssClass": "logs logs-small"
}, },
{ {
@@ -34,7 +34,7 @@
} }
], ],
"fileName": "app.php_errors.log", "fileName": "app.php_errors.log",
"filePath": "/app/front/log/app.php_errors.log", "filePath": "/app/log/app.php_errors.log",
"textAreaCssClass": "logs logs-small" "textAreaCssClass": "logs logs-small"
}, },
{ {
@@ -45,7 +45,7 @@
} }
], ],
"fileName": "execution_queue.log", "fileName": "execution_queue.log",
"filePath": "/app/front/log/execution_queue.log", "filePath": "/app/log/execution_queue.log",
"textAreaCssClass": "logs logs-small" "textAreaCssClass": "logs logs-small"
}, },
{ {
@@ -55,17 +55,6 @@
"filePath": "/var/log/nginx/error.log", "filePath": "/var/log/nginx/error.log",
"textAreaCssClass": "logs logs-small" "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": [ "buttons": [
{ {
@@ -74,7 +63,7 @@
} }
], ],
"fileName": "db_is_locked.log", "fileName": "db_is_locked.log",
"filePath": "/app/front/log/db_is_locked.log", "filePath": "/app/log/db_is_locked.log",
"textAreaCssClass": "logs logs-small" "textAreaCssClass": "logs logs-small"
}, },
{ {
@@ -85,7 +74,7 @@
} }
], ],
"fileName": "stdout.log", "fileName": "stdout.log",
"filePath": "/app/front/log/stdout.log", "filePath": "/app/log/stdout.log",
"textAreaCssClass": "logs logs-small" "textAreaCssClass": "logs logs-small"
}, },
{ {
@@ -96,7 +85,7 @@
} }
], ],
"fileName": "stderr.log", "fileName": "stderr.log",
"filePath": "/app/front/log/stderr.log", "filePath": "/app/log/stderr.log",
"textAreaCssClass": "logs logs-small" "textAreaCssClass": "logs logs-small"
} }
] ]

View File

@@ -10,8 +10,12 @@
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// DB File Path // DB File Path
$DBFILE = dirname(__FILE__).'/../../../db/app.db'; // $DBFILE = dirname(__FILE__).'/../../../db/app.db';
$DBFILE_LOCKED_FILE = dirname(__FILE__).'/../../../front/log/db_is_locked.log'; // $DBFILE_LOCKED_FILE = dirname(__FILE__).'/../../../log/db_is_locked.log';
$scriptDir = realpath(dirname(__FILE__)); // Resolves symlinks to the actual physical path
$DBFILE = $scriptDir . '/../../../db/app.db';
$DBFILE_LOCKED_FILE = $scriptDir . '/../../../log/db_is_locked.log';
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// check if authenticated // check if authenticated
@@ -32,6 +36,14 @@ function SQLite3_connect($trytoreconnect = true, $retryCount = 0) {
global $db_locked; global $db_locked;
$db_locked = false; $db_locked = false;
if (!file_exists($DBFILE)) {
die("Database file not found: $DBFILE");
}
if (!file_exists(dirname($DBFILE_LOCKED_FILE))) {
die("Log directory not found: " . dirname($DBFILE_LOCKED_FILE));
}
// Write unlock status to the locked file // Write unlock status to the locked file
file_put_contents($DBFILE_LOCKED_FILE, '0'); file_put_contents($DBFILE_LOCKED_FILE, '0');
@@ -70,7 +82,7 @@ class CustomDatabaseWrapper {
private $maxRetries; private $maxRetries;
private $retryDelay; private $retryDelay;
public function __construct($filename, $flags = SQLITE3_OPEN_READWRITE | SQLITE3_OPEN_CREATE, $maxRetries = 10, $retryDelay = 1000, $encryptionKey = null) { public function __construct($filename, $flags = SQLITE3_OPEN_READWRITE | SQLITE3_OPEN_CREATE, $maxRetries = 3, $retryDelay = 1000, $encryptionKey = null) {
$this->sqlite = new SQLite3($filename, $flags, $encryptionKey); $this->sqlite = new SQLite3($filename, $flags, $encryptionKey);
$this->maxRetries = $maxRetries; $this->maxRetries = $maxRetries;
$this->retryDelay = $retryDelay; $this->retryDelay = $retryDelay;
@@ -116,7 +128,7 @@ class CustomDatabaseWrapper {
file_put_contents($DBFILE_LOCKED_FILE, '0'); file_put_contents($DBFILE_LOCKED_FILE, '0');
$message = 'Error executing query (attempts: ' . $attempts . '), query: ' . $query; $message = 'Error executing query (attempts: ' . $attempts . '), query: ' . $query;
write_notification($message); // write_notification($message);
error_log("Query failed after {$this->maxRetries} attempts: " . $this->sqlite->lastErrorMsg()); error_log("Query failed after {$this->maxRetries} attempts: " . $this->sqlite->lastErrorMsg());
} }

View File

@@ -63,7 +63,7 @@ require_once $_SERVER['DOCUMENT_ROOT'] . '/php/templates/security.php';
} }
if (isset ($_REQUEST['rawSql'])) { if (isset ($_REQUEST['rawSql'])) {
$rawSql = $_REQUEST['rawSql']; $rawSql = urldecode(base64_decode($_REQUEST['rawSql']));
} }
if (isset ($_REQUEST['dbtable'])) { if (isset ($_REQUEST['dbtable'])) {

View File

@@ -37,6 +37,7 @@
case 'deleteEvents30': deleteEvents30(); break; case 'deleteEvents30': deleteEvents30(); break;
case 'deleteActHistory': deleteActHistory(); break; case 'deleteActHistory': deleteActHistory(); break;
case 'deleteDeviceEvents': deleteDeviceEvents(); break; case 'deleteDeviceEvents': deleteDeviceEvents(); break;
case 'resetDeviceProps': resetDeviceProps(); break;
case 'PiaBackupDBtoArchive': PiaBackupDBtoArchive(); break; case 'PiaBackupDBtoArchive': PiaBackupDBtoArchive(); break;
case 'PiaRestoreDBfromArchive': PiaRestoreDBfromArchive(); break; case 'PiaRestoreDBfromArchive': PiaRestoreDBfromArchive(); break;
case 'PiaPurgeDBBackups': PiaPurgeDBBackups(); break; case 'PiaPurgeDBBackups': PiaPurgeDBBackups(); break;
@@ -105,8 +106,9 @@ function getServerDeviceData() {
"devSSID" => "", "devSSID" => "",
"devSyncHubNode" => "", "devSyncHubNode" => "",
"devSourcePlugin" => "", "devSourcePlugin" => "",
"devCustomProps" => "",
"devStatus" => "Unknown", "devStatus" => "Unknown",
"devRandomMAC" => false, "devIsRandomMAC" => false,
"devSessions" => 0, "devSessions" => 0,
"devEvents" => 0, "devEvents" => 0,
"devDownAlerts" => 0, "devDownAlerts" => 0,
@@ -134,7 +136,7 @@ function getServerDeviceData() {
$deviceData['devFirstConnection'] = formatDate ($row['devFirstConnection']); // Date formated $deviceData['devFirstConnection'] = formatDate ($row['devFirstConnection']); // Date formated
$deviceData['devLastConnection'] = formatDate ($row['devLastConnection']); // Date formated $deviceData['devLastConnection'] = formatDate ($row['devLastConnection']); // Date formated
$deviceData['devRandomMAC'] = isRandomMAC($mac); $deviceData['devIsRandomMAC'] = isRandomMAC($mac);
// Count Totals // Count Totals
$condition = ' WHERE eve_MAC="'. $mac .'" AND eve_DateTime >= '. $periodDate; $condition = ' WHERE eve_MAC="'. $mac .'" AND eve_DateTime >= '. $periodDate;
@@ -190,129 +192,133 @@ function setDeviceData() {
global $db; global $db;
// Sanitize input // Sanitize input
$mac = quotes($_REQUEST['mac']); $mac = quotes($_POST['mac']);
$name = quotes($_REQUEST['name']); $name = urldecode(quotes($_POST['name']));
$owner = quotes($_REQUEST['owner']); $owner = urldecode(quotes($_POST['owner']));
$type = quotes($_REQUEST['type']); $type = urldecode(quotes($_POST['type']));
$vendor = quotes($_REQUEST['vendor']); $vendor = urldecode(quotes($_POST['vendor']));
$icon = quotes($_REQUEST['icon']); $icon = urldecode(quotes($_POST['icon']));
$favorite = quotes($_REQUEST['favorite']); $favorite = quotes($_POST['favorite']);
$group = quotes($_REQUEST['group']); $group = urldecode(quotes($_POST['group']));
$location = quotes($_REQUEST['location']); $location = urldecode(quotes($_POST['location']));
$comments = quotes($_REQUEST['comments']); $comments = urldecode(quotes($_POST['comments']));
$parentMac = quotes($_REQUEST['networknode']); $parentMac = quotes($_POST['networknode']);
$parentPort = quotes($_REQUEST['networknodeport']); $parentPort = quotes($_POST['networknodeport']);
$ssid = quotes($_REQUEST['ssid']); $ssid = urldecode(quotes($_POST['ssid']));
$site = quotes($_REQUEST['networksite']); $site = quotes($_POST['networksite']);
$staticIP = quotes($_REQUEST['staticIP']); $staticIP = quotes($_POST['staticIP']);
$scancycle = quotes($_REQUEST['scancycle']); $scancycle = quotes($_POST['scancycle']);
$alertevents = quotes($_REQUEST['alertevents']); $alertevents = quotes($_POST['alertevents']);
$alertdown = quotes($_REQUEST['alertdown']); $alertdown = quotes($_POST['alertdown']);
$skiprepeated = quotes($_REQUEST['skiprepeated']); $skiprepeated = quotes($_POST['skiprepeated']);
$newdevice = quotes($_REQUEST['newdevice']); $newdevice = quotes($_POST['newdevice']);
$archived = quotes($_REQUEST['archived']); $archived = quotes($_POST['archived']);
$devFirstConnection = quotes($_REQUEST['devFirstConnection']); $devFirstConnection = quotes($_POST['devFirstConnection']);
$devLastConnection = quotes($_REQUEST['devLastConnection']); $devLastConnection = quotes($_POST['devLastConnection']);
$ip = quotes($_REQUEST['ip']); $ip = quotes($_POST['ip']);
$createNew = quotes($_REQUEST['createNew']); $devCustomProps = quotes($_POST['devCustomProps']);
$createNew = quotes($_POST['createNew']);
$devNewGuid = generateGUID(); $devNewGuid = generateGUID();
// an update // An update
if ($_REQUEST['createNew'] == 0) if ($_POST['createNew'] == 0) {
{ // UPDATE SQL query
// UPDATE SQL query $sql = "UPDATE Devices SET
$sql = "UPDATE Devices SET devName = '$name',
devName = '$name', devOwner = '$owner',
devOwner = '$owner', devType = '$type',
devType = '$type', devVendor = '$vendor',
devVendor = '$vendor', devIcon = '$icon',
devIcon = '$icon', devFavorite = '$favorite',
devFavorite = '$favorite', devGroup = '$group',
devGroup = '$group', devLocation = '$location',
devLocation = '$location', devComments = '$comments',
devComments = '$comments', devParentMAC = '$parentMac',
devParentMAC = '$parentMac', devParentPort = '$parentPort',
devParentPort = '$parentPort', devSSID = '$ssid',
devSSID = '$ssid', devSite = '$site',
devSite = '$site', devStaticIP = '$staticIP',
devStaticIP = '$staticIP', devScan = '$scancycle',
devScan = '$scancycle', devAlertEvents = '$alertevents',
devAlertEvents = '$alertevents', devAlertDown = '$alertdown',
devAlertDown = '$alertdown', devSkipRepeated = '$skiprepeated',
devSkipRepeated = '$skiprepeated', devIsNew = '$newdevice',
devIsNew = '$newdevice', devIsArchived = '$archived',
devIsArchived = '$archived' devCustomProps = '$devCustomProps'
WHERE devMac = '$mac'"; WHERE devMac = '$mac'";
} else // an INSERT } else { // An INSERT
{ $sql = "INSERT INTO Devices (
$sql = "INSERT INTO Devices ( devMac,
devMac, devName,
devName, devOwner,
devOwner, devType,
devType, devVendor,
devVendor, devIcon,
devIcon, devFavorite,
devFavorite, devGroup,
devGroup, devLocation,
devLocation, devComments,
devComments, devParentMAC,
devParentMAC, devParentPort,
devParentPort, devSSID,
devSSID, devSite,
devSite, devStaticIP,
devStaticIP, devScan,
devScan, devAlertEvents,
devAlertEvents, devAlertDown,
devAlertDown, devSkipRepeated,
devSkipRepeated, devIsNew,
devIsNew, devIsArchived,
devIsArchived, devLastConnection,
devLastConnection, devFirstConnection,
devFirstConnection, devLastIP,
devLastIP, devGUID,
devGUID devCustomProps,
) VALUES ( devSourcePlugin
'$mac', ) VALUES (
'$name', '$mac',
'$owner', '$name',
'$type', '$owner',
'$vendor', '$type',
'$icon', '$vendor',
'$favorite', '$icon',
'$group', '$favorite',
'$location', '$group',
'$comments', '$location',
'$parentMac', '$comments',
'$parentPort', '$parentMac',
'$ssid', '$parentPort',
'$site', '$ssid',
'$staticIP', '$site',
'$scancycle', '$staticIP',
'$alertevents', '$scancycle',
'$alertdown', '$alertevents',
'$skiprepeated', '$alertdown',
'$newdevice', '$skiprepeated',
'$archived', '$newdevice',
'$devLastConnection', '$archived',
'$devFirstConnection', '$devLastConnection',
'$ip', '$devFirstConnection',
'$devNewGuid' '$ip',
) '$devNewGuid',
"; '$devCustomProps',
'DUMMY'
)";
} }
// update Data // Execute the query
$result = $db->query($sql); $result = $db->query($sql);
// check result // Check the result
if ($result == TRUE) { if ($result == TRUE) {
echo lang('BackDevices_DBTools_UpdDev'); echo lang('BackDevices_DBTools_UpdDev');
} else { } else {
echo lang('BackDevices_DBTools_UpdDevError')."\n\n$sql \n\n". $db->lastErrorMsg(); echo lang('BackDevices_DBTools_UpdDevError')."\n\n$sql \n\n". $db->lastErrorMsg();
} }
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// Delete Device // Delete Device
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
@@ -389,6 +395,26 @@ function deleteDeviceEvents() {
} }
} }
//------------------------------------------------------------------------------
// Delete Device Properties
//------------------------------------------------------------------------------
function resetDeviceProps() {
global $db;
// sql
$sql = 'UPDATE Devices set devCustomProps = "'.getSettingValue("NEWDEV_devCustomProps").'" WHERE devMac="' . $_REQUEST['mac'] .'"';
// execute sql
$result = $db->query($sql);
// check result
if ($result == TRUE) {
echo lang('Gen_Okay');
} else {
echo lang('Gen_Error')."\n\n$sql \n\n". $db->lastErrorMsg();
}
}
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// Delete all devices // Delete all devices
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------

View File

@@ -0,0 +1,53 @@
<?php
// ---- IMPORTS ----
//------------------------------------------------------------------------------
// Check if authenticated
require_once $_SERVER['DOCUMENT_ROOT'] . '/php/templates/security.php';
// Get init.php
require dirname(__FILE__).'/../server/init.php';
// ---- IMPORTS ----
//------------------------------------------------------------------------------
// Handle incoming requests
if ($_SERVER['REQUEST_METHOD'] === 'GET') {
// Get query string parameters ?file=settings_table.json&download=true
$file = isset($_GET['file']) ? $_GET['file'] : null;
$download = isset($_GET['download']) ? $_GET['download'] === 'true' : false;
// Check if file parameter is provided
if ($file) {
// Define the folder where files are located
$filePath = "/app/config/" . basename($file);
// Check if the file exists
if (file_exists($filePath)) {
// Handle download behavior
if ($download) {
// Force file download
header('Content-Description: File Transfer');
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename="' . basename($filePath) . '"');
header('Expires: 0');
header('Cache-Control: must-revalidate');
header('Pragma: public');
header('Content-Length: ' . filesize($filePath));
readfile($filePath);
exit;
} else {
// Display file content
header('Content-Type: text/plain');
echo file_get_contents($filePath);
}
} else {
// File not found response
http_response_code(404);
echo json_encode(["error" => "File not found"]);
}
} else {
// Missing file parameter response
http_response_code(400);
echo json_encode(["error" => "Missing 'file' parameter"]);
}
}
?>

38
front/php/server/query_json.php Executable file
View File

@@ -0,0 +1,38 @@
<?php
// ---- IMPORTS ----
//------------------------------------------------------------------------------
// Check if authenticated
require_once $_SERVER['DOCUMENT_ROOT'] . '/php/templates/security.php';
// Get init.php
require dirname(__FILE__).'/../server/init.php';
// ---- IMPORTS ----
//------------------------------------------------------------------------------
// Handle incoming requests
if ($_SERVER['REQUEST_METHOD'] === 'GET') {
// Get query string parameter ?file=settings_table.json
$file = isset($_GET['file']) ? $_GET['file'] : null;
// Check if file parameter is provided
if ($file) {
// Define the folder where files are located
$filePath = "/app/api/" . basename($file);
// Check if the file exists
if (file_exists($filePath)) {
// Send the response back to the client
header('Content-Type: application/json');
echo file_get_contents($filePath);
} else {
// File not found response
http_response_code(404);
echo json_encode(["error" => "File not found"]);
}
} else {
// Missing file parameter response
http_response_code(400);
echo json_encode(["error" => "Missing 'file' parameter"]);
}
}
?>

38
front/php/server/query_logs.php Executable file
View File

@@ -0,0 +1,38 @@
<?php
// ---- IMPORTS ----
//------------------------------------------------------------------------------
// Check if authenticated
require_once $_SERVER['DOCUMENT_ROOT'] . '/php/templates/security.php';
// Get init.php
require dirname(__FILE__).'/../server/init.php';
// ---- IMPORTS ----
//------------------------------------------------------------------------------
// Handle incoming requests
if ($_SERVER['REQUEST_METHOD'] === 'GET') {
// Get query string parameter ?file=settings_table.json
$file = isset($_GET['file']) ? $_GET['file'] : null;
// Check if file parameter is provided
if ($file) {
// Define the folder where files are located
$filePath = "/app/log/" . basename($file);
// Check if the file exists
if (file_exists($filePath)) {
// Send the response back to the client
header('Content-Type: text/plain');
echo file_get_contents($filePath);
} else {
// File not found response
http_response_code(404);
echo json_encode(["error" => "File not found"]);
}
} else {
// Missing file parameter response
http_response_code(400);
echo json_encode(["error" => "Missing 'file' parameter"]);
}
}
?>

View File

@@ -0,0 +1,55 @@
<?php
// ---- IMPORTS ----
//------------------------------------------------------------------------------
// Check if authenticated
require_once $_SERVER['DOCUMENT_ROOT'] . '/php/templates/security.php';
// Get init.php
require dirname(__FILE__).'/../server/init.php';
// ---- IMPORTS ----
global $fullConfPath;
//------------------------------------------------------------------------------
// Handle incoming requests
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
// Access the 'config' parameter from the POST request
$base64Data = $_POST['config'] ?? null;
if (!$base64Data) {
$msg = "Missing 'config' parameter.";
echo $msg;
http_response_code(400); // Bad request
die($msg);
}
// Decode incoming base64 data
$input = base64_decode($base64Data, true);
if ($input === false) {
$msg = "Invalid base64 data.";
echo $msg;
http_response_code(400); // Bad request
die($msg);
}
// Backup the original file
if (file_exists($fullConfPath)) {
copy($fullConfPath, $fullConfPath . ".bak");
}
// Write the new configuration
$file = fopen($fullConfPath, "w");
if (!$file) {
$msg = "Unable to open file!";
echo $msg;
http_response_code(500); // Server error
die($msg);
}
fwrite($file, $input);
fclose($file);
echo "Configuration saved successfully.";
}
?>

View File

@@ -263,7 +263,7 @@ function cleanLog($logFile)
$path = ""; $path = "";
$allowedFiles = ['app.log', 'app_front.log', 'IP_changes.log', 'stdout.log', 'stderr.log', 'app.php_errors.log', 'execution_queue.log']; $allowedFiles = ['app.log', 'app_front.log', 'IP_changes.log', 'stdout.log', 'stderr.log', 'app.php_errors.log', 'execution_queue.log', 'db_is_locked.log'];
if(in_array($logFile, $allowedFiles)) if(in_array($logFile, $allowedFiles))
{ {
@@ -274,7 +274,7 @@ function cleanLog($logFile)
{ {
// purge content // purge content
$file = fopen($path, "w") or die("Unable to open file!"); $file = fopen($path, "w") or die("Unable to open file!");
fwrite($file, "[".$timestamp. "] Log file manually purged" .PHP_EOL.""); fwrite($file, "");
fclose($file); fclose($file);
displayMessage('File <code>'.$logFile.'</code> purged.', FALSE, TRUE, TRUE, TRUE); displayMessage('File <code>'.$logFile.'</code> purged.', FALSE, TRUE, TRUE, TRUE);
} else } else
@@ -356,6 +356,8 @@ function saveSettings()
$txt .= $setKey . "='" . $val . "'\n"; $txt .= $setKey . "='" . $val . "'\n";
} elseif ($dataType == 'integer') { } elseif ($dataType == 'integer') {
$txt .= $setKey . "=" . $settingValue . "\n"; $txt .= $setKey . "=" . $settingValue . "\n";
} elseif ($dataType == 'none') {
$txt .= $setKey . "=''\n";
} elseif ($dataType == 'json') { } elseif ($dataType == 'json') {
$txt .= $setKey . "=" . $settingValue . "\n"; $txt .= $setKey . "=" . $settingValue . "\n";
} elseif ($dataType == 'boolean') { } elseif ($dataType == 'boolean') {
@@ -430,7 +432,7 @@ function getString ($setKey, $default) {
// ------------------------------------------------------------------------------------------- // -------------------------------------------------------------------------------------------
function getSettingValue($setKey) { function getSettingValue($setKey) {
// Define the JSON endpoint URL // Define the JSON endpoint URL
$url = dirname(__FILE__).'/../../../front/api/table_settings.json'; $url = dirname(__FILE__).'/../../../api/table_settings.json';
// Fetch the JSON data // Fetch the JSON data
$json = file_get_contents($url); $json = file_get_contents($url);
@@ -551,6 +553,7 @@ function decodeSpecialChars($str) {
// ------------------------------------------------------------------------------------------- // -------------------------------------------------------------------------------------------
// used in Export CSV
function getDevicesColumns(){ function getDevicesColumns(){
$columns = ["devMac", $columns = ["devMac",
@@ -582,7 +585,8 @@ function getDevicesColumns(){
"devSyncHubNode", "devSyncHubNode",
"devSite", "devSite",
"devSSID", "devSSID",
"devSourcePlugin" "devSourcePlugin",
"devCustomProps"
]; ];
return $columns; return $columns;

View File

@@ -64,7 +64,7 @@ function generate_guid() {
// ---------------------------------------------------------------------------------------- // ----------------------------------------------------------------------------------------
// Logs a notification in in-app notification system // Logs a notification in in-app notification system
function write_notification($content, $level = "interrupt") { function write_notification($content, $level = "interrupt") {
$NOTIFICATION_API_FILE = '/app/front/api/user_notifications.json'; $NOTIFICATION_API_FILE = '/app/api/user_notifications.json';
// Generate GUID // Generate GUID
$guid = generate_guid(); $guid = generate_guid();
@@ -97,7 +97,7 @@ function write_notification($content, $level = "interrupt") {
// ---------------------------------------------------------------------------------------- // ----------------------------------------------------------------------------------------
// Removes a notification based on GUID // Removes a notification based on GUID
function remove_notification($guid) { function remove_notification($guid) {
$NOTIFICATION_API_FILE = '/app/front/api/user_notifications.json'; $NOTIFICATION_API_FILE = '/app/api/user_notifications.json';
// Read existing notifications // Read existing notifications
$notifications = json_decode(file_get_contents($NOTIFICATION_API_FILE), true); $notifications = json_decode(file_get_contents($NOTIFICATION_API_FILE), true);
@@ -114,7 +114,7 @@ function remove_notification($guid) {
// ---------------------------------------------------------------------------------------- // ----------------------------------------------------------------------------------------
// Deletes all notifications // Deletes all notifications
function notifications_clear() { function notifications_clear() {
$NOTIFICATION_API_FILE = '/app/front/api/user_notifications.json'; $NOTIFICATION_API_FILE = '/app/api/user_notifications.json';
// Clear notifications by writing an empty array to the file // Clear notifications by writing an empty array to the file
file_put_contents($NOTIFICATION_API_FILE, json_encode(array())); file_put_contents($NOTIFICATION_API_FILE, json_encode(array()));
@@ -123,7 +123,7 @@ function notifications_clear() {
// ---------------------------------------------------------------------------------------- // ----------------------------------------------------------------------------------------
// Mark a notification read based on GUID // Mark a notification read based on GUID
function mark_notification_as_read($guid) { function mark_notification_as_read($guid) {
$NOTIFICATION_API_FILE = '/app/front/api/user_notifications.json'; $NOTIFICATION_API_FILE = '/app/api/user_notifications.json';
$max_attempts = 3; $max_attempts = 3;
$attempts = 0; $attempts = 0;
@@ -172,7 +172,7 @@ function notifications_mark_all_read() {
// ---------------------------------------------------------------------------------------- // ----------------------------------------------------------------------------------------
function get_unread_notifications() { function get_unread_notifications() {
$NOTIFICATION_API_FILE = '/app/front/api/user_notifications.json'; $NOTIFICATION_API_FILE = '/app/api/user_notifications.json';
// Read existing notifications // Read existing notifications
if (file_exists($NOTIFICATION_API_FILE) && is_readable($NOTIFICATION_API_FILE)) { if (file_exists($NOTIFICATION_API_FILE) && is_readable($NOTIFICATION_API_FILE)) {

Some files were not shown because too many files have changed in this diff Show More