Compare commits
63 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d8b4091043 | ||
|
|
7ea920efa9 | ||
|
|
7306dfbdd3 | ||
|
|
53c8c41133 | ||
|
|
395dfe5fe7 | ||
|
|
2ec0074a45 | ||
|
|
93dce378ea | ||
|
|
df6fae7aa5 | ||
|
|
57e0705e64 | ||
|
|
f3b31479c4 | ||
|
|
b914be9f0e | ||
|
|
2d0a4b79d8 | ||
|
|
d090b29c55 | ||
|
|
5a8be94cdc | ||
|
|
176a436ad4 | ||
|
|
1a05435691 | ||
|
|
49ce3edbdb | ||
|
|
51df759e25 | ||
|
|
992aa00c3c | ||
|
|
d87d933058 | ||
|
|
e0d639cba0 | ||
|
|
1cbbfb25cc | ||
|
|
f205e6f5c1 | ||
|
|
5e67ea22f5 | ||
|
|
99f522e625 | ||
|
|
f04cd7e28b | ||
|
|
3392a1f17c | ||
|
|
d0d9a1a65d | ||
|
|
16d04fe485 | ||
|
|
43d5c51e7c | ||
|
|
647013f3ff | ||
|
|
2ef631a440 | ||
|
|
86315a245b | ||
|
|
b0ce1b87a9 | ||
|
|
541c16aea6 | ||
|
|
ee1a3fc683 | ||
|
|
b90edcccbd | ||
|
|
4614b93780 | ||
|
|
4f548803cb | ||
|
|
d16dd95d65 | ||
|
|
97f7494c34 | ||
|
|
6179dabfa6 | ||
|
|
05ca683f91 | ||
|
|
170e61e73f | ||
|
|
33f0356ca7 | ||
|
|
4b9117dcb4 | ||
|
|
ec274c90da | ||
|
|
e7a1f013df | ||
|
|
c287bc2f22 | ||
|
|
5b8f8f2c5d | ||
|
|
887c2d0f42 | ||
|
|
06d7aa6623 | ||
|
|
5ed142a6b8 | ||
|
|
da9ca8a1f4 | ||
|
|
1cb5375a92 | ||
|
|
fd11cc30f5 | ||
|
|
a1bfdc0f18 | ||
|
|
e88362ce80 | ||
|
|
b5f0a64e7c | ||
|
|
e3623420b0 | ||
|
|
3344ed4b99 | ||
|
|
b0c91f7804 | ||
|
|
f1dc33761d |
15
.github/workflows/docker_prod.yml
vendored
@@ -12,7 +12,8 @@ name: Publish Docker image
|
||||
on:
|
||||
release:
|
||||
types: [published]
|
||||
|
||||
tags:
|
||||
- '*.*.*'
|
||||
jobs:
|
||||
docker:
|
||||
runs-on: ubuntu-latest
|
||||
@@ -39,14 +40,12 @@ jobs:
|
||||
jokobsk/pi.alert
|
||||
# generate Docker tags based on the following events/attributes
|
||||
tags: |
|
||||
type=raw,value=latest
|
||||
type=schedule
|
||||
type=ref,event=branch
|
||||
type=semver,pattern={{version}},value=${{ inputs.version }}
|
||||
type=semver,pattern={{major}}.{{minor}},value=${{ inputs.version }}
|
||||
type=semver,pattern={{major}},value=${{ inputs.version }}
|
||||
type=ref,event=branch,suffix=-{{ sha }}
|
||||
type=ref,event=pr
|
||||
type=semver,pattern={{version}}
|
||||
type=semver,pattern={{major}}.{{minor}}
|
||||
type=semver,pattern={{major}}
|
||||
type=sha
|
||||
type=raw,value=latest,enable=${{ github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/') }}
|
||||
- name: Login to DockerHub
|
||||
if: github.event_name != 'pull_request'
|
||||
uses: docker/login-action@v2
|
||||
|
||||
2
.gitignore
vendored
@@ -3,6 +3,6 @@
|
||||
config/pialert.conf
|
||||
db/*
|
||||
front/log/*
|
||||
front/api/*
|
||||
front/plugins/**/*.log
|
||||
**/%40eaDir/
|
||||
**/@eaDir/
|
||||
|
||||
70
README.md
@@ -34,16 +34,17 @@ The system continuously scans the network for, **New devices**, **New connection
|
||||
## 🧩 Integrations
|
||||
- [Apprise](https://hub.docker.com/r/caronc/apprise), [Pushsafer](https://www.pushsafer.com/), [NTFY](https://ntfy.sh/)
|
||||
- [Webhooks](https://github.com/jokob-sk/Pi.Alert/blob/main/docs/WEBHOOK_N8N.md) ([sample JSON](docs/webhook_json_sample.json))
|
||||
- Home Assistant via [MQTT](https://www.home-assistant.io/integrations/mqtt/)
|
||||
- discovery ~10s per device, deleting not supported, use [MQTT Explorer](https://mqtt-explorer.com/) for now
|
||||
- A simple [API endpoint](https://github.com/jokob-sk/Pi.Alert/blob/main/docs/API.md)
|
||||
- Home Assistant via [MQTT](https://www.home-assistant.io/integrations/mqtt/) - discovery ~10s per device, use [MQTT Explorer](https://mqtt-explorer.com/) to delete devices
|
||||
- [API endpoint](https://github.com/jokob-sk/Pi.Alert/blob/main/docs/API.md)
|
||||
- [Plugin system](https://github.com/jokob-sk/Pi.Alert/tree/main/front/plugins) for custom script monitoring
|
||||
|
||||
# 📥 Installation
|
||||
<!--- --------------------------------------------------------------------- --->
|
||||
|
||||
## 🔐 Security
|
||||
⚠ Only tested as a [docker container - follow these instructions here](dockerfiles/README.md).
|
||||
> Check out [leiweibau's fork](https://github.com/leiweibau/Pi.Alert/) if you want to install Pi.Alert on the server directly or original instructions for [pucherot's original code](https://github.com/pucherot/Pi.Alert/)
|
||||
|
||||
- Configurable login to prevent unauthorized use.
|
||||
|
||||
## 📑 Features
|
||||
# 📑 Features
|
||||
- Display:
|
||||
- Sessions, Connected devices, Favorites, Events, Presence, Concurrent devices, Down alerts, IP's
|
||||
- Manual Nmap scans, Optional speedtest for Device "Internet"
|
||||
@@ -54,44 +55,30 @@ The system continuously scans the network for, **New devices**, **New connection
|
||||
- Language Selection (English, German, Spanish)
|
||||
- Pause arp-scan
|
||||
- DB maintenance, Backup, Restore tools and CSV Export / Import
|
||||
- Configurable login to prevent unauthorized use.
|
||||
- 🌟(Experimental) [Plugin system](https://github.com/jokob-sk/Pi.Alert/tree/main/front/plugins)
|
||||
- Create custom plugins with automatically generated settings and UI.
|
||||
- Monitor anything for changes
|
||||
- Check the instructions carefully if you are up for a challenge!
|
||||
- Help/FAQ Section
|
||||
|
||||
| ![Screen 1][screen1] | ![Screen 2][screen2] |
|
||||
|----------------------|----------------------|
|
||||
| ![Screen 3][screen3] | ![Screen 4][screen4] |
|
||||
| ![Screen 5][screen5] | ![Screen 6][screen6] |
|
||||
| ![Report 1][report1] | ![Report 2][report2] |
|
||||
| ![Screen 1][screen1] | ![Screen 2][screen2] | ![Screen 5][screen5] |
|
||||
|----------------------|----------------------| ----------------------|
|
||||
| ![Screen 3][screen3] | ![Screen 4][screen4] | ![Screen 6][screen6] |
|
||||
| ![Screen 8][screen8] | ![Report 2][report2] | ![Screen 9][screen9] |
|
||||
|
||||
|
||||
# 📥 Installation
|
||||
<!--- --------------------------------------------------------------------- --->
|
||||
|
||||
⚠ This [fork (jokob-sk)](https://github.com/jokob-sk/Pi.Alert) is only tested as a [docker container](dockerfiles/README.md). Check out [leiweibau's fork](https://github.com/leiweibau/Pi.Alert/) if you want to install Pi.Alert on the server directly.
|
||||
|
||||
Instructions for [pucherot's original code can be found here](https://github.com/pucherot/Pi.Alert/)
|
||||
|
||||
|
||||
## 🔗 Other
|
||||
|
||||
|
||||
<!--- --------------------------------------------------------------------- --->
|
||||
|
||||
<!--- --------------------------------------------------------------------- --->
|
||||
### Alternatives
|
||||
### 🔗 Other Alternatives
|
||||
|
||||
- [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)
|
||||
|
||||
### Old docs
|
||||
|
||||
- [Device Management instructions](docs/DEVICE_MANAGEMENT.md)
|
||||
- [Versions History](docs/VERSIONS_HISTORY.md)
|
||||
Device Management [instructions](docs/DEVICE_MANAGEMENT.md) | Old Versions [History](docs/VERSIONS_HISTORY.md)
|
||||
|
||||
### 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)
|
||||
|
||||
### 🥇 Special thanks
|
||||
|
||||
@@ -102,8 +89,17 @@ Instructions for [pucherot's original code can be found here](https://github.com
|
||||
- [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 translation
|
||||
- [jokob-sk](https://github.com/jokob-sk/Pi.Alert): DB Maintenance tools
|
||||
- Please see the [Git commit history](https://github.com/jokob-sk/Pi.Alert/commits/main) for a full list of people and their contributions to the project
|
||||
- Please see the [Git contributors](https://github.com/jokob-sk/Pi.Alert/graphs/contributors) for a full list of people and their contributions to the project
|
||||
|
||||
## ☕ Support me
|
||||
|
||||
Disclaimer: Please only donate if you don't have any debt yourself. Support yourself first, then others.
|
||||
|
||||
<a href="https://github.com/sponsors/jokob-sk" target="_blank"><img src="https://i.imgur.com/X6p5ACK.png" alt="Sponsor Me on GitHub" style="height: 30px !important;width: 117px !important;" width="150px" ></a>
|
||||
<a href="https://www.buymeacoffee.com/jokobsk" target="_blank"><img src="https://cdn.buymeacoffee.com/buttons/v2/default-yellow.png" alt="Buy Me A Coffee" style="height: 30px !important;width: 117px !important;" width="117px" height="30px" ></a>
|
||||
<a href="https://www.patreon.com/user?u=84385063" target="_blank"><img src="https://upload.wikimedia.org/wikipedia/commons/thumb/8/82/Patreon_logo_with_wordmark.svg/512px-Patreon_logo_with_wordmark.svg.png" alt="Support me on patreon" style="height: 30px !important;width: 117px !important;" width="117px" ></a>
|
||||
|
||||
BTC: 1N8tupjeCK12qRVU2XrV17WvKK7LCawyZM
|
||||
|
||||
<!--- --------------------------------------------------------------------- --->
|
||||
[main]: ./docs/img/devices_split.png "Main screen"
|
||||
@@ -113,7 +109,9 @@ Instructions for [pucherot's original code can be found here](https://github.com
|
||||
[screen4]: ./docs/img/maintenance.png "Screen 4"
|
||||
[screen5]: ./docs/img/network.png "Screen 5"
|
||||
[screen6]: ./docs/img/settings.png "Screen 6"
|
||||
[screen7]: ./docs/img/help_faq.png "Screen 6"
|
||||
[screen7]: ./docs/img/help_faq.png "Screen 7"
|
||||
[screen8]: ./docs/img/plugins_webmon.png "Screen 8"
|
||||
[screen9]: ./docs/img/device_nmap.png "Screen 9"
|
||||
[report1]: ./docs/img/4_report_1.jpg "Report sample 1"
|
||||
[report2]: ./docs/img/4_report_2.jpg "Report sample 2"
|
||||
[main_dark]: /docs/img/1_devices_dark.jpg "Main screen dark"
|
||||
|
||||
1267
back/pialert.py
@@ -34,14 +34,11 @@
|
||||
<td bgcolor=#F5F5F5 height=200 valign=top style="padding: 10px">
|
||||
|
||||
<INTERNET_TABLE>
|
||||
|
||||
<NEW_DEVICES_TABLE>
|
||||
|
||||
<DOWN_DEVICES_TABLE>
|
||||
|
||||
<EVENTS_TABLE>
|
||||
|
||||
<PORTS_TABLE>
|
||||
<PLUGINS_TABLE>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
|
||||
@@ -6,3 +6,4 @@ Server: <SERVER_NAME>
|
||||
<SECTION_EVENTS>
|
||||
<SECTION_INTERNET>
|
||||
<PORTS_TABLE>
|
||||
<PLUGINS_TABLE>
|
||||
|
||||
@@ -38,14 +38,11 @@
|
||||
<td bgcolor=#F5F5F5 height=200 valign=top style="padding: 10px">
|
||||
|
||||
<INTERNET_TABLE>
|
||||
|
||||
<NEW_DEVICES_TABLE>
|
||||
|
||||
<DOWN_DEVICES_TABLE>
|
||||
|
||||
<EVENTS_TABLE>
|
||||
|
||||
<PORTS_TABLE>
|
||||
<PLUGINS_TABLE>
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
@@ -7,17 +7,20 @@ services:
|
||||
network_mode: "host"
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- ${APP_DATA_LOCATION}/pialert/config2:/home/pi/pialert/config
|
||||
- ${APP_DATA_LOCATION}/pialert/config:/home/pi/pialert/config
|
||||
# - ${APP_DATA_LOCATION}/pialert/db/pialert.db:/home/pi/pialert/db/pialert.db
|
||||
- ${APP_DATA_LOCATION}/pialert/db2:/home/pi/pialert/db
|
||||
- ${APP_DATA_LOCATION}/pialert/db:/home/pi/pialert/db
|
||||
# (optional) useful for debugging if you have issues setting up the container
|
||||
- ${LOGS_LOCATION}:/home/pi/pialert/front/log
|
||||
# DELETE START anyone trying to use this file: comment out / delete BELOW lines, they are only for development purposes
|
||||
- ${DEV_LOCATION}/back/pialert.py:/home/pi/pialert/back/pialert.py
|
||||
- ${DEV_LOCATION}/back/report_template.html:/home/pi/pialert/back/report_template.html
|
||||
- ${DEV_LOCATION}/back/report_template_new_version.html:/home/pi/pialert/back/report_template_new_version.html
|
||||
- ${DEV_LOCATION}/back/report_template.txt:/home/pi/pialert/back/report_template.txt
|
||||
- ${DEV_LOCATION}/pholus:/home/pi/pialert/pholus
|
||||
- ${DEV_LOCATION}/dockerfiles:/home/pi/pialert/dockerfiles
|
||||
- ${APP_DATA_LOCATION}/pialert/php.ini:/etc/php/7.4/fpm/php.ini
|
||||
- ${DEV_LOCATION}/front/api:/home/pi/pialert/front/api
|
||||
# - ${DEV_LOCATION}/front/api:/home/pi/pialert/front/api
|
||||
- ${DEV_LOCATION}/front/css:/home/pi/pialert/front/css
|
||||
- ${DEV_LOCATION}/front/lib/AdminLTE:/home/pi/pialert/front/lib/AdminLTE
|
||||
- ${DEV_LOCATION}/front/js:/home/pi/pialert/front/js
|
||||
@@ -25,12 +28,14 @@ services:
|
||||
- ${DEV_LOCATION}/front/deviceDetails.php:/home/pi/pialert/front/deviceDetails.php
|
||||
- ${DEV_LOCATION}/front/devices.php:/home/pi/pialert/front/devices.php
|
||||
- ${DEV_LOCATION}/front/events.php:/home/pi/pialert/front/events.php
|
||||
- ${DEV_LOCATION}/front/plugins.php:/home/pi/pialert/front/plugins.php
|
||||
- ${DEV_LOCATION}/front/help_faq.php:/home/pi/pialert/front/help_faq.php
|
||||
- ${DEV_LOCATION}/front/index.php:/home/pi/pialert/front/index.php
|
||||
- ${DEV_LOCATION}/front/maintenance.php:/home/pi/pialert/front/maintenance.php
|
||||
- ${DEV_LOCATION}/front/network.php:/home/pi/pialert/front/network.php
|
||||
- ${DEV_LOCATION}/front/presence.php:/home/pi/pialert/front/presence.php
|
||||
- ${DEV_LOCATION}/front/settings.php:/home/pi/pialert/front/settings.php
|
||||
- ${DEV_LOCATION}/front/plugins:/home/pi/pialert/front/plugins
|
||||
# DELETE END anyone trying to use this file: comment out / delete ABOVE lines, they are only for development purposes
|
||||
environment:
|
||||
- TZ=${TZ}
|
||||
|
||||
@@ -193,3 +193,5 @@ Disclaimer: Please only donate if you don't have any debt yourself. Support your
|
||||
<a href="https://github.com/sponsors/jokob-sk" target="_blank"><img src="https://i.imgur.com/X6p5ACK.png" alt="Sponsor Me on GitHub" style="height: 30px !important;width: 117px !important;" width="150px" ></a>
|
||||
<a href="https://www.buymeacoffee.com/jokobsk" target="_blank"><img src="https://cdn.buymeacoffee.com/buttons/v2/default-yellow.png" alt="Buy Me A Coffee" style="height: 30px !important;width: 117px !important;" width="117px" height="30px" ></a>
|
||||
<a href="https://www.patreon.com/user?u=84385063" target="_blank"><img src="https://upload.wikimedia.org/wikipedia/commons/thumb/8/82/Patreon_logo_with_wordmark.svg/512px-Patreon_logo_with_wordmark.svg.png" alt="Support me on patreon" style="height: 30px !important;width: 117px !important;" width="117px" ></a>
|
||||
|
||||
BTC: 1N8tupjeCK12qRVU2XrV17WvKK7LCawyZM
|
||||
|
||||
@@ -29,6 +29,10 @@ You can access the following files:
|
||||
| `table_nmap_scan.json` | The current state of the discovered ports by the regular NMAP scans. |
|
||||
| `table_pholus_scan.json` | The latest state of the [pholus](https://github.com/jokob-sk/Pi.Alert/tree/main/pholus) (A multicast DNS and DNS Service Discovery Security Assessment Tool) scan results. |
|
||||
| `table_events_pending_alert.json` | The list of the unprocessed (pending) notification events. |
|
||||
| `table_settings.json` | The content of the settings table. |
|
||||
| `table_plugins_objects.json` | The content of the plugins_objects table. Find more info on the [Plugin system here](https://github.com/jokob-sk/Pi.Alert/tree/main/front/plugins)|
|
||||
| `language_strings.json` | The content of the language_strings table, which in turn is loaded from the plugins `config.json` definitions. |
|
||||
| `table_plugins_events.json` | The content of the plugins_events table. |
|
||||
| `table_custom_endpoint.json` | A custom endpoint generated by the SQL query specified by the `API_CUSTOM_SQL` setting. |
|
||||
|
||||
Current/latest state of the aforementioned files depends on your settings.
|
||||
|
||||
31
docs/DEBUG_INVALID_JSON.md
Executable file
@@ -0,0 +1,31 @@
|
||||
# How to debug the Invalid JSON response error
|
||||
|
||||
Check the the HTTP response of the failing backend call by following these steps:
|
||||
|
||||
- Open developer console in your browser (usually, e. g. for Chrome, key F12 on the keyboard).
|
||||
- Follow the steps in this screenshot:
|
||||
|
||||
![F12DeveloperConsole][F12DeveloperConsole]
|
||||
|
||||
- Copy the URL causing the error and enter it in the address bar of your browser directly and hit enter. The copied URLs could look something like this (notice the query strings at the end):
|
||||
- `http://<pialert URL>:20211/php/server/devices.php?action=getDevicesTotals`
|
||||
- `http://<pialert URL>:20211/php/server/devices.php?action=getDevicesList&status=all`
|
||||
|
||||
- Post the error response in the existing issue thread on GitHub or create a new issue and include the redacted response of the failing query.
|
||||
|
||||
For reference, the above queries should return results in the following format:
|
||||
|
||||
First URL:
|
||||
|
||||
![array][array]
|
||||
|
||||
Second URL:
|
||||
|
||||
![json][json]
|
||||
|
||||
You can copy and paste any JSON result (result of the second query) into an online JSON checker, such as [this one](https://jsonchecker.com/) to check if it's valid.
|
||||
|
||||
|
||||
[F12DeveloperConsole]: ./img/DEBUG/Invalid_JSON_repsonse_debug.png "F12DeveloperConsole"
|
||||
[array]: ./img/DEBUG/array_result_example.png "array"
|
||||
[json]: ./img/DEBUG/JSON_result_example.png "json"
|
||||
10
docs/SUBNETS.md
Executable file
@@ -0,0 +1,10 @@
|
||||
## Subnets configuration
|
||||
|
||||
The arp-scan time itself depends on the number of IP addresses to check.
|
||||
The number of IPs to check depends on the [network mask](https://www.calculator.net/ip-subnet-calculator.html) you set on the `SCAN_SUBNETS` setting.
|
||||
|
||||
For example, a `/24` mask results in 256 IPs to check, where as a `/16` mask checks around 65,536. Every IP takes a couple seconds. This means that with an incorrect configuration the arp-scan will take hours to complete instead of seconds.
|
||||
|
||||
- Specify the network mask. For example, the filter `192.168.1.0/24` covers IP ranges 192.168.1.0 to 192.168.1.255
|
||||
- Run `iwconfig` in your container to find your interface name(s) (e.g.: `eth0`, `eth1`).
|
||||
- Append e.g.: ` -vlan=107` to the interface field (e.g.: `eth0 -vlan=107`) for multiple vlans. More details in this [issue](https://github.com/jokob-sk/Pi.Alert/issues/170)
|
||||
|
Before Width: | Height: | Size: 78 KiB After Width: | Height: | Size: 32 KiB |
BIN
docs/img/DEBUG/Invalid_JSON_repsonse_debug.png
Executable file
|
After Width: | Height: | Size: 197 KiB |
BIN
docs/img/DEBUG/JSON_result_example.png
Executable file
|
After Width: | Height: | Size: 54 KiB |
BIN
docs/img/DEBUG/array_result_example.png
Executable file
|
After Width: | Height: | Size: 7.0 KiB |
BIN
docs/img/device_nmap.png
Executable file
|
After Width: | Height: | Size: 157 KiB |
BIN
docs/img/plugins.png
Executable file
|
After Width: | Height: | Size: 218 KiB |
BIN
docs/img/plugins_json_settings.png
Executable file
|
After Width: | Height: | Size: 185 KiB |
BIN
docs/img/plugins_json_ui.png
Executable file
|
After Width: | Height: | Size: 173 KiB |
BIN
docs/img/plugins_settings.png
Executable file
|
After Width: | Height: | Size: 158 KiB |
BIN
docs/img/plugins_webmon.png
Executable file
|
After Width: | Height: | Size: 92 KiB |
2
front/api/.gitignore
vendored
Executable file
@@ -0,0 +1,2 @@
|
||||
*
|
||||
!.gitignore
|
||||
@@ -747,6 +747,13 @@ height: 50px;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
.pageHelp{
|
||||
position: absolute;
|
||||
font-size: x-small;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
|
||||
#networkTree .box
|
||||
{
|
||||
padding:2px;
|
||||
@@ -799,5 +806,18 @@ height: 50px;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.plugin-content
|
||||
{
|
||||
padding-bottom: 7px;
|
||||
}
|
||||
.plugin-content #tabs-content-location
|
||||
{
|
||||
margin-top: 20px;
|
||||
margin-left: 7px;
|
||||
margin-right: 7px;
|
||||
margin-bottom: 9px;
|
||||
padding-bottom: 8px;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1623,7 +1623,7 @@ function overwriteIconType () {
|
||||
|
||||
var icon = $('#txtIcon').val();
|
||||
|
||||
// Delete device events
|
||||
// Mass update icons
|
||||
$.get('php/server/devices.php?action=overwriteIconType&mac='+ mac + '&icon=' + icon, function(msg) {
|
||||
showMessage (msg);
|
||||
});
|
||||
@@ -1775,7 +1775,7 @@ function initializeTabsNew () {
|
||||
|
||||
function loadNmap()
|
||||
{
|
||||
$(".deviceSpecific").remove(); // remove any previous data listed in teh table
|
||||
$(".deviceSpecific").remove(); // remove any previous data listed in the table
|
||||
|
||||
$.get('php/server/devices.php?action=getNmap&mac='+ mac, function(data) {
|
||||
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
require dirname(__FILE__).'/php/server/init.php';
|
||||
require 'php/templates/security.php';
|
||||
|
||||
|
||||
|
||||
if ($Pia_WebProtection != 'true')
|
||||
{
|
||||
header('Location: devices.php');
|
||||
@@ -9,6 +11,15 @@ if ($Pia_WebProtection != 'true')
|
||||
exit;
|
||||
}
|
||||
|
||||
// Logout
|
||||
if (isset ($_GET["action"]) && $_GET["action"] == 'logout')
|
||||
{
|
||||
setcookie("PiAlert_SaveLogin", '', time()+1); // reset cookie
|
||||
$_SESSION["login"] = 0;
|
||||
header('Location: index.php');
|
||||
exit;
|
||||
}
|
||||
|
||||
// Password without Cookie check -> pass and set initial cookie
|
||||
if (isset ($_POST["loginpassword"]) && $Pia_Password == hash('sha256',$_POST["loginpassword"]))
|
||||
{
|
||||
|
||||
@@ -346,3 +346,4 @@ function openInNewTab (url) {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -244,7 +244,7 @@ if (isset($_POST['submit']) && submit && isset($_POST['skinselector_set'])) {
|
||||
<option value="16"><?= lang('Device_TableHead_Location');?></option>
|
||||
<option value="17"><?= lang('Device_TableHead_Vendor');?></option>
|
||||
</select>
|
||||
<span class="input-group-addon"><i title="<?= lang('DevDetail_GoToNetworkNode');?>" class="fa fa-save pointer" onclick="saveSelectedColumns();"></i></span>
|
||||
<span class="input-group-addon"><i title="<?= lang('Gen_Save');?>" class="fa fa-save pointer" onclick="saveSelectedColumns();"></i></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -365,24 +365,6 @@ if (isset($_POST['submit']) && submit && isset($_POST['skinselector_set'])) {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="log-area">
|
||||
|
||||
<div class="row logs-row">
|
||||
<textarea id="pialert_pholus_log" class="logs" cols="70" rows="10" wrap='off' readonly><?php echo file_get_contents( "./log/pialert_pholus.log" ); ?>
|
||||
</textarea>
|
||||
</div>
|
||||
<div class="row logs-row" >
|
||||
<div>
|
||||
<div class="log-file">pialert_pholus.log<div class="logs-size"><?php echo number_format((filesize("./log/pialert_pholus.log") / 1000000),2,",",".") . ' MB';?>
|
||||
<span class="span-padding"><a href="./log/pialert_pholus.log"><i class="fa fa-download"></i> </a></span>
|
||||
</div></div>
|
||||
<div class="log-purge">
|
||||
<button class="btn btn-primary" onclick="logManage('pialert_pholus.log','cleanLog')"><?= lang('Gen_Purge');?></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="log-area">
|
||||
|
||||
<div class="row logs-row">
|
||||
|
||||
193
front/php/server/dbHelper.php
Executable file
@@ -0,0 +1,193 @@
|
||||
<?php
|
||||
//------------------------------------------------------------------------------
|
||||
// Pi.Alert
|
||||
// Open Source Network Guard / WIFI & LAN intrusion detector
|
||||
//
|
||||
// parameters.php - Front module. Server side. Manage Parameters
|
||||
//------------------------------------------------------------------------------
|
||||
# Puche 2022+ jokob jokob@duck.com GNU GPLv3
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// External files
|
||||
require dirname(__FILE__).'/init.php';
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Action selector
|
||||
//------------------------------------------------------------------------------
|
||||
// Set maximum execution time to 15 seconds
|
||||
ini_set ('max_execution_time','15');
|
||||
|
||||
$skipCache = FALSE;
|
||||
$expireMinutes = 5;
|
||||
$defaultValue = '';
|
||||
$dbtable = '';
|
||||
$columns = '';
|
||||
$values = '';
|
||||
|
||||
|
||||
if (isset ($_REQUEST['skipcache'])) {
|
||||
$skipCache = TRUE;
|
||||
}
|
||||
|
||||
if (isset ($_REQUEST['defaultValue'])) {
|
||||
$defaultValue = $_REQUEST['defaultValue'];
|
||||
}
|
||||
|
||||
if (isset ($_REQUEST['expireMinutes'])) {
|
||||
$expireMinutes = $_REQUEST['expireMinutes'];
|
||||
}
|
||||
|
||||
if (isset ($_REQUEST['columnName'])) {
|
||||
$columnName = $_REQUEST['columnName'];
|
||||
}
|
||||
|
||||
if (isset ($_REQUEST['id'])) {
|
||||
$id = $_REQUEST['id'];
|
||||
}
|
||||
|
||||
if (isset ($_REQUEST['values'])) {
|
||||
$values = $_REQUEST['values'];
|
||||
}
|
||||
|
||||
if (isset ($_REQUEST['columns'])) {
|
||||
$columns = $_REQUEST['columns'];
|
||||
}
|
||||
|
||||
if (isset ($_REQUEST['dbtable'])) {
|
||||
$dbtable = $_REQUEST['dbtable'];
|
||||
}
|
||||
// TODO: Security, read, delete, edge cases
|
||||
// Action functions
|
||||
if (isset ($_REQUEST['action']) && !empty ($_REQUEST['action'])) {
|
||||
$action = $_REQUEST['action'];
|
||||
switch ($action) {
|
||||
case 'create': create($skipCache, $defaultValue, $expireMinutes, $dbtable, $columns, $values ); break;
|
||||
// case 'read' : read($skipCache, $defaultValue, $expireMinutes, $dbtable, $columns, $values); break;
|
||||
case 'update': update($columnName, $id, $skipCache, $defaultValue, $expireMinutes, $dbtable, $columns, $values); break;
|
||||
case 'delete': delete($columnName, $id, $dbtable); break;
|
||||
default: logServerConsole ('Action: '. $action); break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// update
|
||||
//------------------------------------------------------------------------------
|
||||
function update($columnName, $id, $skipCache, $defaultValue, $expireMinutes, $dbtable, $columns, $values) {
|
||||
|
||||
global $db;
|
||||
|
||||
// handle one or multiple columns
|
||||
if(strpos($columns, ',') !== false)
|
||||
{
|
||||
$columnsArr = explode(",", $columns);
|
||||
}else
|
||||
{
|
||||
$columnsArr = array($columns);
|
||||
}
|
||||
|
||||
// handle one or multiple values
|
||||
if(strpos($values, ',') !== false)
|
||||
{
|
||||
$valuesArr = explode(",", $values);
|
||||
} else
|
||||
{
|
||||
$valuesArr = array($values);
|
||||
}
|
||||
|
||||
$columnValues = '';
|
||||
|
||||
$index = 0;
|
||||
foreach($columnsArr as $column)
|
||||
{
|
||||
$columnValues = $columnValues .' "' .$column.'" = "'.$valuesArr[$index] . '",' ;
|
||||
$index = $index + 1;
|
||||
}
|
||||
|
||||
$columnValues = substr($columnValues, 0, -1);
|
||||
|
||||
// Update value
|
||||
$sql = 'UPDATE '.$dbtable.' SET '. $columnValues .'
|
||||
WHERE "'. $columnName .'"="'. $id.'"';
|
||||
$result = $db->query($sql);
|
||||
|
||||
if (! $result == TRUE) {
|
||||
echo "Error updating parameter\n\n$sql \n\n". $db->lastErrorMsg();
|
||||
return;
|
||||
}
|
||||
|
||||
$changes = $db->changes();
|
||||
if ($changes == 0) {
|
||||
// Insert new value
|
||||
create($skipCache, $defaultValue, $expireMinutes, $dbtable, $columns, $values);
|
||||
}
|
||||
|
||||
// update cache
|
||||
$uniqueHash = hash('ripemd160', $dbtable . $columns);
|
||||
setCache($uniqueHash, $values, $expireMinutes);
|
||||
|
||||
echo 'OK';
|
||||
}
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// create
|
||||
//------------------------------------------------------------------------------
|
||||
function create($skipCache, $defaultValue, $expireMinutes, $dbtable, $columns, $values)
|
||||
{
|
||||
global $db;
|
||||
|
||||
// Insert new value
|
||||
$sql = 'INSERT INTO '.$dbtable.' ('.$columns.')
|
||||
VALUES ("'. quotes($parameter) .'",
|
||||
"'. $values .'")';
|
||||
$result = $db->query($sql);
|
||||
|
||||
if (! $result == TRUE) {
|
||||
echo "Error creating entry\n\n$sql \n\n". $db->lastErrorMsg();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// delete
|
||||
//------------------------------------------------------------------------------
|
||||
function delete($columnName, $id, $dbtable)
|
||||
{
|
||||
global $db;
|
||||
|
||||
// handle one or multiple ids
|
||||
if(strpos($id, ',') !== false)
|
||||
{
|
||||
$idsArr = explode(",", $id);
|
||||
}else
|
||||
{
|
||||
$idsArr = array($id);
|
||||
}
|
||||
|
||||
$idsStr = "";
|
||||
|
||||
foreach ($idsArr as $item)
|
||||
{
|
||||
$idsStr = $idsStr . '"' .$item.'"';
|
||||
}
|
||||
|
||||
// Insert new value
|
||||
$sql = 'DELETE FROM '.$dbtable.' WHERE "'.$columnName.'" IN ('. $idsStr .')';
|
||||
$result = $db->query($sql);
|
||||
|
||||
if (! $result == TRUE) {
|
||||
echo "Error deleting entry\n\n$sql \n\n". $db->lastErrorMsg();
|
||||
return;
|
||||
} else
|
||||
{
|
||||
echo lang('Gen_DataUpdatedUITakesTime');
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
?>
|
||||
@@ -15,6 +15,7 @@
|
||||
// Action selector
|
||||
//------------------------------------------------------------------------------
|
||||
// Set maximum execution time to 15 seconds
|
||||
|
||||
ini_set ('max_execution_time','30');
|
||||
|
||||
// Action functions
|
||||
@@ -478,22 +479,29 @@ function ImportCSV() {
|
||||
|
||||
// sql
|
||||
$sql = 'DELETE FROM Devices';
|
||||
|
||||
// execute sql
|
||||
$result = $db->query($sql);
|
||||
|
||||
// Open the CSV file with read-only mode
|
||||
$csvFile = fopen($file, 'r');
|
||||
|
||||
// Skip the first line
|
||||
fgetcsv($csvFile);
|
||||
$data = file_get_contents($file);
|
||||
$data = explode("\n", $data);
|
||||
|
||||
$columns = getDevicesColumns();
|
||||
|
||||
// Parse data from CSV file line by line (max 10000 lines)
|
||||
while (($row = fgetcsv($csvFile, 10000, ",")) !== FALSE)
|
||||
{
|
||||
$sql = 'INSERT INTO Devices ('.implode(',', $columns).') VALUES ("' .implode('","', $row).'")';
|
||||
|
||||
// Parse data from CSV file line by line (max 10000 lines)
|
||||
foreach($data as $row)
|
||||
{
|
||||
// Check if not empty and skipping first line
|
||||
$rowArray = explode(',',$row);
|
||||
|
||||
if(count($rowArray) > 20)
|
||||
{
|
||||
$cleanMac = str_replace("\"","",$rowArray[0]);
|
||||
|
||||
if(filter_var($cleanMac , FILTER_VALIDATE_MAC) == True || $cleanMac == "Internet")
|
||||
{
|
||||
$sql = "INSERT INTO Devices (".implode(',', $columns).") VALUES (" . $row.")";
|
||||
$result = $db->query($sql);
|
||||
|
||||
// check result
|
||||
@@ -503,9 +511,9 @@ function ImportCSV() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Close opened CSV file
|
||||
fclose($csvFile);
|
||||
}
|
||||
|
||||
if($error == "")
|
||||
{
|
||||
@@ -515,7 +523,7 @@ function ImportCSV() {
|
||||
}
|
||||
else{
|
||||
// an error occurred while writing to the DB, display the last error message
|
||||
echo lang('BackDevices_DBTools_ImportCSVError')."\n\n$sql \n\n".$error;
|
||||
echo lang('BackDevices_DBTools_ImportCSVError')."\n\n$sql \n\n".$result;
|
||||
}
|
||||
|
||||
} else {
|
||||
@@ -1184,7 +1192,7 @@ function overwriteIconType()
|
||||
if ($result == TRUE) {
|
||||
echo 'OK';
|
||||
} else {
|
||||
echo 'KO';
|
||||
echo lang('BackDevices_Device_UpdDevError');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,3 +3,4 @@ require dirname(__FILE__).'/../templates/timezone.php';
|
||||
require dirname(__FILE__).'/db.php';
|
||||
require dirname(__FILE__).'/util.php';
|
||||
require dirname(__FILE__).'/../templates/language/lang.php';
|
||||
?>
|
||||
|
||||
@@ -35,7 +35,6 @@ elseif ($FUNCTION == 'cleanLog')
|
||||
cleanLog($SETTINGS);
|
||||
}
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Formatting data functions
|
||||
//------------------------------------------------------------------------------
|
||||
@@ -203,7 +202,7 @@ function cleanLog($logFile)
|
||||
|
||||
$path = "";
|
||||
|
||||
$allowedFiles = ['pialert.log', 'pialert_front.log', 'IP_changes.log', 'stdout.log', 'stderr.log', "pialert_pholus.log", "pialert_pholus_lastrun.log"];
|
||||
$allowedFiles = ['pialert.log', 'pialert_front.log', 'IP_changes.log', 'stdout.log', 'stderr.log', "pialert_pholus_lastrun.log"];
|
||||
|
||||
if(in_array($logFile, $allowedFiles))
|
||||
{
|
||||
@@ -275,28 +274,35 @@ function saveSettings()
|
||||
{
|
||||
if($group == $setting[0])
|
||||
{
|
||||
if($setting[3] == 'text' or $setting[3] == 'password' or $setting[3] == 'readonly' or $setting[3] == 'selecttext')
|
||||
if($setting[2] == 'text' or $setting[2] == 'password' or $setting[2] == 'readonly' or $setting[2] == 'selecttext')
|
||||
{
|
||||
$txt = $txt.$setting[1]."='".$setting[2]."'\n" ;
|
||||
} elseif($setting[3] == 'integer' or $setting[3] == 'selectinteger')
|
||||
$val = encode_single_quotes($setting[3]);
|
||||
$txt = $txt.$setting[1]."='".$val."'\n" ;
|
||||
} elseif($setting[2] == 'integer' or $setting[2] == 'selectinteger')
|
||||
{
|
||||
$txt = $txt.$setting[1]."=".$setting[2]."\n" ;
|
||||
} elseif($setting[3] == 'boolean')
|
||||
$txt = $txt.$setting[1]."=".$setting[3]."\n" ;
|
||||
} elseif($setting[2] == 'boolean')
|
||||
{
|
||||
$val = "False";
|
||||
if($setting[2] == 'true')
|
||||
if($setting[3] == 'true')
|
||||
{
|
||||
$val = "True";
|
||||
}
|
||||
$txt = $txt.$setting[1]."=".$val."\n" ;
|
||||
}elseif($setting[3] == 'multiselect' or $setting[3] == 'subnets')
|
||||
}elseif($setting[2] == 'multiselect' or $setting[2] == 'subnets' or $setting[2] == 'list')
|
||||
{
|
||||
$temp = '[';
|
||||
foreach($setting[2] as $val)
|
||||
|
||||
if (count($setting) > 3 && is_array( $setting[3]) == True){
|
||||
foreach($setting[3] as $val)
|
||||
{
|
||||
$temp = $temp."'". $val."',";
|
||||
$temp = $temp."'". encode_single_quotes($val)."',";
|
||||
}
|
||||
$temp = substr_replace($temp, "", -1).']'; // close brackets and remove last comma ','
|
||||
|
||||
$temp = substr_replace($temp, "", -1); // remove last comma ','
|
||||
}
|
||||
|
||||
$temp = $temp.']'; // close brackets
|
||||
$txt = $txt.$setting[1]."=".$temp."\n" ;
|
||||
}
|
||||
}
|
||||
@@ -322,8 +328,6 @@ function saveSettings()
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------------------------
|
||||
|
||||
|
||||
function getString ($codeName, $default) {
|
||||
|
||||
$result = lang($codeName);
|
||||
@@ -338,6 +342,16 @@ function getString ($codeName, $default) {
|
||||
|
||||
// -------------------------------------------------------------------------------------------
|
||||
|
||||
|
||||
function encode_single_quotes ($val) {
|
||||
|
||||
$result = str_replace ('\'','{s-quote}',$val);
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------------------------
|
||||
|
||||
function getDateFromPeriod () {
|
||||
$period = $_REQUEST['period'];
|
||||
return '"'. date ('Y-m-d', strtotime ('+1 day -'. $period) ) .'"';
|
||||
@@ -403,7 +417,8 @@ function getDevicesColumns(){
|
||||
"dev_Location",
|
||||
"dev_Archived",
|
||||
"dev_Network_Node_port",
|
||||
"dev_Network_Node_MAC_ADDR"];
|
||||
"dev_Network_Node_MAC_ADDR",
|
||||
"dev_Icon"];
|
||||
|
||||
return $columns;
|
||||
}
|
||||
@@ -427,5 +442,3 @@ function setCache($key, $value, $expireMinutes = 5) {
|
||||
|
||||
|
||||
?>
|
||||
|
||||
|
||||
|
||||
@@ -222,6 +222,10 @@ if ($ENABLED_DARKMODE === True) {
|
||||
<a href="network.php"><span><i class="fa fa-fw fa-network-wired"></i> <?= lang('Navigation_Network');?></span></a>
|
||||
</li>
|
||||
|
||||
<li class=" <?php if (in_array (basename($_SERVER['SCRIPT_NAME']), array('plugins.php') ) ){ echo 'active'; } ?>">
|
||||
<a href="plugins.php"><span><i class="fa fa-fw fa-plug"></i> <?= lang('Navigation_Plugins');?></span></a>
|
||||
</li>
|
||||
|
||||
<li class=" <?php if (in_array (basename($_SERVER['SCRIPT_NAME']), array('maintenance.php') ) ){ echo 'active'; } ?>">
|
||||
<div class="new-version myhidden" id="version" data-build-time="<?php echo file_get_contents( "buildtimestamp.txt");?>">🆕</div>
|
||||
<a href="maintenance.php"><i class="fa fa-wrench "></i> <span><?= lang('Navigation_Maintenance');?></span></a>
|
||||
|
||||
@@ -13,6 +13,7 @@ $lang['en_us'] = array(
|
||||
// General
|
||||
//////////////////////////////////////////////////////////////////
|
||||
'Gen_Delete' => 'Delete',
|
||||
'Gen_DeleteAll' => 'Delete all',
|
||||
'Gen_Cancel' => 'Cancel',
|
||||
'Gen_Okay' => 'Ok',
|
||||
'Gen_Save' => 'Save',
|
||||
@@ -24,6 +25,9 @@ $lang['en_us'] = array(
|
||||
'Gen_AreYouSure' => 'Are you sure?',
|
||||
'Gen_Upd' => 'Updated successfully',
|
||||
'Gen_Upd_Fail' => 'Update failed',
|
||||
'Gen_Help' => 'Need help?',
|
||||
'Gen_DataUpdatedUITakesTime' => 'OK - It may take a while for the UI to update if a scan is runnig.',
|
||||
'Gen_LockedDB' => 'ERROR - DB might be locked - Check F12 Dev tools -> Console or try later.',
|
||||
|
||||
//////////////////////////////////////////////////////////////////
|
||||
// Login Page - Update by @TeroRERO 03ago2022
|
||||
@@ -50,9 +54,10 @@ $lang['en_us'] = array(
|
||||
'Navigation_Devices' => 'Devices',
|
||||
'Navigation_Presence' => 'Presence',
|
||||
'Navigation_Events' => 'Events',
|
||||
'Navigation_Network' => 'Network',
|
||||
'Navigation_Plugins' => 'Plugins',
|
||||
'Navigation_Maintenance' => 'Maintenance',
|
||||
'Navigation_Settings' => 'Settings',
|
||||
'Navigation_Network' => 'Network',
|
||||
'Navigation_HelpFAQ' => 'Help / FAQ',
|
||||
'Device_Title' => 'Devices',
|
||||
'Device_Shortcut_AllDevices' => 'All Devices',
|
||||
@@ -347,6 +352,7 @@ $lang['en_us'] = array(
|
||||
'BackDevices_DBTools_ImportCSV' => 'The devices from the CSV file were imported successfully.',
|
||||
'BackDevices_DBTools_ImportCSVError' => 'The CSV file couldn\'t be imported. Make sure the format is correct.',
|
||||
'BackDevices_DBTools_ImportCSVMissing' => 'The CSV file couldn\'t be found under <b>/config/devices.csv.</b>',
|
||||
'BackDevices_Device_UpdDevError' => 'Error updating devices, try later. The database is probable locked due to an ongoing task.',
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////
|
||||
@@ -469,6 +475,14 @@ $lang['en_us'] = array(
|
||||
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////
|
||||
// Plugins
|
||||
//////////////////////////////////////////////////////////////////
|
||||
|
||||
'Plugins_Unprocessed_Events' => 'Unprocessed Events',
|
||||
'Plugins_Objects' => 'Plugin Objects',
|
||||
'Plugins_History' => 'Events History',
|
||||
|
||||
//////////////////////////////////////////////////////////////////
|
||||
// Settings
|
||||
//////////////////////////////////////////////////////////////////
|
||||
@@ -482,23 +496,16 @@ $lang['en_us'] = array(
|
||||
|
||||
|
||||
//General
|
||||
'General_settings_group' => '<i class="fa fa-gears"></i> General',
|
||||
'General_display_name' => 'General',
|
||||
'General_icon' => '<i class="fa fa-gears"></i>',
|
||||
'ENABLE_ARPSCAN_name' => 'Enable ARP scan',
|
||||
'ENABLE_ARPSCAN_description' => 'Arp-scan is a command-line tool that uses the ARP protocol to discover and fingerprint IP hosts on the local network. An alternative to ARP scan is to enable the <a onclick="toggleAllSettings()" href="#PIHOLE_ACTIVE"><code>PIHOLE_ACTIVE</code>PiHole integration settings</a>.',
|
||||
'SCAN_SUBNETS_name' => 'Subnets to scan',
|
||||
'SCAN_SUBNETS_description' => '
|
||||
The arp-scan time itself depends on the number of IP addresses to check.
|
||||
The number of IPs to check depends on the <a target="_blank" href="https://www.calculator.net/ip-subnet-calculator.html">network mask</a> you set here.
|
||||
For example, a <code>/24</code> mask results in 256 IPs to check, where as a <code>/16</code>
|
||||
mask checks around 65,536. Every IP takes a couple seconds. This means that with an incorrect configuration
|
||||
the arp-scan will take hours to complete instead of seconds.
|
||||
<ol>
|
||||
<li>Specify the network mask. For example, the filter <code>192.168.1.0/24</code> covers IP ranges 192.168.1.0 to 192.168.1.255.</li>
|
||||
<li>Run <code>iwconfig</code> in your container to find your interface name(s) (e.g.: <code>eth0</code>, <code>eth1</code>)</li>
|
||||
</ol>
|
||||
The arp-scan time itself depends on the number of IP addresses to check so set this up carefully with the appropriate network mask and interface. Check the <a href="https://github.com/jokob-sk/Pi.Alert/blob/main/docs/SUBNETS.md" target="_blank">subnets documentation</a> for details.
|
||||
',
|
||||
'PRINT_LOG_name' => 'Print additional logging',
|
||||
'PRINT_LOG_description' => 'This setting will enable more verbose logging. Useful for debugging events writing into the database.',
|
||||
'LOG_LEVEL_name' => 'Print additional logging',
|
||||
'LOG_LEVEL_description' => 'This setting will enable more verbose logging. Useful for debugging events writing into the database.',
|
||||
'TIMEZONE_name' => 'Time zone',
|
||||
'TIMEZONE_description' => 'Time zone to display stats correctly. Find your time zone <a target="_blank" href="https://en.wikipedia.org/wiki/List_of_tz_database_time_zones" rel="nofollow">here</a>.',
|
||||
'PIALERT_WEB_PROTECTION_name' => 'Enable login',
|
||||
@@ -508,9 +515,9 @@ the arp-scan will take hours to complete instead of seconds.
|
||||
'INCLUDED_SECTIONS_name' => 'Notify on',
|
||||
'INCLUDED_SECTIONS_description' => 'Specifies which events trigger notifications. Remove the event type(s) you don\'t want to get notified on. This setting overrides device-specific settings in the UI. (<code>CTRL + Click</code> to select / deselect).',
|
||||
'SCAN_CYCLE_MINUTES_name' => 'Scan cycle delay',
|
||||
'SCAN_CYCLE_MINUTES_description' => 'The delay between scans. If using arp-scan, the scan time itself depends on the number of IP addresses to check. This is influenced by the network mask set in the <a href="#SCAN_SUBNETS"><code>SCAN_SUBNETS</code> setting</a> at the top. Every IP takes a couple seconds to scan.',
|
||||
'SCAN_CYCLE_MINUTES_description' => 'The delay between scans in minutes. If using arp-scan, the scan time itself depends on the number of IP addresses to check. This is influenced by the network mask set in the <a href="#SCAN_SUBNETS"><code>SCAN_SUBNETS</code> setting</a> at the top. Every IP takes a couple seconds to scan.',
|
||||
'DAYS_TO_KEEP_EVENTS_name' => 'Delete events older than',
|
||||
'DAYS_TO_KEEP_EVENTS_description' => 'This is a maintenance setting. This specifies the number of days worth of event entries that will be kept. All older events will be deleted periodically.',
|
||||
'DAYS_TO_KEEP_EVENTS_description' => 'This is a maintenance setting. This specifies the number of days worth of event entries that will be kept. All older events will be deleted periodically. Also applies on Plugin Events History.',
|
||||
'REPORT_DASHBOARD_URL_name' => 'Pi.Alert URL',
|
||||
'REPORT_DASHBOARD_URL_description' => 'This URL is used as the base for generating links in the emails. Enter full URL starting with <code>http://</code> including the port number (no trailig slash <code>/</code>).',
|
||||
'DIG_GET_IP_ARG_name' => 'Internet IP discovery',
|
||||
@@ -519,7 +526,8 @@ the arp-scan will take hours to complete instead of seconds.
|
||||
'UI_LANG_description' => 'Select the preferred UI language.',
|
||||
|
||||
//Email
|
||||
'Email_settings_group' => '<i class="fa fa-at"></i> Email',
|
||||
'Email_display_name' => 'Email',
|
||||
'Email_icon' => '<i class="fa fa-at"></i>',
|
||||
'REPORT_MAIL_name' => 'Enable email',
|
||||
'REPORT_MAIL_description' => 'If enabled an email is sent out with a list of changes you\'ve subscribed to. Please also fill out all remaining settings related to the SMTP setup below.',
|
||||
'SMTP_SERVER_name' => 'SMTP server URL',
|
||||
@@ -542,7 +550,8 @@ the arp-scan will take hours to complete instead of seconds.
|
||||
'REPORT_FROM_description' => 'Notification email subject line.',
|
||||
|
||||
//Webhooks
|
||||
'Webhooks_settings_group' => '<i class="fa fa-circle-nodes"></i> Webhooks',
|
||||
'Webhooks_display_name' => 'Webhooks',
|
||||
'Webhooks_icon' => '<i class="fa fa-circle-nodes"></i>',
|
||||
'REPORT_WEBHOOK_name' => 'Enable Webhooks',
|
||||
'REPORT_WEBHOOK_description' => 'Enable webhooks for notifications. Webhooks help you to connect to a lot of 3rd party tools, such as IFTTT, Zapier or <a href="https://n8n.io/" target="_blank">n8n</a> to name a few. Check out this simple <a href="https://github.com/jokob-sk/Pi.Alert/blob/main/docs/WEBHOOK_N8N.md" target="_blank">n8n guide here</a> to get started. If enabled, configure related settings below.',
|
||||
'WEBHOOK_URL_name' => 'Target URL',
|
||||
@@ -553,7 +562,8 @@ the arp-scan will take hours to complete instead of seconds.
|
||||
'WEBHOOK_REQUEST_METHOD_description' => 'The HTTP request method to be used for the webhook call.',
|
||||
|
||||
// Apprise
|
||||
'Apprise_settings_group' => '<i class="fa fa-bullhorn"></i> Apprise',
|
||||
'Apprise_display_name' => 'Apprise',
|
||||
'Apprise_icon' => '<i class="fa fa-bullhorn"></i>',
|
||||
'REPORT_APPRISE_name' => 'Enable Apprise',
|
||||
'REPORT_APPRISE_description' => 'Enable sending notifications via <a target="_blank" href="https://hub.docker.com/r/caronc/apprise">Apprise</a>.',
|
||||
'APPRISE_HOST_name' => 'Apprise host URL',
|
||||
@@ -562,7 +572,8 @@ the arp-scan will take hours to complete instead of seconds.
|
||||
'APPRISE_URL_description' => 'Apprise notification target URL. For example for Telegram it would be <code>tgram://{bot_token}/{chat_id}</code>.',
|
||||
|
||||
// NTFY
|
||||
'NTFY_settings_group' => '<i class="fa fa-terminal"></i> NTFY',
|
||||
'NTFY_display_name' => 'NTFY',
|
||||
'NTFY_icon' => '<i class="fa fa-terminal"></i>',
|
||||
'REPORT_NTFY_name' => 'Enable NTFY',
|
||||
'REPORT_NTFY_description' => 'Enable sending notifications via <a target="_blank" href="https://ntfy.sh/">NTFY</a>.',
|
||||
'NTFY_HOST_name' => 'NTFY host URL',
|
||||
@@ -575,7 +586,8 @@ the arp-scan will take hours to complete instead of seconds.
|
||||
'NTFY_PASSWORD_description' => 'Enter password if you need (host) an instance with enabled authetication.',
|
||||
|
||||
// Pushsafer
|
||||
'PUSHSAFER_settings_group' => '<i class="fa fa-bell"></i> Pushsafer',
|
||||
'PUSHSAFER_display_name' => 'Pushsafer',
|
||||
'PUSHSAFER_icon' => '<i class="fa fa-bell"></i>',
|
||||
'REPORT_PUSHSAFER_name' => 'Enable Pushsafer',
|
||||
'REPORT_PUSHSAFER_description' => 'Enable sending notifications via <a target="_blank" href="https://www.pushsafer.com/">Pushsafer</a>.',
|
||||
'PUSHSAFER_TOKEN_name' => 'Pushsafer token',
|
||||
@@ -585,7 +597,8 @@ the arp-scan will take hours to complete instead of seconds.
|
||||
|
||||
|
||||
// MQTT
|
||||
'MQTT_settings_group' => '<i class="fa fa-square-rss"></i> MQTT',
|
||||
'MQTT_display_name' => 'MQTT',
|
||||
'MQTT_icon' => '<i class="fa fa-square-rss"></i>',
|
||||
'REPORT_MQTT_name' => 'Enable MQTT',
|
||||
'REPORT_MQTT_description' => 'Enable sending notifications via <a target="_blank" href="https://www.home-assistant.io/integrations/mqtt/">MQTT</a> to your Home Assistance instance.',
|
||||
'MQTT_BROKER_name' => 'MQTT broker URL',
|
||||
@@ -602,7 +615,8 @@ the arp-scan will take hours to complete instead of seconds.
|
||||
'MQTT_DELAY_SEC_description' => 'A little hack - delay adding to the queue in case the process is restarted and previous publish processes aborted (it takes ~<code>2</code>s to update a sensor config on the broker). Tested with <code>2</code>-<code>3</code> seconds of delay. This delay is only applied when devices are created (during the first notification loop). It doesn\'t affect subsequent scans or notifications.',
|
||||
|
||||
//DynDNS
|
||||
'DynDNS_settings_group' => '<i class="fa fa-globe"></i> DynDNS',
|
||||
'DynDNS_display_name' => 'DynDNS',
|
||||
'DynDNS_icon' => '<i class="fa fa-globe"></i>',
|
||||
'DDNS_ACTIVE_name' => 'Enable DynDNS',
|
||||
'DDNS_ACTIVE_description' => '',
|
||||
'DDNS_DOMAIN_name' => 'DynDNS domain URL',
|
||||
@@ -615,14 +629,16 @@ the arp-scan will take hours to complete instead of seconds.
|
||||
'DDNS_UPDATE_URL_description' => 'Update URL starting with <code>http://</code> or <code>https://</code>.',
|
||||
|
||||
// PiHole
|
||||
'PiHole_settings_group' => '<i class="fa fa-seedling"></i> PiHole',
|
||||
'PiHole_display_name' => 'PiHole',
|
||||
'PiHole_icon' => '<i class="fa fa-seedling"></i>',
|
||||
'PIHOLE_ACTIVE_name' => 'Enable PiHole mapping',
|
||||
'PIHOLE_ACTIVE_description' => 'You need to map<code>:/etc/pihole/pihole-FTL.db</code> in the <code>docker-compose.yml</code> file if you enable this setting.',
|
||||
'DHCP_ACTIVE_name' => 'Enable PiHole DHCP',
|
||||
'DHCP_ACTIVE_description' => 'You need to map <code>:/etc/pihole/dhcp.leases</code> in the <code>docker-compose.yml</code> file if you enable this setting.',
|
||||
|
||||
// Pholus
|
||||
'Pholus_settings_group' => '<i class="fa fa-search"></i> Pholus',
|
||||
'Pholus_display_name' => 'Pholus',
|
||||
'Pholus_icon' => '<i class="fa fa-search"></i>',
|
||||
'PHOLUS_ACTIVE_name' => 'Cycle run',
|
||||
'PHOLUS_ACTIVE_description' => '<a href="https://github.com/jokob-sk/Pi.Alert/tree/main/pholus" target="_blank" >Pholus</a> is a sniffing tool to discover additional information about the devices on the network, including the device name. If enabled this will execute the scan before every network scan cycle until there are no <code>(unknown)</code> or <code>(name not found)</code> devices. Please be aware it can spam the network with unnecessary traffic. Depends on the <a onclick="toggleAllSettings()" href="#SCAN_SUBNETS"><code>SCAN_SUBNETS</code> setting</a>. For a scheduled or one-off scan, check the <a href="#PHOLUS_RUN"><code>PHOLUS_RUN</code> setting</a>.',
|
||||
'PHOLUS_TIMEOUT_name' => 'Cycle run timeout',
|
||||
@@ -637,10 +653,11 @@ the arp-scan will take hours to complete instead of seconds.
|
||||
'PHOLUS_RUN_SCHD_description' => 'Only enabled if you select <code>schedule</code> in the <a href="#PHOLUS_RUN"><code>PHOLUS_RUN</code> setting</a>. Make sure you enter the schedule in the correct cron-like format
|
||||
(e.g. validate at <a href="https://crontab.guru/" target="_blank">crontab.guru</a>). For example entering <code>0 4 * * *</code> will run the scan after 4 am in the <a onclick="toggleAllSettings()" href="#TIMEZONE"><code>TIMEZONE</code> you set above</a>. Will be run NEXT time the time passes.',
|
||||
'PHOLUS_DAYS_DATA_name' => 'Data retention',
|
||||
'PHOLUS_DAYS_DATA_description' => 'How many days of Pholus scan entries should be kept (globally, not device specific!). The <a href="/maintenance.php#tab_Logging">pialert_pholus.log</a> file is not touched. Enter <code>0</code> to disable.',
|
||||
'PHOLUS_DAYS_DATA_description' => 'How many days of Pholus scan entries should be kept (globally, not device specific!) Enter <code>0</code> to disable.',
|
||||
|
||||
// Nmap
|
||||
'Nmap_settings_group' => '<i class="fa fa-ethernet"></i> Nmap',
|
||||
'Nmap_display_name' => 'Nmap',
|
||||
'Nmap_icon' => '<i class="fa fa-ethernet"></i>',
|
||||
'NMAP_ACTIVE_name' => 'Cycle run',
|
||||
'NMAP_ACTIVE_description' => 'If enabled this will execute a scan on a newly found device. For a scheduled or one-off scan, check the <a href="#NMAP_RUN"><code>NMAP_RUN</code> setting</a>.',
|
||||
'NMAP_TIMEOUT_name' => 'Run timeout',
|
||||
@@ -653,20 +670,15 @@ the arp-scan will take hours to complete instead of seconds.
|
||||
'NMAP_ARGS_description' => 'Arguments used to run the Nmap scan. Be careful to specify <a href="https://linux.die.net/man/1/nmap" target="_blank">the arguments</a> correctly. For example <code>-p -10000</code> scans ports from 1 to 10000.',
|
||||
|
||||
// API
|
||||
'API_settings_group' => '<i class="fa fa-arrow-down-up-across-line"></i> API',
|
||||
'ENABLE_API_name' => 'Enable API',
|
||||
'ENABLE_API_description' => 'If enabled the app will start publishing and updating <a href="https://github.com/jokob-sk/Pi.Alert/blob/main/docs/API.md" target="_blank">simple API endpoints</a> under the <code>/home/pi/pialert/front/api/</code> folder and thus on the <code>pialert_url/api/File_name</code> url.',
|
||||
'API_RUN_name' => 'Scheduling updates',
|
||||
'API_RUN_description' => 'Scheduling settings to specify when the API endpoints should be updated. If set to <code>schedule</code> then endpoints will be updated on a specified cron-like schedule specified by the <code>API_RUN_SCHD</code> setting. Otherwise if set to <code>interval</code> endpoints will be updated every N seconds specified by the <code>API_RUN_INTERVAL</code> setting.',
|
||||
'API_RUN_SCHD_name' => 'Schedule',
|
||||
'API_RUN_SCHD_description' => 'Depends on the <code>API_RUN</code> settings to be set to <code>schedule</code>. Make sure you enter the schedule in the correct cron-like format (e.g. validate at <a href="https://crontab.guru/" target="_blank">crontab.guru</a>).',
|
||||
'API_RUN_INTERVAL_name' => 'Update interval',
|
||||
'API_RUN_INTERVAL_description' => 'Depends on the <code>API_RUN</code> settings to be set to <code>interval</code>. The minimum cycle is <code>5</code> seconds.',
|
||||
'API_display_name' => 'API',
|
||||
'API_icon' => '<i class="fa fa-arrow-down-up-across-line"></i>',
|
||||
'API_CUSTOM_SQL_name' => 'Custom endpoint',
|
||||
'API_CUSTOM_SQL_description' => 'You can specify a custom SQL query which will generate a JSON file and then expose it via the <a href="/api/table_custom_endpoint.json" target="_blank"><code>table_custom_endpoint.json</code> file endpoint</a>.',
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
);
|
||||
|
||||
?>
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
// ###################################
|
||||
|
||||
$defaultLang = "en_us";
|
||||
$allLanguages = ["en_us","es_es","de_de"];
|
||||
|
||||
global $db;
|
||||
|
||||
@@ -17,6 +18,20 @@ switch($result){
|
||||
|
||||
if (isset($pia_lang_selected) == FALSE or (strlen($pia_lang_selected) == 0)) {$pia_lang_selected = $defaultLang;}
|
||||
|
||||
//Language_Strings ("Language_Code", "String_Key", "String_Value", "Extra")
|
||||
$result = $db->query("SELECT * FROM Plugins_Language_Strings");
|
||||
|
||||
// array
|
||||
$strings = array();
|
||||
while ($row = $result -> fetchArray (SQLITE3_ASSOC)) {
|
||||
// Push row data
|
||||
$strings[] = array( 'Language_Code' => $row['Language_Code'],
|
||||
'String_Key' => $row['String_Key'],
|
||||
'String_Value' => $row['String_Value'],
|
||||
'Extra' => $row['Extra']
|
||||
);
|
||||
}
|
||||
|
||||
require dirname(__FILE__).'/../skinUI.php';
|
||||
require dirname(__FILE__).'/en_us.php';
|
||||
require dirname(__FILE__).'/de_de.php';
|
||||
@@ -24,7 +39,13 @@ require dirname(__FILE__).'/es_es.php';
|
||||
|
||||
function lang($key)
|
||||
{
|
||||
global $pia_lang_selected, $lang, $defaultLang;
|
||||
global $pia_lang_selected, $lang, $defaultLang, $strings;
|
||||
|
||||
// get strings from the DB and append them to the ones from the files
|
||||
foreach ($strings as $string)
|
||||
{
|
||||
$lang[$string["Language_Code"]][$string["String_Key"]] = $string["String_Value"];
|
||||
}
|
||||
|
||||
// check if key exists in selected language
|
||||
if(array_key_exists($key, $lang[$pia_lang_selected]) == FALSE)
|
||||
|
||||
462
front/plugins.php
Executable file
@@ -0,0 +1,462 @@
|
||||
<?php
|
||||
|
||||
require 'php/templates/header.php';
|
||||
?>
|
||||
|
||||
<script src="js/pialert_common.js"></script>
|
||||
|
||||
<!-- Page ------------------------------------------------------------------ -->
|
||||
<div class="content-wrapper">
|
||||
|
||||
<!-- Content header--------------------------------------------------------- -->
|
||||
<section class="content-header">
|
||||
<?php require 'php/templates/notification.php'; ?>
|
||||
<h1 id="pageTitle">
|
||||
<i class="fa fa-fw fa-plug"></i> <?= lang('Navigation_Plugins');?>
|
||||
<span class="pageHelp"> <a target="_blank" href="https://github.com/jokob-sk/Pi.Alert/tree/main/front/plugins"><i class="fa fa-circle-question"></i></a><span>
|
||||
</h1>
|
||||
</section>
|
||||
|
||||
<!-- Main content ---------------------------------------------------------- -->
|
||||
<section class="content">
|
||||
<div class="nav-tabs-custom plugin-content" style="margin-bottom: 0px;">
|
||||
<ul id="tabs-location" class="nav nav-tabs">
|
||||
<!-- PLACEHOLDER -->
|
||||
</ul>
|
||||
<div id="tabs-content-location" class="tab-content">
|
||||
<!-- PLACEHOLDER -->
|
||||
</div>
|
||||
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<?php
|
||||
require 'php/templates/footer.php';
|
||||
?>
|
||||
|
||||
<script defer>
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Get form control according to the column definition from config.json > database_column_definitions
|
||||
function getFormControl(dbColumnDef, value, index) {
|
||||
|
||||
result = ''
|
||||
|
||||
switch(dbColumnDef.type)
|
||||
{
|
||||
case 'label':
|
||||
result = `<span>${value}<span>`;
|
||||
break;
|
||||
case 'textboxsave':
|
||||
|
||||
value = value == 'null' ? '' : value; // hide 'null' values
|
||||
|
||||
id = `${dbColumnDef.column}_${index}`
|
||||
|
||||
result = `<span class="form-group">
|
||||
<div class="input-group">
|
||||
<input class="form-control" type="text" value="${value}" id="${id}" data-my-column="${dbColumnDef.column}" data-my-index="${index}" name="${dbColumnDef.column}">
|
||||
<span class="input-group-addon"><i class="fa fa-save pointer" onclick="saveData('${id}');"></i></span>
|
||||
</div>
|
||||
<span>`;
|
||||
break;
|
||||
case 'url':
|
||||
result = `<span><a href="${value}" target="_blank">${value}</a><span>`;
|
||||
break;
|
||||
case 'devicemac':
|
||||
result = `<span class="anonymizeMac"><a href="/deviceDetails.php?mac=${value}" target="_blank">${value}</a><span>`;
|
||||
break;
|
||||
case 'threshold':
|
||||
$.each(dbColumnDef.options, function(index, obj) {
|
||||
if(Number(value) < obj.maximum && result == '')
|
||||
{
|
||||
result = `<div style="background-color:${obj.hexColor}">${value}</div>`
|
||||
// return;
|
||||
}
|
||||
});
|
||||
break;
|
||||
case 'replace':
|
||||
$.each(dbColumnDef.options, function(index, obj) {
|
||||
if(value == obj.equals)
|
||||
{
|
||||
result = `<span title="${value}">${obj.replacement}</span>`
|
||||
}
|
||||
});
|
||||
break;
|
||||
default:
|
||||
result = value;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Update the coresponding DB column and entry
|
||||
function saveData (id) {
|
||||
columnName = $(`#${id}`).attr('data-my-column')
|
||||
index = $(`#${id}`).attr('data-my-index')
|
||||
columnValue = $(`#${id}`).val()
|
||||
|
||||
$.get(`php/server/dbHelper.php?action=update&dbtable=Plugins_Objects&columnName=Index&id=${index}&columns=UserData&values=${columnValue}`, function(data) {
|
||||
|
||||
// var result = JSON.parse(data);
|
||||
console.log(data)
|
||||
|
||||
if(sanitize(data) == 'OK')
|
||||
{
|
||||
showMessage('<?= lang('Gen_DataUpdatedUITakesTime');?>')
|
||||
// Remove navigation prompt "Are you sure you want to leave..."
|
||||
window.onbeforeunload = null;
|
||||
} else
|
||||
{
|
||||
showMessage('<?= lang('Gen_LockedDB');?>')
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Get translated string
|
||||
function localize (obj, key) {
|
||||
|
||||
currLangCode = getCookie("language")
|
||||
|
||||
result = ""
|
||||
en_us = ""
|
||||
|
||||
if(obj.localized && obj.localized.includes(key))
|
||||
{
|
||||
for(i=0;i<obj[key].length;i++)
|
||||
{
|
||||
code = obj[key][i]["language_code"]
|
||||
|
||||
// console.log(code)
|
||||
|
||||
if( code == 'en_us')
|
||||
{
|
||||
en_us = obj[key][i]["string"]
|
||||
}
|
||||
|
||||
if(code == currLangCode)
|
||||
{
|
||||
result = obj[key][i]["string"]
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
result == "" ? en_us : result ;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
pluginDefinitions = []
|
||||
pluginUnprocessedEvents = []
|
||||
pluginObjects = []
|
||||
pluginHistory = []
|
||||
|
||||
function getData(){
|
||||
|
||||
$.get('api/plugins.json', function(res) {
|
||||
|
||||
pluginDefinitions = res["data"];
|
||||
|
||||
$.get('api/table_plugins_events.json', function(res) {
|
||||
|
||||
pluginUnprocessedEvents = res["data"];
|
||||
|
||||
$.get('api/table_plugins_objects.json', function(res) {
|
||||
|
||||
pluginObjects = res["data"];
|
||||
|
||||
$.get('api/table_plugins_history.json', function(res) {
|
||||
|
||||
pluginHistory = res["data"];
|
||||
|
||||
generateTabs()
|
||||
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
function generateTabs()
|
||||
{
|
||||
activetab = 'active'
|
||||
|
||||
$.each(pluginDefinitions, function(index, obj) {
|
||||
$('#tabs-location').append(
|
||||
`<li class=" ${activetab}">
|
||||
<a href="#${obj.unique_prefix}" data-plugin-prefix="${obj.unique_prefix}" id="${obj.unique_prefix}_id" data-toggle="tab" >
|
||||
${localize(obj, 'icon')} ${localize(obj, 'display_name')}
|
||||
</a>
|
||||
</li>`
|
||||
);
|
||||
activetab = '' // only first tab is active
|
||||
});
|
||||
|
||||
activetab = 'active'
|
||||
|
||||
$.each(pluginDefinitions, function(index, obj) {
|
||||
|
||||
headersHtml = ""
|
||||
colDefinitions = []
|
||||
evRows = ""
|
||||
obRows = ""
|
||||
hiRows = ""
|
||||
|
||||
// Generate the header
|
||||
$.each(obj["database_column_definitions"], function(index, colDef){
|
||||
if(colDef.show == true) // select only the ones to show
|
||||
{
|
||||
colDefinitions.push(colDef)
|
||||
headersHtml += `<th class="${colDef.css_classes}" >${localize(colDef, "name" )}</th>`
|
||||
}
|
||||
});
|
||||
|
||||
// Generate the event rows
|
||||
var eveCount = 0;
|
||||
for(i=0;i<pluginUnprocessedEvents.length;i++)
|
||||
{
|
||||
if(pluginUnprocessedEvents[i].Plugin == obj.unique_prefix)
|
||||
{
|
||||
clm = ""
|
||||
|
||||
for(j=0;j<colDefinitions.length;j++)
|
||||
{
|
||||
clm += '<td>'+ pluginUnprocessedEvents[i][colDefinitions[j].column] +'</td>'
|
||||
}
|
||||
evRows += `<tr data-my-index="${pluginUnprocessedEvents[i]["Index"]}" >${clm}</tr>`
|
||||
eveCount++;
|
||||
}
|
||||
}
|
||||
|
||||
// Generate the history rows
|
||||
var histCount = 0
|
||||
var histCountDisplayed = 0
|
||||
|
||||
for(i=pluginHistory.length-1;i >= 0;i--) // from latest to the oldest
|
||||
{
|
||||
if(pluginHistory[i].Plugin == obj.unique_prefix)
|
||||
{
|
||||
if(histCount < 50) // only display 50 entries to optimize performance
|
||||
{
|
||||
clm = ""
|
||||
|
||||
for(j=0;j<colDefinitions.length;j++)
|
||||
{
|
||||
clm += '<td>'+ pluginHistory[i][colDefinitions[j].column] +'</td>'
|
||||
}
|
||||
hiRows += `<tr data-my-index="${pluginHistory[i]["Index"]}" >${clm}</tr>`
|
||||
|
||||
histCountDisplayed++;
|
||||
}
|
||||
histCount++; // count and display the total
|
||||
}
|
||||
}
|
||||
|
||||
// Generate the object rows
|
||||
var obCount = 0;
|
||||
for(var i=0;i<pluginObjects.length;i++)
|
||||
{
|
||||
if(pluginObjects[i].Plugin == obj.unique_prefix)
|
||||
{
|
||||
clm = ""
|
||||
|
||||
for(var j=0;j<colDefinitions.length;j++)
|
||||
{
|
||||
clm += '<td>'+ getFormControl(colDefinitions[j], pluginObjects[i][colDefinitions[j].column], pluginObjects[i]["Index"]) +'</td>'
|
||||
}
|
||||
obRows += `<tr data-my-index="${pluginObjects[i]["Index"]}" >${clm}</tr>`
|
||||
obCount++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$('#tabs-content-location').append(
|
||||
`
|
||||
<div id="${obj.unique_prefix}" class="tab-pane ${activetab}">
|
||||
<div class="nav-tabs-custom" style="margin-bottom: 0px">
|
||||
<ul class="nav nav-tabs">
|
||||
<li class="active" >
|
||||
<a href="#objectsTarget_${obj.unique_prefix}" data-toggle="tab" >
|
||||
|
||||
<i class="fa fa-cube"></i> <?= lang('Plugins_Objects');?> (${obCount})
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li >
|
||||
<a href="#eventsTarget_${obj.unique_prefix}" data-toggle="tab" >
|
||||
|
||||
<i class="fa fa-bolt"></i> <?= lang('Plugins_Unprocessed_Events');?> (${eveCount})
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li >
|
||||
<a href="#historyTarget_${obj.unique_prefix}" data-toggle="tab" >
|
||||
|
||||
<i class="fa fa-clock"></i> <?= lang('Plugins_History');?> (${histCountDisplayed} out of ${histCount} )
|
||||
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<ul>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="tab-content">
|
||||
|
||||
<div id="objectsTarget_${obj.unique_prefix}" class="tab-pane ${activetab}">
|
||||
<table class="table table-striped" data-my-dbtable="Plugins_Objects">
|
||||
<tbody>
|
||||
<tr>
|
||||
${headersHtml}
|
||||
</tr>
|
||||
${obRows}
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="plugin-obj-purge">
|
||||
<button class="btn btn-primary" onclick="purgeAll('${obj.unique_prefix}', 'Plugins_Objects' )"><?= lang('Gen_DeleteAll');?></button>
|
||||
</div>
|
||||
</div>
|
||||
<div id="eventsTarget_${obj.unique_prefix}" class="tab-pane">
|
||||
<table class="table table-striped" data-my-dbtable="Plugins_Events">
|
||||
|
||||
<tbody>
|
||||
<tr>
|
||||
${headersHtml}
|
||||
</tr>
|
||||
${evRows}
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="plugin-obj-purge">
|
||||
<button class="btn btn-primary" onclick="purgeAll('${obj.unique_prefix}', 'Plugins_Events' )"><?= lang('Gen_DeleteAll');?></button>
|
||||
</div>
|
||||
</div>
|
||||
<div id="historyTarget_${obj.unique_prefix}" class="tab-pane">
|
||||
<table class="table table-striped" data-my-dbtable="Plugins_History">
|
||||
|
||||
<tbody>
|
||||
<tr>
|
||||
${headersHtml}
|
||||
</tr>
|
||||
${hiRows}
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="plugin-obj-purge">
|
||||
<button class="btn btn-primary" onclick="purgeAll('${obj.unique_prefix}', 'Plugins_History' )"><?= lang('Gen_DeleteAll');?></button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
${localize(obj, 'description')}
|
||||
|
||||
<span>
|
||||
<a href="https://github.com/jokob-sk/Pi.Alert/tree/main/front/plugins/${obj.code_name}" target="_blank"><?= lang('Gen_Help');?></a>
|
||||
</span>
|
||||
|
||||
</div>
|
||||
`);
|
||||
|
||||
activetab = '' // only first tab is active
|
||||
});
|
||||
|
||||
initTabs()
|
||||
}
|
||||
|
||||
// --------------------------------------------------------
|
||||
// handle first tab (objectsTarget_) display
|
||||
function initTabs()
|
||||
{
|
||||
// events on tab change
|
||||
$('a[data-toggle="tab"]').on('shown.bs.tab', function (e) {
|
||||
var target = $(e.target).attr("href") // activated tab
|
||||
|
||||
// save the last prefix
|
||||
if(target.includes('_') == false )
|
||||
{
|
||||
pref = target.split('#')[1]
|
||||
} else
|
||||
{
|
||||
pref = target.split('_')[1]
|
||||
}
|
||||
|
||||
everythingHidden = false;
|
||||
|
||||
if($('#objectsTarget_'+ pref) != undefined && $('#historyTarget_'+ pref) != undefined && $('#eventsTarget_'+ pref) != undefined)
|
||||
{
|
||||
everythingHidden = $('#objectsTarget_'+ pref).attr('class').includes('active') == false && $('#historyTarget_'+ pref).attr('class').includes('active') == false && $('#eventsTarget_'+ pref).attr('class').includes('active') == false;
|
||||
}
|
||||
|
||||
// show the objectsTarget if no specific pane selected or if selected is hidden
|
||||
if((target == '#'+pref ) && everythingHidden)
|
||||
{
|
||||
var classTmp = $('#objectsTarget_'+ pref).attr('class');
|
||||
|
||||
if($('#objectsTarget_'+ pref).attr('class').includes('active') == false)
|
||||
{
|
||||
classTmp += ' active';
|
||||
$('#objectsTarget_'+ pref).attr('class', classTmp)
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// --------------------------------------------------------
|
||||
plugPrefix = ''
|
||||
dbTable = ''
|
||||
|
||||
function purgeAll(callback) {
|
||||
plugPrefix = arguments[0]; // plugin prefix
|
||||
dbTable = arguments[1]; // DB table
|
||||
// Ask
|
||||
showModalWarning('<?= lang('Gen_Purge');?>' + ' ' + plugPrefix + ' ' + dbTable , '<?= lang('Gen_AreYouSure');?>',
|
||||
'<?= lang('Gen_Cancel');?>', '<?= lang('Gen_Okay');?>', "purgeAllExecute");
|
||||
}
|
||||
|
||||
// --------------------------------------------------------
|
||||
function purgeAllExecute() {
|
||||
$.ajax({
|
||||
method: "POST",
|
||||
url: "php/server/dbHelper.php",
|
||||
data: { action: "delete", dbtable: dbTable, columnName: 'Plugin', id:plugPrefix },
|
||||
success: function(data, textStatus) {
|
||||
showModalOk ('Result', data );
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
// --------------------------------------------------------
|
||||
function purgeVisible() {
|
||||
|
||||
idArr = $(`#${plugPrefix} table[data-my-dbtable="${dbTable}"] tr[data-my-index]`).map(function(){return $(this).attr("data-my-index");}).get();
|
||||
|
||||
$.ajax({
|
||||
method: "POST",
|
||||
url: "php/server/dbHelper.php",
|
||||
data: { action: "delete", dbtable: dbTable, columnName: 'Index', id:idArr.toString() },
|
||||
success: function(data, textStatus) {
|
||||
showModalOk ('Result', data );
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
getData()
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
329
front/plugins/README.md
Executable file
@@ -0,0 +1,329 @@
|
||||
# ⚠ Disclaimer
|
||||
|
||||
Highly experimental feature. Follow the below very carefully and check example plugin(s). Plugin UI is not my priority right now, happy to approve PRs if you are interested in extending/improvintg the UI experience (e.g. making the tables sortable/filterable).
|
||||
|
||||
## ❗ Known issues:
|
||||
|
||||
These issues will be hopefully fixed with time, so please don't report them. Instead, if you know how, feel free to investigate and submit a PR to fix the below. Keep the PRs small as it's easier to approve them:
|
||||
|
||||
* Existing plugin objects sometimes not interpreted correctly and a new object is created instead, resulting in duplicate entries.
|
||||
* Occasional (experienced twice) hanging of processing plugin script file.
|
||||
* UI displaying outdated values until the API endpoints get refreshed.
|
||||
|
||||
## Overview
|
||||
|
||||
PiAlert comes with a plugin system to feed events from third-party scripts into the UI and then send notifications if desired.
|
||||
|
||||
If you wish to develop a plugin, please check the existing plugin structure. Once the settings are saved by the user they need to be removed from the `pialert.conf` file manually if you want to re-initialize them from the `config.json` of the plugin.
|
||||
|
||||
Again, please read the below carefully if you'd like to contribute with a plugin yourself. This documentation file might be outdated, so double check the sample plugins as well.
|
||||
|
||||
## Plugin file structure overview
|
||||
|
||||
> Folder name must be the same as the code name value in: `"code_name": "<value>"`
|
||||
> Unique prefix needs to be unique compared to the other settings prefixes, e.g.: the prefix `APPRISE` is already in use.
|
||||
|
||||
| File | Required | Description |
|
||||
|----------------------|----------------------|----------------------|
|
||||
| `config.json` | yes | Contains the plugin configuration (manifest) including the settings available to the user. |
|
||||
| `script.py` | yes (script) | The Python script itself |
|
||||
| `last_result.log` | yes (script) | The file used to interface between PiAlert and the plugin (script). |
|
||||
| `script.log` | no | Logging output (recommended) |
|
||||
| `README.md` | no | Any setup considerations or overview (recommended) |
|
||||
|
||||
More on specifics below.
|
||||
|
||||
## Supported data sources
|
||||
|
||||
Currently only two data sources are supported:
|
||||
|
||||
- Script
|
||||
- SQL query on the PiAlert database
|
||||
|
||||
You need to set the `data_source` to either `pialert-db-query` or `python-script`:
|
||||
|
||||
```json
|
||||
"data_source": "pialert-db-query"
|
||||
```
|
||||
Any of the above datasources have to return a "table" of the exact structure as outlined below.
|
||||
|
||||
### Column order and values
|
||||
|
||||
| Order | Represented Column | Required | Description |
|
||||
|----------------------|----------------------|----------------------|----------------------|
|
||||
| 0 | `Object_PrimaryID` | yes | The primary ID used to group Events under. |
|
||||
| 1 | `Object_SecondaryID` | no | Optionalsecondary ID to create a relationship beween other entities, such as a MAC address |
|
||||
| 2 | `DateTime` | yes | When the event occured in the format `2023-01-02 15:56:30` |
|
||||
| 3 | `Watched_Value1` | yes | A value that is watched and users can receive notifications if it changed compared to the previously saved entry. For example IP address |
|
||||
| 4 | `Watched_Value2` | no | As above |
|
||||
| 5 | `Watched_Value3` | no | As above |
|
||||
| 6 | `Watched_Value4` | no | As above |
|
||||
| 7 | `Extra` | no | Any other data you want to pass and display in PiAlert and the notifications |
|
||||
| 8 | `ForeignKey` | no | A foreign key that can be used to link to the parent object (usually a MAC address) |
|
||||
|
||||
### "data_source": "python-script"
|
||||
|
||||
Used to interface between PiAlert and the plugin (script). After every scan it should contain only the results from the latest scan/execution.
|
||||
|
||||
- The format is a `csv`-like file with the pipe `|` separator. 8 (eight) values need to be supplied, so every line needs to contain 7 pipe separators. Empty values are represented by `null`
|
||||
- Don't render "headers" for these "columns"
|
||||
- Every scan result / event entry needs to be on a new line
|
||||
- You can find which "columns" need to be present in the script results and if the value is required below.
|
||||
- The order of these "columns" can't be changed
|
||||
|
||||
|
||||
#### Examples
|
||||
|
||||
Valid CSV:
|
||||
|
||||
```csv
|
||||
|
||||
https://www.google.com|null|2023-01-02 15:56:30|200|0.7898|null|null|null|null
|
||||
https://www.duckduckgo.com|192.168.0.1|2023-01-02 15:56:30|200|0.9898|null|null|Best search engine|ff:ee:ff:11:ff:11
|
||||
|
||||
```
|
||||
|
||||
Invalid CSV with different errors on each line:
|
||||
|
||||
```csv
|
||||
|
||||
https://www.google.com|null|2023-01-02 15:56:30|200|0.7898||null|null|null
|
||||
https://www.duckduckgo.com|null|2023-01-02 15:56:30|200|0.9898|null|null|Best search engine|
|
||||
|https://www.duckduckgo.com|null|2023-01-02 15:56:30|200|0.9898|null|null|Best search engine|null
|
||||
null|192.168.1.1|2023-01-02 15:56:30|200|0.9898|null|null|Best search engine
|
||||
https://www.duckduckgo.com|192.168.1.1|2023-01-02 15:56:30|null|0.9898|null|null|Best search engine
|
||||
https://www.google.com|null|2023-01-02 15:56:30|200|0.7898|||
|
||||
https://www.google.com|null|2023-01-02 15:56:30|200|0.7898|
|
||||
|
||||
```
|
||||
|
||||
### "data_source": "pialert-db-query"
|
||||
|
||||
If the datasource is set to `pialert-db-query` the `CMD` setting needs to contain a SQL query rendering the columns as defined in the "Column order and values" section above. The order of columns is important.
|
||||
|
||||
#### Examples
|
||||
|
||||
SQL query example:
|
||||
|
||||
```SQL
|
||||
SELECT dv.dev_Name as Object_PrimaryID,
|
||||
cast(dv.dev_LastIP as VARCHAR(100)) || ':' || cast( SUBSTR(ns.Port ,0, INSTR(ns.Port , '/')) as VARCHAR(100)) as Object_SecondaryID,
|
||||
datetime() as DateTime,
|
||||
ns.Service as Watched_Value1,
|
||||
ns.State as Watched_Value2,
|
||||
'null' as Watched_Value3,
|
||||
'null' as Watched_Value4,
|
||||
ns.Extra as Extra,
|
||||
dv.dev_MAC as ForeignKey
|
||||
FROM
|
||||
(SELECT * FROM Nmap_Scan) ns
|
||||
LEFT JOIN
|
||||
(SELECT dev_Name, dev_MAC, dev_LastIP FROM Devices) dv
|
||||
ON ns.MAC = dv.dev_MAC
|
||||
```
|
||||
|
||||
Required `CMD` setting example with above query (you can set `"type": "label"` if you want it to make uneditable in the UI):
|
||||
|
||||
```json
|
||||
{
|
||||
"function": "CMD",
|
||||
"type": "text",
|
||||
"default_value":"SELECT dv.dev_Name as Object_PrimaryID, cast(dv.dev_LastIP as VARCHAR(100)) || ':' || cast( SUBSTR(ns.Port ,0, INSTR(ns.Port , '/')) as VARCHAR(100)) as Object_SecondaryID, datetime() as DateTime, ns.Service as Watched_Value1, ns.State as Watched_Value2, 'null' as Watched_Value3, 'null' as Watched_Value4, ns.Extra as Extra FROM (SELECT * FROM Nmap_Scan) ns LEFT JOIN (SELECT dev_Name, dev_MAC, dev_LastIP FROM Devices) dv ON ns.MAC = dv.dev_MAC",
|
||||
"options": [],
|
||||
"localized": ["name", "description"],
|
||||
"name" : [{
|
||||
"language_code":"en_us",
|
||||
"string" : "SQL to run"
|
||||
}],
|
||||
"description": [{
|
||||
"language_code":"en_us",
|
||||
"string" : "This SQL query is used to populate the coresponding UI tables under the Plugins section."
|
||||
}]
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### config.json
|
||||
|
||||
#### params
|
||||
|
||||
- `"name":"name_value"` - is used as a wildcard replacement in the `CMD` setting value by using curly brackets `{name_value}`. The wildcard is replaced by the result of the `"value" : "param_value"` and `"type":"type_value"` combo configuration below.
|
||||
- `"type":"<sql|setting>"` - is used to specify the type of the params, currently only 2 supported (`sql`,`setting`).
|
||||
- `"type":"sql"` - will execute the SQL query specified in the `value` property. The sql query needs to return only one column. The column is flattened and separated by commas (`,`), e.g: `SELECT dev_MAC from DEVICES` -> `Internet,74:ac:74:ac:74:ac,44:44:74:ac:74:ac`. This is then used to replace the wildcards in the `CMD`setting.
|
||||
- `"type":"setting"` - The setting code name. A combination of the value from `unique_prefix` + `_` + `function` value, or otherwise the code name you can find in the Settings page under the Setting dispaly name, e.g. `SCAN_CYCLE_MINUTES`.
|
||||
- `"value" : "param_value"` - Needs to contain a setting code name or sql query without wildcards.
|
||||
|
||||
|
||||
Example:
|
||||
|
||||
```json
|
||||
{
|
||||
"params" : [{
|
||||
"name" : "macs",
|
||||
"type" : "sql",
|
||||
"value" : "SELECT dev_MAC from DEVICES"
|
||||
},
|
||||
{
|
||||
"name" : "urls",
|
||||
"type" : "setting",
|
||||
"value" : "WEBMON_urls_to_check"
|
||||
},
|
||||
{
|
||||
"name" : "internet_ip",
|
||||
"type" : "setting",
|
||||
"value" : "WEBMON_SQL_internet_ip"
|
||||
}]
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
#### Setting object struncture
|
||||
|
||||
- `"function": "<see Supported settings function values>"` - What function the setting drives or a simple unique code name
|
||||
- `"type": "<text|integer|boolean|password|readonly|selectinteger|selecttext|multiselect|list>"` - The form control used for the setting displayed in the Settings page and what values are accepted.
|
||||
- `"localized"` - a list of properties on the current JSON level which need to be localized
|
||||
- `"name"` and `"description"` - Displayed in the Settings page. An array of localized strings. (see Localized strings below).
|
||||
|
||||
##### Supported settings `function` values
|
||||
|
||||
- `RUN` - (required) Specifies when the service is executed
|
||||
- Supported Options: "disabled", "once", "schedule" (if included then a `RUN_SCHD` setting needs to be specified), "always_after_scan", "on_new_device"
|
||||
- `RUN_SCHD` - (required if you include the `RUN`) Cron-like scheduling used if the `RUN` setting set to `schedule`
|
||||
- `CMD` - (required) What command should be executed.
|
||||
- `API_SQL` - (optional) Generates a `table_` + code_name + `.json` file as per [API docs](https://github.com/jokob-sk/Pi.Alert/blob/main/docs/API.md).
|
||||
- `RUN_TIMEOUT` - (optional) Max execution time of the script. If not specified a default value of 10 seconds is used to prevent hanging.
|
||||
- `WATCH` - (optional) Which database columns are watched for changes for this particular plugin. If not specified no notifications are sent.
|
||||
- `REPORT_ON` - (optional) Send a notification only on these statuses. Supported options are:
|
||||
- `new` means a new unique (unique combination of PrimaryId and SecondaryId) object was discovered.
|
||||
- `watched-changed` - means that selected `Watched_ValueN` columns changed
|
||||
- `watched-not-changed` - reports even on events where selected `Watched_ValueN` did not change
|
||||
|
||||
|
||||
Example:
|
||||
|
||||
```json
|
||||
{
|
||||
"function": "RUN",
|
||||
"type": "selecttext",
|
||||
"default_value":"disabled",
|
||||
"options": ["disabled", "once", "schedule", "always_after_scan", "on_new_device"],
|
||||
"localized": ["name", "description"],
|
||||
"name" :[{
|
||||
"language_code":"en_us",
|
||||
"string" : "When to run"
|
||||
}],
|
||||
"description": [{
|
||||
"language_code":"en_us",
|
||||
"string" : "Enable a regular scan of your services. If you select <code>schedule</code> the scheduling settings from below are applied. If you select <code>once</code> the scan is run only once on start of the application (container) for the time specified in <a href=\"#WEBMON_RUN_TIMEOUT\"><code>WEBMON_RUN_TIMEOUT</code> setting</a>."
|
||||
}]
|
||||
}
|
||||
```
|
||||
##### Localized strings
|
||||
|
||||
- `"language_code":"<en_us|es_es|de_de>"` - code name of the language string. Only these three currently supported. At least the `"language_code":"en_us"` variant has to be defined.
|
||||
- `"string"` - The string to be displayed in the given language.
|
||||
|
||||
Example:
|
||||
|
||||
```json
|
||||
|
||||
{
|
||||
"language_code":"en_us",
|
||||
"string" : "When to run"
|
||||
}
|
||||
|
||||
```
|
||||
##### UI settings in database_column_definitions
|
||||
|
||||
The UI will adjust how columns are displayed in the UI based on the definition of the `database_column_definitions` object. Thease are the supported form controls and related functionality:
|
||||
|
||||
- Only columns with `"show": true` and also with at least an english translation will be shown in the UI.
|
||||
- Supported types: `label`, `text`, `threshold`, `replace`
|
||||
- `label` makes a column display only
|
||||
- `text` makes a column editable
|
||||
- See below for information on `threshold`, `replace`
|
||||
- The `options` property is used in conjunction with these types:
|
||||
- `threshold` - The `options` array contains objects from lowest `maximum` to highest with corresponding `hexColor` used for the value background color if it's less than the specified `maximum`, but more than the previous one in the `options` array
|
||||
- `replace` - The `options` array contains objects with an `equals` property, that is compared to the "value" and if the values are the same, the string in `replacement` is displayed in the UI instead of the actual "value"
|
||||
- `devicemac` - The value is considered to be a mac adress and a link pointing to the device with the given mac address is generated.
|
||||
- `url` - The value is considered to be a url so a link is generated.
|
||||
|
||||
|
||||
```json
|
||||
{
|
||||
"column": "Watched_Value1",
|
||||
"css_classes": "col-sm-2",
|
||||
"show": true,
|
||||
"type": "threshold",
|
||||
"default_value":"",
|
||||
"options": [
|
||||
{
|
||||
"maximum": 199,
|
||||
"hexColor": "#792D86"
|
||||
},
|
||||
{
|
||||
"maximum": 299,
|
||||
"hexColor": "#5B862D"
|
||||
},
|
||||
{
|
||||
"maximum": 399,
|
||||
"hexColor": "#7D862D"
|
||||
},
|
||||
{
|
||||
"maximum": 499,
|
||||
"hexColor": "#BF6440"
|
||||
},
|
||||
{
|
||||
"maximum": 599,
|
||||
"hexColor": "#D33115"
|
||||
}
|
||||
],
|
||||
"localized": ["name"],
|
||||
"name":[{
|
||||
"language_code":"en_us",
|
||||
"string" : "Status code"
|
||||
}]
|
||||
},
|
||||
{
|
||||
"column": "Status",
|
||||
"show": true,
|
||||
"type": "replace",
|
||||
"default_value":"",
|
||||
"options": [
|
||||
{
|
||||
"equals": "watched-not-changed",
|
||||
"replacement": "<i class='fa-solid fa-square-check'></i>"
|
||||
},
|
||||
{
|
||||
"equals": "watched-changed",
|
||||
"replacement": "<i class='fa-solid fa-triangle-exclamation'></i>"
|
||||
},
|
||||
{
|
||||
"equals": "new",
|
||||
"replacement": "<i class='fa-solid fa-circle-plus'></i>"
|
||||
}
|
||||
],
|
||||
"localized": ["name"],
|
||||
"name":[{
|
||||
"language_code":"en_us",
|
||||
"string" : "Status"
|
||||
}]
|
||||
}
|
||||
```
|
||||
|
||||
## Full Examples
|
||||
|
||||
- Script based plugin: Check the [website_monitor WEBMON) config.json](https://github.com/jokob-sk/Pi.Alert/blob/main/front/plugins/website_monitor/config.json) file for details.
|
||||
- SQL query nased plugin: Check the [nmap_services NMAPSERV) config.json](https://github.com/jokob-sk/Pi.Alert/blob/main/front/plugins/nmap_services/config.json) file for details.
|
||||
|
||||
|
||||
### Screenshots
|
||||
|
||||
| ![Screen 1][screen1] | ![Screen 2][screen2] |
|
||||
|----------------------|----------------------|
|
||||
| ![Screen 3][screen3] | ![Screen 4][screen4] |
|
||||
|
||||
[screen1]: https://raw.githubusercontent.com/jokob-sk/Pi.Alert/main/docs/img/plugins.png "Screen 1"
|
||||
[screen2]: https://raw.githubusercontent.com/jokob-sk/Pi.Alert/main/docs/img/plugins_settings.png "Screen 2"
|
||||
[screen3]: https://raw.githubusercontent.com/jokob-sk/Pi.Alert/main/docs/img/plugins_json_settings.png "Screen 3"
|
||||
[screen4]: https://raw.githubusercontent.com/jokob-sk/Pi.Alert/main/docs/img/plugins_json_ui.png "Screen 4"
|
||||
11
front/plugins/nmap_services/README.md
Executable file
@@ -0,0 +1,11 @@
|
||||
## Overview
|
||||
|
||||
This plugin shows all Services discovered by regular NMAP scans. It's also a sample plugin showcasing how to use a SQL Query to show existing data from the PiAlert database.
|
||||
|
||||
### Usage
|
||||
|
||||
- The sql query from the `NMAPSRV_CMD` setting is used to create source data for this plugin. Column order and values need to adhere to the ones specified in the [documentation](https://github.com/jokob-sk/Pi.Alert/tree/main/front/plugins).
|
||||
|
||||
### Notes
|
||||
|
||||
- N/A
|
||||
282
front/plugins/nmap_services/config.json
Executable file
@@ -0,0 +1,282 @@
|
||||
{
|
||||
"code_name": "nmap_services",
|
||||
"unique_prefix": "NMAPSRV",
|
||||
"enabled": true,
|
||||
"data_source": "pialert-db-query",
|
||||
"localized": ["display_name", "description", "icon"],
|
||||
"display_name" : [{
|
||||
"language_code":"en_us",
|
||||
"string" : "Services (NMAP)"
|
||||
}],
|
||||
"icon":[{
|
||||
"language_code":"en_us",
|
||||
"string" : "<i class=\"fa-solid fa-satellite-dish\"></i>"
|
||||
}],
|
||||
"description": [{
|
||||
"language_code":"en_us",
|
||||
"string" : "This plugin shows all services discovered by NMAP scans."
|
||||
}],
|
||||
"params" : [],
|
||||
"database_column_definitions":
|
||||
[
|
||||
{
|
||||
"column": "Index",
|
||||
"css_classes": "col-sm-2",
|
||||
"show": false,
|
||||
"type": "label",
|
||||
"default_value":"",
|
||||
"options": [],
|
||||
"localized": ["name"],
|
||||
"name":[{
|
||||
"language_code":"en_us",
|
||||
"string" : "N/A"
|
||||
}]
|
||||
} ,
|
||||
{
|
||||
"column": "Plugin",
|
||||
"css_classes": "col-sm-2",
|
||||
"show": false,
|
||||
"type": "label",
|
||||
"default_value":"",
|
||||
"options": [],
|
||||
"localized": ["name"],
|
||||
"name":[{
|
||||
"language_code":"en_us",
|
||||
"string" : "N/A"
|
||||
}]
|
||||
},
|
||||
{
|
||||
"column": "Object_PrimaryID",
|
||||
"css_classes": "col-sm-2",
|
||||
"show": true,
|
||||
"type": "label",
|
||||
"default_value":"",
|
||||
"options": [],
|
||||
"localized": ["name"],
|
||||
"name":[{
|
||||
"language_code":"en_us",
|
||||
"string" : "Device name"
|
||||
}]
|
||||
},
|
||||
{
|
||||
"column": "Object_SecondaryID",
|
||||
"css_classes": "col-sm-2",
|
||||
"show": true,
|
||||
"type": "url",
|
||||
"default_value":"",
|
||||
"options": [],
|
||||
"localized": ["name"],
|
||||
"name":[{
|
||||
"language_code":"en_us",
|
||||
"string" : "Ip and Port"
|
||||
}]
|
||||
} ,
|
||||
{
|
||||
"column": "DateTimeCreated",
|
||||
"css_classes": "col-sm-2",
|
||||
"show": true,
|
||||
"type": "label",
|
||||
"default_value":"",
|
||||
"options": [],
|
||||
"localized": ["name"],
|
||||
"name":[{
|
||||
"language_code":"en_us",
|
||||
"string" : "Created"
|
||||
}]
|
||||
},
|
||||
{
|
||||
"column": "DateTimeChanged",
|
||||
"css_classes": "col-sm-2",
|
||||
"show": true,
|
||||
"type": "label",
|
||||
"default_value":"",
|
||||
"options": [],
|
||||
"localized": ["name"],
|
||||
"name":[{
|
||||
"language_code":"en_us",
|
||||
"string" : "Changed"
|
||||
}]
|
||||
},
|
||||
{
|
||||
"column": "Watched_Value1",
|
||||
"css_classes": "col-sm-1",
|
||||
"show": true,
|
||||
"type": "label",
|
||||
"default_value":"",
|
||||
"localized": ["name"],
|
||||
"name":[{
|
||||
"language_code":"en_us",
|
||||
"string" : "Service"
|
||||
}]
|
||||
},
|
||||
{
|
||||
"column": "Watched_Value2",
|
||||
"css_classes": "col-sm-1",
|
||||
"show": true,
|
||||
"type": "label",
|
||||
"default_value":"",
|
||||
"options": [],
|
||||
"localized": ["name"],
|
||||
"name":[{
|
||||
"language_code":"en_us",
|
||||
"string" : "State"
|
||||
}]
|
||||
},
|
||||
{
|
||||
"column": "Watched_Value3",
|
||||
"css_classes": "col-sm-2",
|
||||
"show": false,
|
||||
"type": "label",
|
||||
"default_value":"",
|
||||
"options": [],
|
||||
"localized": ["name"],
|
||||
"name":[{
|
||||
"language_code":"en_us",
|
||||
"string" : "N/A"
|
||||
}]
|
||||
} ,
|
||||
{
|
||||
"column": "Watched_Value4",
|
||||
"css_classes": "col-sm-2",
|
||||
"show": false,
|
||||
"type": "label",
|
||||
"default_value":"",
|
||||
"options": [],
|
||||
"localized": ["name"],
|
||||
"name":[{
|
||||
"language_code":"en_us",
|
||||
"string" : "N/A"
|
||||
}]
|
||||
} ,
|
||||
{
|
||||
"column": "Extra",
|
||||
"css_classes": "col-sm-3",
|
||||
"show": true,
|
||||
"type": "label",
|
||||
"default_value":"",
|
||||
"options": [],
|
||||
"localized": ["name"],
|
||||
"name":[{
|
||||
"language_code":"en_us",
|
||||
"string" : "Extra"
|
||||
}]
|
||||
},
|
||||
{
|
||||
"column": "ForeignKey",
|
||||
"css_classes": "col-sm-2",
|
||||
"show": true,
|
||||
"type": "devicemac",
|
||||
"default_value":"",
|
||||
"options": [],
|
||||
"localized": ["name"],
|
||||
"name":[{
|
||||
"language_code":"en_us",
|
||||
"string" : "MAC"
|
||||
}]
|
||||
},
|
||||
{
|
||||
"column": "Status",
|
||||
"css_classes": "col-sm-1",
|
||||
"show": true,
|
||||
"type": "replace",
|
||||
"default_value":"",
|
||||
"options": [
|
||||
{
|
||||
"equals": "watched-not-changed",
|
||||
"replacement": "<div style='text-align:center'><i class='fa-solid fa-square-check'></i><div></div>"
|
||||
},
|
||||
{
|
||||
"equals": "watched-changed",
|
||||
"replacement": "<div style='text-align:center'><i class='fa-solid fa-triangle-exclamation'></i></div>"
|
||||
},
|
||||
{
|
||||
"equals": "new",
|
||||
"replacement": "<div style='text-align:center'><i class='fa-solid fa-circle-plus'></i></div>"
|
||||
}
|
||||
],
|
||||
"localized": ["name"],
|
||||
"name":[{
|
||||
"language_code":"en_us",
|
||||
"string" : "Status"
|
||||
}]
|
||||
}
|
||||
],
|
||||
"settings":[
|
||||
{
|
||||
"function": "RUN",
|
||||
"type": "selecttext",
|
||||
"default_value":"disabled",
|
||||
"options": ["disabled", "once", "schedule", "always_after_scan", "on_new_device"],
|
||||
"localized": ["name", "description"],
|
||||
"name" :[{
|
||||
"language_code":"en_us",
|
||||
"string" : "When to run"
|
||||
}],
|
||||
"description": [{
|
||||
"language_code":"en_us",
|
||||
"string" : "Specify when the SQL query is executed."
|
||||
}]
|
||||
},
|
||||
{
|
||||
"function": "CMD",
|
||||
"type": "text",
|
||||
"default_value":"SELECT dv.dev_Name as Object_PrimaryID, cast('http://' || dv.dev_LastIP as VARCHAR(100)) || ':' || cast( SUBSTR(ns.Port ,0, INSTR(ns.Port , '/')) as VARCHAR(100)) as Object_SecondaryID, datetime() as DateTime, ns.Service as Watched_Value1, ns.State as Watched_Value2, 'null' as Watched_Value3, 'null' as Watched_Value4, ns.Extra as Extra, dv.dev_MAC as ForeignKey FROM (SELECT * FROM Nmap_Scan) ns LEFT JOIN (SELECT dev_Name, dev_MAC, dev_LastIP FROM Devices) dv ON ns.MAC = dv.dev_MAC",
|
||||
"options": [],
|
||||
"localized": ["name", "description"],
|
||||
"name" : [{
|
||||
"language_code":"en_us",
|
||||
"string" : "SQL to run"
|
||||
}],
|
||||
"description": [{
|
||||
"language_code":"en_us",
|
||||
"string" : "This SQL query is used to populate the coresponding UI tables under the Plugins section."
|
||||
}]
|
||||
},
|
||||
{
|
||||
"function": "RUN_SCHD",
|
||||
"type": "text",
|
||||
"default_value":"0 2 * * *",
|
||||
"options": [],
|
||||
"localized": ["name", "description"],
|
||||
"name" : [{
|
||||
"language_code":"en_us",
|
||||
"string" : "Schedule"
|
||||
}],
|
||||
"description": [{
|
||||
"language_code":"en_us",
|
||||
"string" : "Only enabled if you select <code>schedule</code> in the <a href=\"#NMAPSRV_RUN\"><code>NMAPSRV_RUN</code> setting</a>. Make sure you enter the schedule in the correct cron-like format (e.g. validate at <a href=\"https://crontab.guru/\" target=\"_blank\">crontab.guru</a>). For example entering <code>0 4 * * *</code> will run the scan after 4 am in the <a onclick=\"toggleAllSettings()\" href=\"#TIMEZONE\"><code>TIMEZONE</code> you set above</a>. Will be run NEXT time the time passes."
|
||||
}]
|
||||
},
|
||||
{
|
||||
"function": "WATCH",
|
||||
"type": "multiselect",
|
||||
"default_value":["Watched_Value1"],
|
||||
"options": ["Watched_Value1","Watched_Value2","Watched_Value3","Watched_Value4"],
|
||||
"localized": ["name", "description"],
|
||||
"name" :[{
|
||||
"language_code":"en_us",
|
||||
"string" : "Watched"
|
||||
}] ,
|
||||
"description":[{
|
||||
"language_code":"en_us",
|
||||
"string" : "Send a notification if selected values change. Use <code>CTRL + Click</code> to select/deselect. <ul> <li><code>Watched_Value1</code> is service type (e.g.: http, ssh)</li><li><code>Watched_Value2</code> is Status (open or closed)</li><li><code>Watched_Value3</code> unused </li><li><code>Watched_Value4</code> unused </li></ul>"
|
||||
}]
|
||||
},
|
||||
{
|
||||
"function": "REPORT_ON",
|
||||
"type": "multiselect",
|
||||
"default_value":["new","watched-changed"],
|
||||
"options": ["new","watched-changed","watched-not-changed"],
|
||||
"localized": ["name", "description"],
|
||||
"name" :[{
|
||||
"language_code":"en_us",
|
||||
"string" : "Report on"
|
||||
}] ,
|
||||
"description":[{
|
||||
"language_code":"en_us",
|
||||
"string" : "Send a notification only on these statuses. <code>new</code> means a new unique (unique combination of PrimaryId and SecondaryId) object was discovered. <code>watched-changed</code> means that selected <code>Watched_ValueN</code> columns changed."
|
||||
}]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
12
front/plugins/website_monitor/README.md
Executable file
@@ -0,0 +1,12 @@
|
||||
## Overview
|
||||
|
||||
A simple sample plugin allowing for monitoring web services or urls. The status code corresponds to the commonly used [HTTP response status codes](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status).
|
||||
|
||||
### Usage
|
||||
|
||||
- The user can specify which services (websites) to monitor via the `WEBMON_urls_to_check` setting.
|
||||
|
||||
### Notes
|
||||
|
||||
- Setting `(WEBMON_)SQL_internet_ip` is not used and specified for demonstration purposes only.
|
||||
- Parameters `macs` and `internet_ip` in the `config.json` file are not used and specified for demonstration purposes only.
|
||||
383
front/plugins/website_monitor/config.json
Executable file
@@ -0,0 +1,383 @@
|
||||
{
|
||||
"code_name": "website_monitor",
|
||||
"unique_prefix": "WEBMON",
|
||||
"enabled": true,
|
||||
"data_source": "python-script",
|
||||
"localized": ["display_name", "description", "icon"],
|
||||
"display_name" : [{
|
||||
"language_code":"en_us",
|
||||
"string" : "Website monitor"
|
||||
}],
|
||||
"icon":[{
|
||||
"language_code":"en_us",
|
||||
"string" : "<i class=\"fa-solid fa-globe\"></i>"
|
||||
}],
|
||||
"description": [{
|
||||
"language_code":"en_us",
|
||||
"string" : "This plugin is to monitor status changes of services or websites."
|
||||
}],
|
||||
"params" : [{
|
||||
"name" : "macs",
|
||||
"type" : "sql",
|
||||
"value" : "SELECT dev_MAC from DEVICES"
|
||||
},
|
||||
{
|
||||
"name" : "urls",
|
||||
"type" : "setting",
|
||||
"value" : "WEBMON_urls_to_check"
|
||||
},
|
||||
{
|
||||
"name" : "internet_ip",
|
||||
"type" : "setting",
|
||||
"value" : "WEBMON_SQL_internet_ip"
|
||||
}],
|
||||
"database_column_definitions":
|
||||
[
|
||||
{
|
||||
"column": "Index",
|
||||
"css_classes": "col-sm-2",
|
||||
"show": false,
|
||||
"type": "label",
|
||||
"default_value":"",
|
||||
"options": [],
|
||||
"localized": ["name"],
|
||||
"name":[{
|
||||
"language_code":"en_us",
|
||||
"string" : "N/A"
|
||||
}]
|
||||
} ,
|
||||
{
|
||||
"column": "Plugin",
|
||||
"css_classes": "col-sm-2",
|
||||
"show": false,
|
||||
"type": "label",
|
||||
"default_value":"",
|
||||
"options": [],
|
||||
"localized": ["name"],
|
||||
"name":[{
|
||||
"language_code":"en_us",
|
||||
"string" : "N/A"
|
||||
}]
|
||||
},
|
||||
{
|
||||
"column": "Object_PrimaryID",
|
||||
"css_classes": "col-sm-2",
|
||||
"show": true,
|
||||
"type": "url",
|
||||
"default_value":"",
|
||||
"options": [],
|
||||
"localized": ["name"],
|
||||
"name":[{
|
||||
"language_code":"en_us",
|
||||
"string" : "Monitored URL"
|
||||
}]
|
||||
},
|
||||
{
|
||||
"column": "Object_SecondaryD",
|
||||
"css_classes": "col-sm-2",
|
||||
"show": false,
|
||||
"type": "label",
|
||||
"default_value":"",
|
||||
"options": [],
|
||||
"localized": ["name"],
|
||||
"name":[{
|
||||
"language_code":"en_us",
|
||||
"string" : "N/A"
|
||||
}]
|
||||
} ,
|
||||
{
|
||||
"column": "DateTimeCreated",
|
||||
"css_classes": "col-sm-2",
|
||||
"show": true,
|
||||
"type": "label",
|
||||
"default_value":"",
|
||||
"options": [],
|
||||
"localized": ["name"],
|
||||
"name":[{
|
||||
"language_code":"en_us",
|
||||
"string" : "Created"
|
||||
}]
|
||||
},
|
||||
{
|
||||
"column": "DateTimeChanged",
|
||||
"css_classes": "col-sm-2",
|
||||
"show": true,
|
||||
"type": "label",
|
||||
"default_value":"",
|
||||
"options": [],
|
||||
"localized": ["name"],
|
||||
"name":[{
|
||||
"language_code":"en_us",
|
||||
"string" : "Changed"
|
||||
}]
|
||||
},
|
||||
{
|
||||
"column": "Watched_Value1",
|
||||
"css_classes": "col-sm-2",
|
||||
"show": true,
|
||||
"type": "threshold",
|
||||
"default_value":"",
|
||||
"options": [
|
||||
{
|
||||
"maximum": 199,
|
||||
"hexColor": "#792D86"
|
||||
},
|
||||
{
|
||||
"maximum": 299,
|
||||
"hexColor": "#5B862D"
|
||||
},
|
||||
{
|
||||
"maximum": 399,
|
||||
"hexColor": "#7D862D"
|
||||
},
|
||||
{
|
||||
"maximum": 499,
|
||||
"hexColor": "#BF6440"
|
||||
},
|
||||
{
|
||||
"maximum": 599,
|
||||
"hexColor": "#D33115"
|
||||
}
|
||||
],
|
||||
"localized": ["name"],
|
||||
"name":[{
|
||||
"language_code":"en_us",
|
||||
"string" : "Status code"
|
||||
}]
|
||||
},
|
||||
{
|
||||
"column": "Watched_Value2",
|
||||
"css_classes": "col-sm-2",
|
||||
"show": true,
|
||||
"type": "label",
|
||||
"default_value":"",
|
||||
"options": [],
|
||||
"localized": ["name"],
|
||||
"name":[{
|
||||
"language_code":"en_us",
|
||||
"string" : "Latency"
|
||||
}]
|
||||
},
|
||||
{
|
||||
"column": "Watched_Value3",
|
||||
"css_classes": "col-sm-2",
|
||||
"show": false,
|
||||
"type": "label",
|
||||
"default_value":"",
|
||||
"options": [],
|
||||
"localized": ["name"],
|
||||
"name":[{
|
||||
"language_code":"en_us",
|
||||
"string" : "N/A"
|
||||
}]
|
||||
} ,
|
||||
{
|
||||
"column": "Watched_Value4",
|
||||
"css_classes": "col-sm-2",
|
||||
"show": false,
|
||||
"type": "label",
|
||||
"default_value":"",
|
||||
"options": [],
|
||||
"localized": ["name"],
|
||||
"name":[{
|
||||
"language_code":"en_us",
|
||||
"string" : "N/A"
|
||||
}]
|
||||
} ,
|
||||
{
|
||||
"column": "UserData",
|
||||
"css_classes": "col-sm-2",
|
||||
"show": true,
|
||||
"type": "textboxsave",
|
||||
"default_value":"",
|
||||
"options": [],
|
||||
"localized": ["name"],
|
||||
"name":[{
|
||||
"language_code":"en_us",
|
||||
"string" : "Comments"
|
||||
}]
|
||||
},
|
||||
{
|
||||
"column": "Status",
|
||||
"css_classes": "col-sm-1",
|
||||
"show": true,
|
||||
"type": "replace",
|
||||
"default_value":"",
|
||||
"options": [
|
||||
{
|
||||
"equals": "watched-not-changed",
|
||||
"replacement": "<div style='text-align:center'><i class='fa-solid fa-square-check'></i><div></div>"
|
||||
},
|
||||
{
|
||||
"equals": "watched-changed",
|
||||
"replacement": "<div style='text-align:center'><i class='fa-solid fa-triangle-exclamation'></i></div>"
|
||||
},
|
||||
{
|
||||
"equals": "new",
|
||||
"replacement": "<div style='text-align:center'><i class='fa-solid fa-circle-plus'></i></div>"
|
||||
}
|
||||
],
|
||||
"localized": ["name"],
|
||||
"name":[{
|
||||
"language_code":"en_us",
|
||||
"string" : "Status"
|
||||
}]
|
||||
},
|
||||
{
|
||||
"column": "Extra",
|
||||
"css_classes": "col-sm-3",
|
||||
"show": false,
|
||||
"type": "label",
|
||||
"default_value":"",
|
||||
"options": [],
|
||||
"localized": ["name"],
|
||||
"name":[{
|
||||
"language_code":"en_us",
|
||||
"string" : "Extra"
|
||||
}]
|
||||
}
|
||||
],
|
||||
"settings":[
|
||||
{
|
||||
"function": "RUN",
|
||||
"type": "selecttext",
|
||||
"default_value":"disabled",
|
||||
"options": ["disabled", "once", "schedule", "always_after_scan", "on_new_device"],
|
||||
"localized": ["name", "description"],
|
||||
"name" :[{
|
||||
"language_code":"en_us",
|
||||
"string" : "When to run"
|
||||
}],
|
||||
"description": [{
|
||||
"language_code":"en_us",
|
||||
"string" : "Enable a regular scan of your services. If you select <code>schedule</code> the scheduling settings from below are applied. If you select <code>once</code> the scan is run only once on start of the application (container) for the time specified in <a href=\"#WEBMON_RUN_TIMEOUT\"><code>WEBMON_RUN_TIMEOUT</code> setting</a>."
|
||||
}]
|
||||
},
|
||||
{
|
||||
"function": "CMD",
|
||||
"type": "text",
|
||||
"default_value":"python3 /home/pi/pialert/front/plugins/website_monitor/script.py urls={urls}",
|
||||
"options": [],
|
||||
"localized": ["name", "description"],
|
||||
"name" : [{
|
||||
"language_code":"en_us",
|
||||
"string" : "Command"
|
||||
}],
|
||||
"description": [{
|
||||
"language_code":"en_us",
|
||||
"string" : "Command to run"
|
||||
}]
|
||||
},
|
||||
{
|
||||
"function": "RUN_SCHD",
|
||||
"type": "text",
|
||||
"default_value":"0 2 * * *",
|
||||
"options": [],
|
||||
"localized": ["name", "description"],
|
||||
"name" : [{
|
||||
"language_code":"en_us",
|
||||
"string" : "Schedule"
|
||||
}],
|
||||
"description": [{
|
||||
"language_code":"en_us",
|
||||
"string" : "Only enabled if you select <code>schedule</code> in the <a href=\"#WEBMON_RUN\"><code>WEBMON_RUN</code> setting</a>. Make sure you enter the schedule in the correct cron-like format (e.g. validate at <a href=\"https://crontab.guru/\" target=\"_blank\">crontab.guru</a>). For example entering <code>0 4 * * *</code> will run the scan after 4 am in the <a onclick=\"toggleAllSettings()\" href=\"#TIMEZONE\"><code>TIMEZONE</code> you set above</a>. Will be run NEXT time the time passes."
|
||||
}]
|
||||
},
|
||||
{
|
||||
"function": "API_SQL",
|
||||
"type": "text",
|
||||
"default_value":"SELECT * FROM plugin_website_monitor",
|
||||
"options": [],
|
||||
"localized": ["name", "description"],
|
||||
"name" : [{
|
||||
"language_code":"en_us",
|
||||
"string" : "API endpoint (not implemented)"
|
||||
}],
|
||||
"description": [{
|
||||
"language_code":"en_us",
|
||||
"string" : "You can specify a custom SQL query which will generate a JSON file and then expose it via the <a href=\"/api/plugin_website_monitor.json\" target=\"_blank\"><code>plugin_website_monitor.json</code> file endpoint</a>."
|
||||
}]
|
||||
},
|
||||
{
|
||||
"function": "RUN_TIMEOUT",
|
||||
"type": "integer",
|
||||
"default_value":5,
|
||||
"options": [],
|
||||
"localized": ["name", "description"],
|
||||
"name" : [{
|
||||
"language_code":"en_us",
|
||||
"string" : "Run timeout"
|
||||
},
|
||||
{
|
||||
"language_code":"de_de",
|
||||
"string" : "Wartezeit"
|
||||
}],
|
||||
"description": [{
|
||||
"language_code":"en_us",
|
||||
"string" : "Maximum time in seconds to wait for the script to finish. If this time is exceeded the script is aborted."
|
||||
}]
|
||||
},
|
||||
{
|
||||
"function": "WATCH",
|
||||
"type": "multiselect",
|
||||
"default_value":["Watched_Value1"],
|
||||
"options": ["Watched_Value1","Watched_Value2","Watched_Value3","Watched_Value4"],
|
||||
"localized": ["name", "description"],
|
||||
"name" :[{
|
||||
"language_code":"en_us",
|
||||
"string" : "Watched"
|
||||
}] ,
|
||||
"description":[{
|
||||
"language_code":"en_us",
|
||||
"string" : "Send a notification if selected values change. Use <code>CTRL + Click</code> to select/deselect. <ul> <li><code>Watched_Value1</code> is response status code (e.g.: 200, 404)</li><li><code>Watched_Value2</code> is Latency (not recommended)</li><li><code>Watched_Value3</code> unused </li><li><code>Watched_Value4</code> unused </li></ul>"
|
||||
}]
|
||||
},
|
||||
{
|
||||
"function": "REPORT_ON",
|
||||
"type": "multiselect",
|
||||
"default_value":["new","watched-changed"],
|
||||
"options": ["new","watched-changed","watched-not-changed"],
|
||||
"localized": ["name", "description"],
|
||||
"name" :[{
|
||||
"language_code":"en_us",
|
||||
"string" : "Report on"
|
||||
}] ,
|
||||
"description":[{
|
||||
"language_code":"en_us",
|
||||
"string" : "Send a notification only on these statuses. <code>new</code> means a new unique (unique combination of PrimaryId and SecondaryId) object was discovered. <code>watched-changed</code> means that selected <code>Watched_ValueN</code> columns changed."
|
||||
}]
|
||||
},
|
||||
{
|
||||
"function": "urls_to_check",
|
||||
"type": "list",
|
||||
"default_value":["https://google.com", "https://duck.com"],
|
||||
"options": [],
|
||||
"localized": ["name", "description"],
|
||||
"name" : [{
|
||||
"language_code":"en_us",
|
||||
"string" : "Arguments"
|
||||
}],
|
||||
"description": [{
|
||||
"language_code":"en_us",
|
||||
"string" : "Services to watch. Enter full URL, e.g. <code>https://google.com</code>."
|
||||
}]
|
||||
},
|
||||
{
|
||||
"function": "SQL_internet_ip",
|
||||
"type": "readonly",
|
||||
"default_value":"SELECT dev_LastIP FROM Devices WHERE dev_MAC = 'Internet'",
|
||||
"options": [],
|
||||
"localized": ["name", "description"],
|
||||
"name" : [{
|
||||
"language_code":"en_us",
|
||||
"string" : "Helper variable"
|
||||
}],
|
||||
"description": [{
|
||||
"language_code":"en_us",
|
||||
"string" : "Unused setting - for demonstration only. Getting the IP address of the Router / Internet. "
|
||||
}]
|
||||
}
|
||||
|
||||
]
|
||||
}
|
||||
|
||||
148
front/plugins/website_monitor/script.py
Executable file
@@ -0,0 +1,148 @@
|
||||
#!/usr/bin/env python
|
||||
# Based on the work of https://github.com/leiweibau/Pi.Alert
|
||||
|
||||
# /home/pi/pialert/front/plugins/website_monitor/script.py urls=http://google.com,http://bing.com
|
||||
from __future__ import unicode_literals
|
||||
from time import sleep, time, strftime
|
||||
import requests
|
||||
import pathlib
|
||||
|
||||
import argparse
|
||||
import io
|
||||
#import smtplib
|
||||
import sys
|
||||
#from smtp_config import sender, password, receivers, host, port
|
||||
from requests.packages.urllib3.exceptions import InsecureRequestWarning
|
||||
|
||||
import pwd
|
||||
import os
|
||||
|
||||
curPath = str(pathlib.Path(__file__).parent.resolve())
|
||||
log_file = curPath + '/script.log'
|
||||
last_run = curPath + '/last_result.log'
|
||||
|
||||
print(last_run)
|
||||
|
||||
# Workflow
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description='Simple URL monitoring tool')
|
||||
parser.add_argument('urls', action="store", help="urls to check separated by ','")
|
||||
values = parser.parse_args()
|
||||
|
||||
if values.urls:
|
||||
with open(last_run, 'w') as last_run_logfile:
|
||||
# empty file
|
||||
last_run_logfile.write("")
|
||||
service_monitoring(values.urls.split('=')[1].split(','))
|
||||
else:
|
||||
return
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
def service_monitoring_log(site, status, latency):
|
||||
# global monitor_logfile
|
||||
|
||||
# Log status message to log file
|
||||
with open(log_file, 'a') as monitor_logfile:
|
||||
monitor_logfile.write("{} | {} | {} | {}\n".format(strftime("%Y-%m-%d %H:%M:%S"),
|
||||
site,
|
||||
status,
|
||||
latency,
|
||||
)
|
||||
)
|
||||
with open(last_run, 'a') as last_run_logfile:
|
||||
# https://www.duckduckgo.com|192.168.0.1|2023-01-02 15:56:30|200|0.9898|null|null|Best search engine|null
|
||||
last_run_logfile.write("{}|{}|{}|{}|{}|{}|{}|{}|{}\n".format(
|
||||
site,
|
||||
'null',
|
||||
strftime("%Y-%m-%d %H:%M:%S"),
|
||||
status,
|
||||
latency,
|
||||
'null',
|
||||
'null',
|
||||
'null',
|
||||
'null',
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
def check_services_health(site):
|
||||
# Enable self signed SSL
|
||||
requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
|
||||
"""Send GET request to input site and return status code"""
|
||||
try:
|
||||
resp = requests.get(site, verify=False, timeout=10)
|
||||
latency = resp.elapsed
|
||||
latency_str = str(latency)
|
||||
latency_str_seconds = latency_str.split(":")
|
||||
format_latency_str = latency_str_seconds[2]
|
||||
if format_latency_str[0] == "0" and format_latency_str[1] != "." :
|
||||
format_latency_str = format_latency_str[1:]
|
||||
return resp.status_code, format_latency_str
|
||||
except requests.exceptions.SSLError:
|
||||
pass
|
||||
except:
|
||||
latency = "99999"
|
||||
return 503, latency
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
def get_username():
|
||||
|
||||
return pwd.getpwuid(os.getuid())[0]
|
||||
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
def service_monitoring(urls):
|
||||
|
||||
# Empty Log and write new header
|
||||
print("Prepare Services Monitoring")
|
||||
print("... Prepare Logfile")
|
||||
with open(log_file, 'w') as monitor_logfile:
|
||||
monitor_logfile.write("Pi.Alert [Prototype]:\n---------------------------------------------------------\n")
|
||||
monitor_logfile.write("Current User: %s \n\n" % get_username())
|
||||
monitor_logfile.write("Monitor Web-Services\n")
|
||||
monitor_logfile.write("Timestamp: " + strftime("%Y-%m-%d %H:%M:%S") + "\n")
|
||||
monitor_logfile.close()
|
||||
|
||||
print("... Get Services List")
|
||||
sites = urls
|
||||
|
||||
|
||||
print("Start Services Monitoring")
|
||||
with open(log_file, 'a') as monitor_logfile:
|
||||
monitor_logfile.write("\nStart Services Monitoring\n\n| Timestamp | URL | StatusCode | ResponseTime |\n-----------------------------------------------\n")
|
||||
monitor_logfile.close()
|
||||
|
||||
while sites:
|
||||
for site in sites:
|
||||
status,latency = check_services_health(site)
|
||||
scantime = strftime("%Y-%m-%d %H:%M:%S")
|
||||
|
||||
# Debugging
|
||||
# print("{} - {} STATUS: {} ResponseTime: {}".format(strftime("%Y-%m-%d %H:%M:%S"),
|
||||
# site,
|
||||
# status,
|
||||
# latency)
|
||||
# )
|
||||
|
||||
# Write Logfile
|
||||
service_monitoring_log(site, status, latency)
|
||||
|
||||
sys.stdout.flush()
|
||||
|
||||
break
|
||||
|
||||
else:
|
||||
with open(log_file, 'a') as monitor_logfile:
|
||||
monitor_logfile.write("\n\nNo site(s) to monitor!")
|
||||
monitor_logfile.close()
|
||||
|
||||
#===============================================================================
|
||||
# BEGIN
|
||||
#===============================================================================
|
||||
if __name__ == '__main__':
|
||||
sys.exit(main())
|
||||
|
||||
@@ -14,11 +14,24 @@ $confPath = "../config/pialert.conf";
|
||||
|
||||
checkPermissions([$dbPath, $confPath]);
|
||||
|
||||
// get settings from the API json file
|
||||
|
||||
// path to your JSON file
|
||||
$file = '../front/api/table_settings.json';
|
||||
// put the content of the file in a variable
|
||||
$data = file_get_contents($file);
|
||||
// JSON decode
|
||||
$settingsJson = json_decode($data);
|
||||
|
||||
// get settings from the DB
|
||||
|
||||
global $db;
|
||||
global $settingKeyOfLists;
|
||||
|
||||
$result = $db->query("SELECT * FROM Settings");
|
||||
|
||||
// array
|
||||
$settingKeyOfLists = array();
|
||||
$settings = array();
|
||||
while ($row = $result -> fetchArray (SQLITE3_ASSOC)) {
|
||||
// Push row data
|
||||
@@ -72,7 +85,7 @@ while ($row = $result -> fetchArray (SQLITE3_ASSOC)) {
|
||||
$html = $html.'<div class=" box panel panel-default">
|
||||
<a data-toggle="collapse" data-parent="#accordion_gen" href="#'.$group.'">
|
||||
<div class="panel-heading">
|
||||
<h4 class="panel-title">'.lang($group.'_settings_group').'</h4>
|
||||
<h4 class="panel-title">'.lang($group.'_icon')." ".lang($group.'_display_name').'</h4>
|
||||
</div>
|
||||
</a>
|
||||
<div id="'.$group.'" data-myid="collapsible" class="panel-collapse collapse '.$isIn.'">
|
||||
@@ -108,7 +121,7 @@ while ($row = $result -> fetchArray (SQLITE3_ASSOC)) {
|
||||
// text - textbox
|
||||
if($set['Type'] == 'text' )
|
||||
{
|
||||
$input = '<input class="form-control" onChange="settingsChanged()" input" id="'.$set['Code_Name'].'" value="'.$set['Value'].'"/>';
|
||||
$input = '<input class="form-control" onChange="settingsChanged()" id="'.$set['Code_Name'].'" value="'.$set['Value'].'"/>';
|
||||
}
|
||||
// password - hidden text
|
||||
elseif ($set['Type'] == 'password')
|
||||
@@ -221,6 +234,37 @@ while ($row = $result -> fetchArray (SQLITE3_ASSOC)) {
|
||||
// Remove all interfaces button
|
||||
$input = $input.'<div><button class="btn btn-primary" onclick="removeInterfaces()">Remove all</button></div>';
|
||||
|
||||
}
|
||||
// list
|
||||
elseif ($set['Type'] == 'list')
|
||||
{
|
||||
|
||||
$settingKeyOfLists[] = $set['Code_Name'];
|
||||
|
||||
$input = $input.
|
||||
'<div class="row form-group">
|
||||
<div class="col-xs-9">
|
||||
<input class="form-control" type="text" id="'.$set['Code_Name'].'_input" placeholder="Enter value"/>
|
||||
</div>';
|
||||
// Add interface button
|
||||
$input = $input.
|
||||
'<div class="col-xs-3"><button class="btn btn-primary" onclick="addList'.$set['Code_Name'].'()" >Add</button></div>
|
||||
</div>';
|
||||
|
||||
// list all interfaces as options
|
||||
$input = $input.'<div class="form-group">
|
||||
<select class="form-control" name="'.$set['Code_Name'].'" id="'.$set['Code_Name'].'" multiple readonly>';
|
||||
|
||||
$options = createArray($set['Value']);
|
||||
|
||||
foreach ($options as $option) {
|
||||
|
||||
$input = $input.'<option value="'.$option.'" disabled>'.$option.'</option>';
|
||||
}
|
||||
$input = $input.'</select></div>';
|
||||
// Remove all interfaces button
|
||||
$input = $input.'<div><button class="btn btn-primary" onclick="removeFromList'.$set['Code_Name'].'()">Remove last</button></div>';
|
||||
|
||||
}
|
||||
|
||||
$html = $html.$input;
|
||||
@@ -280,7 +324,10 @@ while ($row = $result -> fetchArray (SQLITE3_ASSOC)) {
|
||||
<script>
|
||||
|
||||
// number of settings has to be equal to
|
||||
var settingsNumber = 68;
|
||||
|
||||
// display the name of the first person
|
||||
// echo $settingsJson[0]->name;
|
||||
var settingsNumber = <?php echo count($settingsJson->data)?>;
|
||||
|
||||
// Wrong number of settings processing
|
||||
if(<?php echo count($settings)?> != settingsNumber)
|
||||
@@ -288,8 +335,35 @@ while ($row = $result -> fetchArray (SQLITE3_ASSOC)) {
|
||||
showModalOk('WARNING', "<?= lang("settings_missing")?>");
|
||||
}
|
||||
|
||||
<?php
|
||||
// generate javascript methods to handle add and remove items to lists
|
||||
foreach($settingKeyOfLists as $settingKey )
|
||||
{
|
||||
$addList = 'function addList'.$settingKey.'()
|
||||
{
|
||||
input = $("#'.$settingKey.'_input").val();
|
||||
$("#'.$settingKey.'").append($("<option disabled></option>").attr("value", input).text(input));
|
||||
|
||||
|
||||
$("#'.$settingKey.'_input").val("");
|
||||
|
||||
settingsChanged();
|
||||
}
|
||||
';
|
||||
|
||||
|
||||
$remList = 'function removeFromList'.$settingKey.'()
|
||||
{
|
||||
settingsChanged();
|
||||
// $("#'.$settingKey.'").empty();
|
||||
$("#'.$settingKey.'").find("option:last").remove();
|
||||
}';
|
||||
|
||||
echo $remList;
|
||||
echo $addList;
|
||||
}
|
||||
?>
|
||||
|
||||
// ---------------------------------------------------------
|
||||
function addInterface()
|
||||
{
|
||||
@@ -328,17 +402,18 @@ while ($row = $result -> fetchArray (SQLITE3_ASSOC)) {
|
||||
// generate javascript to collect values
|
||||
<?php
|
||||
|
||||
$noConversion = array('text', 'integer', 'password', 'readonly', 'selecttext', 'selectinteger', "multiselect");
|
||||
$noConversion = array('text', 'integer', 'password', 'readonly', 'selecttext', 'selectinteger', 'multiselect');
|
||||
|
||||
foreach ($settings as $set) {
|
||||
if(in_array($set['Type'] , $noConversion))
|
||||
{
|
||||
echo 'settingsArray.push(["'.$set["Group"].'", "'.$set["Code_Name"].'", $("#'.$set["Code_Name"].'").val(), "'.$set["Type"].'" ]);';
|
||||
|
||||
echo 'settingsArray.push(["'.$set["Group"].'", "'.$set["Code_Name"].'", "'.$set["Type"].'", $("#'.$set["Code_Name"].'").val() ]);';
|
||||
}
|
||||
elseif ($set['Type'] == "boolean")
|
||||
{
|
||||
echo 'temp = $("#'.$set["Code_Name"].'").is(":checked") ;';
|
||||
echo 'settingsArray.push(["'.$set["Group"].'", "'.$set["Code_Name"].'", temp, "'.$set["Type"].'" ]);';
|
||||
echo 'settingsArray.push(["'.$set["Group"].'", "'.$set["Code_Name"].'", "'.$set["Type"].'", temp ]);';
|
||||
}
|
||||
elseif ($set["Code_Name"] == "SCAN_SUBNETS")
|
||||
{
|
||||
@@ -349,7 +424,23 @@ while ($row = $result -> fetchArray (SQLITE3_ASSOC)) {
|
||||
});
|
||||
|
||||
";
|
||||
echo 'settingsArray.push(["'.$set["Group"].'", "'.$set["Code_Name"].'", temps, "'.$set["Type"].'" ]);';
|
||||
echo 'settingsArray.push(["'.$set["Group"].'", "'.$set["Code_Name"].'", "'.$set["Type"].'", temps ]);';
|
||||
}
|
||||
elseif ($set['Type'] == "list")
|
||||
{
|
||||
echo 'console.log($("#'.$set["Code_Name"].'"));';
|
||||
echo "var temps = [];
|
||||
|
||||
$( '#".$set["Code_Name"]." option' ).each( function( i, selected ) {
|
||||
vl = $( selected ).val()
|
||||
if (vl != '')
|
||||
{
|
||||
temps.push(vl);
|
||||
}
|
||||
});
|
||||
console.log(temps);
|
||||
";
|
||||
echo 'settingsArray.push(["'.$set["Group"].'", "'.$set["Code_Name"].'", "'.$set["Type"].'", temps ]);';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||