Compare commits

...

56 Commits

Author SHA1 Message Date
github-actions[bot]
dcc43d1f3c [🤖Automation] Update README with sponsors information
Some checks are pending
docker / docker_dev (push) Waiting to run
2024-09-11 11:53:35 +00:00
github-actions[bot]
8f35bf36ff [🤖Automation] Update README with sponsors information
Some checks failed
docker / docker_dev (push) Has been cancelled
2024-09-10 11:53:40 +00:00
jokob-sk
1548168eba Lang
Some checks are pending
docker / docker_dev (push) Waiting to run
2024-09-10 08:26:06 +10:00
github-actions[bot]
2e35bac6ec [🤖Automation] Update README with sponsors information
Some checks are pending
docker / docker_dev (push) Waiting to run
2024-09-09 11:53:47 +00:00
jokob-sk
ba348fc4c2 Merge branch 'main' of https://github.com/jokob-sk/NetAlertX
Some checks are pending
docker / docker_dev (push) Waiting to run
2024-09-09 07:30:38 +10:00
jokob-sk
d3337e75a9 ⚙ Settings/Lang cache improvements #687 + #766 2024-09-09 07:30:33 +10:00
github-actions[bot]
9e0bc043b0 [🤖Automation] Update README with sponsors information
Some checks are pending
docker / docker_dev (push) Waiting to run
2024-09-08 11:53:36 +00:00
jokob-sk
29fdd0b115 Merge branch 'main' of https://github.com/jokob-sk/NetAlertX
Some checks are pending
docker / docker_dev (push) Waiting to run
2024-09-08 13:57:24 +10:00
jokob-sk
48e92a186e 🧪 Override Settings via ENV variable [experimental] #687 2024-09-08 13:57:08 +10:00
github-actions[bot]
1dcb66e972 [🤖Automation] Update README with sponsors information
Some checks are pending
docker / docker_dev (push) Waiting to run
2024-09-07 11:53:42 +00:00
jokob-sk
fa0d6d312d Merge branch 'main' of https://github.com/jokob-sk/NetAlertX
Some checks are pending
docker / docker_dev (push) Waiting to run
2024-09-07 09:28:36 +10:00
jokob-sk
a19fe342e7 🚀 Better upgarde message 2024-09-07 09:28:19 +10:00
jokob-sk
c4fc68cac8 Merge pull request #759 from elraro/fix-mtscan
chore: fixed mtscan and Dockerfile
2024-09-07 09:08:49 +10:00
jokob-sk
3a050c31a7 Update feature_request.yml 2024-09-07 09:06:29 +10:00
jokob-sk
2cd406a390 Merge branch 'main' of https://github.com/jokob-sk/NetAlertX 2024-09-07 08:38:10 +10:00
jokob-sk
b086417686 💾 Cache update for proper status color + All display #779 2024-09-07 08:38:03 +10:00
Hosted Weblate
dbecbfc85f Merge branch 'origin/main' into Weblate.
Some checks are pending
docker / docker_dev (push) Waiting to run
2024-09-06 18:09:25 +02:00
Massimo Pissarello
3f9e4c4425 Translated using Weblate (Italian)
Currently translated at 100.0% (694 of 694 strings)

Translation: NetAlertX/core
Translate-URL: https://hosted.weblate.org/projects/pialert/core/it/
2024-09-06 18:09:21 +02:00
Safeguard
4fd1869bde Translated using Weblate (Russian)
Currently translated at 100.0% (694 of 694 strings)

Translation: NetAlertX/core
Translate-URL: https://hosted.weblate.org/projects/pialert/core/ru/
2024-09-06 18:09:19 +02:00
github-actions[bot]
78025a376c [🤖Automation] Update README with sponsors information 2024-09-06 11:53:57 +00:00
elraro
615fd08f5b chore: changed mtscan type to device_scanner 2024-09-06 00:29:10 +02:00
elraro
4839211fe1 chore: fixed mtscan 2024-09-06 00:23:17 +02:00
jokob-sk
19aaa92fa3 Merge branch 'main' of https://github.com/jokob-sk/NetAlertX
Some checks are pending
docker / docker_dev (push) Waiting to run
2024-09-06 07:59:38 +10:00
jokob-sk
43aa40efbb ⚙ Settings #779 2024-09-06 07:59:35 +10:00
github-actions[bot]
95f48cb70d [🤖Automation] Update README with sponsors information
Some checks are pending
docker / docker_dev (push) Waiting to run
2024-09-05 11:53:47 +00:00
jokob-sk
8c0da1d0df Merge branch 'main' of https://github.com/jokob-sk/NetAlertX
Some checks are pending
docker / docker_dev (push) Waiting to run
2024-09-05 08:09:39 +10:00
jokob-sk
b0d07a6adc ⚙ Settings #779 2024-09-05 08:09:23 +10:00
github-actions[bot]
ee23ae19f7 [🤖Automation] Update README with sponsors information
Some checks are pending
docker / docker_dev (push) Waiting to run
2024-09-04 11:53:54 +00:00
jokob-sk
0c73e49245 Merge pull request #783 from doctorixx/main
Some checks are pending
docker / docker_dev (push) Waiting to run
Update plugins docs after add telegram publisher(and fix typo) - thanks @doctorixx !
2024-09-04 07:23:14 +10:00
github-actions[bot]
899a0c3608 [🤖Automation] Update README with sponsors information
Some checks are pending
docker / docker_dev (push) Waiting to run
2024-09-03 11:53:53 +00:00
Doctorixx
d188b640e4 Fix ordering in tip (in plugins readme) 2024-09-03 13:47:09 +03:00
Doctorixx
a95eb45924 Update plugins list (add telegram publisher) 2024-09-03 13:45:26 +03:00
jokob-sk
f737a71939 Merge branch 'main' of https://github.com/jokob-sk/NetAlertX
Some checks are pending
docker / docker_dev (push) Waiting to run
2024-09-03 07:51:27 +10:00
jokob-sk
9df97e0e33 📡 Upgrade -> Show message 2024-09-03 07:51:17 +10:00
github-actions[bot]
4ce7077599 [🤖Automation] Update README with sponsors information
Some checks are pending
docker / docker_dev (push) Waiting to run
2024-09-02 11:53:50 +00:00
jokob-sk
605a33330b 📡 Upgrade -> Show message
Some checks are pending
docker / docker_dev (push) Waiting to run
2024-09-02 15:53:15 +10:00
jokob-sk
9bd5ff10b4 📡 Upgrade -> Show message
Some checks are pending
docker / docker_dev (push) Waiting to run
2024-09-02 09:15:49 +10:00
jokob-sk
45d3be2439 Merge branch 'main' of https://github.com/jokob-sk/NetAlertX 2024-09-02 08:16:20 +10:00
jokob-sk
46209e3e47 Authelia #780 2024-09-02 08:16:15 +10:00
github-actions[bot]
9b9836cae2 [🤖Automation] Update README with sponsors information
Some checks are pending
docker / docker_dev (push) Waiting to run
2024-09-01 11:53:40 +00:00
jokob-sk
89be97bfb2 Merge branch 'main' of https://github.com/jokob-sk/NetAlertX
Some checks are pending
docker / docker_dev (push) Waiting to run
2024-09-01 08:27:35 +10:00
jokob-sk
3e4f64a7c6 Refactor maintenance.php 2024-09-01 08:27:17 +10:00
github-actions[bot]
50fbd6e616 [🤖Automation] Update README with sponsors information
Some checks are pending
docker / docker_dev (push) Waiting to run
2024-08-31 11:53:55 +00:00
jokob-sk
5a96ad2304 Refactor devices.php
Some checks are pending
docker / docker_dev (push) Waiting to run
2024-08-31 17:32:10 +10:00
jokob-sk
25667014fc Merge branch 'main' of https://github.com/jokob-sk/NetAlertX 2024-08-31 12:57:04 +10:00
jokob-sk
955472ef5c fix HRS_TO_KEEP_NEWDEV #777 2024-08-31 12:56:46 +10:00
github-actions[bot]
e32b60cafc [🤖Automation] Update README with sponsors information
Some checks are pending
docker / docker_dev (push) Waiting to run
2024-08-30 11:53:37 +00:00
jokob-sk
3033c617fa Merge pull request #775 from doctorixx/main
Some checks are pending
docker / docker_dev (push) Waiting to run
Add Telegram publisher by @doctorixx 🙏
2024-08-30 07:32:07 +10:00
Doctorixx
1688836b4f Add Telegram publisher 2024-08-29 16:41:59 +03:00
github-actions[bot]
f30b6b7fc1 [🤖Automation] Update README with sponsors information
Some checks are pending
docker / docker_dev (push) Waiting to run
2024-08-29 11:53:43 +00:00
github-actions[bot]
0c5c754f38 [🤖Automation] Update README with sponsors information
Some checks are pending
docker / docker_dev (push) Waiting to run
2024-08-28 11:53:54 +00:00
github-actions[bot]
da21ee6477 [🤖Automation] Update README with sponsors information
Some checks are pending
docker / docker_dev (push) Waiting to run
2024-08-27 11:53:48 +00:00
github-actions[bot]
3a268add06 [🤖Automation] Update README with sponsors information
Some checks are pending
docker / docker_dev (push) Waiting to run
2024-08-26 11:53:54 +00:00
github-actions[bot]
03b610a6ec [🤖Automation] Update README with sponsors information
Some checks failed
docker / docker_dev (push) Has been cancelled
2024-08-25 11:53:45 +00:00
github-actions[bot]
38f70fd045 [🤖Automation] Update README with sponsors information
Some checks are pending
docker / docker_dev (push) Waiting to run
2024-08-24 11:53:46 +00:00
elraro
ae1673c1c3 chore: fixed mtscan and Dockerfile 2024-08-11 23:55:02 +02:00
44 changed files with 1499 additions and 656 deletions

View File

@@ -9,20 +9,6 @@ body:
options:
- label: I have searched the existing open and closed issues
required: true
- type: checkboxes
attributes:
label: Am I willing to test this? 🧪
description: I rely on the community to test unreleased features. If you are requesting a feature, please be willing to test it within 48h of test request. Otherwise, the feature might be pulled from the code base.
options:
- label: I will do my best to test this feature on the `netlertx-dev` image when requested within 48h and report bugs to help deliver a great user experience for everyone and not to break existing installations.
required: true
- type: checkboxes
attributes:
label: Can I help implement this? 👩‍💻👨‍💻
description: The maintainer will provide guidance and help. The implementer will read the PR guidelines https://github.com/jokob-sk/NetAlertX/tree/main/docs#-pull-requests-prs
options:
- label: "Yes"
- label: "No"
- type: textarea
attributes:
label: Is your feature request related to a problem? Please describe
@@ -50,3 +36,17 @@ body:
Tip: You can attach images or log files by clicking this area to highlight it and then dragging files in.
validations:
required: true
- type: checkboxes
attributes:
label: Am I willing to test this? 🧪
description: I rely on the community to test unreleased features. If you are requesting a feature, please be willing to test it within 48h of test request. Otherwise, the feature might be pulled from the code base.
options:
- label: I will do my best to test this feature on the `netlertx-dev` image when requested within 48h and report bugs to help deliver a great user experience for everyone and not to break existing installations.
required: true
- type: checkboxes
attributes:
label: Can I help implement this? 👩‍💻👨‍💻
description: The maintainer will provide guidance and help. The implementer will read the PR guidelines https://github.com/jokob-sk/NetAlertX/tree/main/docs#-pull-requests-prs
options:
- label: "Yes"
- label: "No"

View File

@@ -1,8 +1,8 @@
FROM alpine:3.20 as builder
FROM alpine:3.20 AS builder
ARG INSTALL_DIR=/app
ENV PYTHONUNBUFFERED 1
ENV PYTHONUNBUFFERED=1
# Install build dependencies
RUN apk add --no-cache bash python3 python3-dev gcc musl-dev libffi-dev openssl-dev \
@@ -21,7 +21,7 @@ RUN pip install netifaces tplink-omada-client pycryptodome requests paho-mqtt sc
&& bash -c "find ${INSTALL_DIR} -type f \( -name '*.sh' -o -name '*.py' -o -name 'speedtest-cli' \) -exec chmod 750 {} \;"
# second stage
FROM alpine:3.20 as runner
FROM alpine:3.20 AS runner
ARG INSTALL_DIR=/app

View File

@@ -1,7 +1,7 @@
FROM debian:bookworm-slim
# default UID and GID
ENV USER=pi USER_ID=1000 USER_GID=1000 PORT=20211
ENV USER=pi USER_ID=1000 USER_GID=1000 PORT=20211
#TZ=Europe/London
# Todo, figure out why using a workdir instead of full paths don't work

View File

@@ -1,13 +1,13 @@
# 🖧🔍 Network scanner & notification framework
Get visibility of what's going on on your WIFI/LAN network. Schedule scans for devices, port changes and get alerts if unknown devices or changes are found. Write your own [Plugins](https://github.com/jokob-sk/NetAlertX/tree/main/front/plugins#readme) with auto-generated UI and in-build notification system. Build out and easily maintain your network source of truth (NSoT).
[![GitHub Committed](https://img.shields.io/github/last-commit/jokob-sk/NetAlertX?color=40ba12&label=Committed&logo=GitHub&logoColor=fff&style=for-the-badge)](https://github.com/jokob-sk/NetAlertX)
[![Docker Size](https://img.shields.io/docker/image-size/jokobsk/netalertx?label=Size&logo=Docker&color=0aa8d2&logoColor=fff&style=for-the-badge)](https://hub.docker.com/r/jokobsk/netalertx)
[![Docker Pulls](https://img.shields.io/docker/pulls/jokobsk/netalertx?label=Pulls&logo=docker&color=0aa8d2&logoColor=fff&style=for-the-badge)](https://hub.docker.com/r/jokobsk/netalertx)
[![GitHub Release](https://img.shields.io/github/v/release/jokob-sk/NetAlertX?color=0aa8d2&logoColor=fff&logo=GitHub&style=for-the-badge)](https://github.com/jokob-sk/NetAlertX/releases)
[![Discord](https://img.shields.io/discord/1274490466481602755?color=0aa8d2&logoColor=fff&logo=Discord&style=for-the-badge)](https://discord.gg/UQnnHNYV)
# 🖧🔍 Network scanner & notification framework
Get visibility of what's going on on your WIFI/LAN network. Schedule scans for devices, port changes and get alerts if unknown devices or changes are found. Write your own [Plugins](https://github.com/jokob-sk/NetAlertX/tree/main/front/plugins#readme) with auto-generated UI and in-build notification system. Build out and easily maintain your network source of truth (NSoT).
| 🐳 [Docker hub](https://registry.hub.docker.com/r/jokobsk/netalertx) | 📑 [Docker guide](https://github.com/jokob-sk/NetAlertX/blob/main/dockerfiles/README.md) |🆕 [Release notes](https://github.com/jokob-sk/NetAlertX/releases) | 📚 [All Docs](https://github.com/jokob-sk/NetAlertX/tree/main/docs) |
|----------------------|----------------------| ----------------------| ----------------------|

View File

@@ -64,7 +64,9 @@ services:
# DELETE END anyone trying to use this file: comment out / delete ABOVE lines, they are only for development purposes
# ---------------------------------------------------------------------------
environment:
# - APP_CONF_OVERRIDE={"SCAN_SUBNETS":"['192.168.1.0/24 --interface=eth1']","UI_dark_mode":"True"}
- TZ=${TZ}
- PORT=${PORT}
- PORT=${PORT}
# ❗ DANGER ZONE BELOW - Setting ALWAYS_FRESH_INSTALL=true will delete the content of the /db & /config folders
- ALWAYS_FRESH_INSTALL=${ALWAYS_FRESH_INSTALL}

View File

@@ -41,6 +41,16 @@ if [ "$ALWAYS_FRESH_INSTALL" = true ]; then
rm -rf "$INSTALL_DIR_OLD/db/"*
fi
# OVERRIDE settings: Handling APP_CONF_OVERRIDE
# Check if APP_CONF_OVERRIDE is set
if [ -z "$APP_CONF_OVERRIDE" ]; then
echo "APP_CONF_OVERRIDE is not set. Skipping config file creation."
else
# Save the APP_CONF_OVERRIDE env variable as a JSON file
echo "$APP_CONF_OVERRIDE" > "${INSTALL_DIR}/config/app_conf_override.json"
echo "Config file saved to ${INSTALL_DIR}/config/app_conf_override.json"
fi
# 🔻 FOR BACKWARD COMPATIBILITY - REMOVE AFTER 12/12/2024
# Check if pialert.db exists, then create a symbolic link to app.db

View File

@@ -17,7 +17,7 @@
| Pholus_Scan | Scan results of the Pholus python network penetration script. | ![Screen8][screen8] |
| Plugins_Events | For capturing events exposed by a plugin via the `last_result.log` file. If unique then saved into the `Plugins_Objects` table. Entries are deleted once processed and stored in the `Plugins_History` and/or `Plugins_Objects` tables. | ![Screen10][screen10] |
| Plugins_History | History of all entries from the `Plugins_Events` table | ![Screen11][screen11] |
| Plugins_Language_Strings | Language strings colelcted from the plugin `config.json` files used for string resolution in the frontend. | ![Screen12][screen12] |
| Plugins_Language_Strings | Language strings collected from the plugin `config.json` files used for string resolution in the frontend. | ![Screen12][screen12] |
| Plugins_Objects | Unique objects detected by individual plugins. | ![Screen13][screen13] |
| Sessions | Used to display sessions in the charts | ![Screen15][screen15] |
| Settings | Database representation of the sum of all settings from `app.conf` and plugins coming from `config.json` files. | ![Screen16][screen16] |

View File

@@ -15,6 +15,7 @@ You need to specify the network interface and the network mask. You can also con
* One subnet: `SCAN_SUBNETS = ['192.168.1.0/24 --interface=eth0']`
* Two subnets: `SCAN_SUBNETS = ['192.168.1.0/24 --interface=eth0','192.168.1.0/24 --interface=eth1 -vlan=107']`
If you get timeout messages, decrease the network mask (e.g.: from a `/16` to `/24`) or increase the `TIMEOUT` setting (e.g.: `ARPSCAN_RUN_TIMEOUT` to `300` (a timeout of 5min)) for the plugin and the interval between scans (e.g.: `ARPSCAN_RUN_SCHD` to `*/10 * * * *` (scans every 10 min)).
## Explanation

View File

@@ -745,6 +745,18 @@ height: 50px;
.infobox_label {
font-size: 16px !important;
}
.deviceSelector
{
display: block;
}
.deviceSelector input
{
width: 100% !important;
display: inline-grid;
}
/* --------------------------------------------------------- */
/* report */
/* --------------------------------------------------------- */
@@ -1079,6 +1091,27 @@ input[readonly] {
margin-bottom:20px;
}
#settingsPage .select2-selection
{
width: initial;
display: inline-block;
}
#settingsPage .select2-selection
{
background-color: rgb(96, 96, 96);
}
#settingsPage .select2-container
{
width: 100% !important;
}
#settingsPage .select2-container .selection
{
width: 100% !important;
display: inline-grid;
}
/* ----------------------------------------------------------------- */
/* Devices page */
/* ----------------------------------------------------------------- */
@@ -1110,18 +1143,18 @@ input[readonly] {
cursor: -webkit-grab;
}
.db_info_table_row .select2-container--default .select2-selection--multiple .select2-selection__choice
.select2-container--default .select2-selection--multiple .select2-selection__choice
{
background-color:#258744;
background-color:#258744 !important;
}
.db_info_table_row .select2-container--default .select2-selection--multiple
.select2-container--default .select2-selection--multiple
{
background-color:#606060;
background-color:#606060 !important;
}
.select2-container .select2-dropdown
{
background-color:#606060;
background-color:#606060 !important;
}
.networkPageHelp{

View File

@@ -693,7 +693,6 @@ if ($ENABLED_DARKMODE === True) {
var pos = -1;
var parPeriod = 'Front_Details_Period';
var parTab = 'Front_Details_Tab';
var parSessionsRows = 'Front_Details_Sessions_Rows';
var parEventsRows = 'Front_Details_Events_Rows';
var parEventsHide = 'Front_Details_Events_Hide';
@@ -736,7 +735,7 @@ function main () {
$('#chkHideConnectionEvents')[0].checked = eval(eventsHide == 'true');
// Initialize components with parameters
initializeTabs();
initializeTabsNew();
initializeiCheck();
initializeCombos();
initializeDatatables();
@@ -763,17 +762,6 @@ function main () {
}
// -----------------------------------------------------------------------------
function initializeTabs () {
// Activate panel
$('.nav-tabs a[id='+ tab +']').tab('show');
// When changed save new current tab
$('a[data-toggle="tab"]').on('shown.bs.tab', function (e) {
setParameter (parTab, $(e.target).attr('id'));
});
}
// -----------------------------------------------------------------------------
function initializeiCheck () {
// Blue

View File

@@ -137,136 +137,70 @@
<!-- page script ----------------------------------------------------------- -->
<script>
var deviceStatus = 'all';
var parTableRows = 'Front_Devices_Rows';
var parTableOrder = 'Front_Devices_Order';
var tableRows = 10;
var tableOrder = [[3,'desc'], [0,'asc']];
var tableRows = getCookie ("nax_parTableRows") == "" ? 10 : getCookie ("nax_parTableRows") ;
var tableOrder = getCookie ("nax_parTableOrder") == "" ? [[3,'desc'], [0,'asc']] : JSON.parse(getCookie ("nax_parTableOrder")) ;
var tableColumnHide = [];
var tableColumnOrder = [];
var tableColumnVisible = [];
// Read parameters & Initialize components
callAfterAppInitialized(main)
showSpinner();
// -----------------------------------------------------------------------------
function main () {
//initialize the table headers in the correct order
var headersDefaultOrder = [
getString('Device_TableHead_Name'),
getString('Device_TableHead_Owner'),
getString('Device_TableHead_Type'),
getString('Device_TableHead_Icon'),
getString('Device_TableHead_Favorite'),
getString('Device_TableHead_Group'),
getString('Device_TableHead_FirstSession'),
getString('Device_TableHead_LastSession'),
getString('Device_TableHead_LastIP'),
getString('Device_TableHead_MAC'),
getString('Device_TableHead_Status'),
getString('Device_TableHead_MAC_full'),
getString('Device_TableHead_LastIPOrder'),
getString('Device_TableHead_Rowid'),
getString('Device_TableHead_Parent_MAC'),
getString('Device_TableHead_Connected_Devices'),
getString('Device_TableHead_Location'),
getString('Device_TableHead_Vendor'),
getString('Device_TableHead_Port'),
getString('Device_TableHead_GUID'),
getString('Device_TableHead_SyncHubNodeName'),
getString('Device_TableHead_NetworkSite'),
getString('Device_TableHead_SSID')
];
//initialize the table headers in the correct order
var availableColumns = getSettingOptions("UI_device_columns").split(",");
var headersDefaultOrder = availableColumns.map(val => getString(val));
var selectedColumns = JSON.parse(getSetting("UI_device_columns").replace(/'/g, '"'));
// generate default order lists of given length
var columnsStr = JSON.stringify(Array.from({ length: headersDefaultOrder.length }, (_, i) => i));
tableColumnOrder = Array.from({ length: headersDefaultOrder.length }, (_, i) => i);
tableColumnVisible = tableColumnOrder;
tableColumnVisible = [];
handleLoadingDialog()
// Initialize tableColumnVisible by including all columns from selectedColumns, preserving their order.
tableColumnVisible = selectedColumns.map(column => availableColumns.indexOf(column)).filter(index => index !== -1);
// Add any columns from availableColumns that are not in selectedColumns to the end.
const remainingColumns = availableColumns.map((column, index) => index).filter(index => !tableColumnVisible.includes(index));
// Combine both arrays.
tableColumnOrder = tableColumnVisible.concat(remainingColumns);
// Generate the full array of numbers from 0 to totalLength - 1 of tableColumnOrder
const fullArray = Array.from({ length: tableColumnOrder.length }, (_, i) => i);
// Filter out the elements already present in inputArray
const missingNumbers = fullArray.filter(num => !tableColumnVisible.includes(num));
// Concatenate the inputArray with the missingNumbers
tableColumnOrder = [...tableColumnVisible, ...missingNumbers];
// render table headers
html = '';
for(index = 0; index < tableColumnOrder.length; index++)
{
html += '<th>' + headersDefaultOrder[tableColumnOrder[index]] + '</th>';
}
$('#tableDevices tr').html(html);
// Hide UI elements as per settings
// setTimeout(() => {
hideUIelements("UI_DEV_SECTIONS")
// }, 10);
// Initialize components with parameters
initializeDatatable(getUrlAnchor('my_devices'));
// check if data outdated and show spinner if so
handleLoadingDialog()
// get from cookie if available (need to use decodeURI as saved as part of URI in PHP)
cookieColumnsVisibleStr = decodeURI(getCookie("Front_Devices_Columns_Visible")).replaceAll('%2C',',')
defaultValue = cookieColumnsVisibleStr == "" ? columnsStr : cookieColumnsVisibleStr;
// get visible columns
$.get('php/server/parameters.php?action=get&expireMinutes=525600&defaultValue='+defaultValue+'&parameter=Front_Devices_Columns_Visible&skipcache', function(data) {
handle_locked_DB(data)
// save which columns are in the Devices page visible
tableColumnVisible = numberArrayFromString(data);
// get from cookie if available (need to use decodeURI as saved as part of URI in PHP)
cookieColumnsOrderStr = decodeURI(getCookie("Front_Devices_Columns_Order")).replaceAll('%2C',',')
defaultValue = cookieColumnsOrderStr == "" ? columnsStr : cookieColumnsOrderStr;
// get the custom order specified by the user
$.get('php/server/parameters.php?action=get&expireMinutes=525600&defaultValue='+defaultValue+'&parameter=Front_Devices_Columns_Order&skipcache', function(data) {
handle_locked_DB(data)
// save the columns order in the Devices page
tableColumnOrder = numberArrayFromString(data);
html = '';
for(index = 0; index < tableColumnOrder.length; index++)
{
html += '<th>' + headersDefaultOrder[tableColumnOrder[index]] + '</th>';
}
$('#tableDevices tr').html(html);
// get parameter value
$.get('php/server/parameters.php?action=get&defaultValue=50&parameter='+ parTableRows, function(data) {
var result = JSON.parse(data);
result = parseInt(result, 10)
if (Number.isInteger (result) ) {
tableRows = result;
}
// get parameter value
$.get('php/server/parameters.php?action=get&defaultValue=[[3,"desc"],[0,"asc"]]&parameter='+ parTableOrder, function(data) {
var result = JSON.parse(data);
result = JSON.parse(result);
if (Array.isArray (result) ) {
tableOrder = result;
}
// Initialize components with parameters
initializeDatatable(getUrlAnchor('my_devices'));
// check if data outdated and show spinner if so
handleLoadingDialog()
});
});
});
});
}
// -----------------------------------------------------------------------------
@@ -467,6 +401,11 @@ function initializeDatatable (status) {
}
$.get('api/table_devices.json?nocache=' + Date.now(), function(result) {
// refresh devices cache
devicesListAll_JSON = result["data"]
devicesListAll_JSON_str = JSON.stringify(devicesListAll_JSON)
setCache('devicesListAll_JSON', devicesListAll_JSON_str)
// query data
getDevicesTotals(result.data);
@@ -515,10 +454,6 @@ function initializeDatatable (status) {
})
};
// TODO displayed columns
// Check if the DataTable already exists
if ($.fn.dataTable.isDataTable('#tableDevices')) {
// The DataTable exists, so destroy it
@@ -526,12 +461,12 @@ function initializeDatatable (status) {
table.clear().destroy();
}
var table=
var table =
$('#tableDevices').DataTable({
'data' : dataArray["data"],
'paging' : true,
'lengthChange' : true,
'lengthMenu' : [[10, 25, 50, 100, 500, -1], [10, 25, 50, 100, 500, getString('Device_Tablelenght_all')]],
'lengthMenu' : [[10, 25, 50, 100, 500, 100000], [10, 25, 50, 100, 500, getString('Device_Tablelenght_all')]],
'searching' : true,
'ordering' : true,
@@ -679,11 +614,11 @@ function initializeDatatable (status) {
// Save cookie Rows displayed, and Parameters rows & order
$('#tableDevices').on( 'length.dt', function ( e, settings, len ) {
setParameter (parTableRows, len);
setCookie ("nax_parTableRows", len);
} );
$('#tableDevices').on( 'order.dt', function () {
setParameter (parTableOrder, JSON.stringify (table.order()) );
setCookie ("nax_parTableOrder", JSON.stringify (table.order()) );
setCache ('devicesList', getDevicesFromTable(table) );
} );
@@ -703,8 +638,6 @@ function initializeDatatable (status) {
// Check if any row is selected
var anyRowSelected = $('#tableDevices tr.selected').length > 0;
console.log(anyRowSelected);
// Toggle visibility of element with ID 'multiEdit'
$('#multiEdit').toggle(anyRowSelected);
}, 200);

View File

@@ -12,7 +12,7 @@ var timerRefreshData = ''
var emptyArr = ['undefined', "", undefined, null, 'null'];
var UI_LANG = "English";
const allLanguages = ["en_us", "es_es", "de_de", "fr_fr", "it_it", "ru_ru", "nb_no", "pl_pl", "zh_cn", "cs_cz"]; // needs to be same as in lang.php
const allLanguages = ["en_us", "es_es", "de_de", "fr_fr", "it_it", "ru_ru", "nb_no", "pl_pl", "pt_br", "tr_tr", "zh_cn", "cs_cz"]; // needs to be same as in lang.php
var settingsJSON = {}
@@ -212,7 +212,8 @@ function cacheStrings() {
return new Promise((resolve, reject) => {
// Create a promise for each language
const languagePromises = allLanguages.map((language_code) => {
languagesToLoad = ['en_us', getLangCode()]
const languagePromises = languagesToLoad.map((language_code) => {
return new Promise((resolveLang, rejectLang) => {
// Fetch core strings and translations
@@ -267,6 +268,29 @@ function cacheStrings() {
function getString(key) {
function fetchString(key) {
lang_code = getLangCode();
let result = getCache(`pia_lang_${key}_${lang_code}`, true);
if (isEmpty(result)) {
result = getCache(`pia_lang_${key}_en_us`, true);
}
return result;
}
if (isAppInitialized()) {
return fetchString(key);
} else {
callAfterAppInitialized(() => fetchString(key));
}
}
// -----------------------------------------------------------------------------
// Get current language ISO code
function getLangCode() {
UI_LANG = getSetting("UI_LANG");
let lang_code = 'en_us';
@@ -310,20 +334,7 @@ function getString(key) {
break;
}
let result = getCache(`pia_lang_${key}_${lang_code}`, true);
if (isEmpty(result)) {
result = getCache(`pia_lang_${key}_en_us`, true);
}
return result;
}
if (isAppInitialized()) {
return fetchString(key);
} else {
callAfterAppInitialized(() => fetchString(key));
}
return lang_code;
}
@@ -1135,6 +1146,54 @@ function arraysContainSameValues(arr1, arr2) {
}
}
// -----------------------------------------------------------------------------
// Hide elements on the page based on the supplied setting
function hideUIelements(settingKey) {
hiddenSectionsSetting = getSetting(settingKey)
if(hiddenSectionsSetting != "") // handle if settings not yet initialized
{
sectionsArray = createArray(hiddenSectionsSetting)
// remove spaces to get IDs
var newArray = $.map(sectionsArray, function(value) {
return value.replace(/\s/g, '');
});
$.each(newArray, function(index, hiddenSection) {
if($('#' + hiddenSection))
{
$('#' + hiddenSection).hide()
}
});
}
}
// -----------------------------------------------------------------------------
// apply dark mode
$(document).ready(function() {
// Assume getSetting is a function that returns true or false for dark mode
if (getSetting("UI_dark_mode") === "True") {
// Add the dark mode stylesheet
setCookie("UI_dark_mode", "True")
$('head').append('<link rel="stylesheet" href="css/dark-patch.css">');
// Set the background image for dark mode
$('body').attr('style', 'background-image: url(\'img/boxed-bg-dark.png\');');
} else {
setCookie("UI_dark_mode", "False")
// Set the background image for light mode
$('body').attr('style', 'background-image: url(\'img/background.png\');');
}
});
// -----------------------------------------------------------------------------
// initialize
// -----------------------------------------------------------------------------
@@ -1145,7 +1204,7 @@ const sessionStorageKey = "myScriptExecuted_common_js";
var completedCalls = []
var completedCalls_final = ['cacheSettings', 'cacheStrings', 'cacheDevices'];
var completedCallsCount = 0;
var completedCallsCount_final = allLanguages.length + 2; // number of language files + cacheDevices + cacheSettings
var completedCallsCount_final;
// -----------------------------------------------------------------------------
// Clearing all the caches
@@ -1201,7 +1260,11 @@ function callAfterAppInitialized(callback) {
// Check if the code has been executed before by checking sessionStorage
function isAppInitialized() {
// return arraysContainSameValues(getCache("completedCalls").split(',').filter(Boolean), completedCalls_final);
return (parseInt(getCache("completedCallsCount")) == completedCallsCount_final);
// loading settings + 1 (or 2 language files if not english) + device cache.
completedCallsCount_final = getLangCode() == 'en_us' ? 3 : 4 ;
return (parseInt(getCache("completedCallsCount")) >= completedCallsCount_final);
}
// Define a function that will execute the code only once

View File

@@ -304,6 +304,54 @@ function removeAllOptions(element) {
$(`#${$(element).attr("my-input-to")}`).empty();
}
// -------------------------------------------------------------------
// Add all options
function selectAll(element) {
settingsChanged();
// Get the <select> element with the class 'deviceSelector'
// var selectElement = $('.deviceSelector select');
var selectElement = $(`#${$(element).attr("my-input-to")}`);
// Iterate over each option within the select element
selectElement.find('option').each(function() {
// Mark each option as selected
$(this).prop('selected', true);
});
// Trigger the 'change' event to notify Bootstrap Select of the changes
selectElement.trigger('change');
}
// -----------------------------------------------------------------------------
// UN-Select All
function unselectAll(element) {
settingsChanged();
// Get the <select> element with the class 'deviceSelector'
// var selectElement = $('.deviceSelector select');
var selectElement = $(`#${$(element).attr("my-input-to")}`);
// Iterate over each option within the select element
selectElement.find('option').each(function() {
// Unselect each option
$(this).prop('selected', false);
});
// Trigger the 'change' event to notify Bootstrap Select of the changes
selectElement.trigger('change');
}
// -----------------------------------------------------------------------------
// Trigger change to open up the dropdown filed
function selectChange(element) {
settingsChanged();
// Get the <select> element with the class 'deviceSelector'
// var selectElement = $('.deviceSelector select');
var selectElement = $(`#${$(element).attr("my-input-to")}`);
selectElement.parent().find("input").focus().click();
}
// -------------------------------------------------------------------
// Function to initialize remove functionality on select options
@@ -437,8 +485,6 @@ function filterRows(inputText) {
}
setTimeout(() => {
// Event listener for input change
$("#settingsSearch").on("input", function () {
@@ -568,6 +614,10 @@ function applyTransformers(val, transformers) {
val = btoa(val);
}
break;
case "getString":
// no change
val = val;
break;
default:
console.warn(`Unknown transformer: ${transformer}`);
}
@@ -590,6 +640,10 @@ function reverseTransformers(val, transformers) {
val = atob(val);
}
break;
case "getString":
// retrieve string
val = getString(val);
break;
default:
console.warn(`Unknown transformer: ${transformer}`);
}
@@ -703,8 +757,9 @@ function generateOptions(options, valuesArray, targetField, transformers, placeh
resultArray = []
selectedArray = []
cssClass = ""
// determine if options or values are used in teh listing
// determine if options or values are used in the listing
if (valuesArray.length > 0 && options.length > 0){
// multiselect list -> options only + selected the ones in valuesArray
@@ -722,21 +777,31 @@ function generateOptions(options, valuesArray, targetField, transformers, placeh
resultArray = options;
}
// Create a map to track the index of each item in valuesArray
const orderMap = new Map(valuesArray.map((item, index) => [item, index]));
// Sort resultArray based on the order in valuesArray
resultArray.sort((a, b) => {
const indexA = orderMap.has(a.id) ? orderMap.get(a.id) : valuesArray.length;
const indexB = orderMap.has(b.id) ? orderMap.get(b.id) : valuesArray.length;
return indexA - indexB;
});
resultArray.forEach(function(item) {
let labelName = item.name;
labelName = item.name
if(labelName != '❌None')
{
labelName = reverseTransformers(labelName, transformers)
if (labelName !== '❌None') {
labelName = reverseTransformers(labelName, transformers);
}
// needs to happen always if options ued as source
let selected = options.length != 0 && valuesArray.includes(item.id) ? 'selected' : '';
// Always include selected if options are used as a source
let selected = options.length !== 0 && valuesArray.includes(item.id) ? 'selected' : '';
optionsHtml += `<option class="${cssClass}" value="${item.id}" ${selected}>${labelName}</option>`;
});
// Place the resulting HTML into the specified placeholder div
$("#" + placeholder).replaceWith(optionsHtml);
}

View File

@@ -68,38 +68,6 @@ function initDeviceSelectors(devicesListAll_JSON) {
}
// -----------------------------------------------------------------------------
// Hide elements on the page based on the supplied setting
function hideUIelements(settingKey) {
hiddenSectionsSetting = getSetting(settingKey)
if(hiddenSectionsSetting != "") // handle if settings not yet initialized
{
sectionsArray = createArray(hiddenSectionsSetting)
// remove spaces to get IDs
var newArray = $.map(sectionsArray, function(value) {
return value.replace(/\s/g, '');
});
$.each(newArray, function(index, hiddenSection) {
if($('#' + hiddenSection))
{
$('#' + hiddenSection).hide()
}
});
}
}
// -----------------------------------------------------------------------------
// Updates the icon preview
function updateIconPreview (inputId) {
@@ -249,6 +217,7 @@ function initSelect2() {
// --------------------------------------------------------
//Initialize Select2 Elements and make them sortable
$(function () {
// Iterate over each Select2 dropdown
$('.select2').each(function() {
@@ -283,10 +252,15 @@ function initSelect2() {
}
}
// try to initialize select2
setTimeout(() => {
initSelect2()
}, 500);
// init select2 after dom laoded
window.addEventListener("load", function() {
// try to initialize select2
setTimeout(() => {
initSelect2()
}, 1000);
});
console.log("init ui_components.js")

View File

@@ -11,25 +11,7 @@
# cvc90 2023 https://github.com/cvc90 GNU GPLv3 #
#---------------------------------------------------------------------------------#
// Skin selector config ----------------------------------------------------
//
// For security reasons, new language files must be entered into this array.
// The files in the language directory are compared with this array and only
// then accepted.
//
$pia_installed_skins = array('skin-black-light',
'skin-black',
'skin-blue-light',
'skin-blue',
'skin-green-light',
'skin-green',
'skin-purple-light',
'skin-purple',
'skin-red-light',
'skin-red',
'skin-yellow-light',
'skin-yellow');
//------------------------------------------------------------------------------
?>
@@ -38,7 +20,7 @@ $pia_installed_skins = array('skin-black-light',
require 'php/templates/header.php';
?>
<!-- Page ------------------------------------------------------------------ -->
<div class="content-wrapper">
<div class="content-wrapper" id="maintenancePage">
<!-- Content header--------------------------------------------------------- -->
<section class="content-header">
@@ -91,34 +73,6 @@ if (count($latestfiles) > 0)
$latestbackup_date = date ("Y-m-d H:i:s", filemtime($latestbackup));
}
// Skin selector -----------------------------------------------------------------
if (isset($_POST['submit']) && submit && isset($_POST['skinselector_set'])) {
$pia_skin_set_dir = '../db/';
$pia_skin_selector = htmlspecialchars($_POST['skinselector']);
if (in_array($pia_skin_selector, $pia_installed_skins)) {
foreach ($pia_installed_skins as $file) {
unlink ($pia_skin_set_dir.'/setting_'.$file);
}
foreach ($pia_installed_skins as $file) {
if (file_exists($pia_skin_set_dir.'/setting_'.$file)) {
$pia_skin_error = True;
break;
} else {
$pia_skin_error = False;
}
}
if ($pia_skin_error == False) {
$testskin = fopen($pia_skin_set_dir.'setting_'.$pia_skin_selector, 'w');
$pia_skin_test = '';
echo("<meta http-equiv='refresh' content='1'>");
} else {
$pia_skin_test = '';
echo("<meta http-equiv='refresh' content='1'>");
}
}
}
// Table sizes -----------------------------------------------------------------
$tableSizesHTML = "";
@@ -215,13 +169,7 @@ $db->close();
<div class="nav-tabs-custom">
<ul class="nav nav-tabs">
<li class="active">
<a id="tab_Settings_id" href="#tab_Settings" data-toggle="tab">
<i class="fa fa-cogs"></i>
<?= lang('Maintenance_Tools_Tab_UISettings');?>
</a>
</li>
<li>
<li class="active">
<a id="tab_DBTools_id" href="#tab_DBTools" data-toggle="tab">
<i class="fa fa-toolbox"></i>
<?= lang('Maintenance_Tools_Tab_Tools');?>
@@ -247,84 +195,7 @@ $db->close();
</li>
</ul>
<div class="tab-content">
<div class="tab-pane active" id="tab_Settings">
<div class="db_info_table">
<div class="db_info_table_row">
<div class="db_tools_table_cell_a" style="text-align: center;">
<form method="post" action="maintenance.php">
<div style="display: inline-block; text-align: center;">
<select name="skinselector" class="form-control bg-green" style="width:160px; margin-bottom:5px;">
<option value=""><?= lang('Maintenance_themeselector_empty');?></option>
<option value="skin-black-light">black light</option>
<option value="skin-black">black</option>
<option value="skin-blue-light">blue light</option>
<option value="skin-blue">blue</option>
<option value="skin-green-light">green light</option>
<option value="skin-green">green</option>
<option value="skin-purple-light">purple light</option>
<option value="skin-purple">purple</option>
<option value="skin-red-light">red light</option>
<option value="skin-red">red</option>
<option value="skin-yellow-light">yellow light</option>
<option value="skin-yellow">yellow</option>
</select></div>
<div style="display: block;"><input type="submit" name="skinselector_set" value="<?= lang('Maintenance_themeselector_apply');?>" class="btn bg-green" style="width:160px;">
<?php // echo $pia_skin_test; ?>
</div>
</form>
</div>
<div class="db_info_table_cell" style="padding: 10px; height:40px; text-align:left; vertical-align: middle;">
<?= lang('Maintenance_themeselector_text'); ?>
</div>
</div>
<div class="db_info_table_row">
<div class="db_tools_table_cell_a">
<button type="button" class="btn bg-green dbtools-button" id="btnToggleDarkmode" onclick="askToggleDarkmode()"><?= lang('Maintenance_Tool_darkmode');?></button>
</div>
<div class="db_tools_table_cell_b"><?= lang('Maintenance_Tool_darkmode_text');?></div>
</div>
<div class="db_info_table_row">
<div class="db_tools_table_cell_a">
<div class="form-group" >
<div class="input-group" >
<select id="columnsSelect" class="form-control select2 select2-hidden-accessible" multiple="" style="width: 100%;" tabindex="-1" aria-hidden="true">
<option value="0"><?= lang('Device_TableHead_Name');?></option>
<option value="1"><?= lang('Device_TableHead_Owner');?></option>
<option value="2"><?= lang('Device_TableHead_Type');?></option>
<option value="3"><?= lang('Device_TableHead_Icon');?></option>
<option value="4"><?= lang('Device_TableHead_Favorite');?></option>
<option value="5"><?= lang('Device_TableHead_Group');?></option>
<option value="6"><?= lang('Device_TableHead_FirstSession');?></option>
<option value="7"><?= lang('Device_TableHead_LastSession');?></option>
<option value="8"><?= lang('Device_TableHead_LastIP');?></option>
<option value="9"><?= lang('Device_TableHead_MAC');?></option>
<option value="10"><?= lang('Device_TableHead_Status');?></option>
<option value="11"><?= lang('Device_TableHead_MAC_full');?></option>
<option value="12"><?= lang('Device_TableHead_LastIPOrder');?></option>
<option value="13"><?= lang('Device_TableHead_Rowid');?></option>
<option value="14"><?= lang('Device_TableHead_Parent_MAC');?></option>
<option value="15"><?= lang('Device_TableHead_Connected_Devices');?></option>
<option value="16"><?= lang('Device_TableHead_Location');?></option>
<option value="17"><?= lang('Device_TableHead_Vendor');?></option>
<option value="18"><?= lang('Device_TableHead_Port');?></option>
<option value="19"><?= lang('Device_TableHead_GUID');?></option>
<option value="20"><?= lang('Device_TableHead_SyncHubNodeName');?></option>
<option value="21"><?= lang('Device_TableHead_NetworkSite');?></option>
<option value="22"><?= lang('Device_TableHead_SSID');?></option>
</select>
<span class="input-group-addon"><i title="<?= lang('Gen_Save');?>" class="fa fa-save pointer" onclick="saveSelectedColumns();"></i></span>
</div>
</div>
</div>
<div class="db_tools_table_cell_b"><?= lang('Maintenance_Tool_displayed_columns_text');?></div>
</div>
</div>
</div>
<div class="tab-pane" id="tab_DBTools">
<div class="tab-pane active" id="tab_DBTools">
<div class="db_info_table">
<div class="db_info_table_row">
<div class="db_tools_table_cell_a" >
@@ -483,7 +354,7 @@ $db->close();
<script>
var emptyArr = ['undefined', "", undefined, null];
var selectedTab = 'tab_Settings_id';
var selectedTab = 'tab_DBTools_id';
initializeTabs();
@@ -709,42 +580,6 @@ function ImportPastedCSV()
}
// --------------------------------------------------------
// Switch Darkmode
function askToggleDarkmode() {
// Ask
showModalWarning('<?= lang('Maintenance_Tool_darkmode_noti');?>', '<?= lang('Maintenance_Tool_darkmode_noti_text');?>',
'<?= lang('Gen_Cancel');?>', '<?= lang('Gen_Switch');?>', 'ToggleDarkmode');
}
// --------------------------------------------------------
function ToggleDarkmode()
{
// get parameter Front_Dark_Mode_Enabled value
$.get('php/server/parameters.php?action=get&defaultValue=false&expireMinutes=525600&parameter=Front_Dark_Mode_Enabled', function(data) {
var result = JSON.parse(data);
if (result) {
darkModeEnabled = result == 'true';
// invert value
darkModeEnabled = !darkModeEnabled;
// save inverted value
$.get('php/server/parameters.php?action=set&parameter=Front_Dark_Mode_Enabled&expireMinutes=525600&value='+ darkModeEnabled,
function(data) {
if (data != "OK") {
showMessage (data);
setTimeout(function (){location.reload()}, 1000);
} else {
showMessage (data);
};
} );
}
});
}
// --------------------------------------------------------
// Clean log file
@@ -797,122 +632,56 @@ function scrollDown() {
}
// --------------------------------------------------------
// Manage displayed columns
// --------------------------------------------------------
colDefaultOrder = ['0','1','2','3','4','5','6','7','8','9','10','11','12','13','14','15','16','17'];
colDefaultOrderTxt = '[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17]';
function saveSelectedColumns () {
$.get('php/server/parameters.php?action=set&expireMinutes=525600&value=['+ $('#columnsSelect').val().toString() +']&parameter=Front_Devices_Columns_Visible', function(data) {
// save full order of all columns to simplify mapping later on
colDisplayed = $('#columnsSelect').val();
colNewOrder = colDisplayed;
// append the remaining columns in the previous order
for(i = 0; i < colDefaultOrder.length; i++)
{
if(!colDisplayed.includes(colDefaultOrder[i]))
{
colNewOrder.push(colDefaultOrder[i])
}
}
// save the setting in the DB
$.get('php/server/parameters.php?action=set&expireMinutes=525600&value=['+ colNewOrder.toString() +']&parameter=Front_Devices_Columns_Order', function(data) {
showMessage(data);
});
});
}
// --------------------------------------------------------
function initializeSelectedColumns () {
$.get('php/server/parameters.php?action=get&expireMinutes=525600&defaultValue='+colDefaultOrderTxt+'&parameter=Front_Devices_Columns_Visible', function(data) {
handle_locked_DB(data)
tableColumnShow = numberArrayFromString(data);
for(i=0; i < tableColumnShow.length; i++)
{
// create the option and append to Select2
var option = new Option($('#columnsSelect option[value='+tableColumnShow[i]+']').html(), tableColumnShow[i] , true, true);
$("#columnsSelect").append(option).trigger('change');
}
});
}
// --------------------------------------------------------
// General initialization
// --------------------------------------------------------
function initializeTabs () {
setTimeout(function() {
key = "activeMaintenanceTab"
function initializeTabs() {
setTimeout(() => {
const key = "activeMaintenanceTab";
// default selection
selectedTab = "tab_Settings"
let selectedTab = "tab_DBTools_id";
// the #target from the url
target = window.location.hash.substr(1)
// the #target from the URL
let target = window.location.hash.substr(1);
console.log(selectedTab);
// get only the part between #...?
if(target.includes('?'))
{
target = target.split('?')[0]
if (target.includes('?')) {
target = target.split('?')[0];
}
console.log(target);
// update cookie if target specified
if(target != "")
{
if (!selectedTab.endsWith("_id")) {
selectedTab = target + "_id";
}
setCache(key, selectedTab) // _id is added so it doesn't conflict with AdminLTE tab behavior
if (target) {
selectedTab = target.endsWith("_id") ? target : `${target}_id`;
setCache(key, selectedTab); // _id is added so it doesn't conflict with AdminLTE tab behavior
}
// get the tab id from the cookie (already overriden by the target)
if(!emptyArr.includes(getCache(key)))
{
selectedTab = getCache(key);
// get the tab id from the cookie (already overridden by the target)
const cachedTab = getCache(key);
if (cachedTab && !emptyArr.includes(cachedTab)) {
selectedTab = cachedTab;
}
// Activate panel
$('.nav-tabs a[id='+ selectedTab +']').tab('show');
// When changed save new current tab
$('a[data-toggle="tab"]').on('shown.bs.tab', function (e) {
setCache(key, $(e.target).attr('id'))
$('a[data-toggle="tab"]').on('shown.bs.tab', (e) => {
const newTabId = $(e.target).attr('id');
setCache(key, newTabId);
});
// events on tab change
$('a[data-toggle="tab"]').on('shown.bs.tab', function (e) {
var target = $(e.target).attr("href") // activated tab
$('a[data-toggle="tab"]').on('shown.bs.tab', (e) => {
const newTarget = $(e.target).attr("href"); // activated tab
});
hideSpinner();
}, 50);
}
//------------------------------------------------------------------------------
// Logs render functionality
//------------------------------------------------------------------------------
@@ -932,7 +701,7 @@ function toggleAutoRefresh() {
}
//------------------------------------------------------------------------------
// Manages thefilter application
// Manages the filter application on the logs
function applyFilter() {
const filterText = $("#logsFilter").val().toLowerCase();
@@ -979,7 +748,6 @@ function renderLogs(customData) {
//------------------------------------------------------------------------------
// Init
window.onload = function asyncFooter() {
initializeSelectedColumns();
renderLogs();
// initializeTabs();

View File

@@ -13,7 +13,7 @@
<h3 class="box-title"><?= lang('Gen_Selected_Devices');?></h3>
</div>
<div class="deviceSelector col-md-9" style="z-index:5"></div>
<div class="deviceSelector col-md-9 col-sm-12" style="z-index:5"></div>
<div class="col-md-3">
<button type="button" class="btn btn-default" onclick="markAllSelected()">

View File

@@ -54,7 +54,7 @@
<!-- NetAlertX -->
<script src="js/handle_version.js"></script>
<script src="js/ui_components.js?v=<?php include 'php/templates/version.php'; ?>"></script>
<script defer src="js/ui_components.js?v=<?php include 'php/templates/version.php'; ?>"></script>
<!-- Select2 JavaScript -->

View File

@@ -67,14 +67,16 @@ require dirname(__FILE__).'/security.php';
<link id="favicon" rel="icon" type="image/x-icon" href="img/NetAlertX_logo.png">
<!-- For better UX on Mobile Devices using the Shortcut on the Homescreen -->
<link rel="manifest" href="img/manifest.json">
<link rel="manifest" href="img/manifest.json" crossorigin="use-credentials">
<!-- Dark-Mode Patch -->
<?php
if ($ENABLED_DARKMODE === True) {
echo '<link rel="stylesheet" href="css/dark-patch.css">';
$BACKGROUND_IMAGE_PATCH='style="background-image: url(\'img/boxed-bg-dark.png\');"';
} else { $BACKGROUND_IMAGE_PATCH='style="background-image: url(\'img/background.png\');"';}
?>
<?php
if ($ENABLED_DARKMODE === True) {
echo '<link rel="stylesheet" href="css/dark-patch.css">';
$BACKGROUND_IMAGE_PATCH='style="background-image: url(\'img/boxed-bg-dark.png\');"';
} else { $BACKGROUND_IMAGE_PATCH='style="background-image: url(\'img/background.png\');"';}
?>
<!-- Servertime to the right of the hostname -->
@@ -312,9 +314,6 @@ if ($ENABLED_DARKMODE === True) {
</span>
</a>
<ul class="treeview-menu" style="display: <?php if (in_array (basename($_SERVER['SCRIPT_NAME']), array('maintenance.php') ) ){ echo 'block'; } else {echo 'none';} ?>;">
<li>
<a href="maintenance.php#tab_Settings" onclick="initializeTabs()"> <?= lang("Maintenance_Tools_Tab_UISettings");?> </a>
</li>
<li>
<a href="maintenance.php#tab_DBTools" onclick="initializeTabs()"> <?= lang("Maintenance_Tools_Tab_Tools");?> </a>
</li>

View File

@@ -274,6 +274,7 @@
"Gen_AreYouSure": "",
"Gen_Backup": "",
"Gen_Cancel": "",
"Gen_Change": "",
"Gen_Copy": "",
"Gen_DataUpdatedUITakesTime": "",
"Gen_Delete": "",
@@ -556,6 +557,7 @@
"RandomMAC_hover": "",
"Reports_Sent_Log": "",
"SCAN_SUBNETS_description": "",
"SCAN_SUBNETS_name": "",
"SYSTEM_TITLE": "",
"Setting_Override": "",
"Setting_Override_Description": "",
@@ -654,6 +656,8 @@
"UI_PRESENCE_name": "",
"UI_REFRESH_description": "",
"UI_REFRESH_name": "",
"VERSION_description": "",
"VERSION_name": "",
"devices_old": "",
"general_event_description": "",
"general_event_title": "",

View File

@@ -286,6 +286,7 @@
"Gen_AreYouSure": "Sind Sie sich sicher?",
"Gen_Backup": "Sichern",
"Gen_Cancel": "Abbrechen",
"Gen_Change": "",
"Gen_Copy": "Run",
"Gen_DataUpdatedUITakesTime": "OK - It may take a while for the UI to update if a scan is runnig",
"Gen_Delete": "Löschen",
@@ -611,6 +612,7 @@
"RandomMAC_hover": "Autodetected - indicates if the device randomizes it's MAC address.",
"Reports_Sent_Log": "Protokoll gesendeter Berichte",
"SCAN_SUBNETS_description": "",
"SCAN_SUBNETS_name": "",
"SMTP_FORCE_SSL_description": "Force SSL when connecting to your SMTP server.",
"SMTP_FORCE_SSL_name": "Force SSL",
"SMTP_PASS_description": "The SMTP server password. ",
@@ -723,6 +725,8 @@
"UI_PRESENCE_name": "Anzeige im Präsenzdiagramm",
"UI_REFRESH_description": "",
"UI_REFRESH_name": "Automatisch Aktualisieren",
"VERSION_description": "",
"VERSION_name": "",
"WEBHOOK_PAYLOAD_description": "The Webhook payload data format for the <code>body</code> > <code>attachments</code> > <code>text</code> attribute in the payload json. See an example of the payload <a target=\"_blank\" href=\"https://github.com/jokob-sk/NetAlertX/blob/main/front/report_templates/webhook_json_sample.json\">here</a>. (e.g.: for discord use <code>text</code>)",
"WEBHOOK_PAYLOAD_name": "Payload type",
"WEBHOOK_REQUEST_METHOD_description": "The HTTP request method to be used for the webhook call.",

View File

@@ -274,6 +274,7 @@
"Gen_AreYouSure": "Are you sure?",
"Gen_Backup": "Run Backup",
"Gen_Cancel": "Cancel",
"Gen_Change": "Change",
"Gen_Copy": "Run",
"Gen_DataUpdatedUITakesTime": "OK - It may take a while for the UI to update if a scan is running.",
"Gen_Delete": "Delete",
@@ -556,6 +557,7 @@
"RandomMAC_hover": "Autodetected - indicates if the device randomizes it's MAC address.",
"Reports_Sent_Log": "Sent Reports Log",
"SCAN_SUBNETS_description": "Most on-network scanners (ARP-SCAN, NMAP, NSLOOKUP, DIG, PHOLUS) rely on scanning specific network interfaces and subnets. Check the <a href=\"https://github.com/jokob-sk/NetAlertX/blob/main/docs/SUBNETS.md\" target=\"_blank\">subnets documentation</a> for help on this setting, especially VLANs, what VLANs are supported, or how to figure out the network mask and your interface. <br/> <br/> An alternative to on-network scanners is to enable some other Device scanners/importers that don't rely on NetAlert<sup>X</sup> having access to the network (UNIFI, dhcp.leases, PiHole, etc.). <br/> <br/> Note: The scan time itself depends on the number of IP addresses to check, so set this up carefully with the appropriate network mask and interface.",
"SCAN_SUBNETS_name": "Networks to scan",
"SYSTEM_TITLE": "System Information",
"Setting_Override": "Override value",
"Setting_Override_Description": "Enabling this option will override an App supplied default value with the value specified above.",
@@ -654,6 +656,8 @@
"UI_PRESENCE_name": "Show in presence chart",
"UI_REFRESH_description": "Enter number of seconds after which the UI reloads. Set to <code>0</code> to disable.",
"UI_REFRESH_name": "Auto-refresh UI",
"VERSION_description": "Version or timestamp helper value to check if app was upgraded.",
"VERSION_name": "Version or timestamp",
"devices_old": "Refreshing...",
"general_event_description": "The event you have triggered might take a while until background processes finish. The execution ended once the below execution queue empties (Check the <a href='/maintenance.php#tab_Logging'>error log</a> if you encounter issues). <br/> <br/> Execution queue:",
"general_event_title": "Executing an ad-hoc event",

View File

@@ -284,6 +284,7 @@
"Gen_AreYouSure": "¿Estás seguro?",
"Gen_Backup": "Ejecutar copia de seguridad",
"Gen_Cancel": "Cancelar",
"Gen_Change": "",
"Gen_Copy": "Ejecutar",
"Gen_DataUpdatedUITakesTime": "Correcto - La interfaz puede tardar en actualizarse si se está ejecutando un escaneo.",
"Gen_Delete": "Eliminar",
@@ -723,6 +724,8 @@
"UI_PRESENCE_name": "Mostrar en el gráfico de presencia",
"UI_REFRESH_description": "Ingrese el número de segundos después de los cuales se recarga la interfaz de usuario. Ajustado a <code> 0 </code> para desactivar.",
"UI_REFRESH_name": "Actualización automática de la interfaz de usuario",
"VERSION_description": "",
"VERSION_name": "",
"WEBHOOK_PAYLOAD_description": "El formato de datos de carga de Webhook para el atributo <code>body</code> > <code>attachments</code> > <code>text</code> en el json de carga. Vea un ejemplo de la carga <a target=\"_blank\" href=\"https://github.com/jokob-sk/NetAlertX/blob/main/front/report_templates/webhook_json_sample.json\">aquí</a>. (por ejemplo: para discord use <code>text</code>)",
"WEBHOOK_PAYLOAD_name": "Tipo de carga",
"WEBHOOK_REQUEST_METHOD_description": "El método de solicitud HTTP que se utilizará para la llamada de webhook.",
@@ -770,4 +773,4 @@
"settings_update_item_warning": "Actualice el valor a continuación. Tenga cuidado de seguir el formato anterior. <b>O la validación no se realiza.</b>",
"test_event_icon": "fa-vial-circle-check",
"test_event_tooltip": "Guarda tus cambios antes de probar nuevos ajustes."
}
}

View File

@@ -274,6 +274,7 @@
"Gen_AreYouSure": "Êtes-vous sûr?",
"Gen_Backup": "Lancer la sauvegarde",
"Gen_Cancel": "Annuler",
"Gen_Change": "",
"Gen_Copy": "Lancer",
"Gen_DataUpdatedUITakesTime": "OK - cela peut prendre du temps à l'interface pour se mettre à jour si un scan est en cours.",
"Gen_Delete": "Supprimer",
@@ -556,6 +557,7 @@
"RandomMAC_hover": "Détecté automatiquement - indique si l'appareil dispose d'une adresse MAC générée aléatoirement.",
"Reports_Sent_Log": "Rapports de log transmis",
"SCAN_SUBNETS_description": "La plupart des scanners sur le réseau (scan ARP, NMAP, Nslookup, DIG, Pholud) se base sur le scan d'une partie spécifique des interfaces réseau ou de sous-réseau. Consulter la <a href=\"https://github.com/jokob-sk/NetAlertX/blob/main/docs/SUBNETS.md\" target=\"_blank\">documentation des sous-réseaux</a> pour plus d'aide sur ce paramètre, notamment pour des VLAN, lesquels sont supportés ou sur comment identifier le masque réseau et votre interface réseau. <br/> <br/> Une alternative à ces scanner sur le réseau et d'activer d'autres scanners d'appareils ou des importe, qui ne dépendent pas du fait de laisser NetAlert<sup>X</sup> accéder au réseau (Unifié, baux DHCP, Pi-hole, etc.).<br/><br/> Remarque: la durée du scan en lui-même dépend du nombre d'adresses IP à scanner, renseignez donc soigneusement avec le bon masque réseau et la bonne interface réseau.",
"SCAN_SUBNETS_name": "",
"SYSTEM_TITLE": "Informations système",
"Setting_Override": "Remplacer la valeur",
"Setting_Override_Description": "Activer cette option va remplacer la valeur fournie par défaut par une application par la valeur renseignée au-dessus.",
@@ -654,6 +656,8 @@
"UI_PRESENCE_name": "Afficher dans le graphique de présence",
"UI_REFRESH_description": "Renseignez le nombre de secondes après lequel rafraîchir l'interface graphique. Renseignez <code>0</code> pour désactiver.",
"UI_REFRESH_name": "Rafraîchir automatiquement l'interface graphique",
"VERSION_description": "",
"VERSION_name": "",
"devices_old": "Rafraichissement...",
"general_event_description": "L'événement que vous avez lancé peut prendre du temps avant que les tâches de fond ne soit terminées. La durée d'exécution finira une fois que la file d'exécution ci-dessous sera vide (consulter les <a href='/maintenance.php#tab_Logging'>journaux d'erreur</a> si vous rencontrez des erreurs). <br/> <br/> File d'exécution:",
"general_event_title": "Lancement d'un événement sur mesure",
@@ -690,4 +694,4 @@
"settings_update_item_warning": "Mettre à jour la valeur ci-dessous. Veillez à bien suivre le même format qu'auparavant. <b>Il n'y a pas de pas de contrôle.</b>",
"test_event_icon": "fa-vial-circle-check",
"test_event_tooltip": "Enregistrer d'abord vos modifications avant de tester vôtre paramétrage."
}
}

View File

@@ -274,6 +274,7 @@
"Gen_AreYouSure": "Sei sicuro?",
"Gen_Backup": "Esegui backup",
"Gen_Cancel": "Annulla",
"Gen_Change": "Modifica",
"Gen_Copy": "Esegui",
"Gen_DataUpdatedUITakesTime": "OK: l'aggiornamento dell'interfaccia utente potrebbe richiedere del tempo se è in esecuzione una scansione.",
"Gen_Delete": "Elimina",
@@ -556,6 +557,7 @@
"RandomMAC_hover": "Rilevato automaticamente: indica se il dispositivo genera il suo indirizzo MAC casualmente.",
"Reports_Sent_Log": "Log rapporti inviati",
"SCAN_SUBNETS_description": "La maggior parte degli scanner di rete (ARP-SCAN, NMAP, NSLOOKUP, DIG, PHOLUS) si basano sulla scansione di interfacce di rete e sottoreti specifiche. Consulta la <a href=\"https://github.com/jokob-sk/NetAlertX/blob/main/docs/SUBNETS.md\" target=\"_blank\">documentazione sulle sottoreti</a> per assistenza su questa impostazione, in particolare VLAN, quali VLAN sono supportate o come individuare la maschera di rete e l'interfaccia. <br/> <br/> Un'alternativa agli scanner in rete è abilitare altri scanner/importatori di dispositivi che non si affidano a NetAlert<sup>X</sup> che hanno accesso alla rete (UNIFI, dhcp.leases , PiHole, ecc.). <br/> <br/> Nota: il tempo di scansione stesso dipende dal numero di indirizzi IP da controllare, quindi impostalo attentamente con la maschera di rete e l'interfaccia appropriate.",
"SCAN_SUBNETS_name": "",
"SYSTEM_TITLE": "Informazioni sistema",
"Setting_Override": "Sovrascrivi valore",
"Setting_Override_Description": "L'abilitazione di questa opzione sovrascriverà il valore predefinito fornito dall'app con il valore specificato sopra.",
@@ -654,6 +656,8 @@
"UI_PRESENCE_name": "Mostra nel grafico delle presenze",
"UI_REFRESH_description": "Inserisci il numero di secondi dopo il quale la UI si ricarica. Imposta a <code>0</code> per disabilitare.",
"UI_REFRESH_name": "Aggiorna automaticamente la UI",
"VERSION_description": "Valore di supporto della versione o della marca temporale per verificare se l'app è stata aggiornata.",
"VERSION_name": "Versione o marca temporale",
"devices_old": "Aggiornamento...",
"general_event_description": "L'evento che hai attivato potrebbe richiedere del tempo prima che i processi in background vengano completati. L'esecuzione è terminata una volta che la coda di esecuzione sottostante si è svuotata (controlla il <a href='/maintenance.php#tab_Logging'>log degli errori</a> se riscontri problemi). <br/> <br/> Coda di esecuzione:",
"general_event_title": "Esecuzione di un evento ad-hoc",

View File

@@ -5,7 +5,8 @@
// ###################################
$defaultLang = "en_us";
$allLanguages = ["en_us","es_es","de_de", "nb_no", "pl_pl", "pt_br", "ru_ru", "fr_fr", "it_it", "zh_cn", "cs_cz"];
$allLanguages = ["en_us", "es_es", "de_de", "fr_fr", "it_it", "ru_ru", "nb_no", "pl_pl", "pt_br", "tr_tr", "zh_cn", "cs_cz"];
global $db;

View File

@@ -274,6 +274,7 @@
"Gen_AreYouSure": "Er du sikker?",
"Gen_Backup": "Kjør sikkerhetskopiering",
"Gen_Cancel": "Avbryt",
"Gen_Change": "",
"Gen_Copy": "Kjør",
"Gen_DataUpdatedUITakesTime": "OK - Det kan ta litt tid før brukergrensesnittet oppdateres hvis en skanning kjøres.",
"Gen_Delete": "Slett",
@@ -556,6 +557,7 @@
"RandomMAC_hover": "Autodetektert - indikerer om enheten randomiserer MAC-adressen sin.",
"Reports_Sent_Log": "Sendte rapport logger",
"SCAN_SUBNETS_description": "De fleste skannere på nettet (ARP-Scan, NMAP, NSlookup, Dig, Pholus) er avhengige av å skanne spesifikke nettverksgrensesnitt og undernett. Sjekk <a href=\"https://github.com/jokob-sk/NetAlertX/blob/main/docs/SUBNETS.md\" target=\"_blank\">subnett dokumentasjonen</a> for hjelp på denne innstillingen, spesielt VLAN-er, hvilke VLAN-er som støttes, eller hvordan du kan finne ut nettverksmasken og grensesnittet ditt. <br/> <br/> Et alternativ til skannere på nettet er å aktivere noen andre enhetsskannere/importører som ikke er avhengige av Netalert<sup>X</sup> med tilgang til nettverket (UniFi, DHCP-Leaser, Pihole, osv.). <br/> <br/> Merk: Selve skanningstiden avhenger av antall IP -adresser som skal sjekkes, så sett dette opp nøye med riktig nettverksmaske og grensesnitt.",
"SCAN_SUBNETS_name": "",
"SYSTEM_TITLE": "Systeminformasjon",
"Setting_Override": "Overstyr verdi",
"Setting_Override_Description": "Aktivering av dette alternativet vil overstyre en App som leveres standard-verdi med verdien som er spesifisert ovenfor.",
@@ -654,6 +656,8 @@
"UI_PRESENCE_name": "Vis i tilstedeværelse-diagrammet",
"UI_REFRESH_description": "Skriv inn antall sekunder før UI laster inn på nytt. Sett til <code>0</code> for å deaktivere.",
"UI_REFRESH_name": "Oppdater UI automatisk",
"VERSION_description": "",
"VERSION_name": "",
"devices_old": "Oppdaterer...",
"general_event_description": "Hendelsen du har utløst kan ta en stund til før bakgrunnsprosesser er ferdig. Utførelsen ble avsluttet når utførelseskøen nedenfor tømmes (sjekk <a href='/maintenance.php#tab_Logging'>Feillogg</a> Hvis du møter problemer). <br/> <br/> Utførelseskø:",
"general_event_title": "Utfører en ad-hoc hendelse",

View File

@@ -274,6 +274,7 @@
"Gen_AreYouSure": "Jesteś pewien?",
"Gen_Backup": "Wykonaj Kopie Zapasową",
"Gen_Cancel": "Anuluj",
"Gen_Change": "",
"Gen_Copy": "Wykonaj",
"Gen_DataUpdatedUITakesTime": "OK - Aktualizacja UI może chwile potrwać jeżeli wykonywany jest skan.",
"Gen_Delete": "Usuń",
@@ -556,6 +557,7 @@
"RandomMAC_hover": "Auto wykrywanie - oznacza czy urządzenie randomizuje swój adres MAC.",
"Reports_Sent_Log": "Wyślij zgłoszenie logów",
"SCAN_SUBNETS_description": "Większość skanerów sieciowych (ARP-SCAN, NMAP, NSLOOKUP, DIG, PHOLUS) opiera się na konkretnych interfejsach sieciowych oraz podsieci. Sprawdź <a href=\"https://github.com/jokob-sk/NetAlertX/blob/main/docs/SUBNETS.md\" target=\"_blank\"> dokumentacji podsieci</a> jeżeli potrzebujesz pomocy w ustawieniach, a szczególnie z VLAN'ami, jakie VLAN'y są wspierane oraz jak rozgryźć maskę podsieci twojego interfejsu.<br/><br/> Alternatywą do skanerów sieciowych jest uruchomienie innego Skanera Urządzeń/Importera który nie polega by NetAlert<sup>X</sup> miał dostęp do sieci (UNIFI, dhcp.leases, PiHole, itp.).<br/><br/> Notatka: Czas skanu zależy od liczby adresów IP do sprawdzenia, więc ustaw go tak by skanował odpowiedni interfejs i maskę sieciową.",
"SCAN_SUBNETS_name": "",
"SYSTEM_TITLE": "Informacje o Systemie",
"Setting_Override": "Nadpisz wartość",
"Setting_Override_Description": "Włączanie tej opcji nadpisze podstawową wartość na wartość podaną powyżej.",
@@ -654,6 +656,8 @@
"UI_PRESENCE_name": "Pokaż w tabeli obecności",
"UI_REFRESH_description": "Wprowadź liczbę sekund po której UI ma się przeładować. Ustaw na <code>0</code> by wyłączyć.",
"UI_REFRESH_name": "Automatycznie odświeżaj UI",
"VERSION_description": "",
"VERSION_name": "",
"devices_old": "Odświeżanie...",
"general_event_description": "Wydarzenie które wyzwoliłeś może chwilę zająć dopóki procesy w tle nie zakończą się. Wykonanie zakończy się kiedy kolejka się opróżni (Sprawdź <a href='/maintenance.php#tab_Logging'>logi błędów</a> jeżeli napotkasz błędy).<br/><br/> Kolejka wykonywania:",
"general_event_title": "Wykonywanie wydarzeń ad-hoc",

View File

@@ -274,6 +274,7 @@
"Gen_AreYouSure": "Tem certeza?",
"Gen_Backup": "Executar backup",
"Gen_Cancel": "Cancelar",
"Gen_Change": "",
"Gen_Copy": "Executar",
"Gen_DataUpdatedUITakesTime": "OK - Pode levar um tempo para a interface do usuário ser atualizada se uma verificação estiver em execução.",
"Gen_Delete": "Excluir",
@@ -556,6 +557,7 @@
"RandomMAC_hover": "",
"Reports_Sent_Log": "",
"SCAN_SUBNETS_description": "",
"SCAN_SUBNETS_name": "",
"SYSTEM_TITLE": "",
"Setting_Override": "",
"Setting_Override_Description": "",
@@ -654,6 +656,8 @@
"UI_PRESENCE_name": "",
"UI_REFRESH_description": "",
"UI_REFRESH_name": "",
"VERSION_description": "",
"VERSION_name": "",
"devices_old": "",
"general_event_description": "",
"general_event_title": "",

View File

@@ -274,6 +274,7 @@
"Gen_AreYouSure": "Вы уверены?",
"Gen_Backup": "Запустить резервное копирование",
"Gen_Cancel": "Отмена",
"Gen_Change": "Изменить",
"Gen_Copy": "Запустить",
"Gen_DataUpdatedUITakesTime": "ОК - Обновление UI может занять некоторое время, если сканирование выполняется.",
"Gen_Delete": "Удалить",
@@ -556,6 +557,7 @@
"RandomMAC_hover": "Автоматически обнаружено — указывает, рандомизирует ли устройство свой MAC-адрес.",
"Reports_Sent_Log": "Отправить журнал логов",
"SCAN_SUBNETS_description": "Большинство сетевых сканеров (ARP-SCAN, NMAP, NSLOOKUP, DIG, PHOLUS) полагаются на сканирование определенных сетевых интерфейсов и подсетей. Дополнительную информацию по этому параметру можно найти в <a href=\"https://github.com/jokob-sk/NetAlertX/blob/main/docs/SUBNETS.md\" target=\"_blank\">документации по подсетям</a>, особенно VLAN, какие VLAN поддерживаются или как разобраться в маске сети и своем интерфейсе. <br/> <br/> Альтернативой сетевым сканерам является включение некоторых других сканеров/импортеров устройств, которые не полагаются на NetAlert<sup>X</sup>, имеющий доступ к сети (UNIFI, dhcp.leases , PiHole и др.). <br/> <br/> Примечание. Само время сканирования зависит от количества проверяемых IP-адресов, поэтому тщательно настройте его, указав соответствующую маску сети и интерфейс.",
"SCAN_SUBNETS_name": "",
"SYSTEM_TITLE": "Системная информация",
"Setting_Override": "Переопределить значение",
"Setting_Override_Description": "Включение этой опции приведет к переопределению значения по умолчанию, предоставленного приложением, на значение, указанное выше.",
@@ -654,6 +656,8 @@
"UI_PRESENCE_name": "Показать в диаграмме присутствия",
"UI_REFRESH_description": "Введите количество секунд, по истечении которых пользовательский интерфейс перезагружается. Установите значение <code>0</code>, чтобы отключить.",
"UI_REFRESH_name": "Автоматическое обновление интерфейса",
"VERSION_description": "Вспомогательное значение версии или метки времени, позволяющее проверить, было ли приложение обновлено.",
"VERSION_name": "Версия или временная метка",
"devices_old": "Актуализируется...",
"general_event_description": "Событие, которое вы инициировали, может занять некоторое время, прежде чем фоновые процессы завершатся. Выполнение завершится, как только очередь выполнения, указанная ниже, опустеет (Проверьте <a href='/maintenance.php#tab_Logging'>журнал ошибок</a> при возникновении проблем). <br/> <br/>· · Очередь выполнения:",
"general_event_title": "Выполнение специального события",
@@ -690,4 +694,4 @@
"settings_update_item_warning": "Обновить значение ниже. Будьте осторожны, следуя предыдущему формату. <b>Проверка не выполняется.</b>",
"test_event_icon": "fa-vial-circle-check",
"test_event_tooltip": "Сначала сохраните изменения, прежде чем проверять настройки."
}
}

View File

@@ -274,6 +274,7 @@
"Gen_AreYouSure": "Emin misiniz?",
"Gen_Backup": "",
"Gen_Cancel": "İptal",
"Gen_Change": "",
"Gen_Copy": "Çalıştır",
"Gen_DataUpdatedUITakesTime": "TAMAM - Eğer bir tarama çalışıyorsa arayüzün güncellenmesi biraz zaman alabilir",
"Gen_Delete": "Sil",
@@ -556,6 +557,7 @@
"RandomMAC_hover": "",
"Reports_Sent_Log": "",
"SCAN_SUBNETS_description": "",
"SCAN_SUBNETS_name": "",
"SYSTEM_TITLE": "",
"Setting_Override": "",
"Setting_Override_Description": "",
@@ -654,6 +656,8 @@
"UI_PRESENCE_name": "",
"UI_REFRESH_description": "",
"UI_REFRESH_name": "",
"VERSION_description": "",
"VERSION_name": "",
"devices_old": "Yenileniyor...",
"general_event_description": "",
"general_event_title": "",

View File

@@ -274,6 +274,7 @@
"Gen_AreYouSure": "你确定吗?",
"Gen_Backup": "运行备份",
"Gen_Cancel": "取消",
"Gen_Change": "",
"Gen_Copy": "运行",
"Gen_DataUpdatedUITakesTime": "好的 - 如果扫描正在运行UI 可能需要一段时间才能更新。",
"Gen_Delete": "删除",
@@ -556,6 +557,7 @@
"RandomMAC_hover": "自动检测 - 表示设备是否随机化其 MAC 地址。",
"Reports_Sent_Log": "已发送报告日志",
"SCAN_SUBNETS_description": "大多数网络扫描器ARP-SCAN、NMAP、NSLOOKUP、DIG、PHOLUS依赖于扫描特定的网络接口和子网。查看<a href=\"https://github.com/jokob-sk/NetAlertX/blob/main/docs/SUBNETS.md\" target=\"_blank\">子网文档</a>以获取有关此设置的帮助,尤其是 VLAN、支持哪些 VLAN或者如何确定网络掩码和接口。<br/> <br/> 网络扫描器的替代方法是启用一些其他不依赖于 NetAlert<sup>X</sup> 访问网络的设备扫描器/导入器UNIFI、dhcp.leases、PiHole 等)。<br/> <br/> 注意:扫描时间本身取决于要检查的 IP 地址数量,因此请使用适当的网络掩码和接口仔细设置。",
"SCAN_SUBNETS_name": "",
"SYSTEM_TITLE": "系统信息",
"Setting_Override": "覆盖值",
"Setting_Override_Description": "启用此选项将用上面指定的值覆盖应用程序提供的默认值。",
@@ -654,6 +656,8 @@
"UI_PRESENCE_name": "在存在图表中显示",
"UI_REFRESH_description": "输入界面重新加载的秒数。设置为 <code>0</code> 可禁用。",
"UI_REFRESH_name": "自动刷新界面",
"VERSION_description": "",
"VERSION_name": "",
"devices_old": "刷新中...",
"general_event_description": "您触发的事件可能需要一段时间才能完成后台进程。一旦以下执行队列清空,执行就会结束(如果遇到问题,请检查<a href='/maintenance.php#tab_Logging'>错误日志</a>)。<br/> <br/> 执行队列:",
"general_event_title": "执行自组织网络事件",
@@ -690,4 +694,4 @@
"settings_update_item_warning": "更新下面的值。请注意遵循先前的格式。<b>未执行验证。</b>",
"test_event_icon": "",
"test_event_tooltip": "在测试设置之前,请先保存更改。"
}
}

View File

@@ -4,9 +4,9 @@
// ## GUI settings processing start
// ###################################
if( isset($_COOKIE['Front_Dark_Mode_Enabled']))
if( isset($_COOKIE['UI_dark_mode']))
{
$ENABLED_DARKMODE = $_COOKIE['Front_Dark_Mode_Enabled'] == "true";
$ENABLED_DARKMODE = $_COOKIE['UI_dark_mode'] == "True";
}else
{
$ENABLED_DARKMODE = False;

View File

@@ -11,12 +11,12 @@ NetAlertX supports additional plugins to extend its functionality, each with its
> You can load additional Plugins via the General -> `LOADED_PLUGINS` setting. Use `Ctrl + Click` to select/deselect.
1. Pick your `🔍 dev scanner` plugin (e.g. `ARPSCAN` or `NMAPDEV`), or import devices into the application with an `📥 importer` plugin. (See **✅Enabling plugins** below)
1. Pick a `▶️ publisher` plugin, if you want to send notifications. If you don't see a publisher you'd like to use, look at the [📚_publisher_apprise](/front/plugins/_publisher_apprise/) plugin which is a proxy for over 80 notification services.
1. Setup your [Network topology diagram](/docs/NETWORK_TREE.md)
1. Fine-tune [Notifications](/docs/NOTIFICATIONS.md)
1. [Backup your setup](/docs/BACKUPS.md)
1. Contribute and [Create custom plugins](/docs/PLUGINS_DEV.md)
1. Consider [donating](https://github.com/jokob-sk/NetAlertX?tab=readme-ov-file#-sponsors) to keep me going
2. Pick a `▶️ publisher` plugin, if you want to send notifications. If you don't see a publisher you'd like to use, look at the [📚_publisher_apprise](/front/plugins/_publisher_apprise/) plugin which is a proxy for over 80 notification services.
3. Setup your [Network topology diagram](/docs/NETWORK_TREE.md)
4. Fine-tune [Notifications](/docs/NOTIFICATIONS.md)
5. [Backup your setup](/docs/BACKUPS.md)
6. Contribute and [Create custom plugins](/docs/PLUGINS_DEV.md)
7. Consider [donating](https://github.com/jokob-sk/NetAlertX?tab=readme-ov-file#-sponsors) to keep me going
## 📑 Available Plugins
@@ -53,6 +53,7 @@ Device-detecting plugins insert values into the `CurrentScan` database table. T
| `SMTP` | ▶️ | Email notifications | | | Script | [_publisher_email](/front/plugins/_publisher_email/) |
| `SNMPDSC` | 🔍/📥 | SNMP device import & sync | | | Script | [snmp_discovery](/front/plugins/snmp_discovery/) |
| `SYNC` | 🔍/⚙/📥| Sync & import from NetAlertX instances | 🖧 🔄 | | Script | [sync](/front/plugins/sync/) |
| `TELEGRAM` | ▶️ | Telegram notifications | | | Script | [_publisher_telegram](/front/plugins/_publisher_telegram/) |
| `UNDIS` | 🔍/📥 | Create dummy devices | | | Script | [undiscoverables](/front/plugins/undiscoverables/) |
| `UNFIMP` | 🔍/📥 | UniFi device import & sync | 🖧 | | Script | [unifi_import](/front/plugins/unifi_import/) |
| `VNDRPDT` | ⚙ | Vendor database update | | | Script | [vendor_update](/front/plugins/vendor_update/) |

View File

@@ -0,0 +1,10 @@
## Overview
You can send notifications via Telegram
## Notes
You need Telegram bot to send notifications
### Usage
- Go to settings and fill in relevant details.

View File

@@ -0,0 +1,470 @@
{
"code_name": "_publisher_telegram",
"unique_prefix": "TELEGRAM",
"plugin_type": "publisher",
"enabled": true,
"data_source": "script",
"show_ui": true,
"localized": ["display_name", "description", "icon"],
"display_name": [
{
"language_code": "en_us",
"string": "Telegram publisher"
}
],
"icon": [
{
"language_code": "en_us",
"string": "<i class=\"fa-solid fa-bullhorn\"></i>"
}
],
"description": [
{
"language_code": "en_us",
"string": "A plugin to publish a notification via Telegram."
}
],
"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"
},
{
"language_code": "es_es",
"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"
},
{
"language_code": "es_es",
"string": "N/A"
}
]
},
{
"column": "Object_PrimaryID",
"css_classes": "col-sm-2",
"show": false,
"type": "url",
"default_value": "",
"options": [],
"localized": ["name"],
"name": [
{
"language_code": "en_us",
"string": "N/A"
}
]
},
{
"column": "Object_SecondaryID",
"css_classes": "col-sm-2",
"show": false,
"type": "label",
"default_value": "",
"options": [],
"localized": ["name"],
"name": [
{
"language_code": "en_us",
"string": "N/A"
},
{
"language_code": "es_es",
"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": "Sent when"
}
]
},
{
"column": "DateTimeChanged",
"css_classes": "col-sm-2",
"show": false,
"type": "label",
"default_value": "",
"options": [],
"localized": ["name"],
"name": [
{
"language_code": "en_us",
"string": "Changed"
},
{
"language_code": "es_es",
"string": "Cambiado"
}
]
},
{
"column": "Watched_Value1",
"css_classes": "col-sm-2",
"show": true,
"type": "eval",
"default_value": "",
"options": [
{
"type": "eval",
"param": "`<a href='/report.php?guid=${value}'>${value}</a>`"
}
],
"localized": ["name"],
"name": [
{
"language_code": "en_us",
"string": "Notification GUID"
}
]
},
{
"column": "Watched_Value2",
"css_classes": "col-sm-8",
"show": true,
"type": "textarea_readonly",
"default_value": "",
"options": [],
"localized": ["name"],
"name": [
{
"language_code": "en_us",
"string": "Result"
}
]
},
{
"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"
},
{
"language_code": "es_es",
"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"
},
{
"language_code": "es_es",
"string": "N/A"
}
]
},
{
"column": "UserData",
"css_classes": "col-sm-2",
"show": false,
"type": "textbox_save",
"default_value": "",
"options": [],
"localized": ["name"],
"name": [
{
"language_code": "en_us",
"string": "Comments"
},
{
"language_code": "es_es",
"string": "Comentarios"
}
]
},
{
"column": "Status",
"css_classes": "col-sm-1",
"show": false,
"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>"
},
{
"equals": "missing-in-last-scan",
"replacement": "<div style='text-align:center'><i class='fa-solid fa-question'></i></div>"
}
],
"localized": ["name"],
"name": [
{
"language_code": "en_us",
"string": "Status"
},
{
"language_code": "es_es",
"string": "Estado"
}
]
},
{
"column": "Extra",
"css_classes": "col-sm-3",
"show": false,
"type": "label",
"default_value": "",
"options": [],
"localized": ["name"],
"name": [
{
"language_code": "en_us",
"string": "Extra"
},
{
"language_code": "es_es",
"string": "Extra"
}
]
}
],
"settings": [
{
"function": "RUN",
"events": ["test"],
"type": {
"dataType": "string",
"elements": [
{ "elementType": "select", "elementOptions": [], "transformers": [] }
]
},
"default_value": "disabled",
"options": ["disabled", "on_notification"],
"localized": ["name", "description"],
"name": [
{
"language_code": "en_us",
"string": "When to run"
},
{
"language_code": "es_es",
"string": "Cuando ejecuta"
}
],
"description": [
{
"language_code": "en_us",
"string": "Enable sending notifications via a Telegram messanger"
}
]
},
{
"function": "CMD",
"type": {
"dataType": "string",
"elements": [
{
"elementType": "input",
"elementOptions": [{ "readonly": "true" }],
"transformers": []
}
]
},
"default_value": "python3 /app/front/plugins/_publisher_telegram/tg.py",
"options": [],
"localized": ["name", "description"],
"name": [
{
"language_code": "en_us",
"string": "Command"
},
{
"language_code": "es_es",
"string": "Comando"
}
],
"description": [
{
"language_code": "en_us",
"string": "Command to run"
},
{
"language_code": "es_es",
"string": "Comando a ejecutar"
}
]
},
{
"function": "RUN_TIMEOUT",
"type": {
"dataType": "integer",
"elements": [
{
"elementType": "input",
"elementOptions": [{ "type": "number" }],
"transformers": []
}
]
},
"default_value": 10,
"options": [],
"localized": ["name", "description"],
"name": [
{
"language_code": "en_us",
"string": "Run timeout"
},
{
"language_code": "es_es",
"string": "Tiempo de espera de ejecución"
},
{
"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."
},
{
"language_code": "es_es",
"string": "Tiempo máximo en segundos para esperar a que finalice el script. Si se supera este tiempo, el script se cancela."
}
]
},
{
"function": "HOST",
"type": {
"dataType": "string",
"elements": [
{ "elementType": "input", "elementOptions": [], "transformers": [] }
]
},
"default_value": "",
"options": [],
"localized": ["name", "description"],
"name": [
{
"language_code": "en_us",
"string": "Telegram chat id"
}
],
"description": [
{
"language_code": "en_us",
"string": "Telegram chat id. If you want to send messages to user, paste user id (Example: <code>1234123412</code>)"
}
]
},
{
"function": "URL",
"type": {
"dataType": "string",
"elements": [
{ "elementType": "input", "elementOptions": [], "transformers": [] }
]
},
"default_value": "",
"options": [],
"localized": ["name", "description"],
"name": [
{
"language_code": "en_us",
"string": "Telegram bot token"
}
],
"description": [
{
"language_code": "en_us",
"string": "Telegram bot token. You cat get at from <a target=\"_blank\" href=\"https://t.me/BotFather\">BotFather</a>"
}
]
},
{
"function": "SIZE",
"type": {
"dataType": "integer",
"elements": [
{
"elementType": "input",
"elementOptions": [{ "type": "number" }],
"transformers": []
}
]
},
"default_value": 1024,
"options": [],
"localized": ["name", "description"],
"name": [
{
"language_code": "en_us",
"string": "Max payload size"
},
{
"language_code": "es_es",
"string": "Tamaño máximo de carga útil"
}
],
"description": [
{
"language_code": "en_us",
"string": "The maximum size of the payload as number of characters in the passed string. If above limit, it will be truncated and a <code>(text was truncated)</code> message is appended."
}
]
}
]
}

View File

@@ -0,0 +1,124 @@
#!/usr/bin/env python
import json
import subprocess
import argparse
import os
import pathlib
import sys
from datetime import datetime
# Register NetAlertX directories
INSTALL_PATH = "/app"
sys.path.extend([f"{INSTALL_PATH}/front/plugins", f"{INSTALL_PATH}/server"])
import conf
from const import confFileName
from plugin_helper import Plugin_Objects
from logger import mylog, append_line_to_file
from helper import timeNowTZ, get_setting_value
from notification import Notification_obj
from database import DB
from pytz import timezone
# Make sure the TIMEZONE for logging is correct
conf.tz = timezone(get_setting_value('TIMEZONE'))
CUR_PATH = str(pathlib.Path(__file__).parent.resolve())
RESULT_FILE = os.path.join(CUR_PATH, 'last_result.log')
pluginName = 'TELEGRAM'
def main():
mylog('verbose', [f'[{pluginName}](publisher) In script'])
# Check if basic config settings supplied
if check_config() == False:
mylog('none', [
f'[{pluginName}] ⚠ ERROR: Publisher notification gateway not set up correctly. Check your {confFileName} {pluginName}_* variables.'])
return
# Create a database connection
db = DB() # instance of class DB
db.open()
# Initialize the Plugin obj output file
plugin_objects = Plugin_Objects(RESULT_FILE)
# Create a Notification_obj instance
notifications = Notification_obj(db)
# Retrieve new notifications
new_notifications = notifications.getNew()
# Process the new notifications (see the Notifications DB table for structure or check the /api/table_notifications.json endpoint)
for notification in new_notifications:
# Send notification
result = send(notification["Text"])
# Log result
plugin_objects.add_object(
primaryId=pluginName,
secondaryId=timeNowTZ(),
watched1=notification["GUID"],
watched2=result,
watched3='null',
watched4='null',
extra='null',
foreignKey=notification["GUID"]
)
plugin_objects.write_result_file()
# -------------------------------------------------------------------------------
def check_config():
return True
# -------------------------------------------------------------------------------
def send(text):
# limit = 1024 * 1024 # 1MB limit (1024 bytes * 1024 bytes = 1MB)
limit = get_setting_value('TELEGRAM_SIZE')
if len(text) > limit:
payloadData = text[:limit] + " (text was truncated)"
else:
payloadData = text
try:
# try runnning a subprocess
req = """curl --location 'https://api.telegram.org/bot%s/sendMessage' \\
--header 'Content-Type: application/json' \\
--data '{
"chat_id": "%s",
"text": "%s",
"disable_notification": false
}'""" % (get_setting_value('TELEGRAM_URL'), get_setting_value('TELEGRAM_HOST'), payloadData)
mylog('debug', [req])
p = subprocess.Popen(req, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, shell=True)
stdout, stderr = p.communicate()
# write stdout and stderr into .log files for debugging if needed
# Log the stdout and stderr
mylog('debug', [stdout, stderr])
# log result
result = stdout
except subprocess.CalledProcessError as e:
# An error occurred, handle it
mylog('none', [e.output])
# log result
result = e.output
return result
if __name__ == '__main__':
sys.exit(main())

View File

@@ -147,8 +147,9 @@ def cleanup_database (dbPath, DAYS_TO_KEEP_EVENTS, PHOLUS_DAYS_DATA, HRS_TO_KEEP
# Cleanup New Devices
if HRS_TO_KEEP_NEWDEV != 0:
mylog('verbose', [f'[{pluginName}] Devices: Delete all New Devices older than {str(HRS_TO_KEEP_NEWDEV)} hours (HRS_TO_KEEP_NEWDEV setting)'])
cursor.execute (f"""DELETE FROM Devices
WHERE dev_NewDevice = 1 AND dev_FirstConnection < date('now', '+{str(HRS_TO_KEEP_NEWDEV)} hour')""")
query = f"""DELETE FROM Devices WHERE dev_NewDevice = 1 AND dev_FirstConnection < date('now', '-{str(HRS_TO_KEEP_NEWDEV)} hour')"""
mylog('verbose', [f'[{pluginName}] Query: {query} '])
cursor.execute (query)
# -----------------------------------------------------
# Cleanup Pholus_Scan

View File

@@ -1,7 +1,7 @@
{
"code_name": "mikrotik_scan",
"unique_prefix": "MTSCAN",
"plugin_type": "other",
"plugin_type": "device_scanner",
"execution_order" : "Layer_4",
"enabled": true,
"data_source": "script",
@@ -11,7 +11,7 @@
"display_name": [
{
"language_code": "en_us",
"string": "Mikrotik (Name discovery)"
"string": "Mikrotik (Device discovery)"
}
],
"icon": [
@@ -23,7 +23,7 @@
"description": [
{
"language_code": "en_us",
"string": "A plugin to discover device names."
"string": "A plugin to discover devices via Mikrotik."
}
],
"params": [

View File

@@ -1,6 +1,4 @@
#!/usr/bin/env python
# test script by running:
# tbc
import os
import pathlib
@@ -40,42 +38,35 @@ pluginName = 'MTSCAN'
def main():
mylog('verbose', [f'[{pluginName}] In script'])
mylog('verbose', [f'[{pluginName}] In script'])
mt_host = get_setting_value('MTSCAN_MT_HOST')
mt_port = get_setting_value('MTSCAN_MT_PORT')
mt_user = get_setting_value('MTSCAN_MT_USER')
mt_password = get_setting_value('MTSCAN_MT_PASS')
#mylog('verbose', [f'[{pluginName}] Router: {mt_host}:{mt_port} user: {mt_user}, pass: {mt_password}'])
# Create a database connection
db = DB() # instance of class DB
db.open()
# init global variables
global MT_HOST, MT_PORT, MT_USER, MT_PASS
# Initialize the Plugin obj output file
plugin_objects = Plugin_Objects(RESULT_FILE)
# Create a Device_obj instance
device_handler = Device_obj(db)
# Mikrotik settings
MT_HOST = get_setting_value('MTSCAN_MT_HOST')
MT_PORT = get_setting_value('MTSCAN_MT_PORT')
MT_USER = get_setting_value('MTSCAN_MT_USER')
MT_PASS = get_setting_value('MTSCAN_MT_PASS')
# Retrieve devices
#unknown_devices = device_handler.getUnknown()
#mylog('verbose', [f'[{pluginName}] Unknown devices count: {len(unknown_devices)}'])
plugin_objects = get_entries(plugin_objects)
all_devices = device_handler.getAll()
plugin_objects.write_result_file()
mylog('verbose', [f'[{pluginName}] Scan finished, found {len(plugin_objects)} devices'])
mylog('verbose', [f'[{pluginName}] all devices count: {len(all_devices)}'])
device_map = {d['dev_MAC']:d['dev_LastIP'] for d in all_devices}
def get_entries(plugin_objects: Plugin_Objects) -> Plugin_Objects:
try:
# connect router
api = connect(username=mt_user, password=mt_password, host=mt_host, port=mt_port)
api = connect(username=MT_USER, password=MT_PASS, host=MT_HOST, port=MT_PORT)
# get dhcp leases
leases = api('/ip/dhcp-server/lease/print')
for lease in leases:
lease_id = lease.get('.id')
@@ -84,56 +75,31 @@ def main():
host_name = lease.get('host-name')
comment = lease.get('comment')
last_seen = lease.get('last-seen')
status = lease.get('status')
mylog('verbose', [f"ID: {lease_id}, Address: {address}, MAC Address: {mac_address}, Host Name: {host_name}, Comment: {comment}, Last Seen: {last_seen}"])
if mac_address in device_map.keys():
device_name = host_name
if comment != '':
device_name = comment
mylog('verbose', [f"ID: {lease_id}, Address: {address}, MAC Address: {mac_address}, Host Name: {host_name}, Comment: {comment}, Last Seen: {last_seen}, Status: {status}"])
if (status == "bound"):
plugin_objects.add_object(
# "Name-MAC", "LastIP", "IP", "Name","Host","LastSeen","Comment"
primaryId = mac_address,
secondaryId = device_map[mac_address],
secondaryId = '',
watched1 = address,
watched2 = device_name,
watched3 = host_name,
watched4 = last_seen,
watched2 = host_name,
watched3 = last_seen,
watched4 = '',
extra = '',
helpVal1 = comment,
foreignKey = mac_address)
plugin_objects.write_result_file()
except TrapError as e:
mylog('error', [f"An error occurred: {e}"])
except Exception as e:
mylog('error', [f"Failed to connect to MikroTik API: {e}"])
#for device in unknown_devices:
# domain_name, dns_server = execute_nslookup(device['dev_LastIP'], timeout)
# if domain_name != '':
# plugin_objects.add_object(
# # "MAC", "IP", "Server", "Name"
# primaryId = device['dev_MAC'],
# secondaryId = device['dev_LastIP'],
# watched1 = dns_server,
# watched2 = domain_name,
# watched3 = '',
# watched4 = '',
# extra = '',
# foreignKey = device['dev_MAC'])
#plugin_objects.write_result_file()
mylog('verbose', [f'[{pluginName}] Script finished'])
return 0
return plugin_objects
#===============================================================================
# BEGIN

View File

@@ -3,7 +3,7 @@
NMAP-scan is a command-line tool to discover and fingerprint IP hosts on the local network. The NMAP-scan (and other Network-scan plugin times using the `SCAN_SUBNETS` setting) time depends on the number of IP addresses to check so set this up carefully with the appropriate network mask and interface. Check the [subnets documentation](https://github.com/jokob-sk/NetAlertX/blob/main/docs/SUBNETS.md) for help with setting up VLANs, what VLANs are supported, or how to figure out the network mask and your interface.
> [!NOTE]
> The `NMAPDEV` plugin is great for detecting the availability of devices, however ARP scan might be better covering multiple VLANS. You can always combine different scan methods. You can find all available network scanning options (marked as `🔍 dev scanner`) in the [Plugins overview](https://github.com/jokob-sk/NetAlertX/blob/main/front/plugins/README.md) readme.
> The `NMAPDEV` plugin is great for detecting the availability of devices, however ARP scan might be better covering multiple VLANS and subnets as NMAP can't pickup the MAC address from other subnets (this is an NMAP limitation) which are necessary to identify a device. You can always combine different scan methods. You can find all available network scanning options (marked as `🔍 dev scanner`) in the [Plugins overview](https://github.com/jokob-sk/NetAlertX/blob/main/front/plugins/README.md) readme.
### Usage

View File

@@ -26,6 +26,275 @@
],
"params": [],
"settings": [
{
"function": "NOT_RANDOM_MAC",
"type": {
"dataType": "array",
"elements": [
{
"elementType": "input",
"elementOptions": [
{ "placeholder": "Enter value" },
{ "suffix": "_in" },
{ "cssClasses": "col-sm-10" },
{ "prefillValue": "null" }
],
"transformers": []
},
{
"elementType": "button",
"elementOptions": [
{ "sourceSuffixes": ["_in"] },
{ "separator": "" },
{ "cssClasses": "col-xs-12" },
{ "onClick": "addList(this,false)" },
{ "getStringKey": "Gen_Add" }
],
"transformers": []
},
{
"elementType": "select",
"elementHasInputValue": 1,
"elementOptions": [
{ "multiple": "true" },
{ "readonly": "true" },
{ "editable": "true" }
],
"transformers": []
},
{
"elementType": "button",
"elementOptions": [
{ "sourceSuffixes": [] },
{ "separator": "" },
{ "cssClasses": "col-xs-6" },
{ "onClick": "removeAllOptions(this)" },
{ "getStringKey": "Gen_Remove_All" }
],
"transformers": []
},
{
"elementType": "button",
"elementOptions": [
{ "sourceSuffixes": [] },
{ "separator": "" },
{ "cssClasses": "col-xs-6" },
{ "onClick": "removeFromList(this)" },
{ "getStringKey": "Gen_Remove_Last" }
],
"transformers": []
}
]
},
"maxLength": 50,
"default_value": [],
"options": [],
"localized": [],
"name": [
{
"string": "_GLOBAL_LANG_FILES_"
}
],
"description": [
{
"string": "_GLOBAL_LANG_FILES_"
}
]
},
{
"function": "ICONS",
"type": {
"dataType": "array",
"elements": [
{
"elementType": "input",
"elementOptions": [
{ "placeholder": "Enter value" },
{ "suffix": "_in" },
{ "cssClasses": "col-sm-10" },
{ "prefillValue": "null" }
],
"transformers": []
},
{
"elementType": "button",
"elementOptions": [
{ "sourceSuffixes": ["_in"] },
{ "separator": "" },
{ "cssClasses": "col-xs-12" },
{ "onClick": "addList(this,false)" },
{ "getStringKey": "Gen_Add" }
],
"transformers": []
},
{
"elementType": "select",
"elementHasInputValue": 1,
"elementOptions": [
{ "multiple": "true" },
{ "readonly": "true" },
{ "editable": "true" }
],
"transformers": []
},
{
"elementType": "button",
"elementOptions": [
{ "sourceSuffixes": [] },
{ "separator": "" },
{ "cssClasses": "col-xs-6" },
{ "onClick": "removeAllOptions(this)" },
{ "getStringKey": "Gen_Remove_All" }
],
"transformers": []
},
{
"elementType": "button",
"elementOptions": [
{ "sourceSuffixes": [] },
{ "separator": "" },
{ "cssClasses": "col-xs-6" },
{ "onClick": "removeFromList(this)" },
{ "getStringKey": "Gen_Remove_Last" }
],
"transformers": []
}
]
},
"maxLength": 50,
"default_value": [
"PGkgY2xhc3M9J2ZhIGZhLXdpZmknPjwvaT4=",
"PGkgY2xhc3M9ImZhIGZhLWNvbXB1dGVyIj48L2k+",
"PGkgY2xhc3M9ImZhIGZhLWV0aGVybmV0Ij48L2k+",
"PGkgY2xhc3M9ImZhIGZhLWdhbWVwYWQiPjwvaT4",
"PGkgY2xhc3M9ImZhIGZhLWdsb2JlIj48L2k+",
"PGkgY2xhc3M9ImZhIGZhLWxhcHRvcCI+PC9pPg==",
"PGkgY2xhc3M9ImZhIGZhLWxpZ2h0YnVsYiI+PC9pPg==",
"PGkgY2xhc3M9ImZhIGZhLXNoaWVsZCI+PC9pPg==",
"PGkgY2xhc3M9ImZhIGZhLXdpZmkiPjwvaT4",
"PGkgY2xhc3M9J2ZhIGZhLWdhbWVwYWQnPjwvaT4"
],
"options": [],
"localized": [],
"name": [
{
"string": "_GLOBAL_LANG_FILES_"
}
],
"description": [
{
"string": "_GLOBAL_LANG_FILES_"
}
]
},
{
"function": "REFRESH",
"type": {
"dataType": "integer",
"elements": [
{
"elementType": "input",
"elementOptions": [{ "type": "number" }],
"transformers": []
}
]
},
"maxLength": 50,
"default_value": 0,
"options": [],
"localized": [],
"name": [
{
"string": "_GLOBAL_LANG_FILES_"
}
],
"description": [
{
"string": "_GLOBAL_LANG_FILES_"
}
]
},
{
"function": "DEV_SECTIONS",
"type": {
"dataType": "array",
"elements": [
{
"elementType": "select",
"elementOptions": [{ "multiple": "true" }],
"transformers": []
}
]
},
"maxLength": 50,
"default_value": [],
"options": ["Tile Cards", "Device Presence"],
"localized": [],
"name": [
{
"string": "_GLOBAL_LANG_FILES_"
}
],
"description": [
{
"string": "_GLOBAL_LANG_FILES_"
}
]
},
{
"function": "PRESENCE",
"type": {
"dataType": "array",
"elements": [
{
"elementType": "select",
"elementOptions": [{ "multiple": "true" }],
"transformers": []
}
]
},
"maxLength": 50,
"default_value": ["online", "offline", "archived"],
"options": ["online", "offline", "archived"],
"localized": [],
"name": [
{
"string": "_GLOBAL_LANG_FILES_"
}
],
"description": [
{
"string": "_GLOBAL_LANG_FILES_"
}
]
},
{
"function": "MY_DEVICES",
"type": {
"dataType": "array",
"elements": [
{
"elementType": "select",
"elementOptions": [{ "multiple": "true" }],
"transformers": []
}
]
},
"maxLength": 50,
"default_value": ["online", "offline", "archived", "new", "down"],
"options": ["online", "offline", "archived", "new", "down"],
"localized": [],
"name": [
{
"string": "_GLOBAL_LANG_FILES_"
}
],
"description": [
{
"string": "_GLOBAL_LANG_FILES_"
}
]
},
{
"function": "device_columns",
"type": {
@@ -34,43 +303,76 @@
{
"elementType": "select",
"elementOptions": [{ "multiple": "true", "ordeable": "true" }],
"transformers": ["getString"]
},
{
"elementType": "button",
"elementOptions": [
{ "sourceSuffixes": [] },
{ "separator": "" },
{ "cssClasses": "col-xs-4" },
{ "onClick": "selectAll(this)" },
{ "getStringKey": "Gen_Add_All" }
],
"transformers": []
},
{
"elementType": "button",
"elementOptions": [
{ "sourceSuffixes": [] },
{ "separator": "" },
{ "cssClasses": "col-xs-4" },
{ "onClick": "unselectAll(this)" },
{ "getStringKey": "Gen_Remove_All" }
],
"transformers": []
},
{
"elementType": "button",
"elementOptions": [
{ "sourceSuffixes": [] },
{ "separator": "" },
{ "cssClasses": "col-xs-4" },
{ "onClick": "selectChange(this)" },
{ "getStringKey": "Gen_Change" }
],
"transformers": []
}
]
},
"maxLength": 50,
"default_value": [
"getString-Device_TableHead_Icon-value-3",
"getString-Device_TableHead_Name-value-0",
"getString-Device_TableHead_Type-value-2",
"getString-Device_TableHead_LastIP-value-8",
"getString-Device_TableHead_Status-value-10",
"getString-Device_TableHead_MAC_full-value-11"
"Device_TableHead_Icon",
"Device_TableHead_Name",
"Device_TableHead_Type",
"Device_TableHead_LastIP",
"Device_TableHead_Status",
"Device_TableHead_MAC_full"
],
"options": [
"getString-Device_TableHead_Name-value-0",
"getString-Device_TableHead_Owner-value-1",
"getString-Device_TableHead_Type-value-2",
"getString-Device_TableHead_Icon-value-3",
"getString-Device_TableHead_Favorite-value-4",
"getString-Device_TableHead_Group-value-5",
"getString-Device_TableHead_FirstSession-value-6",
"getString-Device_TableHead_LastSession-value-7",
"getString-Device_TableHead_LastIP-value-8",
"getString-Device_TableHead_MAC-value-9",
"getString-Device_TableHead_Status-value-10",
"getString-Device_TableHead_MAC_full-value-11",
"getString-Device_TableHead_LastIPOrder-value-12",
"getString-Device_TableHead_Rowid-value-13",
"getString-Device_TableHead_Parent_MAC-value-14",
"getString-Device_TableHead_Connected_Devices-value-15",
"getString-Device_TableHead_Location-value-16",
"getString-Device_TableHead_Vendor-value-17",
"getString-Device_TableHead_Port-value-18",
"getString-Device_TableHead_GUID-value-19",
"getString-Device_TableHead_SyncHubNodeName-value-20",
"getString-Device_TableHead_NetworkSite-value-21",
"getString-Device_TableHead_SSID-value-22"
"Device_TableHead_Name",
"Device_TableHead_Owner",
"Device_TableHead_Type",
"Device_TableHead_Icon",
"Device_TableHead_Favorite",
"Device_TableHead_Group",
"Device_TableHead_FirstSession",
"Device_TableHead_LastSession",
"Device_TableHead_LastIP",
"Device_TableHead_MAC",
"Device_TableHead_Status",
"Device_TableHead_MAC_full",
"Device_TableHead_LastIPOrder",
"Device_TableHead_Rowid",
"Device_TableHead_Parent_MAC",
"Device_TableHead_Connected_Devices",
"Device_TableHead_Location",
"Device_TableHead_Vendor",
"Device_TableHead_Port",
"Device_TableHead_GUID",
"Device_TableHead_SyncHubNodeName",
"Device_TableHead_NetworkSite",
"Device_TableHead_SSID"
],
"localized": ["name", "description"],
"name": [
@@ -82,7 +384,7 @@
"description": [
{
"language_code": "en_us",
"string": "Which columns to show on the Devices page."
"string": "Columns and their order that are shown on the Devices page. Drag and drop the order of columns, click <code>x</code> to remove columns. You can also click into the field to selectivelly add fields."
}
]
},
@@ -158,6 +460,34 @@
"string": "Hide Device tiles with zero results."
}
]
},
{
"function": "dark_mode",
"type": {
"dataType": "boolean",
"elements": [
{
"elementType": "input",
"elementOptions": [{ "type": "checkbox" }],
"transformers": []
}
]
},
"default_value": false,
"options": [],
"localized": ["name", "description"],
"name": [
{
"language_code": "en_us",
"string": "Dark mode"
}
],
"description": [
{
"language_code": "en_us",
"string": "Enable dark mode."
}
]
}
]
}

View File

@@ -552,7 +552,7 @@ $settingsJSON_DB = json_encode($settings, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX
});
// EVENTS
// process events (e.g. run ascan, or test a notification) if associated with the setting
// process events (e.g. run a scan, or test a notification) if associated with the setting
let eventsHtml = "";
const eventsList = createArray(set['Events']);
@@ -591,6 +591,8 @@ $settingsJSON_DB = json_encode($settings, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX
}, 50);
setupSmoothScrolling()
// try to initialize select2
initSelect2()
hideSpinner()
}
@@ -669,26 +671,34 @@ $settingsJSON_DB = json_encode($settings, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX
value = applyTransformers(value, transformers);
settingsArray.push([prefix, setCodeName, dataType, value]);
} else if (inputType === 'checkbox') {
} else if (dataType === 'boolean') {
value = $(`#${setCodeName}`).is(':checked') ? 1 : 0;
value = applyTransformers(value, transformers);
settingsArray.push([prefix, setCodeName, dataType, value]);
} else if (dataType === "array" ) {
// make sure to collect all if set as "editable" or selected only otherwise
$(`#${setCodeName}`).attr("my-editable") == "true" ? additionalSelector = "" : additionalSelector = ":selected"
const temps = [];
$(`#${setCodeName} option${additionalSelector}`).each(function() {
const vl = $(this).val();
if (vl !== '') {
temps.push(applyTransformers(vl, transformers));
}
});
let temps = [];
if(isOrdeable)
{
temps = $(`#${setCodeName}`).val()
} else
{
// make sure to collect all if set as "editable" or selected only otherwise
$(`#${setCodeName}`).attr("my-editable") == "true" ? additionalSelector = "" : additionalSelector = ":selected";
$(`#${setCodeName} option${additionalSelector}`).each(function() {
const vl = $(this).val();
if (vl !== '') {
temps.push(applyTransformers(vl, transformers));
}
});
}
value = JSON.stringify(temps);
settingsArray.push([prefix, setCodeName, dataType, value]);
} else if (dataType === "json") {
@@ -784,35 +794,34 @@ $settingsJSON_DB = json_encode($settings, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX
setTimeout("handleLoadingDialog()", 1000);
} else
{
// check if the app is initialized and hide the spinner
if(isAppInitialized())
{
// init page
getData()
// reload page if outdated information might be displayed
if(secondsSincePageLoad() > 5)
{
clearCache()
}
}
else
{
console.log("isAppInitialized() returned false, reloading in 3s");
// reload the page if not initialized to give time the background tasks to finish
setTimeout(() => {
window.location.reload()
}, 3000);
}
{
checkInitialization();
}
document.getElementById('lastImportedTime').innerHTML = humanReadable;
})
}
function checkInitialization() {
if (isAppInitialized()) {
// App is initialized, hide spinner and proceed with initialization
console.log("App initialized, proceeding...");
getData();
// Reload page if outdated information might be displayed
if (secondsSincePageLoad() > 10) {
clearCache();
}
} else {
console.log("App not initialized, checking again in 1s...");
// Check again after a delay
setTimeout(checkInitialization, 1000);
}
}
showSpinner()

View File

@@ -15,6 +15,7 @@ pluginsPath = applicationPath + '/front/plugins'
logPath = applicationPath + '/front/log'
apiPath = applicationPath + '/front/api/'
reportTemplatesPath = applicationPath + '/front/report_templates/'
fullConfFolder = applicationPath + '/config'
fullConfPath = applicationPath + confPath
fullDbPath = applicationPath + dbPath
vendorsPath = '/usr/share/arp-scan/ieee-oui.txt'

View File

@@ -11,13 +11,14 @@ import re
import conf
from const import fullConfPath
from helper import collect_lang_strings, updateSubnets, initOrSetParam, isJsonObject, updateState, setting_value_to_python_type
from const import fullConfPath, applicationPath, fullConfFolder
from helper import collect_lang_strings, updateSubnets, initOrSetParam, isJsonObject, updateState, setting_value_to_python_type, timeNowTZ, get_setting_value
from logger import mylog
from api import update_api
from scheduler import schedule_class
from plugin import print_plugin_info, run_plugin_scripts
from plugin_utils import get_plugins_configs, get_plugin_setting_obj
from notification import write_notification
#===============================================================================
# Initialise user defined values
@@ -28,7 +29,8 @@ from plugin_utils import get_plugins_configs, get_plugin_setting_obj
# Check config dictionary
#-------------------------------------------------------------------------------
def ccd(key, default, config_dir, name, inputtype, options, group, events=None, desc="", regex="", setJsonMetadata=None, overrideTemplate=None):
# managing application settings, ensuring SQL safety for user input, and updating internal configuration lists
def ccd(key, default, config_dir, name, inputtype, options, group, events=None, desc="", regex="", setJsonMetadata=None, overrideTemplate=None, forceDefault=False):
if events is None:
events = []
if setJsonMetadata is None:
@@ -40,7 +42,7 @@ def ccd(key, default, config_dir, name, inputtype, options, group, events=None,
result = default
# Use existing value if already supplied, otherwise default value is used
if key in config_dir:
if forceDefault == False and key in config_dir:
result = config_dir[key]
# Single quotes might break SQL queries, replacing them
@@ -69,16 +71,20 @@ def update_or_append(settings_list, item_tuple, key):
if settings_list is None:
settings_list = []
# mylog('debug', ['[Import Config] update_or_append debug '])
# mylog('debug', ['[Import Config] update_or_append ', settings_list])
# mylog('debug', ['[Import Config] update_or_append item_tuple ' , item_tuple])
for index, item in enumerate(settings_list):
if item[0] == key:
settings_list[index] = item_tuple
mylog('debug', ['[Import Config] FOUND key : ', key])
return settings_list
mylog('trace', ['[Import Config] OLD TUPLE : ', item])
# Keep values marked as "_KEEP_"
updated_tuple = tuple(
new_val if new_val != "_KEEP_" else old_val
for old_val, new_val in zip(item, item_tuple)
)
mylog('trace', ['[Import Config] NEW TUPLE : ', updated_tuple])
settings_list[index] = updated_tuple
mylog('trace', ['[Import Config] FOUND key : ', key])
return settings_list
settings_list.append(item_tuple)
return settings_list
@@ -140,16 +146,11 @@ def importConfigs (db, all_plugins):
conf.DAYS_TO_KEEP_EVENTS = ccd('DAYS_TO_KEEP_EVENTS', 90 , c_d, 'Delete events days', '{"dataType":"integer", "elements": [{"elementType" : "input", "elementOptions" : [{"type": "number"}] ,"transformers": []}]}', '[]', 'General')
conf.HRS_TO_KEEP_NEWDEV = ccd('HRS_TO_KEEP_NEWDEV', 0 , c_d, 'Keep new devices for', '{"dataType":"integer", "elements": [{"elementType" : "input", "elementOptions" : [{"type": "number"}] ,"transformers": []}]}', "[]", 'General')
conf.API_CUSTOM_SQL = ccd('API_CUSTOM_SQL', 'SELECT * FROM Devices WHERE dev_PresentLastScan = 0' , c_d, 'Custom endpoint', '{"dataType":"string", "elements": [{"elementType" : "input", "elementOptions" : [] ,"transformers": []}]}', '[]', 'General')
conf.VERSION = ccd('VERSION', '' , c_d, 'Version', '{"dataType":"string", "elements": [{"elementType" : "input", "elementOptions" : [{ "readonly": "true" }] ,"transformers": []}]}', '', 'General')
conf.NETWORK_DEVICE_TYPES = ccd('NETWORK_DEVICE_TYPES', ['AP', 'Gateway', 'Firewall', 'Hypervisor', 'Powerline', 'Switch', 'WLAN', 'PLC', 'Router','USB LAN Adapter', 'USB WIFI Adapter', 'Internet'] , c_d, 'Network device types', '{"dataType":"array","elements":[{"elementType":"input","elementOptions":[{"placeholder":"Enter value"},{"suffix":"_in"},{"cssClasses":"col-sm-10"},{"prefillValue":"null"}],"transformers":[]},{"elementType":"button","elementOptions":[{"sourceSuffixes":["_in"]},{"separator":""},{"cssClasses":"col-xs-12"},{"onClick":"addList(this,false)"},{"getStringKey":"Gen_Add"}],"transformers":[]},{"elementType":"select", "elementHasInputValue":1,"elementOptions":[{"multiple":"true"},{"readonly":"true"},{"editable":"true"}],"transformers":[]},{"elementType":"button","elementOptions":[{"sourceSuffixes":[]},{"separator":""},{"cssClasses":"col-xs-6"},{"onClick":"removeAllOptions(this)"},{"getStringKey":"Gen_Remove_All"}],"transformers":[]},{"elementType":"button","elementOptions":[{"sourceSuffixes":[]},{"separator":""},{"cssClasses":"col-xs-6"},{"onClick":"removeFromList(this)"},{"getStringKey":"Gen_Remove_Last"}],"transformers":[]}]}', '[]', 'General')
# UI
conf.UI_LANG = ccd('UI_LANG', 'English' , c_d, 'Language Interface', '{"dataType":"string", "elements": [{"elementType" : "select", "elementOptions" : [] ,"transformers": []}]}', "['English', 'French', 'German', 'Norwegian', 'Russian', 'Spanish', 'Italian (it_it)', 'Portuguese (pt_br)', 'Polish (pl_pl)', 'Turkish (tr_tr)', 'Chinese (zh_cn)', 'Czech (cs_cz)' ]", 'UI')
conf.UI_NOT_RANDOM_MAC = ccd('UI_NOT_RANDOM_MAC', [] , c_d, 'Exlude from Random Prefix', '{"dataType": "array","elements": [ {"elementType": "input","elementOptions": [{ "placeholder": "Enter value" },{ "suffix": "_in" },{ "cssClasses": "col-sm-10" },{ "prefillValue": "null" }],"transformers": [] }, {"elementType": "button","elementOptions": [{ "sourceSuffixes": ["_in"] },{ "separator": "" },{ "cssClasses": "col-xs-12" },{ "onClick": "addList(this, false)" },{ "getStringKey": "Gen_Add" }],"transformers": [] }, {"elementType": "button","elementOptions": [{ "sourceSuffixes": [] },{ "separator": "" },{ "cssClasses": "col-xs-6" },{ "onClick": "removeAllOptions(this)" },{ "getStringKey": "Gen_Remove_All" }],"transformers": []},{"elementType": "button","elementOptions": [{ "sourceSuffixes": [] },{ "separator": "" },{ "cssClasses": "col-xs-6" },{ "onClick": "removeFromList(this)" },{ "getStringKey": "Gen_Remove_Last" }],"transformers": []}, {"elementType": "select","elementOptions": [{ "multiple": "true" },{ "readonly": "true" },{ "editable": "true" }],"transformers": [] }]}', "[]", 'UI')
conf.UI_ICONS = ccd('UI_ICONS', ['PGkgY2xhc3M9J2ZhIGZhLXdpZmknPjwvaT4=', 'PGkgY2xhc3M9ImZhIGZhLWNvbXB1dGVyIj48L2k+', 'PGkgY2xhc3M9ImZhIGZhLWV0aGVybmV0Ij48L2k+', 'PGkgY2xhc3M9ImZhIGZhLWdhbWVwYWQiPjwvaT4', 'PGkgY2xhc3M9ImZhIGZhLWdsb2JlIj48L2k+', 'PGkgY2xhc3M9ImZhIGZhLWxhcHRvcCI+PC9pPg==', 'PGkgY2xhc3M9ImZhIGZhLWxpZ2h0YnVsYiI+PC9pPg==', 'PGkgY2xhc3M9ImZhIGZhLXNoaWVsZCI+PC9pPg==', 'PGkgY2xhc3M9ImZhIGZhLXdpZmkiPjwvaT4', 'PGkgY2xhc3M9J2ZhIGZhLWdhbWVwYWQnPjwvaT4'] , c_d, 'Icons', '{"dataType": "array","elements": [ {"elementType": "input","elementOptions": [{ "placeholder": "Enter value" },{ "suffix": "_in" },{ "cssClasses": "col-sm-10" },{ "prefillValue": "null" }],"transformers": [] }, {"elementType": "button","elementOptions": [{ "sourceSuffixes": ["_in"] },{ "separator": "" },{ "cssClasses": "col-xs-12" },{ "onClick": "addList(this, false)" },{ "getStringKey": "Gen_Add" }],"transformers": [] }, {"elementType": "button","elementOptions": [{ "sourceSuffixes": [] },{ "separator": "" },{ "cssClasses": "col-xs-6" },{ "onClick": "removeAllOptions(this)" },{ "getStringKey": "Gen_Remove_All" }],"transformers": []},{"elementType": "button","elementOptions": [{ "sourceSuffixes": [] },{ "separator": "" },{ "cssClasses": "col-xs-6" },{ "onClick": "removeFromList(this)" },{ "getStringKey": "Gen_Remove_Last" }],"transformers": []}, {"elementType": "select","elementOptions": [{ "multiple": "true" },{ "readonly": "true" },{ "editable": "true" }],"transformers": [] }]}', "[]", 'UI')
conf.UI_REFRESH = ccd('UI_REFRESH', 0 , c_d, 'Refresh interval', '{"dataType":"integer", "elements": [{"elementType" : "input", "elementOptions" : [{"type": "number"}] ,"transformers": []}]}', "[]", 'UI')
conf.UI_DEV_SECTIONS = ccd('UI_DEV_SECTIONS', [] , c_d, 'Show sections', '{"dataType":"array", "elements": [{"elementType" : "select", "elementOptions" : [{"multiple":"true"}] ,"transformers": []}]}', "['Tile Cards', 'Device Presence']", 'UI')
conf.UI_PRESENCE = ccd('UI_PRESENCE', ['online', 'offline', 'archived'] , c_d, 'Include in presence', '{"dataType":"array", "elements": [{"elementType" : "select", "elementOptions" : [{"multiple":"true"}] ,"transformers": []}]}', "['online', 'offline', 'archived']", 'UI')
conf.UI_MY_DEVICES = ccd('UI_MY_DEVICES', ['online', 'offline', 'archived', 'new', 'down'] , c_d, 'Include in My Devices', '{"dataType":"array", "elements": [{"elementType" : "select", "elementOptions" : [{"multiple":"true"}] ,"transformers": []}]}', "['online', 'offline', 'archived', 'new', 'down']", 'UI')
conf.UI_LANG = ccd('UI_LANG', 'English' , c_d, 'Language Interface', '{"dataType":"string", "elements": [{"elementType" : "select", "elementOptions" : [] ,"transformers": []}]}', "['English', 'French', 'German', 'Norwegian', 'Russian', 'Spanish', 'Italian (it_it)', 'Portuguese (pt_br)', 'Polish (pl_pl)', 'Turkish (tr_tr)', 'Chinese (zh_cn)', 'Czech (cs_cz)' ]", 'UI')
# Init timezone in case it changed
conf.tz = timezone(conf.TIMEZONE)
@@ -285,8 +286,7 @@ def importConfigs (db, all_plugins):
for plugin in all_plugins:
pref = plugin["unique_prefix"]
loaded_plugins_prefixes.append(pref)
# save the newly discovered plugins as options and default values
conf.LOADED_PLUGINS = ccd('LOADED_PLUGINS', loaded_plugins_prefixes , c_d, 'Loaded plugins', '{"dataType":"array", "elements": [{"elementType" : "select", "elementOptions" : [{"multiple":"true"}] ,"transformers": []}]}', str(sorted(all_plugins_prefixes)), 'General')
@@ -296,7 +296,54 @@ def importConfigs (db, all_plugins):
conf.plugins_once_run = False
# -----------------
# Plugins END
# TODO check app_conf_override.json
# Assuming fullConfFolder is defined elsewhere
app_conf_override_path = fullConfFolder + '/app_conf_override.json'
if os.path.exists(app_conf_override_path):
with open(app_conf_override_path, 'r') as f:
try:
# Load settings_override from the JSON file
settings_override = json.load(f)
# Loop through settings_override dictionary
for setting_name, value in settings_override.items():
# Ensure the value is treated as a string and passed directly
if isinstance(value, str):
# Log the value being passed
# ccd(key, default, config_dir, name, inputtype, options, group, events=None, desc="", regex="", setJsonMetadata=None, overrideTemplate=None, forceDefault=False)
mylog('debug', [f"[Config] Setting override {setting_name} with value: {value}"])
ccd(setting_name, value, c_d, '_KEEP_', '_KEEP_', '_KEEP_', '_KEEP_', None, "_KEEP_", "", None, None, True)
else:
# Convert to string and log
# ccd(key, default, config_dir, name, inputtype, options, group, events=None, desc="", regex="", setJsonMetadata=None, overrideTemplate=None, forceDefault=False)
mylog('debug', [f"[Config] Setting override {setting_name} with value: {str(value)}"])
ccd(setting_name, str(value), c_d, '_KEEP_', '_KEEP_', '_KEEP_', '_KEEP_', None, "_KEEP_", "", None, None, True)
except json.JSONDecodeError:
mylog('none', [f"[Config] [ERROR] Setting override decoding JSON from {app_conf_override_path}"])
else:
mylog('debug', [f"[Config] File {app_conf_override_path} does not exist."])
# Check if app was upgraded
with open(applicationPath + '/front/buildtimestamp.txt', 'r') as f:
buildTimestamp = int(f.read().strip())
cur_version = conf.VERSION
mylog('debug', [f"[Config] buildTimestamp: '{buildTimestamp}'"])
mylog('debug', [f"[Config] conf.VERSION : '{cur_version}'"])
if str(cur_version) != str(buildTimestamp):
mylog('none', ['[Config] App upgraded 🚀'])
# ccd(key, default, config_dir, name, inputtype, options, group, events=None, desc="", regex="", setJsonMetadata=None, overrideTemplate=None, forceDefault=False)
ccd('VERSION', buildTimestamp , c_d, '_KEEP_', '_KEEP_', '_KEEP_', '_KEEP_', None, "_KEEP_", "", None, None, True)
write_notification(f'[Upgrade] : App upgraded 🚀 Please clear the cache: <ol> <li>Clear the browser cache (shift + browser refresh button)</li> <li> Clear app cache with the 🔄 (reload) button in the header</li></ol> Check out new features and what has changed in the <a href="https://github.com/jokob-sk/NetAlertX/releases" target="_blank">📓 release notes</a>.', 'interrupt', timeNowTZ())
# Insert settings into the DB
sql.execute ("DELETE FROM Settings")