FQDN, Dig refactor, docs #1065

This commit is contained in:
jokob-sk
2025-06-01 13:59:54 +10:00
parent 941e838c74
commit f4a3717859
50 changed files with 941 additions and 874 deletions

View File

@@ -20,7 +20,7 @@
DISCOVER_PLUGINS=True DISCOVER_PLUGINS=True
SCAN_SUBNETS=['192.168.1.0/24 --interface=eth0'] SCAN_SUBNETS=['192.168.1.0/24 --interface=eth0']
TIMEZONE='Europe/Berlin' TIMEZONE='Europe/Berlin'
LOADED_PLUGINS=['ARPSCAN','CSVBCKP','DBCLNP', 'INTRNT','MAINT','NEWDEV','NSLOOKUP','NTFPRCS', 'AVAHISCAN', 'SETPWD','SMTP', 'SYNC', 'VNDRPDT', 'WORKFLOWS', 'UI'] LOADED_PLUGINS=['ARPSCAN','CSVBCKP','DBCLNP', 'DIGSCAN', 'INTRNT','MAINT','NEWDEV', 'NBTSCAN', 'NSLOOKUP','NTFPRCS', 'AVAHISCAN', 'SETPWD','SMTP', 'SYNC', 'VNDRPDT', 'WORKFLOWS', 'UI']
DAYS_TO_KEEP_EVENTS=90 DAYS_TO_KEEP_EVENTS=90
# Used for generating links in emails. Make sure not to add a trailing slash! # Used for generating links in emails. Make sure not to add a trailing slash!

Binary file not shown.

BIN
back/app_clean.db Executable file

Binary file not shown.

BIN
back/app_old.db Executable file

Binary file not shown.

View File

@@ -38,6 +38,7 @@
| `devSyncHubNode` | The NetAlertX node ID used for synchronization between NetAlertX instances. | `node_1` | | `devSyncHubNode` | The NetAlertX node ID used for synchronization between NetAlertX instances. | `node_1` |
| `devSourcePlugin` | Source plugin that discovered the device. | `ARPSCAN` | | `devSourcePlugin` | Source plugin that discovered the device. | `ARPSCAN` |
| `devCustomProps` | [Custom properties](./CUSTOM_PROPERTIES.md) related to the device. The value is a base64-encoded JSON object. | `PHN2ZyB...` | | `devCustomProps` | [Custom properties](./CUSTOM_PROPERTIES.md) related to the device. The value is a base64-encoded JSON object. | `PHN2ZyB...` |
| `devFQDN` | Fully qualified domain name. | `raspberrypi.local` |
To understand how values of these fields influuence application behavior, such as Notifications or Network topology, see also: To understand how values of these fields influuence application behavior, such as Notifications or Network topology, see also:

View File

@@ -9,6 +9,7 @@ For best results, ensure the following name resolution plugins are enabled:
- **AVAHISCAN** Uses mDNS/Avahi to resolve local network names. - **AVAHISCAN** Uses mDNS/Avahi to resolve local network names.
- **NBTSCAN** Queries NetBIOS to find device names. - **NBTSCAN** Queries NetBIOS to find device names.
- **NSLOOKUP** Performs standard DNS lookups. - **NSLOOKUP** Performs standard DNS lookups.
- **DIGSCAN** Performs Name Resolution with the Dig utility (DNS).
You can check which plugins are active in your _Settings_ section and enable any that are missing. You can check which plugins are active in your _Settings_ section and enable any that are missing.

View File

@@ -56,6 +56,7 @@ Device-detecting plugins insert values into the `CurrentScan` database table. T
| `DDNS` | ⚙ | DDNS update | | | Script | [ddns_update](https://github.com/jokob-sk/NetAlertX/tree/main/front/plugins/ddns_update/) | | `DDNS` | ⚙ | DDNS update | | | Script | [ddns_update](https://github.com/jokob-sk/NetAlertX/tree/main/front/plugins/ddns_update/) |
| `DHCPLSS` | 🔍/📥/🆎| Import devices from DHCP leases | | | Script | [dhcp_leases](https://github.com/jokob-sk/NetAlertX/tree/main/front/plugins/dhcp_leases/) | | `DHCPLSS` | 🔍/📥/🆎| Import devices from DHCP leases | | | Script | [dhcp_leases](https://github.com/jokob-sk/NetAlertX/tree/main/front/plugins/dhcp_leases/) |
| `DHCPSRVS` | ♻ | DHCP servers | | | Script | [dhcp_servers](https://github.com/jokob-sk/NetAlertX/tree/main/front/plugins/dhcp_servers/) | | `DHCPSRVS` | ♻ | DHCP servers | | | Script | [dhcp_servers](https://github.com/jokob-sk/NetAlertX/tree/main/front/plugins/dhcp_servers/) |
| `DIGSCAN` | 🆎 | Dig (DNS) Name resolution | | | Script | [dig_scan](https://github.com/jokob-sk/NetAlertX/tree/main/front/plugins/dig_scan/) |
| `FREEBOX` | 🔍/♻/🆎| Pull data and names from Freebox/Iliadbox | | | Script | [freebox](https://github.com/jokob-sk/NetAlertX/tree/main/front/plugins/freebox/) | | `FREEBOX` | 🔍/♻/🆎| Pull data and names from Freebox/Iliadbox | | | Script | [freebox](https://github.com/jokob-sk/NetAlertX/tree/main/front/plugins/freebox/) |
| `ICMP` | ♻ | ICMP (ping) status checker | | | Script | [icmp_scan](https://github.com/jokob-sk/NetAlertX/tree/main/front/plugins/icmp_scan/) | | `ICMP` | ♻ | ICMP (ping) status checker | | | Script | [icmp_scan](https://github.com/jokob-sk/NetAlertX/tree/main/front/plugins/icmp_scan/) |
| `INTRNT` | 🔍 | Internet IP scanner | | | Script | [internet_ip](https://github.com/jokob-sk/NetAlertX/tree/main/front/plugins/internet_ip/) | | `INTRNT` | 🔍 | Internet IP scanner | | | Script | [internet_ip](https://github.com/jokob-sk/NetAlertX/tree/main/front/plugins/internet_ip/) |

View File

@@ -69,8 +69,8 @@
// columns to hide // columns to hide
hiddenFields = ["NEWDEV_devScan", "NEWDEV_devPresentLastScan" ] hiddenFields = ["NEWDEV_devScan", "NEWDEV_devPresentLastScan" ]
// columns to disable - conditional depending if a new dummy device is created // columns to disable/readonly - conditional depending if a new dummy device is created
disabledFields = mac == "new" ? ["NEWDEV_devLastNotification", "NEWDEV_devFirstConnection", "NEWDEV_devLastConnection"] : ["NEWDEV_devLastNotification", "NEWDEV_devFirstConnection", "NEWDEV_devLastConnection", "NEWDEV_devMac", "NEWDEV_devLastIP", "NEWDEV_devSyncHubNode" ]; disabledFields = mac == "new" ? ["NEWDEV_devLastNotification", "NEWDEV_devFirstConnection", "NEWDEV_devLastConnection"] : ["NEWDEV_devLastNotification", "NEWDEV_devFirstConnection", "NEWDEV_devLastConnection", "NEWDEV_devMac", "NEWDEV_devLastIP", "NEWDEV_devSyncHubNode", "NEWDEV_devFQDN" ];
// Grouping of fields into categories with associated documentation links // Grouping of fields into categories with associated documentation links
const fieldGroups = { const fieldGroups = {
@@ -82,15 +82,6 @@
inputGroupClasses: "field-group main-group col-lg-4 col-sm-6 col-xs-12", inputGroupClasses: "field-group main-group col-lg-4 col-sm-6 col-xs-12",
labelClasses: "col-sm-4 col-xs-12 control-label", labelClasses: "col-sm-4 col-xs-12 control-label",
inputClasses: "col-sm-8 col-xs-12 input-group" inputClasses: "col-sm-8 col-xs-12 input-group"
},
// Group for session information
DevDetail_SessionInfo_Title: {
data: ["devStatus", "devLastConnection", "devFirstConnection"],
docs: "https://github.com/jokob-sk/NetAlertX/blob/main/docs/SESSION_INFO.md",
iconClass: "fa fa-calendar",
inputGroupClasses: "field-group session-group col-lg-4 col-sm-6 col-xs-12",
labelClasses: "col-sm-4 col-xs-12 control-label",
inputClasses: "col-sm-8 col-xs-12 input-group"
}, },
// Group for event and alert settings // Group for event and alert settings
DevDetail_EveandAl_Title: { DevDetail_EveandAl_Title: {
@@ -119,6 +110,15 @@
labelClasses: "col-sm-4 col-xs-12 control-label", labelClasses: "col-sm-4 col-xs-12 control-label",
inputClasses: "col-sm-8 col-xs-12 input-group" inputClasses: "col-sm-8 col-xs-12 input-group"
}, },
// Group for session information
DevDetail_SessionInfo_Title: {
data: ["devStatus", "devLastConnection", "devFirstConnection", "devFQDN"],
docs: "https://github.com/jokob-sk/NetAlertX/blob/main/docs/SESSION_INFO.md",
iconClass: "fa fa-calendar",
inputGroupClasses: "field-group session-group col-lg-4 col-sm-6 col-xs-12",
labelClasses: "col-sm-4 col-xs-12 control-label",
inputClasses: "col-sm-8 col-xs-12 input-group"
},
// Group for Custom properties. // Group for Custom properties.
DevDetail_CustomProperties_Title: { DevDetail_CustomProperties_Title: {
data: ["devCustomProps"], data: ["devCustomProps"],

View File

@@ -538,7 +538,8 @@ function mapColumnIndexToFieldName(index, tableColumnVisible) {
"devSourcePlugin", "devSourcePlugin",
"devPresentLastScan", "devPresentLastScan",
"devAlertDown", "devAlertDown",
"devCustomProps" "devCustomProps",
"devFQDN"
]; ];
// console.log("OrderBy: " + columnNames[tableColumnOrder[index]]); // console.log("OrderBy: " + columnNames[tableColumnOrder[index]]);
@@ -648,6 +649,7 @@ function initializeDatatable (status) {
devParentChildrenCount devParentChildrenCount
devIpLong devIpLong
devCustomProps devCustomProps
devFQDN
} }
count count
} }
@@ -722,7 +724,8 @@ function initializeDatatable (status) {
device.devSourcePlugin || "", device.devSourcePlugin || "",
device.devPresentLastScan || "", device.devPresentLastScan || "",
device.devAlertDown || "", device.devAlertDown || "",
device.devCustomProps || "" device.devCustomProps || "",
device.devFQDN || ""
]; ];
const newRow = []; const newRow = [];
@@ -760,7 +763,7 @@ function initializeDatatable (status) {
{visible: false, targets: tableColumnHide }, {visible: false, targets: tableColumnHide },
{className: 'text-center', targets: [mapIndx(4), mapIndx(9), mapIndx(10), mapIndx(15), mapIndx(18)] }, {className: 'text-center', targets: [mapIndx(4), mapIndx(9), mapIndx(10), mapIndx(15), mapIndx(18)] },
{className: 'iconColumn text-center', targets: [mapIndx(3)]}, {className: 'iconColumn text-center', targets: [mapIndx(3)]},
{width: '80px', targets: [mapIndx(6), mapIndx(7), mapIndx(15)] }, {width: '80px', targets: [mapIndx(6), mapIndx(7), mapIndx(15), mapIndx(27)] },
{width: '85px', targets: [mapIndx(9)] }, {width: '85px', targets: [mapIndx(9)] },
{width: '30px', targets: [mapIndx(3), mapIndx(10), mapIndx(13), mapIndx(18)] }, {width: '30px', targets: [mapIndx(3), mapIndx(10), mapIndx(13), mapIndx(18)] },
{orderData: [mapIndx(12)], targets: mapIndx(8) }, {orderData: [mapIndx(12)], targets: mapIndx(8) },

View File

@@ -112,7 +112,8 @@ function getServerDeviceData() {
"devSessions" => 0, "devSessions" => 0,
"devEvents" => 0, "devEvents" => 0,
"devDownAlerts" => 0, "devDownAlerts" => 0,
"devPresenceHours" => 0 "devPresenceHours" => 0,
"devFQDN" => ""
]; ];
echo json_encode($deviceData); echo json_encode($deviceData);
return; return;

View File

@@ -586,7 +586,8 @@ function getDevicesColumns(){
"devSite", "devSite",
"devSSID", "devSSID",
"devSourcePlugin", "devSourcePlugin",
"devCustomProps" "devCustomProps",
"devFQDN"
]; ];
return $columns; return $columns;

View File

@@ -211,6 +211,7 @@
"Device_TableHead_AlertDown": "", "Device_TableHead_AlertDown": "",
"Device_TableHead_Connected_Devices": "", "Device_TableHead_Connected_Devices": "",
"Device_TableHead_CustomProps": "", "Device_TableHead_CustomProps": "",
"Device_TableHead_FQDN": "",
"Device_TableHead_Favorite": "", "Device_TableHead_Favorite": "",
"Device_TableHead_FirstSession": "", "Device_TableHead_FirstSession": "",
"Device_TableHead_GUID": "", "Device_TableHead_GUID": "",
@@ -559,6 +560,8 @@
"Presence_Shortcut_Favorites": "", "Presence_Shortcut_Favorites": "",
"Presence_Shortcut_NewDevices": "", "Presence_Shortcut_NewDevices": "",
"Presence_Title": "", "Presence_Title": "",
"REFRESH_FQDN_description": "",
"REFRESH_FQDN_name": "",
"REPORT_DASHBOARD_URL_description": "", "REPORT_DASHBOARD_URL_description": "",
"REPORT_DASHBOARD_URL_name": "", "REPORT_DASHBOARD_URL_name": "",
"REPORT_ERROR": "", "REPORT_ERROR": "",

View File

@@ -211,6 +211,7 @@
"Device_TableHead_AlertDown": "Cancel·lar alerta", "Device_TableHead_AlertDown": "Cancel·lar alerta",
"Device_TableHead_Connected_Devices": "Connexions", "Device_TableHead_Connected_Devices": "Connexions",
"Device_TableHead_CustomProps": "Props / Accions", "Device_TableHead_CustomProps": "Props / Accions",
"Device_TableHead_FQDN": "",
"Device_TableHead_Favorite": "Favorit", "Device_TableHead_Favorite": "Favorit",
"Device_TableHead_FirstSession": "Primera Sessió", "Device_TableHead_FirstSession": "Primera Sessió",
"Device_TableHead_GUID": "GUID", "Device_TableHead_GUID": "GUID",
@@ -559,6 +560,8 @@
"Presence_Shortcut_Favorites": "Favorits", "Presence_Shortcut_Favorites": "Favorits",
"Presence_Shortcut_NewDevices": "Nous dispositius", "Presence_Shortcut_NewDevices": "Nous dispositius",
"Presence_Title": "Detecció de dispositius", "Presence_Title": "Detecció de dispositius",
"REFRESH_FQDN_description": "",
"REFRESH_FQDN_name": "",
"REPORT_DASHBOARD_URL_description": "Aquesta URL s'utilitza com a base per generar enllaços en informes HTML (per exemple: correus electrònics). Introduïu la URL completa començant per <code>http://</code> incloent el número de port (sense barra inicial <code>/</code>).", "REPORT_DASHBOARD_URL_description": "Aquesta URL s'utilitza com a base per generar enllaços en informes HTML (per exemple: correus electrònics). Introduïu la URL completa començant per <code>http://</code> incloent el número de port (sense barra inicial <code>/</code>).",
"REPORT_DASHBOARD_URL_name": "URL NetAlertX", "REPORT_DASHBOARD_URL_name": "URL NetAlertX",
"REPORT_ERROR": "Si us plau, introdueix dins de la caixa de text els caràcters que veu a la imatge de sota. Això és requerit per evitar enviaments automàtics", "REPORT_ERROR": "Si us plau, introdueix dins de la caixa de text els caràcters que veu a la imatge de sota. Això és requerit per evitar enviaments automàtics",

View File

@@ -211,6 +211,7 @@
"Device_TableHead_AlertDown": "", "Device_TableHead_AlertDown": "",
"Device_TableHead_Connected_Devices": "", "Device_TableHead_Connected_Devices": "",
"Device_TableHead_CustomProps": "", "Device_TableHead_CustomProps": "",
"Device_TableHead_FQDN": "",
"Device_TableHead_Favorite": "", "Device_TableHead_Favorite": "",
"Device_TableHead_FirstSession": "", "Device_TableHead_FirstSession": "",
"Device_TableHead_GUID": "", "Device_TableHead_GUID": "",
@@ -559,6 +560,8 @@
"Presence_Shortcut_Favorites": "", "Presence_Shortcut_Favorites": "",
"Presence_Shortcut_NewDevices": "", "Presence_Shortcut_NewDevices": "",
"Presence_Title": "", "Presence_Title": "",
"REFRESH_FQDN_description": "",
"REFRESH_FQDN_name": "",
"REPORT_DASHBOARD_URL_description": "", "REPORT_DASHBOARD_URL_description": "",
"REPORT_DASHBOARD_URL_name": "", "REPORT_DASHBOARD_URL_name": "",
"REPORT_ERROR": "", "REPORT_ERROR": "",

View File

@@ -223,6 +223,7 @@
"Device_TableHead_AlertDown": "Alarm aus", "Device_TableHead_AlertDown": "Alarm aus",
"Device_TableHead_Connected_Devices": "Verbindungen", "Device_TableHead_Connected_Devices": "Verbindungen",
"Device_TableHead_CustomProps": "Eigenschaften / Aktionen", "Device_TableHead_CustomProps": "Eigenschaften / Aktionen",
"Device_TableHead_FQDN": "",
"Device_TableHead_Favorite": "Favorit", "Device_TableHead_Favorite": "Favorit",
"Device_TableHead_FirstSession": "Erste Sitzung", "Device_TableHead_FirstSession": "Erste Sitzung",
"Device_TableHead_GUID": "GUID", "Device_TableHead_GUID": "GUID",
@@ -600,6 +601,8 @@
"Presence_Shortcut_Favorites": "Favoriten", "Presence_Shortcut_Favorites": "Favoriten",
"Presence_Shortcut_NewDevices": "Neue Geräte", "Presence_Shortcut_NewDevices": "Neue Geräte",
"Presence_Title": "Anwesenheit pro Gerät", "Presence_Title": "Anwesenheit pro Gerät",
"REFRESH_FQDN_description": "",
"REFRESH_FQDN_name": "",
"REPORT_APPRISE_description": "Enable sending notifications via <a target=\"_blank\" href=\"https://hub.docker.com/r/caronc/apprise\">Apprise</a>.", "REPORT_APPRISE_description": "Enable sending notifications via <a target=\"_blank\" href=\"https://hub.docker.com/r/caronc/apprise\">Apprise</a>.",
"REPORT_APPRISE_name": "Enable Apprise", "REPORT_APPRISE_name": "Enable Apprise",
"REPORT_DASHBOARD_URL_description": "Diese URL wird als Basis fürs Erstellen von Links in E-Mails genutzt. Geben Sie die gesamte URL startend mit <code>http://</code> inklusive der genutzten Portnummer ein (keinen nachfolgenden Schrägstrich <code>/</code> nutzen).", "REPORT_DASHBOARD_URL_description": "Diese URL wird als Basis fürs Erstellen von Links in E-Mails genutzt. Geben Sie die gesamte URL startend mit <code>http://</code> inklusive der genutzten Portnummer ein (keinen nachfolgenden Schrägstrich <code>/</code> nutzen).",

View File

@@ -211,6 +211,7 @@
"Device_TableHead_AlertDown": "Alert Down", "Device_TableHead_AlertDown": "Alert Down",
"Device_TableHead_Connected_Devices": "Connections", "Device_TableHead_Connected_Devices": "Connections",
"Device_TableHead_CustomProps": "Props / Actions", "Device_TableHead_CustomProps": "Props / Actions",
"Device_TableHead_FQDN": "FQDN",
"Device_TableHead_Favorite": "Favorite", "Device_TableHead_Favorite": "Favorite",
"Device_TableHead_FirstSession": "First Session", "Device_TableHead_FirstSession": "First Session",
"Device_TableHead_GUID": "GUID", "Device_TableHead_GUID": "GUID",
@@ -559,6 +560,8 @@
"Presence_Shortcut_Favorites": "Favorites", "Presence_Shortcut_Favorites": "Favorites",
"Presence_Shortcut_NewDevices": "New Devices", "Presence_Shortcut_NewDevices": "New Devices",
"Presence_Title": "Presence by Device", "Presence_Title": "Presence by Device",
"REFRESH_FQDN_description": "Rescans all devices and refreshes their Fully Qualified Domain Name (FQDN). If disabled, only devices without a known name are scanned to improve performance. In this case, FQDN is updated only during initial device discovery.",
"REFRESH_FQDN_name": "Refresh FQDN",
"REPORT_DASHBOARD_URL_description": "This URL is used as the base for generating links in HTML reports (e.g.: emails). Enter full URL starting with <code>http://</code> including the port number (no trailing slash <code>/</code>).", "REPORT_DASHBOARD_URL_description": "This URL is used as the base for generating links in HTML reports (e.g.: emails). Enter full URL starting with <code>http://</code> including the port number (no trailing slash <code>/</code>).",
"REPORT_DASHBOARD_URL_name": "NetAlertX URL", "REPORT_DASHBOARD_URL_name": "NetAlertX URL",
"REPORT_ERROR": "The page you are looking for is temporarily unavailable, please try again after a few seconds", "REPORT_ERROR": "The page you are looking for is temporarily unavailable, please try again after a few seconds",

View File

@@ -221,6 +221,7 @@
"Device_TableHead_AlertDown": "Alerta desactivada", "Device_TableHead_AlertDown": "Alerta desactivada",
"Device_TableHead_Connected_Devices": "Conexiones", "Device_TableHead_Connected_Devices": "Conexiones",
"Device_TableHead_CustomProps": "Propiedades / Acciones", "Device_TableHead_CustomProps": "Propiedades / Acciones",
"Device_TableHead_FQDN": "",
"Device_TableHead_Favorite": "Favorito", "Device_TableHead_Favorite": "Favorito",
"Device_TableHead_FirstSession": "1ra. sesión", "Device_TableHead_FirstSession": "1ra. sesión",
"Device_TableHead_GUID": "GUID", "Device_TableHead_GUID": "GUID",
@@ -598,6 +599,8 @@
"Presence_Shortcut_Favorites": "Favorito(s)", "Presence_Shortcut_Favorites": "Favorito(s)",
"Presence_Shortcut_NewDevices": "Nuevo(s)", "Presence_Shortcut_NewDevices": "Nuevo(s)",
"Presence_Title": "Historial por dispositivo", "Presence_Title": "Historial por dispositivo",
"REFRESH_FQDN_description": "",
"REFRESH_FQDN_name": "",
"REPORT_APPRISE_description": "Habilitar el envío de notificaciones a través de <a target=\"_blank\" href=\"https://hub.docker.com/r/caronc/apprise\">Apprise</a>.", "REPORT_APPRISE_description": "Habilitar el envío de notificaciones a través de <a target=\"_blank\" href=\"https://hub.docker.com/r/caronc/apprise\">Apprise</a>.",
"REPORT_APPRISE_name": "Habilitar Apprise", "REPORT_APPRISE_name": "Habilitar Apprise",
"REPORT_DASHBOARD_URL_description": "Esta URL se utiliza como base para generar enlaces en los correos electrónicos. Ingrese la URL completa que comienza con <code>http://</code>, incluido el número de puerto (sin barra inclinada al final <code>/</code>).", "REPORT_DASHBOARD_URL_description": "Esta URL se utiliza como base para generar enlaces en los correos electrónicos. Ingrese la URL completa que comienza con <code>http://</code>, incluido el número de puerto (sin barra inclinada al final <code>/</code>).",

View File

@@ -211,6 +211,7 @@
"Device_TableHead_AlertDown": "Alerter si En panne", "Device_TableHead_AlertDown": "Alerter si En panne",
"Device_TableHead_Connected_Devices": "Connexions", "Device_TableHead_Connected_Devices": "Connexions",
"Device_TableHead_CustomProps": "Champs / Actions", "Device_TableHead_CustomProps": "Champs / Actions",
"Device_TableHead_FQDN": "",
"Device_TableHead_Favorite": "Favori", "Device_TableHead_Favorite": "Favori",
"Device_TableHead_FirstSession": "Première session", "Device_TableHead_FirstSession": "Première session",
"Device_TableHead_GUID": "GUID", "Device_TableHead_GUID": "GUID",
@@ -559,6 +560,8 @@
"Presence_Shortcut_Favorites": "Favoris", "Presence_Shortcut_Favorites": "Favoris",
"Presence_Shortcut_NewDevices": "Nouveaux appareils", "Presence_Shortcut_NewDevices": "Nouveaux appareils",
"Presence_Title": "Présence par appareil", "Presence_Title": "Présence par appareil",
"REFRESH_FQDN_description": "",
"REFRESH_FQDN_name": "",
"REPORT_DASHBOARD_URL_description": "Cette URL est utilisée comme base pour générer les liens des rapports HTML (par ex. les courriels). Renseignez l'adresse complète, commençant par <code>http://</code> et incluznt le numero de port (sans slash <code>/</code> à la fin).", "REPORT_DASHBOARD_URL_description": "Cette URL est utilisée comme base pour générer les liens des rapports HTML (par ex. les courriels). Renseignez l'adresse complète, commençant par <code>http://</code> et incluznt le numero de port (sans slash <code>/</code> à la fin).",
"REPORT_DASHBOARD_URL_name": "URL de NetAlertX", "REPORT_DASHBOARD_URL_name": "URL de NetAlertX",
"REPORT_ERROR": "La page que vous cherchez est temporairement indisponible. Merci de réessayer dans quelques secondes", "REPORT_ERROR": "La page que vous cherchez est temporairement indisponible. Merci de réessayer dans quelques secondes",

View File

@@ -211,6 +211,7 @@
"Device_TableHead_AlertDown": "Avviso disconnessione", "Device_TableHead_AlertDown": "Avviso disconnessione",
"Device_TableHead_Connected_Devices": "Connessioni", "Device_TableHead_Connected_Devices": "Connessioni",
"Device_TableHead_CustomProps": "Proprietà/Azioni", "Device_TableHead_CustomProps": "Proprietà/Azioni",
"Device_TableHead_FQDN": "",
"Device_TableHead_Favorite": "Preferito", "Device_TableHead_Favorite": "Preferito",
"Device_TableHead_FirstSession": "Prima sessione", "Device_TableHead_FirstSession": "Prima sessione",
"Device_TableHead_GUID": "GUID", "Device_TableHead_GUID": "GUID",
@@ -559,6 +560,8 @@
"Presence_Shortcut_Favorites": "Preferiti", "Presence_Shortcut_Favorites": "Preferiti",
"Presence_Shortcut_NewDevices": "Nuovi dispositivi", "Presence_Shortcut_NewDevices": "Nuovi dispositivi",
"Presence_Title": "Presenza per dispositivo", "Presence_Title": "Presenza per dispositivo",
"REFRESH_FQDN_description": "",
"REFRESH_FQDN_name": "",
"REPORT_DASHBOARD_URL_description": "Questo URL viene utilizzato come base per generare collegamenti nei report HTML (ad esempio: e-mail). Inserisci l'URL completo che inizia con <code>http://</code> incluso il numero di porta (nessuna barra finale <code>/</code>).", "REPORT_DASHBOARD_URL_description": "Questo URL viene utilizzato come base per generare collegamenti nei report HTML (ad esempio: e-mail). Inserisci l'URL completo che inizia con <code>http://</code> incluso il numero di porta (nessuna barra finale <code>/</code>).",
"REPORT_DASHBOARD_URL_name": "URL NetAlertX", "REPORT_DASHBOARD_URL_name": "URL NetAlertX",
"REPORT_ERROR": "La pagina che stai cercando è momentaneamente non disponibile, riprova tra qualche secondo", "REPORT_ERROR": "La pagina che stai cercando è momentaneamente non disponibile, riprova tra qualche secondo",

View File

@@ -211,6 +211,7 @@
"Device_TableHead_AlertDown": "", "Device_TableHead_AlertDown": "",
"Device_TableHead_Connected_Devices": "Tilkoblinger", "Device_TableHead_Connected_Devices": "Tilkoblinger",
"Device_TableHead_CustomProps": "", "Device_TableHead_CustomProps": "",
"Device_TableHead_FQDN": "",
"Device_TableHead_Favorite": "Favoritt", "Device_TableHead_Favorite": "Favoritt",
"Device_TableHead_FirstSession": "Første Økt", "Device_TableHead_FirstSession": "Første Økt",
"Device_TableHead_GUID": "GUID", "Device_TableHead_GUID": "GUID",
@@ -559,6 +560,8 @@
"Presence_Shortcut_Favorites": "Favoritter", "Presence_Shortcut_Favorites": "Favoritter",
"Presence_Shortcut_NewDevices": "Nye enheter", "Presence_Shortcut_NewDevices": "Nye enheter",
"Presence_Title": "Tilstedeværelse etter enhet", "Presence_Title": "Tilstedeværelse etter enhet",
"REFRESH_FQDN_description": "",
"REFRESH_FQDN_name": "",
"REPORT_DASHBOARD_URL_description": "Denne URL-en brukes som base for å generere lenker i HTML-rapporter (f.eks.: E -post). Skriv inn full URL som starter med <code>http://</code> inkludert portnummeret (ingen etterfølgende slash <code>/</code>).", "REPORT_DASHBOARD_URL_description": "Denne URL-en brukes som base for å generere lenker i HTML-rapporter (f.eks.: E -post). Skriv inn full URL som starter med <code>http://</code> inkludert portnummeret (ingen etterfølgende slash <code>/</code>).",
"REPORT_DASHBOARD_URL_name": "NetAlertX URL", "REPORT_DASHBOARD_URL_name": "NetAlertX URL",
"REPORT_ERROR": "Siden du leter etter er midlertidig utilgjengelig, prøv igjen etter noen sekunder", "REPORT_ERROR": "Siden du leter etter er midlertidig utilgjengelig, prøv igjen etter noen sekunder",

View File

@@ -211,6 +211,7 @@
"Device_TableHead_AlertDown": "", "Device_TableHead_AlertDown": "",
"Device_TableHead_Connected_Devices": "Połączenia", "Device_TableHead_Connected_Devices": "Połączenia",
"Device_TableHead_CustomProps": "", "Device_TableHead_CustomProps": "",
"Device_TableHead_FQDN": "",
"Device_TableHead_Favorite": "Ulubione", "Device_TableHead_Favorite": "Ulubione",
"Device_TableHead_FirstSession": "Pierwsza Sesja", "Device_TableHead_FirstSession": "Pierwsza Sesja",
"Device_TableHead_GUID": "GUID", "Device_TableHead_GUID": "GUID",
@@ -559,6 +560,8 @@
"Presence_Shortcut_Favorites": "Ulubione", "Presence_Shortcut_Favorites": "Ulubione",
"Presence_Shortcut_NewDevices": "Nowe Urządzenia", "Presence_Shortcut_NewDevices": "Nowe Urządzenia",
"Presence_Title": "Obecność Urządzenia", "Presence_Title": "Obecność Urządzenia",
"REFRESH_FQDN_description": "",
"REFRESH_FQDN_name": "",
"REPORT_DASHBOARD_URL_description": "Link jest używany jako podstawa do generowania linków dla zgłoszeń HTML (np. e-maile). Wprowadź pełen adres zaczynając od <code>http://</code> oraz dodając numer portu (bez zakańczania ukośnikiem <code>/</code>).", "REPORT_DASHBOARD_URL_description": "Link jest używany jako podstawa do generowania linków dla zgłoszeń HTML (np. e-maile). Wprowadź pełen adres zaczynając od <code>http://</code> oraz dodając numer portu (bez zakańczania ukośnikiem <code>/</code>).",
"REPORT_DASHBOARD_URL_name": "Link NetAlertX", "REPORT_DASHBOARD_URL_name": "Link NetAlertX",
"REPORT_ERROR": "Strona której szukasz jest tymczasowo niedostępna, spróbuj ponownie za kilka sekund", "REPORT_ERROR": "Strona której szukasz jest tymczasowo niedostępna, spróbuj ponownie za kilka sekund",

View File

@@ -211,6 +211,7 @@
"Device_TableHead_AlertDown": "Alerta em baixo", "Device_TableHead_AlertDown": "Alerta em baixo",
"Device_TableHead_Connected_Devices": "Conexões", "Device_TableHead_Connected_Devices": "Conexões",
"Device_TableHead_CustomProps": "", "Device_TableHead_CustomProps": "",
"Device_TableHead_FQDN": "",
"Device_TableHead_Favorite": "Favorito", "Device_TableHead_Favorite": "Favorito",
"Device_TableHead_FirstSession": "Primeira sessão", "Device_TableHead_FirstSession": "Primeira sessão",
"Device_TableHead_GUID": "GUID", "Device_TableHead_GUID": "GUID",
@@ -559,6 +560,8 @@
"Presence_Shortcut_Favorites": "", "Presence_Shortcut_Favorites": "",
"Presence_Shortcut_NewDevices": "", "Presence_Shortcut_NewDevices": "",
"Presence_Title": "", "Presence_Title": "",
"REFRESH_FQDN_description": "",
"REFRESH_FQDN_name": "",
"REPORT_DASHBOARD_URL_description": "", "REPORT_DASHBOARD_URL_description": "",
"REPORT_DASHBOARD_URL_name": "", "REPORT_DASHBOARD_URL_name": "",
"REPORT_ERROR": "", "REPORT_ERROR": "",

View File

@@ -211,6 +211,7 @@
"Device_TableHead_AlertDown": "Оповещение о сост. ВЫКЛ", "Device_TableHead_AlertDown": "Оповещение о сост. ВЫКЛ",
"Device_TableHead_Connected_Devices": "Соединения", "Device_TableHead_Connected_Devices": "Соединения",
"Device_TableHead_CustomProps": "Свойства / Действия", "Device_TableHead_CustomProps": "Свойства / Действия",
"Device_TableHead_FQDN": "",
"Device_TableHead_Favorite": "Избранное", "Device_TableHead_Favorite": "Избранное",
"Device_TableHead_FirstSession": "Первый сеанс", "Device_TableHead_FirstSession": "Первый сеанс",
"Device_TableHead_GUID": "GUID", "Device_TableHead_GUID": "GUID",
@@ -559,6 +560,8 @@
"Presence_Shortcut_Favorites": "Избранные", "Presence_Shortcut_Favorites": "Избранные",
"Presence_Shortcut_NewDevices": "Новые устройства", "Presence_Shortcut_NewDevices": "Новые устройства",
"Presence_Title": "Присутствие по устройству", "Presence_Title": "Присутствие по устройству",
"REFRESH_FQDN_description": "",
"REFRESH_FQDN_name": "",
"REPORT_DASHBOARD_URL_description": "Этот URL-адрес используется в качестве основы для создания ссылок в отчетах HTML (например, в электронных письмах). Введите полный URL-адрес, начинающийся с <code>http://</code>, включая номер порта (без косой черты <code>/</code>).", "REPORT_DASHBOARD_URL_description": "Этот URL-адрес используется в качестве основы для создания ссылок в отчетах HTML (например, в электронных письмах). Введите полный URL-адрес, начинающийся с <code>http://</code>, включая номер порта (без косой черты <code>/</code>).",
"REPORT_DASHBOARD_URL_name": "NetAlertX URL", "REPORT_DASHBOARD_URL_name": "NetAlertX URL",
"REPORT_ERROR": "Страница, которую вы ищете, временно недоступна, повторите попытку через несколько секунд", "REPORT_ERROR": "Страница, которую вы ищете, временно недоступна, повторите попытку через несколько секунд",

View File

@@ -211,6 +211,7 @@
"Device_TableHead_AlertDown": "Çalışmama Alarmı", "Device_TableHead_AlertDown": "Çalışmama Alarmı",
"Device_TableHead_Connected_Devices": "Bağlantılar", "Device_TableHead_Connected_Devices": "Bağlantılar",
"Device_TableHead_CustomProps": "Özellikler / Eylemler", "Device_TableHead_CustomProps": "Özellikler / Eylemler",
"Device_TableHead_FQDN": "",
"Device_TableHead_Favorite": "Favori", "Device_TableHead_Favorite": "Favori",
"Device_TableHead_FirstSession": "İlk Oturum", "Device_TableHead_FirstSession": "İlk Oturum",
"Device_TableHead_GUID": "GUID", "Device_TableHead_GUID": "GUID",
@@ -559,6 +560,8 @@
"Presence_Shortcut_Favorites": "Favoriler", "Presence_Shortcut_Favorites": "Favoriler",
"Presence_Shortcut_NewDevices": "Yeni Cihazlar", "Presence_Shortcut_NewDevices": "Yeni Cihazlar",
"Presence_Title": "", "Presence_Title": "",
"REFRESH_FQDN_description": "",
"REFRESH_FQDN_name": "",
"REPORT_DASHBOARD_URL_description": "", "REPORT_DASHBOARD_URL_description": "",
"REPORT_DASHBOARD_URL_name": "", "REPORT_DASHBOARD_URL_name": "",
"REPORT_ERROR": "", "REPORT_ERROR": "",

View File

@@ -211,6 +211,7 @@
"Device_TableHead_AlertDown": "Сповіщення вниз", "Device_TableHead_AlertDown": "Сповіщення вниз",
"Device_TableHead_Connected_Devices": "Зв'язки", "Device_TableHead_Connected_Devices": "Зв'язки",
"Device_TableHead_CustomProps": "Реквізит / дії", "Device_TableHead_CustomProps": "Реквізит / дії",
"Device_TableHead_FQDN": "",
"Device_TableHead_Favorite": "улюблений", "Device_TableHead_Favorite": "улюблений",
"Device_TableHead_FirstSession": "Перша сесія", "Device_TableHead_FirstSession": "Перша сесія",
"Device_TableHead_GUID": "GUID", "Device_TableHead_GUID": "GUID",
@@ -559,6 +560,8 @@
"Presence_Shortcut_Favorites": "Вибране", "Presence_Shortcut_Favorites": "Вибране",
"Presence_Shortcut_NewDevices": "Нові пристрої", "Presence_Shortcut_NewDevices": "Нові пристрої",
"Presence_Title": "Присутність за пристроєм", "Presence_Title": "Присутність за пристроєм",
"REFRESH_FQDN_description": "",
"REFRESH_FQDN_name": "",
"REPORT_DASHBOARD_URL_description": "Ця URL-адреса використовується як основа для створення посилань у звітах HTML (наприклад, електронних листах). Введіть повну URL-адресу, починаючи з <code>http://</code>, включаючи номер порту (без скісної риски <code>/</code>).", "REPORT_DASHBOARD_URL_description": "Ця URL-адреса використовується як основа для створення посилань у звітах HTML (наприклад, електронних листах). Введіть повну URL-адресу, починаючи з <code>http://</code>, включаючи номер порту (без скісної риски <code>/</code>).",
"REPORT_DASHBOARD_URL_name": "URL-адреса NetAlertX", "REPORT_DASHBOARD_URL_name": "URL-адреса NetAlertX",
"REPORT_ERROR": "Сторінка, яку ви шукаєте, тимчасово недоступна, спробуйте ще раз через кілька секунд", "REPORT_ERROR": "Сторінка, яку ви шукаєте, тимчасово недоступна, спробуйте ще раз через кілька секунд",

View File

@@ -211,6 +211,7 @@
"Device_TableHead_AlertDown": "", "Device_TableHead_AlertDown": "",
"Device_TableHead_Connected_Devices": "链接", "Device_TableHead_Connected_Devices": "链接",
"Device_TableHead_CustomProps": "", "Device_TableHead_CustomProps": "",
"Device_TableHead_FQDN": "",
"Device_TableHead_Favorite": "收藏", "Device_TableHead_Favorite": "收藏",
"Device_TableHead_FirstSession": "加入", "Device_TableHead_FirstSession": "加入",
"Device_TableHead_GUID": "GUID", "Device_TableHead_GUID": "GUID",
@@ -559,6 +560,8 @@
"Presence_Shortcut_Favorites": "收藏夹", "Presence_Shortcut_Favorites": "收藏夹",
"Presence_Shortcut_NewDevices": "新设备", "Presence_Shortcut_NewDevices": "新设备",
"Presence_Title": "按设备显示状态", "Presence_Title": "按设备显示状态",
"REFRESH_FQDN_description": "",
"REFRESH_FQDN_name": "",
"REPORT_DASHBOARD_URL_description": "此 URL 用作生成 HTML 报告(例如电子邮件)中链接的基础。输入以 <code>http://</code> 开头的完整 URL包括端口号无尾部斜杠 <code>/</code>)。", "REPORT_DASHBOARD_URL_description": "此 URL 用作生成 HTML 报告(例如电子邮件)中链接的基础。输入以 <code>http://</code> 开头的完整 URL包括端口号无尾部斜杠 <code>/</code>)。",
"REPORT_DASHBOARD_URL_name": "NetAlertX 网址", "REPORT_DASHBOARD_URL_name": "NetAlertX 网址",
"REPORT_ERROR": "您正在浏览的页面暂时不可用,请稍后重试", "REPORT_ERROR": "您正在浏览的页面暂时不可用,请稍后重试",

View File

@@ -11,3 +11,31 @@ You need to bring your own separate Apprise instance to use this publisher gatew
- Go to settings and fill in relevant details. - Go to settings and fill in relevant details.
- Use the Apprise container's URL in the `APPRISE_HOST` setting. - Use the Apprise container's URL in the `APPRISE_HOST` setting.
## Examples
### Telegram
![Telegram config](apprise_telegram.png)
#### Troubleshooting
1. Replace `<bottoken>` and `<chatid>` with your values.
2. Test telegram notification in browser
```
https://api.telegram.org/bot<bottoken>/sendMessage?chat_id=<chatid>&text=%40%40TEXT%40%40
```
3. Test apprise notification in console (replace `192.168.1.2:9999` with your apprise ip and port)
```
curl -X POST -d '{"urls":"tgram://<bottoken>/<chatid>","body":"test body from curl","title":"test title from curl"}' -H "Content-Type: application/json" "http://192.168.1.2:9999/notify/"
```
4. Test from the docker apprise container console
```
apprise -vv -t "Test Message from apprise console" -b "Test Message from apprise console" \
tgram://<bottoken>/<chatid>/
```

Binary file not shown.

After

Width:  |  Height:  |  Size: 139 KiB

View File

@@ -57,22 +57,25 @@ def main():
device_handler = DeviceInstance(db) device_handler = DeviceInstance(db)
# Retrieve devices # Retrieve devices
unknown_devices = device_handler.getUnknown() if get_setting_value("REFRESH_FQDN"):
devices = device_handler.getUnknown()
else:
devices = device_handler.getAll()
mylog('verbose', [f'[{pluginName}] Devices count: {len(devices)}'])
# Mock list of devices (replace with actual device_handler.getUnknown() in production) # Mock list of devices (replace with actual device_handler.getUnknown() in production)
# unknown_devices = [ # devices = [
# {'devMac': '00:11:22:33:44:55', 'devLastIP': '192.168.1.121'}, # {'devMac': '00:11:22:33:44:55', 'devLastIP': '192.168.1.121'},
# {'devMac': '00:11:22:33:44:56', 'devLastIP': '192.168.1.9'}, # {'devMac': '00:11:22:33:44:56', 'devLastIP': '192.168.1.9'},
# {'devMac': '00:11:22:33:44:57', 'devLastIP': '192.168.1.82'}, # {'devMac': '00:11:22:33:44:57', 'devLastIP': '192.168.1.82'},
# ] # ]
mylog('verbose', [f'[{pluginName}] Unknown devices count: {len(unknown_devices)}']) if len(devices) > 0:
if len(unknown_devices) > 0:
# ensure service is running # ensure service is running
ensure_avahi_running() ensure_avahi_running()
for device in unknown_devices: for device in devices:
domain_name = execute_name_lookup(device['devLastIP'], timeout) domain_name = execute_name_lookup(device['devLastIP'], timeout)
# check if found and not a timeout ('to') # check if found and not a timeout ('to')

View File

@@ -0,0 +1,7 @@
## Overview
Plugin for device name discovery via the [nbtscan](https://linuxcommandlibrary.com/man/nbtscan) network utility supporting NetBIOS.
### Usage
- Check the Settings page for details.

View File

@@ -0,0 +1,385 @@
{
"code_name": "dig_scan",
"unique_prefix": "DIGSCAN",
"plugin_type": "other",
"enabled": true,
"data_source": "script",
"execution_order" : "Layer_7",
"show_ui": true,
"data_filters": [
{
"compare_column": "Object_PrimaryID",
"compare_operator": "==",
"compare_field_id": "txtMacFilter",
"compare_js_template": "'{value}'.toString()",
"compare_use_quotes": true
}
],
"localized": ["display_name", "description", "icon"],
"display_name": [
{
"language_code": "en_us",
"string": "Dig (Name resolution)"
}
],
"icon": [
{
"language_code": "en_us",
"string": "<i class=\"fa-solid fa-search\"></i>"
}
],
"description": [
{
"language_code": "en_us",
"string": "A plugin to resolve device names via Dig."
}
],
"params": [
{
"name": "ips",
"type": "sql",
"value": "SELECT devLastIP from DEVICES order by devMac",
"timeoutMultiplier": true
}
],
"settings": [
{
"function": "RUN",
"events": ["run"],
"type": {
"dataType": "string",
"elements": [
{ "elementType": "select", "elementOptions": [], "transformers": [] }
]
},
"default_value": "before_name_updates",
"options": [
"disabled",
"before_name_updates",
"on_new_device",
"once",
"schedule",
"always_after_scan"
],
"localized": ["name", "description"],
"name": [
{
"language_code": "en_us",
"string": "When to run"
},
{
"language_code": "es_es",
"string": "Cuándo ejecutar"
},
{
"language_code": "de_de",
"string": "Wann laufen"
}
],
"description": [
{
"language_code": "en_us",
"string": "When the plugin should be executed. If enabled this will execute the scan until there are no <code>(unknown)</code> or <code>(name not found)</code> devices. Setting this to <code>before_name_updates</code> is recommended.<br/><br/> Depends on the <a onclick=\"toggleAllSettings()\" href=\"#SCAN_SUBNETS\"><code>SCAN_SUBNETS</code> setting</a>."
}
]
},
{
"function": "CMD",
"type": {
"dataType": "string",
"elements": [
{
"elementType": "input",
"elementOptions": [{ "readonly": "true" }],
"transformers": []
}
]
},
"default_value": "python3 /app/front/plugins/dig_scan/digscan.py",
"options": [],
"localized": ["name", "description"],
"name": [
{
"language_code": "en_us",
"string": "Command"
},
{
"language_code": "es_es",
"string": "Comando"
},
{
"language_code": "de_de",
"string": "Befehl"
}
],
"description": [
{
"language_code": "en_us",
"string": "Command to run. This can not be changed"
},
{
"language_code": "es_es",
"string": "Comando a ejecutar. Esto no se puede cambiar"
},
{
"language_code": "de_de",
"string": "Befehl zum Ausführen. Dies kann nicht geändert werden"
}
]
},
{
"function": "RUN_SCHD",
"type": {
"dataType": "string",
"elements": [
{
"elementType": "span",
"elementOptions": [
{
"cssClasses": "input-group-addon validityCheck"
},
{
"getStringKey": "Gen_ValidIcon"
}
],
"transformers": []
},
{
"elementType": "input",
"elementOptions": [
{
"onChange": "validateRegex(this)"
},
{
"base64Regex": "Xig/OlwqfCg/OlswLTldfFsxLTVdWzAtOV18WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlswLTldfDFbMC05XXwyWzAtM118WzAtOV0rLVswLTldK3xcKi9bMC05XSspKVxzKyg/OlwqfCg/OlsxLTldfFsxMl1bMC05XXwzWzAxXXxbMC05XSstWzAtOV0rfFwqL1swLTldKykpXHMrKD86XCp8KD86WzEtOV18MVswLTJdfFswLTldKy1bMC05XSt8XCovWzAtOV0rKSlccysoPzpcKnwoPzpbMC02XXxbMC02XS1bMC02XXxcKi9bMC05XSspKSQ="
}
],
"transformers": []
}
]
},
"default_value": "*/30 * * * *",
"options": [],
"localized": ["name", "description"],
"name": [
{
"language_code": "en_us",
"string": "Schedule"
},
{
"language_code": "es_es",
"string": "Schedule"
},
{
"language_code": "de_de",
"string": "Schedule"
}
],
"description": [
{
"language_code": "en_us",
"string": "Only enabled if you select <code>schedule</code> in the <a href=\"#NBTSCAN_RUN\"><code>NBTSCAN_RUN</code> setting</a>. Make sure you enter the schedule in the correct cron-like format (e.g. validate at <a href=\"https://crontab.guru/\" target=\"_blank\">crontab.guru</a>). For example entering <code>0 4 * * *</code> will run the scan after 4 am in the <a onclick=\"toggleAllSettings()\" href=\"#TIMEZONE\"><code>TIMEZONE</code> you set above</a>. Will be run NEXT time the time passes."
},
{
"language_code": "es_es",
"string": "Solo está habilitado si selecciona <code>schedule</code> en la configuración <a href=\"#NBTSCAN_RUN\"><code>NBTSCAN_RUN</code></a>. Asegúrese de ingresar la programación en el formato similar a cron correcto (por ejemplo, valide en <a href=\"https://crontab.guru/\" target=\"_blank\">crontab.guru</a>). Por ejemplo, ingresar <code>0 4 * * *</code> ejecutará el escaneo después de las 4 a.m. en el <a onclick=\"toggleAllSettings()\" href=\"#TIMEZONE\"><code>TIMEZONE</ código> que configuró arriba</a>. Se ejecutará la PRÓXIMA vez que pase el tiempo."
},
{
"language_code": "de_de",
"string": "Nur aktiviert, wenn Sie <code>schedule</code> in der <a href=\"#NBTSCAN_RUN\"><code>NBTSCAN_RUN</code>-Einstellung</a> auswählen. Stellen Sie sicher, dass Sie den Zeitplan im richtigen Cron-ähnlichen Format eingeben (z. B. validieren unter <a href=\"https://crontab.guru/\" target=\"_blank\">crontab.guru</a>). Wenn Sie beispielsweise <code>0 4 * * *</code> eingeben, wird der Scan nach 4 Uhr morgens in der <a onclick=\"toggleAllSettings()\" href=\"#TIMEZONE\"><code>TIMEZONE</ ausgeführt. Code> den Sie oben festgelegt haben</a>. Wird das NÄCHSTE Mal ausgeführt, wenn die Zeit vergeht."
}
]
},
{
"function": "RUN_TIMEOUT",
"type": {
"dataType": "integer",
"elements": [
{
"elementType": "input",
"elementOptions": [{ "type": "number" }],
"transformers": []
}
]
},
"default_value": 5,
"options": [],
"localized": ["name", "description"],
"name": [
{
"language_code": "en_us",
"string": "Run timeout"
},
{
"language_code": "es_es",
"string": "Tiempo límite de ejecución"
},
{
"language_code": "de_de",
"string": "Zeitüberschreitung"
}
],
"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."
},
{
"language_code": "de_de",
"string": "Maximale Zeit in Sekunden, die auf den Abschluss des Skripts gewartet werden soll. Bei Überschreitung dieser Zeit wird das Skript abgebrochen."
}
]
}
],
"database_column_definitions": [
{
"column": "Index",
"css_classes": "col-sm-2",
"show": true,
"type": "none",
"default_value": "",
"options": [],
"localized": ["name"],
"name": [
{
"language_code": "en_us",
"string": "Index"
}
]
},
{
"column": "Object_PrimaryID",
"css_classes": "col-sm-2",
"show": true,
"type": "device_name_mac",
"default_value": "",
"options": [],
"localized": ["name"],
"name": [
{
"language_code": "en_us",
"string": "MAC (name)"
},
{
"language_code": "es_es",
"string": "MAC"
}
]
},
{
"column": "Object_SecondaryID",
"css_classes": "col-sm-2",
"show": true,
"type": "label",
"default_value": "",
"options": [],
"localized": ["name"],
"name": [
{
"language_code": "en_us",
"string": "IP"
},
{
"language_code": "es_es",
"string": "IP"
}
]
},
{
"column": "Watched_Value1",
"css_classes": "col-sm-2",
"show": true,
"type": "label",
"default_value": "",
"options": [],
"localized": ["name"],
"name": [
{
"language_code": "en_us",
"string": "Server"
}
]
},
{
"column": "Watched_Value2",
"css_classes": "col-sm-2",
"show": true,
"type": "label",
"default_value": "",
"options": [],
"localized": ["name"],
"name": [
{
"language_code": "en_us",
"string": "Name"
}
]
},
{
"column": "DateTimeCreated",
"css_classes": "col-sm-2",
"show": true,
"type": "label",
"default_value": "",
"options": [],
"localized": ["name"],
"name": [
{
"language_code": "en_us",
"string": "Created"
}
]
},
{
"column": "DateTimeChanged",
"css_classes": "col-sm-2",
"show": true,
"type": "label",
"default_value": "",
"options": [],
"localized": ["name"],
"name": [
{
"language_code": "en_us",
"string": "Changed"
}
]
},
{
"column": "Status",
"css_classes": "col-sm-1",
"show": true,
"type": "replace",
"default_value": "",
"options": [
{
"equals": "watched-not-changed",
"replacement": "<div style='text-align:center'><i class='fa-solid fa-square-check'></i><div></div>"
},
{
"equals": "watched-changed",
"replacement": "<div style='text-align:center'><i class='fa-solid fa-triangle-exclamation'></i></div>"
},
{
"equals": "new",
"replacement": "<div style='text-align:center'><i class='fa-solid fa-circle-plus'></i></div>"
},
{
"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"
}
]
}
]
}

133
front/plugins/dig_scan/digscan.py Executable file
View File

@@ -0,0 +1,133 @@
#!/usr/bin/env python
import os
import pathlib
import sys
import json
import sqlite3
import subprocess
# Define the installation path and extend the system path for plugin imports
INSTALL_PATH = "/app"
sys.path.extend([f"{INSTALL_PATH}/front/plugins", f"{INSTALL_PATH}/server"])
from plugin_helper import Plugin_Object, Plugin_Objects, decodeBase64
from plugin_utils import get_plugins_configs
from logger import mylog, Logger
from const import pluginsPath, fullDbPath, logPath
from helper import timeNowTZ, get_setting_value
from messaging.in_app import write_notification
from database import DB
from models.device_instance import DeviceInstance
import conf
from pytz import timezone
# Make sure the TIMEZONE for logging is correct
conf.tz = timezone(get_setting_value('TIMEZONE'))
# Make sure log level is initialized correctly
Logger(get_setting_value('LOG_LEVEL'))
pluginName = 'DIGSCAN'
# Define the current path and log file paths
LOG_PATH = logPath + '/plugins'
LOG_FILE = os.path.join(LOG_PATH, f'script.{pluginName}.log')
RESULT_FILE = os.path.join(LOG_PATH, f'last_result.{pluginName}.log')
# Initialize the Plugin obj output file
plugin_objects = Plugin_Objects(RESULT_FILE)
def main():
mylog('verbose', [f'[{pluginName}] In script'])
timeout = get_setting_value('DIGSCAN_RUN_TIMEOUT')
# 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 DeviceInstance instance
device_handler = DeviceInstance(db)
# Retrieve devices
if get_setting_value("REFRESH_FQDN"):
devices = device_handler.getUnknown()
else:
devices = device_handler.getAll()
mylog('verbose', [f'[{pluginName}] Devices count: {len(devices)}'])
# TEST - below is a WINDOWS host IP
# execute_name_lookup('192.168.1.121', timeout)
for device in devices:
domain_name, dns_server = execute_name_lookup(device['devLastIP'], timeout)
if domain_name != '':
plugin_objects.add_object(
# "MAC", "IP", "Server", "Name"
primaryId = device['devMac'],
secondaryId = device['devLastIP'],
watched1 = dns_server,
watched2 = domain_name,
watched3 = '',
watched4 = '',
extra = '',
foreignKey = device['devMac'])
plugin_objects.write_result_file()
mylog('verbose', [f'[{pluginName}] Script finished'])
return 0
#===============================================================================
# Execute scan
#===============================================================================
def execute_name_lookup (ip, timeout):
"""
Execute the DIG command on IP.
"""
args = ['dig', '+short', '-x', ip]
# Execute command
output = ""
try:
mylog('verbose', [f'[{pluginName}] DEBUG CMD :', args])
# try runnning a subprocess with a forced (timeout) in case the subprocess hangs
output = subprocess.check_output (args, universal_newlines=True, stderr=subprocess.STDOUT, timeout=(timeout), text=True).strip()
mylog('verbose', [f'[{pluginName}] DEBUG OUTPUT : {output}'])
domain_name = output
dns_server = ''
mylog('verbose', [f'[{pluginName}] Domain Name: {domain_name}'])
return domain_name, dns_server
except subprocess.CalledProcessError as e:
mylog('verbose', [f'[{pluginName}] ⚠ ERROR - {e.output}'])
except subprocess.TimeoutExpired as timeErr:
mylog('verbose', [f'[{pluginName}] TIMEOUT - the process forcefully terminated as timeout reached'])
if output == "": # check if the subprocess failed
mylog('verbose', [f'[{pluginName}] Scan: FAIL - check logs'])
else:
mylog('verbose', [f'[{pluginName}] Scan: SUCCESS'])
return '', ''
if __name__ == '__main__':
main()

View File

@@ -52,7 +52,7 @@
{ "elementType": "select", "elementOptions": [], "transformers": [] } { "elementType": "select", "elementOptions": [], "transformers": [] }
] ]
}, },
"default_value": "disabled", "default_value": "before_name_updates",
"options": [ "options": [
"disabled", "disabled",
"before_name_updates", "before_name_updates",

View File

@@ -57,14 +57,17 @@ def main():
device_handler = DeviceInstance(db) device_handler = DeviceInstance(db)
# Retrieve devices # Retrieve devices
unknown_devices = device_handler.getUnknown() if get_setting_value("REFRESH_FQDN"):
devices = device_handler.getUnknown()
else:
devices = device_handler.getAll()
mylog('verbose', [f'[{pluginName}] Unknown devices count: {len(unknown_devices)}']) mylog('verbose', [f'[{pluginName}] Devices count: {len(devices)}'])
# TEST - below is a WINDOWS host IP # TEST - below is a WINDOWS host IP
# execute_name_lookup('192.168.1.121', timeout) # execute_name_lookup('192.168.1.121', timeout)
for device in unknown_devices: for device in devices:
domain_name, dns_server = execute_name_lookup(device['devLastIP'], timeout) domain_name, dns_server = execute_name_lookup(device['devLastIP'], timeout)
if domain_name != '': if domain_name != '':

View File

@@ -1629,6 +1629,42 @@
"string": "Custom device properties to store additional data or to perform an action on the device. Check the <a href=\"https://github.com/jokob-sk/NetAlertX/blob/main/docs/CUSTOM_PROPERTIES.md\" target=\"_blank\">documentation on Custom Properties</a> for additional details." "string": "Custom device properties to store additional data or to perform an action on the device. Check the <a href=\"https://github.com/jokob-sk/NetAlertX/blob/main/docs/CUSTOM_PROPERTIES.md\" target=\"_blank\">documentation on Custom Properties</a> for additional details."
} }
] ]
},
{
"function": "devFQDN",
"type": {
"dataType": "string",
"elements": [
{
"elementType": "input",
"elementOptions": [
{
"readonly": "true"
}
],
"transformers": []
}
]
},
"maxLength": 50,
"default_value": "",
"options": [],
"localized": [
"name",
"description"
],
"name": [
{
"language_code": "en_us",
"string": "FQDN"
}
],
"description": [
{
"language_code": "en_us",
"string": "Fully Qualified Domain Name - Autodetected and Uneditable. Can be auto-refreshed by enabling the <code>REFRESH_FQDN</code> setting."
}
]
} }
], ],
"required": [], "required": [],

View File

@@ -59,11 +59,17 @@ def main():
device_handler = DeviceInstance(db) device_handler = DeviceInstance(db)
# Retrieve devices # Retrieve devices
unknown_devices = device_handler.getUnknown() if get_setting_value("REFRESH_FQDN"):
devices = device_handler.getUnknown()
else:
devices = device_handler.getAll()
mylog('verbose', [f'[{pluginName}] Unknown devices count: {len(unknown_devices)}']) mylog('verbose', [f'[{pluginName}] Devices count: {len(devices)}'])
for device in unknown_devices: # TEST - below is a WINDOWS host IP
# execute_name_lookup('192.168.1.121', timeout)
for device in devices:
domain_name, dns_server = execute_nslookup(device['devLastIP'], timeout) domain_name, dns_server = execute_nslookup(device['devLastIP'], timeout)
if domain_name != '': if domain_name != '':

View File

@@ -377,7 +377,8 @@
"Device_TableHead_SourcePlugin", "Device_TableHead_SourcePlugin",
"Device_TableHead_PresentLastScan", "Device_TableHead_PresentLastScan",
"Device_TableHead_AlertDown", "Device_TableHead_AlertDown",
"Device_TableHead_CustomProps" "Device_TableHead_CustomProps",
"Device_TableHead_FQDN"
], ],
"localized": ["name", "description"], "localized": ["name", "description"],
"name": [ "name": [

View File

@@ -44,7 +44,7 @@ let fieldOptions = [
"devLastIP", "devStaticIP", "devScan", "devLogEvents", "devAlertEvents", "devLastIP", "devStaticIP", "devScan", "devLogEvents", "devAlertEvents",
"devAlertDown", "devSkipRepeated", "devLastNotification", "devPresentLastScan", "devAlertDown", "devSkipRepeated", "devLastNotification", "devPresentLastScan",
"devIsNew", "devLocation", "devIsArchived", "devParentMAC", "devParentPort", "devIsNew", "devLocation", "devIsArchived", "devParentMAC", "devParentPort",
"devIcon", "devSite", "devSSID", "devSyncHubNode", "devSourcePlugin" "devIcon", "devSite", "devSSID", "devSyncHubNode", "devSourcePlugin", "devFQDN"
]; ];
let triggerTypes = [ let triggerTypes = [

19
scripts/db_empty/README.md Executable file
View File

@@ -0,0 +1,19 @@
# Overview
A script for deleting all data from the database.
# Usage
1. **Run the Script**
`python ./db_empty.py`
### Other info
- Version: 1.0
- Release Date: 01-Jun-2025
- Author: [jokob-sk](https://github.com/jokob-sk)
> [!NOTE]
> This is a community supplied script and not maintained.

26
scripts/db_empty/db_empty.py Executable file
View File

@@ -0,0 +1,26 @@
import sqlite3
# Connect to the database
conn = sqlite3.connect("/app/db/app.db")
cursor = conn.cursor()
# Get the names of all tables (excluding SQLite internal tables)
cursor.execute("SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%';")
tables = cursor.fetchall()
# Disable foreign key constraints temporarily
cursor.execute("PRAGMA foreign_keys = OFF;")
# Delete all rows from each table
for (table_name,) in tables:
cursor.execute(f"DELETE FROM {table_name};")
# Commit changes and re-enable foreign keys
conn.commit()
cursor.execute("PRAGMA foreign_keys = ON;")
# Vacuum to shrink database file
cursor.execute("VACUUM;")
# Close connection
conn.close()

View File

@@ -60,6 +60,7 @@ sql_devices_all = """
IFNULL(devSyncHubNode, '') AS devSyncHubNode, IFNULL(devSyncHubNode, '') AS devSyncHubNode,
IFNULL(devSourcePlugin, '') AS devSourcePlugin, IFNULL(devSourcePlugin, '') AS devSourcePlugin,
IFNULL(devCustomProps, '') AS devCustomProps, IFNULL(devCustomProps, '') AS devCustomProps,
IFNULL(devFQDN, '') AS devFQDN,
CASE CASE
WHEN devIsNew = 1 THEN 'New' WHEN devIsNew = 1 THEN 'New'
WHEN devPresentLastScan = 1 THEN 'On-line' WHEN devPresentLastScan = 1 THEN 'On-line'

View File

@@ -81,369 +81,9 @@ class DB():
Check the current tables in the DB and upgrade them if neccessary Check the current tables in the DB and upgrade them if neccessary
""" """
self.sql.execute(""" # -------------------------------------------------------------------------
CREATE TABLE IF NOT EXISTS "Online_History" ( # Alter Devices table
"Index" INTEGER, # -------------------------------------------------------------------------
"Scan_Date" TEXT,
"Online_Devices" INTEGER,
"Down_Devices" INTEGER,
"All_Devices" INTEGER,
"Archived_Devices" INTEGER,
"Offline_Devices" INTEGER,
PRIMARY KEY("Index" AUTOINCREMENT)
);
""")
# -------------------------------------------------------------------
# DevicesNew - cleanup after 6/6/2025 - need to update also DB in the source code!
# check if migration already done based on devMac
devMac_missing = self.sql.execute ("""
SELECT COUNT(*) AS CNTREC FROM pragma_table_info('Devices') WHERE name='devMac'
""").fetchone()[0] == 0
if devMac_missing:
# -------------------------------------------------------------------------
# Alter Devices table
# -------------------------------------------------------------------------
# dev_Network_Node_MAC_ADDR column
dev_Network_Node_MAC_ADDR_missing = self.sql.execute ("""
SELECT COUNT(*) AS CNTREC FROM pragma_table_info('Devices') WHERE name='dev_Network_Node_MAC_ADDR'
""").fetchone()[0] == 0
if dev_Network_Node_MAC_ADDR_missing :
mylog('verbose', ["[upgradeDB] Adding dev_Network_Node_MAC_ADDR to the Devices table"])
self.sql.execute("""
ALTER TABLE "Devices" ADD "dev_Network_Node_MAC_ADDR" TEXT
""")
# dev_Network_Node_port column
dev_Network_Node_port_missing = self.sql.execute ("""
SELECT COUNT(*) AS CNTREC FROM pragma_table_info('Devices') WHERE name='dev_Network_Node_port'
""").fetchone()[0] == 0
if dev_Network_Node_port_missing :
mylog('verbose', ["[upgradeDB] Adding dev_Network_Node_port to the Devices table"])
self.sql.execute("""
ALTER TABLE "Devices" ADD "dev_Network_Node_port" INTEGER
""")
# dev_Icon column
dev_Icon_missing = self.sql.execute ("""
SELECT COUNT(*) AS CNTREC FROM pragma_table_info('Devices') WHERE name='dev_Icon'
""").fetchone()[0] == 0
if dev_Icon_missing :
mylog('verbose', ["[upgradeDB] Adding dev_Icon to the Devices table"])
self.sql.execute("""
ALTER TABLE "Devices" ADD "dev_Icon" TEXT
""")
# dev_GUID column
dev_GUID_missing = self.sql.execute ("""
SELECT COUNT(*) AS CNTREC FROM pragma_table_info('Devices') WHERE name='dev_GUID'
""").fetchone()[0] == 0
if dev_GUID_missing :
mylog('verbose', ["[upgradeDB] Adding dev_GUID to the Devices table"])
self.sql.execute("""
ALTER TABLE "Devices" ADD "dev_GUID" TEXT
""")
# dev_NetworkSite column
dev_NetworkSite_missing = self.sql.execute ("""
SELECT COUNT(*) AS CNTREC FROM pragma_table_info('Devices') WHERE name='dev_NetworkSite'
""").fetchone()[0] == 0
if dev_NetworkSite_missing :
mylog('verbose', ["[upgradeDB] Adding dev_NetworkSite to the Devices table"])
self.sql.execute("""
ALTER TABLE "Devices" ADD "dev_NetworkSite" TEXT
""")
# dev_SSID column
dev_SSID_missing = self.sql.execute ("""
SELECT COUNT(*) AS CNTREC FROM pragma_table_info('Devices') WHERE name='dev_SSID'
""").fetchone()[0] == 0
if dev_SSID_missing :
mylog('verbose', ["[upgradeDB] Adding dev_SSID to the Devices table"])
self.sql.execute("""
ALTER TABLE "Devices" ADD "dev_SSID" TEXT
""")
# SQL query to update missing dev_GUID
self.sql.execute(f'''
UPDATE Devices
SET dev_GUID = {sql_generateGuid}
WHERE dev_GUID IS NULL
''')
# dev_SyncHubNodeName column
dev_SyncHubNodeName_missing = self.sql.execute ("""
SELECT COUNT(*) AS CNTREC FROM pragma_table_info('Devices') WHERE name='dev_SyncHubNodeName'
""").fetchone()[0] == 0
if dev_SyncHubNodeName_missing :
mylog('verbose', ["[upgradeDB] Adding dev_SyncHubNodeName to the Devices table"])
self.sql.execute("""
ALTER TABLE "Devices" ADD "dev_SyncHubNodeName" TEXT
""")
# dev_SourcePlugin column
dev_SourcePlugin_missing = self.sql.execute ("""
SELECT COUNT(*) AS CNTREC FROM pragma_table_info('Devices') WHERE name='dev_SourcePlugin'
""").fetchone()[0] == 0
if dev_SourcePlugin_missing :
mylog('verbose', ["[upgradeDB] Adding dev_SourcePlugin to the Devices table"])
self.sql.execute("""
ALTER TABLE "Devices" ADD "dev_SourcePlugin" TEXT
""")
# SQL to create Devices table with indexes
sql_create_devices_new_tmp = """
CREATE TABLE IF NOT EXISTS Devices_tmp (
devMac STRING (50) PRIMARY KEY NOT NULL COLLATE NOCASE,
devName STRING (50) NOT NULL DEFAULT "(unknown)",
devOwner STRING (30) DEFAULT "(unknown)" NOT NULL,
devType STRING (30),
devVendor STRING (250),
devFavorite BOOLEAN CHECK (devFavorite IN (0, 1)) DEFAULT (0) NOT NULL,
devGroup STRING (10),
devComments TEXT,
devFirstConnection DATETIME NOT NULL,
devLastConnection DATETIME NOT NULL,
devLastIP STRING (50) NOT NULL COLLATE NOCASE,
devStaticIP BOOLEAN DEFAULT (0) NOT NULL CHECK (devStaticIP IN (0, 1)),
devScan INTEGER DEFAULT (1) NOT NULL,
devLogEvents BOOLEAN NOT NULL DEFAULT (1) CHECK (devLogEvents IN (0, 1)),
devAlertEvents BOOLEAN NOT NULL DEFAULT (1) CHECK (devAlertEvents IN (0, 1)),
devAlertDown BOOLEAN NOT NULL DEFAULT (0) CHECK (devAlertDown IN (0, 1)),
devSkipRepeated INTEGER DEFAULT 0 NOT NULL,
devLastNotification DATETIME,
devPresentLastScan BOOLEAN NOT NULL DEFAULT (0) CHECK (devPresentLastScan IN (0, 1)),
devIsNew BOOLEAN NOT NULL DEFAULT (1) CHECK (devIsNew IN (0, 1)),
devLocation STRING (250) COLLATE NOCASE,
devIsArchived BOOLEAN NOT NULL DEFAULT (0) CHECK (devIsArchived IN (0, 1)),
devParentMAC TEXT,
devParentPort INTEGER,
devIcon TEXT,
devGUID TEXT,
devSite TEXT,
devSSID TEXT,
devSyncHubNode TEXT,
devSourcePlugin TEXT
);
CREATE INDEX IF NOT EXISTS IDX_dev_PresentLastScan ON Devices_tmp (devPresentLastScan);
CREATE INDEX IF NOT EXISTS IDX_dev_FirstConnection ON Devices_tmp (devFirstConnection);
CREATE INDEX IF NOT EXISTS IDX_dev_AlertDeviceDown ON Devices_tmp (devAlertDown);
CREATE INDEX IF NOT EXISTS IDX_dev_StaticIP ON Devices_tmp (devStaticIP);
CREATE INDEX IF NOT EXISTS IDX_dev_ScanCycle ON Devices_tmp (devScan);
CREATE INDEX IF NOT EXISTS IDX_dev_Favorite ON Devices_tmp (devFavorite);
CREATE INDEX IF NOT EXISTS IDX_dev_LastIP ON Devices_tmp (devLastIP);
CREATE INDEX IF NOT EXISTS IDX_dev_NewDevice ON Devices_tmp (devIsNew);
CREATE INDEX IF NOT EXISTS IDX_dev_Archived ON Devices_tmp (devIsArchived);
"""
# Execute the creation of the Devices table and indexes
self.sql.executescript(sql_create_devices_new_tmp)
# copy over data
sql_copy_from_devices = """
INSERT OR IGNORE INTO Devices_tmp (
devMac,
devName,
devOwner,
devType,
devVendor,
devFavorite,
devGroup,
devComments,
devFirstConnection,
devLastConnection,
devLastIP,
devStaticIP,
devScan,
devLogEvents,
devAlertEvents,
devAlertDown,
devSkipRepeated,
devLastNotification,
devPresentLastScan,
devIsNew,
devLocation,
devIsArchived,
devParentMAC,
devParentPort,
devIcon,
devGUID,
devSite,
devSSID,
devSyncHubNode,
devSourcePlugin
)
SELECT
dev_MAC AS devMac,
dev_Name AS devName,
dev_Owner AS devOwner,
dev_DeviceType AS devType,
dev_Vendor AS devVendor,
dev_Favorite AS devFavorite,
dev_Group AS devGroup,
dev_Comments AS devComments,
dev_FirstConnection AS devFirstConnection,
dev_LastConnection AS devLastConnection,
dev_LastIP AS devLastIP,
dev_StaticIP AS devStaticIP,
dev_ScanCycle AS devScan,
dev_LogEvents AS devLogEvents,
dev_AlertEvents AS devAlertEvents,
dev_AlertDeviceDown AS devAlertDown,
dev_SkipRepeated AS devSkipRepeated,
dev_LastNotification AS devLastNotification,
dev_PresentLastScan AS devPresentLastScan,
dev_NewDevice AS devIsNew,
dev_Location AS devLocation,
dev_Archived AS devIsArchived,
dev_Network_Node_MAC_ADDR AS devParentMAC,
dev_Network_Node_port AS devParentPort,
dev_Icon AS devIcon,
dev_GUID AS devGUID,
dev_NetworkSite AS devSite,
dev_SSID AS devSSID,
dev_SyncHubNodeName AS devSyncHubNode,
dev_SourcePlugin AS devSourcePlugin
FROM Devices;
"""
self.sql.execute(sql_copy_from_devices)
self.sql.execute(""" DROP TABLE Devices;""")
# SQL to create Devices table with indexes
sql_create_devices_new = """
CREATE TABLE IF NOT EXISTS Devices (
devMac STRING (50) PRIMARY KEY NOT NULL COLLATE NOCASE,
devName STRING (50) NOT NULL DEFAULT "(unknown)",
devOwner STRING (30) DEFAULT "(unknown)" NOT NULL,
devType STRING (30),
devVendor STRING (250),
devFavorite BOOLEAN CHECK (devFavorite IN (0, 1)) DEFAULT (0) NOT NULL,
devGroup STRING (10),
devComments TEXT,
devFirstConnection DATETIME NOT NULL,
devLastConnection DATETIME NOT NULL,
devLastIP STRING (50) NOT NULL COLLATE NOCASE,
devStaticIP BOOLEAN DEFAULT (0) NOT NULL CHECK (devStaticIP IN (0, 1)),
devScan INTEGER DEFAULT (1) NOT NULL,
devLogEvents BOOLEAN NOT NULL DEFAULT (1) CHECK (devLogEvents IN (0, 1)),
devAlertEvents BOOLEAN NOT NULL DEFAULT (1) CHECK (devAlertEvents IN (0, 1)),
devAlertDown BOOLEAN NOT NULL DEFAULT (0) CHECK (devAlertDown IN (0, 1)),
devSkipRepeated INTEGER DEFAULT 0 NOT NULL,
devLastNotification DATETIME,
devPresentLastScan BOOLEAN NOT NULL DEFAULT (0) CHECK (devPresentLastScan IN (0, 1)),
devIsNew BOOLEAN NOT NULL DEFAULT (1) CHECK (devIsNew IN (0, 1)),
devLocation STRING (250) COLLATE NOCASE,
devIsArchived BOOLEAN NOT NULL DEFAULT (0) CHECK (devIsArchived IN (0, 1)),
devParentMAC TEXT,
devParentPort INTEGER,
devIcon TEXT,
devGUID TEXT,
devSite TEXT,
devSSID TEXT,
devSyncHubNode TEXT,
devSourcePlugin TEXT
);
CREATE INDEX IF NOT EXISTS IDX_dev_PresentLastScan ON Devices (devPresentLastScan);
CREATE INDEX IF NOT EXISTS IDX_dev_FirstConnection ON Devices (devFirstConnection);
CREATE INDEX IF NOT EXISTS IDX_dev_AlertDeviceDown ON Devices (devAlertDown);
CREATE INDEX IF NOT EXISTS IDX_dev_StaticIP ON Devices (devStaticIP);
CREATE INDEX IF NOT EXISTS IDX_dev_ScanCycle ON Devices (devScan);
CREATE INDEX IF NOT EXISTS IDX_dev_Favorite ON Devices (devFavorite);
CREATE INDEX IF NOT EXISTS IDX_dev_LastIP ON Devices (devLastIP);
CREATE INDEX IF NOT EXISTS IDX_dev_NewDevice ON Devices (devIsNew);
CREATE INDEX IF NOT EXISTS IDX_dev_Archived ON Devices (devIsArchived);
"""
# Execute the creation of the Devices table and indexes
self.sql.executescript(sql_create_devices_new)
# copy over data
sql_copy_from_devices_tmp = """
INSERT OR IGNORE INTO Devices (
devMac,
devName,
devOwner,
devType,
devVendor,
devFavorite,
devGroup,
devComments,
devFirstConnection,
devLastConnection,
devLastIP,
devStaticIP,
devScan,
devLogEvents,
devAlertEvents,
devAlertDown,
devSkipRepeated,
devLastNotification,
devPresentLastScan,
devIsNew,
devLocation,
devIsArchived,
devParentMAC,
devParentPort,
devIcon,
devGUID,
devSite,
devSSID,
devSyncHubNode,
devSourcePlugin
)
SELECT
devMac,
devName,
devOwner,
devType,
devVendor,
devFavorite,
devGroup,
devComments,
devFirstConnection,
devLastConnection,
devLastIP,
devStaticIP,
devScan,
devLogEvents,
devAlertEvents,
devAlertDown,
devSkipRepeated,
devLastNotification,
devPresentLastScan,
devIsNew,
devLocation,
devIsArchived,
devParentMAC,
devParentPort,
devIcon,
devGUID,
devSite,
devSSID,
devSyncHubNode,
devSourcePlugin
FROM Devices_tmp;
"""
self.sql.execute(sql_copy_from_devices_tmp)
self.sql.execute(""" DROP TABLE Devices_tmp;""")
# VIEWS # VIEWS
@@ -478,17 +118,19 @@ class DB():
# add fields if missing # add fields if missing
# devCustomProps column # devFQDN missing?
devCustomProps_missing = self.sql.execute (""" devFQDN_missing = self.sql.execute ("""
SELECT COUNT(*) AS CNTREC FROM pragma_table_info('Devices') WHERE name='devCustomProps' SELECT COUNT(*) AS CNTREC FROM pragma_table_info('Devices') WHERE name='devFQDN'
""").fetchone()[0] == 0 """).fetchone()[0] == 0
if devCustomProps_missing : if devFQDN_missing:
mylog('verbose', ["[upgradeDB] Adding devCustomProps to the Devices table"])
mylog('verbose', ["[upgradeDB] Adding devFQDN to the Devices table"])
self.sql.execute(""" self.sql.execute("""
ALTER TABLE "Devices" ADD "devCustomProps" TEXT ALTER TABLE "Devices" ADD "devFQDN" TEXT
""") """)
# ------------------------------------------------------------------------- # -------------------------------------------------------------------------
# Settings table setup # Settings table setup
# ------------------------------------------------------------------------- # -------------------------------------------------------------------------
@@ -564,48 +206,6 @@ class DB():
); """ ); """
self.sql.execute(sql_Plugins_Objects) self.sql.execute(sql_Plugins_Objects)
# -----------------------------------------
# REMOVE after 6/6/2025 - START
# -----------------------------------------
# syncHubNodeName column
plug_SyncHubNodeName_missing = self.sql.execute ("""
SELECT COUNT(*) AS CNTREC FROM pragma_table_info('Plugins_Objects') WHERE name='SyncHubNodeName'
""").fetchone()[0] == 0
if plug_SyncHubNodeName_missing :
mylog('verbose', ["[upgradeDB] Adding SyncHubNodeName to the Plugins_Objects table"])
self.sql.execute("""
ALTER TABLE "Plugins_Objects" ADD "SyncHubNodeName" TEXT
""")
# helper columns HelpVal1-4
plug_HelpValues_missing = self.sql.execute ("""
SELECT COUNT(*) AS CNTREC FROM pragma_table_info('Plugins_Objects') WHERE name='HelpVal1'
""").fetchone()[0] == 0
if plug_HelpValues_missing :
mylog('verbose', ["[upgradeDB] Adding HelpVal1-4 to the Plugins_Objects table"])
self.sql.execute('ALTER TABLE "Plugins_Objects" ADD COLUMN "HelpVal1" TEXT')
self.sql.execute('ALTER TABLE "Plugins_Objects" ADD COLUMN "HelpVal2" TEXT')
self.sql.execute('ALTER TABLE "Plugins_Objects" ADD COLUMN "HelpVal3" TEXT')
self.sql.execute('ALTER TABLE "Plugins_Objects" ADD COLUMN "HelpVal4" TEXT')
# plug_ObjectGUID_missing column
plug_ObjectGUID_missing = self.sql.execute ("""
SELECT COUNT(*) AS CNTREC FROM pragma_table_info('Plugins_Objects') WHERE name='ObjectGUID'
""").fetchone()[0] == 0
if plug_ObjectGUID_missing :
mylog('verbose', ["[upgradeDB] Adding ObjectGUID to the Plugins_Objects table"])
self.sql.execute("""
ALTER TABLE "Plugins_Objects" ADD "ObjectGUID" TEXT
""")
# -----------------------------------------
# REMOVE after 6/6/2025 - END
# -----------------------------------------
# Plugin execution results # Plugin execution results
sql_Plugins_Events = """ CREATE TABLE IF NOT EXISTS Plugins_Events( sql_Plugins_Events = """ CREATE TABLE IF NOT EXISTS Plugins_Events(
"Index" INTEGER, "Index" INTEGER,
@@ -631,49 +231,6 @@ class DB():
); """ ); """
self.sql.execute(sql_Plugins_Events) self.sql.execute(sql_Plugins_Events)
# -----------------------------------------
# REMOVE after 6/6/2025 - START
# -----------------------------------------
# syncHubNodeName column
plug_SyncHubNodeName_missing = self.sql.execute ("""
SELECT COUNT(*) AS CNTREC FROM pragma_table_info('Plugins_Events') WHERE name='SyncHubNodeName'
""").fetchone()[0] == 0
if plug_SyncHubNodeName_missing :
mylog('verbose', ["[upgradeDB] Adding SyncHubNodeName to the Plugins_Events table"])
self.sql.execute("""
ALTER TABLE "Plugins_Events" ADD "SyncHubNodeName" TEXT
""")
# helper columns HelpVal1-4
plug_HelpValues_missing = self.sql.execute ("""
SELECT COUNT(*) AS CNTREC FROM pragma_table_info('Plugins_Events') WHERE name='HelpVal1'
""").fetchone()[0] == 0
if plug_HelpValues_missing :
mylog('verbose', ["[upgradeDB] Adding HelpVal1-4 to the Plugins_Events table"])
self.sql.execute('ALTER TABLE "Plugins_Events" ADD COLUMN "HelpVal1" TEXT')
self.sql.execute('ALTER TABLE "Plugins_Events" ADD COLUMN "HelpVal2" TEXT')
self.sql.execute('ALTER TABLE "Plugins_Events" ADD COLUMN "HelpVal3" TEXT')
self.sql.execute('ALTER TABLE "Plugins_Events" ADD COLUMN "HelpVal4" TEXT')
# plug_ObjectGUID_missing column
plug_ObjectGUID_missing = self.sql.execute ("""
SELECT COUNT(*) AS CNTREC FROM pragma_table_info('Plugins_Events') WHERE name='ObjectGUID'
""").fetchone()[0] == 0
if plug_ObjectGUID_missing :
mylog('verbose', ["[upgradeDB] Adding ObjectGUID to the Plugins_Events table"])
self.sql.execute("""
ALTER TABLE "Plugins_Events" ADD "ObjectGUID" TEXT
""")
# -----------------------------------------
# REMOVE after 6/6/2025 - END
# -----------------------------------------
# Plugin execution history # Plugin execution history
sql_Plugins_History = """ CREATE TABLE IF NOT EXISTS Plugins_History( sql_Plugins_History = """ CREATE TABLE IF NOT EXISTS Plugins_History(
"Index" INTEGER, "Index" INTEGER,
@@ -699,48 +256,6 @@ class DB():
); """ ); """
self.sql.execute(sql_Plugins_History) self.sql.execute(sql_Plugins_History)
# -----------------------------------------
# REMOVE after 6/6/2025 - START
# -----------------------------------------
# syncHubNodeName column
plug_SyncHubNodeName_missing = self.sql.execute ("""
SELECT COUNT(*) AS CNTREC FROM pragma_table_info('Plugins_History') WHERE name='SyncHubNodeName'
""").fetchone()[0] == 0
if plug_SyncHubNodeName_missing :
mylog('verbose', ["[upgradeDB] Adding SyncHubNodeName to the Plugins_History table"])
self.sql.execute("""
ALTER TABLE "Plugins_History" ADD "SyncHubNodeName" TEXT
""")
# helper columns HelpVal1-4
plug_HelpValues_missing = self.sql.execute ("""
SELECT COUNT(*) AS CNTREC FROM pragma_table_info('Plugins_History') WHERE name='HelpVal1'
""").fetchone()[0] == 0
if plug_HelpValues_missing :
mylog('verbose', ["[upgradeDB] Adding HelpVal1-4 to the Plugins_History table"])
self.sql.execute('ALTER TABLE "Plugins_History" ADD COLUMN "HelpVal1" TEXT')
self.sql.execute('ALTER TABLE "Plugins_History" ADD COLUMN "HelpVal2" TEXT')
self.sql.execute('ALTER TABLE "Plugins_History" ADD COLUMN "HelpVal3" TEXT')
self.sql.execute('ALTER TABLE "Plugins_History" ADD COLUMN "HelpVal4" TEXT')
# plug_ObjectGUID_missing column
plug_ObjectGUID_missing = self.sql.execute ("""
SELECT COUNT(*) AS CNTREC FROM pragma_table_info('Plugins_History') WHERE name='ObjectGUID'
""").fetchone()[0] == 0
if plug_ObjectGUID_missing :
mylog('verbose', ["[upgradeDB] Adding ObjectGUID to the Plugins_History table"])
self.sql.execute("""
ALTER TABLE "Plugins_History" ADD "ObjectGUID" TEXT
""")
# -----------------------------------------
# REMOVE after 6/6/2025 - END
# -----------------------------------------
# ------------------------------------------------------------------------- # -------------------------------------------------------------------------
# Plugins_Language_Strings table setup # Plugins_Language_Strings table setup
@@ -845,21 +360,6 @@ class DB():
# Init the AppEvent database table # Init the AppEvent database table
AppEvent_obj(self) AppEvent_obj(self)
# -------------------------------------------------------------------------
# DELETING OBSOLETE TABLES - to remove with updated db file after 9/9/2024
# -------------------------------------------------------------------------
# Deletes obsolete ScanCycles
self.sql.execute(""" DROP TABLE IF EXISTS ScanCycles;""")
self.sql.execute(""" DROP TABLE IF EXISTS DHCP_Leases;""")
self.sql.execute(""" DROP TABLE IF EXISTS PiHole_Network;""")
self.commitDB()
# -------------------------------------------------------------------------
# DELETING OBSOLETE TABLES - to remove with updated db file after 9/9/2024
# -------------------------------------------------------------------------
#------------------------------------------------------------------------------- #-------------------------------------------------------------------------------
def get_table_as_json(self, sqlQuery): def get_table_as_json(self, sqlQuery):

View File

@@ -73,6 +73,7 @@ class Device(ObjectType):
devParentChildrenCount = Int() devParentChildrenCount = Int()
devIpLong = Int() devIpLong = Int()
devFilterStatus = String() devFilterStatus = String()
devFQDN = String()
class DeviceResult(ObjectType): class DeviceResult(ObjectType):
@@ -180,7 +181,7 @@ class Query(ObjectType):
searchable_fields = [ searchable_fields = [
"devName", "devMac", "devOwner", "devType", "devVendor", "devLastIP", "devName", "devMac", "devOwner", "devType", "devVendor", "devLastIP",
"devGroup", "devComments", "devLocation", "devStatus", "devGroup", "devComments", "devLocation", "devStatus",
"devSSID", "devSite", "devSourcePlugin", "devSyncHubNode" "devSSID", "devSite", "devSourcePlugin", "devSyncHubNode", "devFQDN"
] ]
search_term = options.search.lower() search_term = options.search.lower()

View File

@@ -18,6 +18,7 @@ import hashlib
import random import random
import string import string
import ipaddress import ipaddress
import dns.resolver
import conf import conf
from const import * from const import *
@@ -427,211 +428,6 @@ def check_IP_format (pIP):
# Return IP # Return IP
return IP.group(0) return IP.group(0)
#-------------------------------------------------------------------------------
def get_device_name_mdns(db, pMAC, pIP):
nameNotFound = "(name not found)"
sql = db.sql
name = nameNotFound
# get names from the AVAHISCAN plugin entries vased on MAC
sql.execute(
f"""
SELECT Watched_Value2 FROM Plugins_Objects
WHERE
Plugin = 'AVAHISCAN' AND
Object_PrimaryID = '{pMAC}'
"""
)
nameEntry = sql.fetchall()
db.commitDB()
if len(nameEntry) != 0:
name = cleanDeviceName(nameEntry[0][0], False)
return name
# get names from the AVAHISCAN plugin entries based on IP
sql.execute(
f"""
SELECT Watched_Value2 FROM Plugins_Objects
WHERE
Plugin = 'AVAHISCAN' AND
Object_SecondaryID = '{pIP}'
"""
)
nameEntry = sql.fetchall()
db.commitDB()
if len(nameEntry) != 0:
name = cleanDeviceName(nameEntry[0][0], True)
return name
return name
#-------------------------------------------------------------------------------
def get_device_name_nslookup(db, pMAC, pIP):
nameNotFound = "(name not found)"
sql = db.sql
name = nameNotFound
# get names from the NSLOOKUP plugin entries vased on MAC
sql.execute(
f"""
SELECT Watched_Value2 FROM Plugins_Objects
WHERE
Plugin = 'NSLOOKUP' AND
Object_PrimaryID = '{pMAC}'
"""
)
nameEntry = sql.fetchall()
db.commitDB()
if len(nameEntry) != 0:
name = cleanDeviceName(nameEntry[0][0], False)
return name
# get names from the NSLOOKUP plugin entries based on IP
sql.execute(
f"""
SELECT Watched_Value2 FROM Plugins_Objects
WHERE
Plugin = 'NSLOOKUP' AND
Object_SecondaryID = '{pIP}'
"""
)
nameEntry = sql.fetchall()
db.commitDB()
if len(nameEntry) != 0:
name = cleanDeviceName(nameEntry[0][0], True)
return name
return name
#-------------------------------------------------------------------------------
def get_device_name_nbtlookup(db, pMAC, pIP):
nameNotFound = "(name not found)"
sql = db.sql
name = nameNotFound
# get names from the NBTSCAN plugin entries vased on MAC
sql.execute(
f"""
SELECT Watched_Value2 FROM Plugins_Objects
WHERE
Plugin = 'NBTSCAN' AND
Object_PrimaryID = '{pMAC}'
"""
)
nameEntry = sql.fetchall()
db.commitDB()
if len(nameEntry) != 0:
name = cleanDeviceName(nameEntry[0][0], False)
return name
# get names from the NSLOOKUP plugin entries based on IP
sql.execute(
f"""
SELECT Watched_Value2 FROM Plugins_Objects
WHERE
Plugin = 'NBTSCAN' AND
Object_SecondaryID = '{pIP}'
"""
)
nameEntry = sql.fetchall()
db.commitDB()
if len(nameEntry) != 0:
name = cleanDeviceName(nameEntry[0][0], True)
return name
return name
#-------------------------------------------------------------------------------
def resolve_device_name_dig (pMAC, pIP):
nameNotFound = "(name not found)"
dig_args = ['dig', '+short', '-x', pIP]
# Execute command
try:
# try runnning a subprocess
newName = subprocess.check_output (dig_args, universal_newlines=True)
# Check returns
newName = newName.strip()
if len(newName) == 0 :
return nameNotFound
# Cleanup
newName = cleanDeviceName(newName, True)
if newName == "" or len(newName) == 0 or newName == '-1' or newName == -1 or "communications error" in newName or 'malformed message packet' in newName :
return nameNotFound
# all checks passed
mylog('debug', [f'[resolve_device_name_dig] Found a new name: "{newName}"'])
return newName
except subprocess.CalledProcessError as e:
# An error occured, handle it
mylog('none', ['[resolve_device_name_dig] ⚠ ERROR: ', e.output])
# newName = "Error - check logs"
return nameNotFound
#-------------------------------------------------------------------------------
# DNS record (Name resolution) cleanup methods
#-------------------------------------------------------------------------------
import dns.resolver
def cleanDeviceName(str, match_IP):
mylog('debug', ["[cleanDeviceName] input: " + str])
# add matching info
if match_IP:
str = str + " (IP match)"
# Applying cleanup REGEXEs
mylog('debug', ["[Name cleanup] Using old cleanDeviceName(" + str + ")"])
regexes = get_setting_value('NEWDEV_NAME_CLEANUP_REGEX')
for rgx in regexes:
mylog('trace', ["[cleanDeviceName] applying regex : " + rgx])
mylog('trace', ["[cleanDeviceName] name before regex : " + str])
str = re.sub(rgx, "", str)
mylog('trace', ["[cleanDeviceName] name after regex : " + str])
# str = re.sub(r'\.\b', '', str) # trailing dot after words
str = re.sub(r'\.$', '', str) # trailing dot at the end of the string
str = str.replace(". (IP match)", " (IP match)") # Remove dot if (IP match) is added
mylog('debug', ["[cleanDeviceName] output: " + str])
return str
#------------------------------------------------------------------------------- #-------------------------------------------------------------------------------
# String manipulation methods # String manipulation methods
#------------------------------------------------------------------------------- #-------------------------------------------------------------------------------

View File

@@ -168,6 +168,7 @@ def importConfigs (db, all_plugins):
conf.HRS_TO_KEEP_NEWDEV = ccd('HRS_TO_KEEP_NEWDEV', 0 , c_d, 'Keep new devices for', '{"dataType":"integer", "elements": [{"elementType" : "input", "elementOptions" : [{"type": "number"}] ,"transformers": []}]}', "[]", 'General') conf.HRS_TO_KEEP_NEWDEV = ccd('HRS_TO_KEEP_NEWDEV', 0 , c_d, 'Keep new devices for', '{"dataType":"integer", "elements": [{"elementType" : "input", "elementOptions" : [{"type": "number"}] ,"transformers": []}]}', "[]", 'General')
conf.HRS_TO_KEEP_OFFDEV = ccd('HRS_TO_KEEP_OFFDEV', 0 , c_d, 'Keep offline devices for', '{"dataType":"integer", "elements": [{"elementType" : "input", "elementOptions" : [{"type": "number"}] ,"transformers": []}]}', "[]", 'General') conf.HRS_TO_KEEP_OFFDEV = ccd('HRS_TO_KEEP_OFFDEV', 0 , c_d, 'Keep offline devices for', '{"dataType":"integer", "elements": [{"elementType" : "input", "elementOptions" : [{"type": "number"}] ,"transformers": []}]}', "[]", 'General')
conf.CLEAR_NEW_FLAG = ccd('CLEAR_NEW_FLAG', 0 , c_d, 'Clear new flag', '{"dataType":"integer", "elements": [{"elementType" : "input", "elementOptions" : [{"type": "number"}] ,"transformers": []}]}', "[]", 'General') conf.CLEAR_NEW_FLAG = ccd('CLEAR_NEW_FLAG', 0 , c_d, 'Clear new flag', '{"dataType":"integer", "elements": [{"elementType" : "input", "elementOptions" : [{"type": "number"}] ,"transformers": []}]}', "[]", 'General')
conf.REFRESH_FQDN = ccd('REFRESH_FQDN', False , c_d, 'Refresh FQDN', """{"dataType": "boolean","elements": [{"elementType": "input","elementOptions": [{ "type": "checkbox" }],"transformers": []}]}""", '[]', 'General')
conf.API_CUSTOM_SQL = ccd('API_CUSTOM_SQL', 'SELECT * FROM Devices WHERE devPresentLastScan = 0' , c_d, 'Custom endpoint', '{"dataType":"string", "elements": [{"elementType" : "input", "elementOptions" : [] ,"transformers": []}]}', '[]', 'General') conf.API_CUSTOM_SQL = ccd('API_CUSTOM_SQL', 'SELECT * FROM Devices WHERE devPresentLastScan = 0' , c_d, 'Custom endpoint', '{"dataType":"string", "elements": [{"elementType" : "input", "elementOptions" : [] ,"transformers": []}]}', '[]', 'General')
conf.VERSION = ccd('VERSION', '' , c_d, 'Version', '{"dataType":"string", "elements": [{"elementType" : "input", "elementOptions" : [{ "readonly": "true" }] ,"transformers": []}]}', '', 'General') conf.VERSION = ccd('VERSION', '' , c_d, 'Version', '{"dataType":"string", "elements": [{"elementType" : "input", "elementOptions" : [{ "readonly": "true" }] ,"transformers": []}]}', '', 'General')
conf.NETWORK_DEVICE_TYPES = ccd('NETWORK_DEVICE_TYPES', ['AP', 'Gateway', 'Firewall', 'Hypervisor', 'Powerline', 'Switch', 'WLAN', 'PLC', 'Router','USB LAN Adapter', 'USB WIFI Adapter', 'Internet'] , c_d, 'Network device types', '{"dataType":"array","elements":[{"elementType":"input","elementOptions":[{"placeholder":"Enter value"},{"suffix":"_in"},{"cssClasses":"col-sm-10"},{"prefillValue":"null"}],"transformers":[]},{"elementType":"button","elementOptions":[{"sourceSuffixes":["_in"]},{"separator":""},{"cssClasses":"col-xs-12"},{"onClick":"addList(this,false)"},{"getStringKey":"Gen_Add"}],"transformers":[]},{"elementType":"select", "elementHasInputValue":1,"elementOptions":[{"multiple":"true"},{"readonly":"true"},{"editable":"true"}],"transformers":[]},{"elementType":"button","elementOptions":[{"sourceSuffixes":[]},{"separator":""},{"cssClasses":"col-xs-6"},{"onClick":"removeAllOptions(this)"},{"getStringKey":"Gen_Remove_All"}],"transformers":[]},{"elementType":"button","elementOptions":[{"sourceSuffixes":[]},{"separator":""},{"cssClasses":"col-xs-6"},{"onClick":"removeFromList(this)"},{"getStringKey":"Gen_Remove_Last"}],"transformers":[]}]}', '[]', 'General') conf.NETWORK_DEVICE_TYPES = ccd('NETWORK_DEVICE_TYPES', ['AP', 'Gateway', 'Firewall', 'Hypervisor', 'Powerline', 'Switch', 'WLAN', 'PLC', 'Router','USB LAN Adapter', 'USB WIFI Adapter', 'Internet'] , c_d, 'Network device types', '{"dataType":"array","elements":[{"elementType":"input","elementOptions":[{"placeholder":"Enter value"},{"suffix":"_in"},{"cssClasses":"col-sm-10"},{"prefillValue":"null"}],"transformers":[]},{"elementType":"button","elementOptions":[{"sourceSuffixes":["_in"]},{"separator":""},{"cssClasses":"col-xs-12"},{"onClick":"addList(this,false)"},{"getStringKey":"Gen_Add"}],"transformers":[]},{"elementType":"select", "elementHasInputValue":1,"elementOptions":[{"multiple":"true"},{"readonly":"true"},{"editable":"true"}],"transformers":[]},{"elementType":"button","elementOptions":[{"sourceSuffixes":[]},{"separator":""},{"cssClasses":"col-xs-6"},{"onClick":"removeAllOptions(this)"},{"getStringKey":"Gen_Remove_All"}],"transformers":[]},{"elementType":"button","elementOptions":[{"sourceSuffixes":[]},{"separator":""},{"cssClasses":"col-xs-6"},{"onClick":"removeFromList(this)"},{"getStringKey":"Gen_Remove_Last"}],"transformers":[]}]}', '[]', 'General')
@@ -445,46 +446,6 @@ replacements = {
r'\bREPORT_TO\b': 'SMTP_REPORT_TO', r'\bREPORT_TO\b': 'SMTP_REPORT_TO',
r'\bSYNC_api_token\b': 'API_TOKEN', r'\bSYNC_api_token\b': 'API_TOKEN',
r'\bAPI_TOKEN=\'\'': f'API_TOKEN=\'t_{generate_random_string(20)}\'', r'\bAPI_TOKEN=\'\'': f'API_TOKEN=\'t_{generate_random_string(20)}\'',
r'\bREPORT_FROM\b': 'SMTP_REPORT_FROM',
r'\bPIALERT_WEB_PROTECTION\b': 'SETPWD_enable_password',
r'\bPIALERT_WEB_PASSWORD\b': 'SETPWD_password',
r'REPORT_MAIL=True': "SMTP_RUN='on_notification'",
r'REPORT_APPRISE=True': "APPRISE_RUN='on_notification'",
r'REPORT_NTFY=True': "NTFY_RUN='on_notification'",
r'REPORT_WEBHOOK=True': "WEBHOOK_RUN='on_notification'",
r'REPORT_PUSHSAFER=True': "PUSHSAFER_RUN='on_notification'",
r'REPORT_MQTT=True': "MQTT_RUN='on_notification'",
# r'PIHOLE_CMD=': 'PIHOLE_CMD_OLD=',
r'\bINCLUDED_SECTIONS\b': 'NTFPRCS_INCLUDED_SECTIONS',
r'\bDIG_GET_IP_ARG\b': 'INTRNT_DIG_GET_IP_ARG',
r'dev_MAC': 'devMac',
r'dev_Name': 'devName',
r'dev_Favorite': 'devFavorite',
r'dev_Group': 'devGroup',
r'dev_Comments': 'devComments',
r'dev_FirstConnection': 'devFirstConnection',
r'dev_LastConnection': 'devLastConnection',
r'dev_LastIP': 'devLastIP',
r'dev_StaticIP': 'devStaticIP',
r'dev_ScanCycle': 'devScan',
r'dev_LogEvents': 'devLogEvents',
r'dev_AlertEvents': 'devAlertEvents',
r'dev_AlertDeviceDown': 'devAlertDown',
r'dev_SkipRepeated': 'devSkipRepeated',
r'dev_LastNotification': 'devLastNotification',
r'dev_PresentLastScan': 'devPresentLastScan',
r'dev_NewDevice': 'devIsNew',
r'dev_Location': 'devLocation',
r'dev_Archived': 'devIsArchived',
r'dev_Network_Node_MAC_ADDR': 'devParentMAC',
r'dev_Network_Node_port': 'devParentPort',
r'dev_Icon': 'devIcon',
r'dev_GUID': 'devGUID',
r'dev_NetworkSite': 'devSite',
r'dev_SSID': 'devSSID',
r'dev_SyncHubNodeName': 'devSyncHubNode',
r'dev_SourcePlugin': 'devSourcePlugin',
r'/home/pi/pialert\b': '/app'
} }

View File

@@ -8,10 +8,11 @@ import subprocess
import conf import conf
import os import os
import re import re
from helper import timeNowTZ, get_setting, get_setting_value, list_to_where, resolve_device_name_dig, get_device_name_nbtlookup, get_device_name_nslookup, get_device_name_mdns, check_IP_format, sanitize_SQL_input from helper import timeNowTZ, get_setting, get_setting_value, list_to_where, check_IP_format, sanitize_SQL_input
from logger import mylog from logger import mylog
from const import vendorsPath, vendorsPathNewest, sql_generateGuid from const import vendorsPath, vendorsPathNewest, sql_generateGuid
from models.device_instance import DeviceInstance from models.device_instance import DeviceInstance
from scan.name_resolution import NameResolver
#------------------------------------------------------------------------------- #-------------------------------------------------------------------------------
# Removing devices from the CurrentScan DB table which the user chose to ignore by MAC or IP # Removing devices from the CurrentScan DB table which the user chose to ignore by MAC or IP
@@ -481,88 +482,109 @@ def update_devices_data_from_scan (db):
mylog('debug','[Update Devices] Update devices end') mylog('debug','[Update Devices] Update devices end')
#------------------------------------------------------------------------------- #-------------------------------------------------------------------------------
def update_devices_names (db): def update_devices_names(db):
sql = db.sql #TO-DO sql = db.sql
# Initialize variables resolver = NameResolver(db)
recordsToUpdate = [] device_handler = DeviceInstance(db)
recordsNotFound = []
nameNotFound = "(name not found)" nameNotFound = "(name not found)"
ignored = 0 # Define resolution strategies in priority order
notFound = 0 strategies = [
(resolver.resolve_dig, 'dig'),
(resolver.resolve_mdns, 'mdns'),
(resolver.resolve_nslookup, 'nslookup'),
(resolver.resolve_nbtlookup, 'nbtlookup')
]
foundDig = 0 def resolve_devices(devices, resolve_both_name_and_fqdn=True):
foundmDNSLookup = 0 """
foundNsLookup = 0 Attempts to resolve device names and/or FQDNs using available strategies.
foundNbtLookup = 0
# Gen unknown devices Parameters:
device_handler = DeviceInstance(db) devices (list): List of devices to resolve.
# Retrieve devices resolve_both_name_and_fqdn (bool): If True, resolves both name and FQDN.
If False, resolves only FQDN.
Returns:
recordsToUpdate (list): List of [newName, newFQDN, devMac] or [newFQDN, devMac] for DB update.
recordsNotFound (list): List of [nameNotFound, devMac] for DB update.
foundStats (dict): Number of successes per strategy.
notFound (int): Number of devices not resolved.
"""
recordsToUpdate = []
recordsNotFound = []
foundStats = {label: 0 for _, label in strategies}
notFound = 0
for device in devices:
newName = nameNotFound
newFQDN = ''
# Attempt each resolution strategy in order
for resolve_fn, label in strategies:
resolved = resolve_fn(device['devMac'], device['devLastIP'])
# Only use name if resolving both name and FQDN
newName = resolved.cleaned if resolve_both_name_and_fqdn else None
newFQDN = resolved.raw
# If a valid result is found, record it and stop further attempts
if newFQDN not in [nameNotFound, '', 'localhost.'] and ' communications error to ' not in newFQDN:
foundStats[label] += 1
if resolve_both_name_and_fqdn:
recordsToUpdate.append([newName, newFQDN, device['devMac']])
else:
recordsToUpdate.append([newFQDN, device['devMac']])
break
# If no name was resolved, queue device for "(name not found)" update
if resolve_both_name_and_fqdn and newName == nameNotFound:
notFound += 1
if device['devName'] != nameNotFound:
recordsNotFound.append([nameNotFound, device['devMac']])
return recordsToUpdate, recordsNotFound, foundStats, notFound
# --- Step 1: Update device names for unknown devices ---
unknownDevices = device_handler.getUnknown() unknownDevices = device_handler.getUnknown()
if unknownDevices:
mylog('verbose', f'[Update Device Name] Trying to resolve devices without name. Unknown devices count: {len(unknownDevices)}')
# skip checks if no unknown devices # Try resolving both name and FQDN
if len(unknownDevices) == 0: recordsToUpdate, recordsNotFound, foundStats, notFound = resolve_devices(unknownDevices)
return
# Devices without name # Log summary
mylog('verbose', f'[Update Device Name] Trying to resolve devices without name. Unknown devices count: {len(unknownDevices)}') mylog('verbose', f"[Update Device Name] Names Found (DiG/mDNS/NSLOOKUP/NBTSCAN): {len(recordsToUpdate)} ({foundStats['dig']}/{foundStats['mdns']}/{foundStats['nslookup']}/{foundStats['nbtlookup']})")
mylog('verbose', f'[Update Device Name] Names Not Found : {notFound}')
for device in unknownDevices: # Apply updates to database
newName = nameNotFound sql.executemany("UPDATE Devices SET devName = ? WHERE devMac = ?", recordsNotFound)
sql.executemany("UPDATE Devices SET devName = ?, devFQDN = ? WHERE devMac = ?", recordsToUpdate)
# Resolve device name with DiG # --- Step 2: Optionally refresh FQDN for all devices ---
newName = resolve_device_name_dig (device['devMac'], device['devLastIP']) if get_setting_value("REFRESH_FQDN"):
allDevices = device_handler.getAll()
if allDevices:
mylog('verbose', f'[Update FQDN] Trying to resolve FQDN. Devices count: {len(allDevices)}')
# count # Try resolving only FQDN
if newName != nameNotFound: recordsToUpdate, _, foundStats, notFound = resolve_devices(allDevices, resolve_both_name_and_fqdn=False)
foundDig += 1
# Resolve device name with AVAHISCAN plugin data # Log summary
if newName == nameNotFound: mylog('verbose', f"[Update FQDN] Names Found (DiG/mDNS/NSLOOKUP/NBTSCAN): {len(recordsToUpdate)} ({foundStats['dig']}/{foundStats['mdns']}/{foundStats['nslookup']}/{foundStats['nbtlookup']})")
newName = get_device_name_mdns(db, device['devMac'], device['devLastIP']) mylog('verbose', f'[Update FQDN] Names Not Found : {notFound}')
if newName != nameNotFound: # Apply FQDN-only updates
foundmDNSLookup += 1 sql.executemany("UPDATE Devices SET devFQDN = ? WHERE devMac = ?", recordsToUpdate)
# Resolve device name with NSLOOKUP plugin data # Commit all database changes
if newName == nameNotFound:
newName = get_device_name_nslookup(db, device['devMac'], device['devLastIP'])
if newName != nameNotFound:
foundNsLookup += 1
# Resolve device name with NBTLOOKUP plugin data
if newName == nameNotFound:
newName = get_device_name_nbtlookup(db, device['devMac'], device['devLastIP'])
if newName != nameNotFound:
foundNbtLookup += 1
# if still not found update name so we can distinguish the devices where we tried already
if newName == nameNotFound :
notFound += 1
# if devName is the same as what we will change it to, take no action
# this mitigates a race condition which would overwrite a users edits that occured since the select earlier
if device['devName'] != nameNotFound:
recordsNotFound.append (["(name not found)", device['devMac']])
else:
# name was found
recordsToUpdate.append ([newName, device['devMac']])
# Print log
mylog('verbose', [f'[Update Device Name] Names Found (DiG/mDNS/NSLOOKUP/NBTSCAN): {len(recordsToUpdate)} ({foundDig}/{foundmDNSLookup}/{foundNsLookup}/{foundNbtLookup})'] )
mylog('verbose', [f'[Update Device Name] Names Not Found : {notFound}'] )
# update not found devices with (name not found)
sql.executemany ("UPDATE Devices SET devName = ? WHERE devMac = ? ", recordsNotFound )
# update names of devices which we were bale to resolve
sql.executemany ("UPDATE Devices SET devName = ? WHERE devMac = ? ", recordsToUpdate )
db.commitDB() db.commitDB()
#------------------------------------------------------------------------------- #-------------------------------------------------------------------------------
# Check if the variable contains a valid MAC address or "Internet" # Check if the variable contains a valid MAC address or "Internet"
def check_mac_or_internet(input_str): def check_mac_or_internet(input_str):

83
server/scan/name_resolution.py Executable file
View File

@@ -0,0 +1,83 @@
import sys
import re
import subprocess
import socket
import dns.resolver
# Register NetAlertX directories
INSTALL_PATH = "/app"
sys.path.extend([f"{INSTALL_PATH}/server"])
import conf
from const import *
from logger import mylog
from helper import get_setting_value
class ResolvedName:
def __init__(self, raw: str = "(name not found)", cleaned: str = "(name not found)"):
self.raw = raw
self.cleaned = cleaned
def __str__(self):
return self.cleaned
class NameResolver:
def __init__(self, db):
self.db = db
def resolve_from_plugin(self, plugin: str, pMAC: str, pIP: str) -> ResolvedName:
sql = self.db.sql
nameNotFound = ResolvedName()
# Check by MAC
sql.execute(f"""
SELECT Watched_Value2 FROM Plugins_Objects
WHERE Plugin = '{plugin}' AND Object_PrimaryID = '{pMAC}'
""")
result = sql.fetchall()
self.db.commitDB()
if result:
raw = result[0][0]
return ResolvedName(raw, self.clean_device_name(raw, False))
# Check by IP
sql.execute(f"""
SELECT Watched_Value2 FROM Plugins_Objects
WHERE Plugin = '{plugin}' AND Object_SecondaryID = '{pIP}'
""")
result = sql.fetchall()
self.db.commitDB()
if result:
raw = result[0][0]
return ResolvedName(raw, self.clean_device_name(raw, True))
return nameNotFound
def resolve_mdns(self, pMAC, pIP) -> ResolvedName:
return self.resolve_from_plugin("AVAHISCAN", pMAC, pIP)
def resolve_nslookup(self, pMAC, pIP) -> ResolvedName:
return self.resolve_from_plugin("NSLOOKUP", pMAC, pIP)
def resolve_nbtlookup(self, pMAC, pIP) -> ResolvedName:
return self.resolve_from_plugin("NBTSCAN", pMAC, pIP)
def resolve_dig(self, pMAC, pIP) -> ResolvedName:
return self.resolve_from_plugin("DIGSCAN", pMAC, pIP)
def clean_device_name(self, name: str, match_ip: bool) -> str:
mylog('debug', [f"[cleanDeviceName] input: {name}"])
if match_ip:
name += " (IP match)"
regexes = get_setting_value('NEWDEV_NAME_CLEANUP_REGEX') or []
for rgx in regexes:
mylog('trace', [f"[cleanDeviceName] applying regex: {rgx}"])
name = re.sub(rgx, "", name)
name = re.sub(r'\.$', '', name)
name = name.replace(". (IP match)", " (IP match)")
mylog('debug', [f"[cleanDeviceName] output: {name}"])
return name

View File

@@ -170,7 +170,7 @@ class AppEvent_obj:
END; END;
""" """
mylog("verbose", [query]) # mylog("verbose", [query])
self.db.sql.execute(query) self.db.sql.execute(query)

View File

@@ -86,9 +86,10 @@ def insert_devices(db_path, num_entries=1):
devSSID, devSSID,
devSyncHubNode, devSyncHubNode,
devSourcePlugin, devSourcePlugin,
devCustomProps devCustomProps,
devFQDN
) )
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?); VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);
""" """
# List of device types, vendors, groups, locations # List of device types, vendors, groups, locations
@@ -130,6 +131,7 @@ def insert_devices(db_path, num_entries=1):
dev_sync_hub_node = "" # Left as NULL dev_sync_hub_node = "" # Left as NULL
dev_source_plugin = "" # Left as NULL dev_source_plugin = "" # Left as NULL
dev_devCustomProps = "" # Left as NULL dev_devCustomProps = "" # Left as NULL
dev_devFQDN = "" # Left as NULL
# Execute the insert query # Execute the insert query
cursor.execute(insert_query, ( cursor.execute(insert_query, (
@@ -163,7 +165,8 @@ def insert_devices(db_path, num_entries=1):
dev_ssid, dev_ssid,
dev_sync_hub_node, dev_sync_hub_node,
dev_source_plugin, dev_source_plugin,
dev_devCustomProps dev_devCustomProps,
dev_devFQDN
)) ))
# Commit after every 1000 rows to improve performance # Commit after every 1000 rows to improve performance