diff --git a/Dockerfile b/Dockerfile index dbf63f7e..c44ff04d 100755 --- a/Dockerfile +++ b/Dockerfile @@ -15,7 +15,7 @@ ENV PATH="/opt/venv/bin:$PATH" COPY . ${INSTALL_DIR}/ -RUN pip install netifaces tplink-omada-client pycryptodome requests paho-mqtt scapy cron-converter pytz json2table dhcp-leases pyunifi speedtest-cli chardet python-nmap dnspython librouteros \ +RUN pip install graphene flask netifaces tplink-omada-client pycryptodome requests paho-mqtt scapy cron-converter pytz json2table dhcp-leases pyunifi speedtest-cli chardet python-nmap dnspython librouteros \ && bash -c "find ${INSTALL_DIR} -type d -exec chmod 750 {} \;" \ && bash -c "find ${INSTALL_DIR} -type f -exec chmod 640 {} \;" \ && bash -c "find ${INSTALL_DIR} -type f \( -name '*.sh' -o -name '*.py' -o -name 'speedtest-cli' \) -exec chmod 750 {} \;" diff --git a/dockerfiles/README.md b/dockerfiles/README.md index 00924195..4e7ac884 100755 --- a/dockerfiles/README.md +++ b/dockerfiles/README.md @@ -44,6 +44,8 @@ docker run -d --rm --network=host \ |`APP_CONF_OVERRIDE` | JSON override for settings, e.g. `{"SCAN_SUBNETS":"['192.168.1.0/24 --interface=eth1']","UI_theme":"Dark"}` (Experimental 🧪) | `N/A` | |`ALWAYS_FRESH_INSTALL` | If `true` will delete the content of the `/db` & `/config` folders. For testing purposes. Can be coupled with [watchtower](https://github.com/containrrr/watchtower) to have an always freshly installed `netalertx`/`netalertx-dev` image. | `N/A` | +> You can override the default GraphQL port setting `GRAPHQL_PORT` (set to `20212`) by using the `APP_CONF_OVERRIDE` env variable. + ### Docker paths > [!NOTE] diff --git a/docs/API.md b/docs/API.md index 69dbf5a8..b6f4db6f 100755 --- a/docs/API.md +++ b/docs/API.md @@ -58,38 +58,38 @@ Example JSON of the `table_devices.json` endpoint with two Devices (database row { "data": [ { - "dev_MAC": "Internet", - "dev_Name": "Net - Huawei", - "dev_DeviceType": "Router", - "dev_Vendor": null, - "dev_Group": "Always on", - "dev_FirstConnection": "2021-01-01 00:00:00", - "dev_LastConnection": "2021-01-28 22:22:11", - "dev_LastIP": "192.168.1.24", - "dev_StaticIP": 0, - "dev_PresentLastScan": 1, - "dev_LastNotification": "2023-01-28 22:22:28.998715", - "dev_NewDevice": 0, - "dev_Network_Node_MAC_ADDR": "", - "dev_Network_Node_port": "", - "dev_Icon": "globe" + "devMac": "Internet", + "devName": "Net - Huawei", + "devType": "Router", + "devVendor": null, + "devGroup": "Always on", + "devFirstConnection": "2021-01-01 00:00:00", + "devLastConnection": "2021-01-28 22:22:11", + "devLastIP": "192.168.1.24", + "devStaticIP": 0, + "devPresentLastScan": 1, + "devLastNotification": "2023-01-28 22:22:28.998715", + "devIsNew": 0, + "devParentMAC": "", + "devParentPort": "", + "devIcon": "globe" }, { - "dev_MAC": "a4:8f:ff:aa:ba:1f", - "dev_Name": "Net - USG", - "dev_DeviceType": "Firewall", - "dev_Vendor": "Ubiquiti Inc", - "dev_Group": "", - "dev_FirstConnection": "2021-02-12 22:05:00", - "dev_LastConnection": "2021-07-17 15:40:00", - "dev_LastIP": "192.168.1.1", - "dev_StaticIP": 1, - "dev_PresentLastScan": 1, - "dev_LastNotification": "2021-07-17 15:40:10.667717", - "dev_NewDevice": 0, - "dev_Network_Node_MAC_ADDR": "Internet", - "dev_Network_Node_port": 1, - "dev_Icon": "shield-halved" + "devMac": "a4:8f:ff:aa:ba:1f", + "devName": "Net - USG", + "devType": "Firewall", + "devVendor": "Ubiquiti Inc", + "devGroup": "", + "devFirstConnection": "2021-02-12 22:05:00", + "devLastConnection": "2021-07-17 15:40:00", + "devLastIP": "192.168.1.1", + "devStaticIP": 1, + "devPresentLastScan": 1, + "devLastNotification": "2021-07-17 15:40:10.667717", + "devIsNew": 0, + "devParentMAC": "Internet", + "devParentPort": 1, + "devIcon": "shield-halved" } ] } diff --git a/docs/HW_INSTALL.md b/docs/HW_INSTALL.md index 94e37368..d00982b4 100755 --- a/docs/HW_INSTALL.md +++ b/docs/HW_INSTALL.md @@ -5,6 +5,8 @@ To download and install NetAlertX on the hardware/server directly use the `curl` > [!NOTE] > This is an Experimental feature 🧪 and it relies on community support. > +> Looking for maintainers for this installation method 🙂 +> > There is no guarantee that the install script or any other script will gracefully handle other installed software. > Data loss is a possibility, **it is recommended to install NetAlertX using the supplied Docker image**. diff --git a/docs/PLUGINS_DEV.md b/docs/PLUGINS_DEV.md index 7fb40eec..96ae41e2 100755 --- a/docs/PLUGINS_DEV.md +++ b/docs/PLUGINS_DEV.md @@ -170,20 +170,20 @@ This SQL query is executed on the `app.db` SQLite database file. > SQL query example: > > ```SQL -> SELECT dv.dev_Name as Object_PrimaryID, -> cast(dv.dev_LastIP as VARCHAR(100)) || ':' || cast( SUBSTR(ns.Port ,0, INSTR(ns.Port , '/')) as VARCHAR(100)) as Object_SecondaryID, +> SELECT dv.devName as Object_PrimaryID, +> cast(dv.devLastIP as VARCHAR(100)) || ':' || cast( SUBSTR(ns.Port ,0, INSTR(ns.Port , '/')) as VARCHAR(100)) as Object_SecondaryID, > datetime() as DateTime, > ns.Service as Watched_Value1, > ns.State as Watched_Value2, > 'null' as Watched_Value3, > 'null' as Watched_Value4, > ns.Extra as Extra, -> dv.dev_MAC as ForeignKey +> dv.devMac as ForeignKey > FROM > (SELECT * FROM Nmap_Scan) ns > LEFT JOIN -> (SELECT dev_Name, dev_MAC, dev_LastIP FROM Devices) dv -> ON ns.MAC = dv.dev_MAC +> (SELECT devName, devMac, devLastIP FROM Devices) dv +> ON ns.MAC = dv.devMac > ``` > > Required `CMD` setting example with above query (you can set `"type": "label"` if you want it to make uneditable in the UI): @@ -192,7 +192,7 @@ This SQL query is executed on the `app.db` SQLite database file. > { > "function": "CMD", > "type": {"dataType":"string", "elements": [{"elementType" : "input", "elementOptions" : [] ,"transformers": []}]}, -> "default_value":"SELECT dv.dev_Name as Object_PrimaryID, cast(dv.dev_LastIP as VARCHAR(100)) || ':' || cast( SUBSTR(ns.Port ,0, INSTR(ns.Port , '/')) as VARCHAR(100)) as Object_SecondaryID, datetime() as DateTime, ns.Service as Watched_Value1, ns.State as Watched_Value2, 'null' as Watched_Value3, 'null' as Watched_Value4, ns.Extra as Extra FROM (SELECT * FROM Nmap_Scan) ns LEFT JOIN (SELECT dev_Name, dev_MAC, dev_LastIP FROM Devices) dv ON ns.MAC = dv.dev_MAC", +> "default_value":"SELECT dv.devName as Object_PrimaryID, cast(dv.devLastIP as VARCHAR(100)) || ':' || cast( SUBSTR(ns.Port ,0, INSTR(ns.Port , '/')) as VARCHAR(100)) as Object_SecondaryID, datetime() as DateTime, ns.Service as Watched_Value1, ns.State as Watched_Value2, 'null' as Watched_Value3, 'null' as Watched_Value4, ns.Extra as Extra FROM (SELECT * FROM Nmap_Scan) ns LEFT JOIN (SELECT devName, devMac, devLastIP FROM Devices) dv ON ns.MAC = dv.devMac", > "options": [], > "localized": ["name", "description"], > "name" : [{ @@ -460,7 +460,7 @@ Below are some general additional notes, when defining `params`: - `"name":"name_value"` - is used as a wildcard replacement in the `CMD` setting value by using curly brackets `{name_value}`. The wildcard is replaced by the result of the `"value" : "param_value"` and `"type":"type_value"` combo configuration below. - `"type":""` - is used to specify the type of the params, currently only 2 supported (`sql`,`setting`). - - `"type":"sql"` - will execute the SQL query specified in the `value` property. The sql query needs to return only one column. The column is flattened and separated by commas (`,`), e.g: `SELECT dev_MAC from DEVICES` -> `Internet,74:ac:74:ac:74:ac,44:44:74:ac:74:ac`. This is then used to replace the wildcards in the `CMD` setting. + - `"type":"sql"` - will execute the SQL query specified in the `value` property. The sql query needs to return only one column. The column is flattened and separated by commas (`,`), e.g: `SELECT devMac from DEVICES` -> `Internet,74:ac:74:ac:74:ac,44:44:74:ac:74:ac`. This is then used to replace the wildcards in the `CMD` setting. - `"type":"setting"` - The setting code name. A combination of the value from `unique_prefix` + `_` + `function` value, or otherwise the code name you can find in the Settings page under the Setting display name, e.g. `PIHOLE_RUN`. - `"value": "param_value"` - Needs to contain a setting code name or SQL query without wildcards. - `"timeoutMultiplier" : true` - used to indicate if the value should multiply the max timeout for the whole script run by the number of values in the given parameter. @@ -474,13 +474,13 @@ Below are some general additional notes, when defining `params`: > "params" : [{ > "name" : "ips", > "type" : "sql", -> "value" : "SELECT dev_LastIP from DEVICES", +> "value" : "SELECT devLastIP from DEVICES", > "timeoutMultiplier" : true > }, > { > "name" : "macs", > "type" : "sql", -> "value" : "SELECT dev_MAC from DEVICES" +> "value" : "SELECT devMac from DEVICES" > }, > { > "name" : "timeout", @@ -527,7 +527,7 @@ The UI component is defined as a JSON object containing a list of `elements`. Ea ```json { - "function": "dev_Icon", + "function": "devIcon", "type": { "dataType": "string", "elements": [ @@ -536,7 +536,7 @@ The UI component is defined as a JSON object containing a list of `elements`. Ea "elementOptions": [ { "cssClasses": "input-group-addon iconPreview" }, { "getStringKey": "Gen_SelectToPreview" }, - { "customId": "NEWDEV_dev_Icon_preview" } + { "customId": "NEWDEV_devIcon_preview" } ], "transformers": [] }, @@ -548,7 +548,7 @@ The UI component is defined as a JSON object containing a list of `elements`. Ea { "onChange": "updateIconPreview(this)" }, - { "customParams": "NEWDEV_dev_Icon,NEWDEV_dev_Icon_preview" } + { "customParams": "NEWDEV_devIcon,NEWDEV_devIcon_preview" } ], "transformers": [] } @@ -649,7 +649,7 @@ The UI will adjust how columns are displayed in the UI based on the resolvers de | See below for information on `threshold`, `replace`. | | | | | | `options` Property | Used in conjunction with types like `threshold`, `replace`, `regex`. | -| `options_params` Property | Used in conjunction with a `"options": "[{value}]"` template and `text.select`/`list.select`. Can specify SQL query (needs to return 2 columns `SELECT dev_Name as name, dev_Mac as id`) or Setting (not tested) to populate the dropdown. Check example below or have a look at the `NEWDEV` plugin `config.json` file. | +| `options_params` Property | Used in conjunction with a `"options": "[{value}]"` template and `text.select`/`list.select`. Can specify SQL query (needs to return 2 columns `SELECT devName as name, devMac as id`) or Setting (not tested) to populate the dropdown. Check example below or have a look at the `NEWDEV` plugin `config.json` file. | | `threshold` | The `options` array contains objects ordered from the lowest `maximum` to the highest. The corresponding `hexColor` is used for the value background color if it's less than the specified `maximum` but more than the previous one in the `options` array. | | `replace` | The `options` array contains objects with an `equals` property, which is compared to the "value." If the values are the same, the string in `replacement` is displayed in the UI instead of the actual "value". | | `regex` | Applies a regex to the value. The `options` array contains objects with an `type` (must be set to `regex`) and `param` (must contain the regex itself) property. | @@ -669,7 +669,7 @@ The UI will adjust how columns are displayed in the UI based on the resolvers de ```json - "function": "dev_DeviceType", + "function": "devType", "type": {"dataType":"string", "elements": [{"elementType" : "select", "elementOptions" : [] ,"transformers": []}]}, "maxLength": 30, "default_value": "", @@ -678,7 +678,7 @@ The UI will adjust how columns are displayed in the UI based on the resolvers de { "name" : "value", "type" : "sql", - "value" : "SELECT '' as id, '' as name UNION SELECT dev_DeviceType as id, dev_DeviceType as name FROM (SELECT dev_DeviceType FROM Devices UNION SELECT 'Smartphone' UNION SELECT 'Tablet' UNION SELECT 'Laptop' UNION SELECT 'PC' UNION SELECT 'Printer' UNION SELECT 'Server' UNION SELECT 'NAS' UNION SELECT 'Domotic' UNION SELECT 'Game Console' UNION SELECT 'SmartTV' UNION SELECT 'Clock' UNION SELECT 'House Appliance' UNION SELECT 'Phone' UNION SELECT 'AP' UNION SELECT 'Gateway' UNION SELECT 'Firewall' UNION SELECT 'Switch' UNION SELECT 'WLAN' UNION SELECT 'Router' UNION SELECT 'Other') AS all_devices ORDER BY id;" + "value" : "SELECT '' as id, '' as name UNION SELECT devType as id, devType as name FROM (SELECT devType FROM Devices UNION SELECT 'Smartphone' UNION SELECT 'Tablet' UNION SELECT 'Laptop' UNION SELECT 'PC' UNION SELECT 'Printer' UNION SELECT 'Server' UNION SELECT 'NAS' UNION SELECT 'Domotic' UNION SELECT 'Game Console' UNION SELECT 'SmartTV' UNION SELECT 'Clock' UNION SELECT 'House Appliance' UNION SELECT 'Phone' UNION SELECT 'AP' UNION SELECT 'Gateway' UNION SELECT 'Firewall' UNION SELECT 'Switch' UNION SELECT 'WLAN' UNION SELECT 'Router' UNION SELECT 'Other') AS all_devices ORDER BY id;" }, { "name" : "uilang", diff --git a/docs/UPDATES.md b/docs/UPDATES.md new file mode 100755 index 00000000..6beb8496 --- /dev/null +++ b/docs/UPDATES.md @@ -0,0 +1,110 @@ +# Docker Update Strategies for NetAlertX + +This guide outlines several approaches for updating Docker containers, specifically using NetAlertX. Each method offers different benefits depending on the situation. Here are the methods: + +- Manual: Direct commands to stop, remove, and rebuild containers. +- Dockcheck: Semi-automated with more control, suited for bulk updates. +- Watchtower: Fully automated, runs continuously to check and update containers. + +You can choose any approach that fits your workflow. + +> In the examples I assume that the container name is `netalertx` and the image name is `netalertx` as well. + +## 1. Manual Updates + +Use this method when you need precise control over a single container or when dealing with a broken container that needs immediate attention. +Example Commands + +To manually update the `netalertx` container, stop it, delete it, remove the old image, and start a fresh one with `docker-compose`. + +```bash +# Stop the container +sudo docker container stop netalertx + +# Remove the container +sudo docker container rm netalertx + +# Remove the old image +sudo docker image rm netalertx + +# Pull and start a new container +sudo docker-compose up -d +``` + +### Alternative: Force Pull with Docker Compose + +You can also use `--pull always` to ensure Docker pulls the latest image before starting the container: + +```bash +sudo docker-compose up --pull always -d +``` + +## 2. Dockcheck for Bulk Container Updates + +Always check the [Dockcheck](https://github.com/mag37/dockcheck) docs if encountering issues with the guide below. + +Dockcheck is a useful tool if you have multiple containers to update and some flexibility for handling potential issues that might arise during mass updates. Dockcheck allows you to inspect each container and decide when to update. + +### Example Workflow with Dockcheck + +You might use Dockcheck to: + +- Inspect container versions. +- Pull the latest images in bulk. +- Apply updates selectively. + +Dockcheck can help streamline bulk updates, especially if you’re managing multiple containers. + +Below is a script I use to run an update of the Dockcheck script and start a check for new containers: + +```bash +cd /path/to/Docker && +rm dockcheck.sh && +wget https://raw.githubusercontent.com/mag37/dockcheck/main/dockcheck.sh && +sudo chmod +x dockcheck.sh && +sudo ./dockcheck.sh +``` + +## 3. Automated Updates with Watchtower + +Always check the [watchtower](https://github.com/containrrr/watchtower) docs if encountering issues with the guide below. + +Watchtower monitors your Docker containers and automatically updates them when new images are available. This is ideal for ongoing updates without manual intervention. + +### Setting Up Watchtower + +#### 1. Pull the Watchtower Image: + +```bash +docker pull containrrr/watchtower +``` + +#### 2. Run Watchtower to update all images: + +```bash +docker run -d \ + --name watchtower \ + -v /var/run/docker.sock:/var/run/docker.sock \ + containrrr/watchtower \ + --interval 300 # Check for updates every 5 minutes +``` + +#### 3. Run Watchtower to update only NetAlertX: + +You can specify which containers to monitor by listing them. For example, to monitor netalertx only: + +```bash +docker run -d \ + --name watchtower \ + -v /var/run/docker.sock:/var/run/docker.sock \ + containrrr/watchtower netalertx + +``` + +## Summary + +- Manual: Ideal for individual or critical updates. +- Dockcheck: Suitable for controlled, mass updates. +- Watchtower: Fully automated, best for continuous deployment setups. + +These approaches allow you to maintain flexibility in how you update Docker containers, depending on the urgency and scale of the update. diff --git a/front/deviceDetails.php b/front/deviceDetails.php index fcd76a92..8165d781 100755 --- a/front/deviceDetails.php +++ b/front/deviceDetails.php @@ -681,7 +681,7 @@ switch ($UI_THEME) { // Iterate through the data and filter only visible devices $.each(devicesList, function(index, item) { // Check if the current item's MAC exists in visibleDevicesMACs - if (visibleDevicesMACs.includes(item.dev_MAC)) { + if (visibleDevicesMACs.includes(item.devMac)) { devicesList_tmp.push(item); } }); @@ -827,12 +827,12 @@ function initializeCombos () { // nameTransformer) // callback to transform name - generateOptionsOrSetOptions("NEWDEV_dev_Icon", [], "dropdownIcon_tmp", genListWithInputSet, 'txtIcon', ["base64"]) - generateOptionsOrSetOptions("NEWDEV_dev_DeviceType", [], "dropdownDeviceType_tmp", genListWithInputSet, 'txtDeviceType' ) - generateOptionsOrSetOptions("NEWDEV_dev_Owner", [], "dropdownOwner_tmp", genListWithInputSet, 'txtOwner' ) - generateOptionsOrSetOptions("NEWDEV_dev_Group", [], "dropdownGroup_tmp", genListWithInputSet, 'txtGroup' ) - generateOptionsOrSetOptions("NEWDEV_dev_Location", [], "dropdownLocation_tmp", genListWithInputSet, 'txtLocation' ) - generateOptionsOrSetOptions("NEWDEV_dev_Network_Node_MAC_ADDR", [], "dropdownNetworkNodeMac_tmp", genListWithInputSet, 'txtNetworkNodeMac' ) + generateOptionsOrSetOptions("NEWDEV_devIcon", [], "dropdownIcon_tmp", genListWithInputSet, 'txtIcon', ["base64"]) + generateOptionsOrSetOptions("NEWDEV_devType", [], "dropdownDeviceType_tmp", genListWithInputSet, 'txtDeviceType' ) + generateOptionsOrSetOptions("NEWDEV_devOwner", [], "dropdownOwner_tmp", genListWithInputSet, 'txtOwner' ) + generateOptionsOrSetOptions("NEWDEV_devGroup", [], "dropdownGroup_tmp", genListWithInputSet, 'txtGroup' ) + generateOptionsOrSetOptions("NEWDEV_devLocation", [], "dropdownLocation_tmp", genListWithInputSet, 'txtLocation' ) + generateOptionsOrSetOptions("NEWDEV_devParentMAC", [], "dropdownNetworkNodeMac_tmp", genListWithInputSet, 'txtNetworkNodeMac' ) // Initialize static combos initializeComboSkipRepeated (); @@ -1171,7 +1171,7 @@ function getDeviceData (readAllData=false) { var deviceData = JSON.parse(data); // check device exists - if (deviceData['dev_MAC'] == null) { + if (deviceData['devMac'] == null) { // Status $('#deviceStatus').html ('--'); $('#deviceStatus')[0].className = 'text-gray'; @@ -1227,16 +1227,16 @@ function getDeviceData (readAllData=false) { } else { // Name - if (deviceData['dev_Owner'] == null || deviceData['dev_Owner'] == '' || - (deviceData['dev_Name'].toString()).indexOf (deviceData['dev_Owner']) != -1 ) { - $('#pageTitle').html (deviceData['dev_Name']); + if (deviceData['devOwner'] == null || deviceData['devOwner'] == '' || + (deviceData['devName'].toString()).indexOf (deviceData['devOwner']) != -1 ) { + $('#pageTitle').html (deviceData['devName']); } else { - $('#pageTitle').html (deviceData['dev_Name'] + ' ('+ deviceData['dev_Owner'] +')'); + $('#pageTitle').html (deviceData['devName'] + ' ('+ deviceData['devOwner'] +')'); } // Status - $('#deviceStatus').html (deviceData['dev_Status'].replace('-', '')); - switch (deviceData['dev_Status']) { + $('#deviceStatus').html (deviceData['devStatus'].replace('-', '')); + switch (deviceData['devStatus']) { case 'On-line': icon='fa fa-check'; color='text-green'; break; case 'Off-line': icon='fa fa-close'; color='text-gray'; break; case 'Down': icon='fa fa-warning'; color='text-red'; break; @@ -1247,16 +1247,16 @@ function getDeviceData (readAllData=false) { $('#deviceStatusIcon')[0].className = icon +' '+ color; // Totals - $('#deviceSessions').html (deviceData['dev_Sessions'].toLocaleString()); - $('#deviceDownAlerts').html (deviceData['dev_DownAlerts'].toLocaleString()); + $('#deviceSessions').html (deviceData['devSessions'].toLocaleString()); + $('#deviceDownAlerts').html (deviceData['devDownAlerts'].toLocaleString()); // Presence $('#deviceEventsTitle').html ('Presence'); $('#deviceEventsIcon').html (''); - if (deviceData['dev_PresenceHours'] == null || deviceData['dev_PresenceHours'] < 0) { + if (deviceData['devPresenceHours'] == null || deviceData['devPresenceHours'] < 0) { $('#deviceEvents').html ('0 h.'); } else { - $('#deviceEvents').html (deviceData['dev_PresenceHours'].toLocaleString() +' h.'); + $('#deviceEvents').html (deviceData['devPresenceHours'].toLocaleString() +' h.'); } // Device info @@ -1264,7 +1264,7 @@ function getDeviceData (readAllData=false) { // Activate controls $('#panDetails :input').attr('disabled', false); - mac = deviceData['dev_MAC']; + mac = deviceData['devMac']; // update the mac parameter in the URL, this makes the selected device persistent when the page is reloaded var searchParams = new URLSearchParams(window.location.search); @@ -1275,51 +1275,51 @@ function getDeviceData (readAllData=false) { devicesList = getDevicesList(); - // handle empty dev_Network_Node_MAC_ADDR - networkParentMac = deviceData['dev_Network_Node_MAC_ADDR'] + // handle empty devParentMAC + networkParentMac = deviceData['devParentMAC'] if(networkParentMac) { - networkParentMacName = getDeviceDataByMac(deviceData['dev_Network_Node_MAC_ADDR'], "dev_Name") + networkParentMacName = getDeviceDataByMac(deviceData['devParentMAC'], "devName") } else { networkParentMacName = '--' } - $('#txtMAC').val (deviceData['dev_MAC']); - $('#txtName').val (deviceData['dev_Name']); - $('#txtOwner').val (deviceData['dev_Owner']); - $('#txtDeviceType').val (deviceData['dev_DeviceType']); - $('#txtVendor').val (deviceData['dev_Vendor']); - $('#txtIcon').val (initDefault(deviceData['dev_Icon'], 'PGkgY2xhc3M9ImZhIGZhLWxhcHRvcCI+PC9pPg==')); // base64 laptop icon + $('#txtMAC').val (deviceData['devMac']); + $('#txtName').val (deviceData['devName']); + $('#txtOwner').val (deviceData['devOwner']); + $('#txtDeviceType').val (deviceData['devType']); + $('#txtVendor').val (deviceData['devVendor']); + $('#txtIcon').val (initDefault(deviceData['devIcon'], 'PGkgY2xhc3M9ImZhIGZhLWxhcHRvcCI+PC9pPg==')); // base64 laptop icon $('#txtIcon').trigger('change') - if (deviceData['dev_Favorite'] == 1) {$('#chkFavorite').iCheck('check');} else {$('#chkFavorite').iCheck('uncheck');} - $('#txtGroup').val (deviceData['dev_Group']); - $('#txtLocation').val (deviceData['dev_Location']); - $('#txtComments').val (decodeSpecialChars(deviceData['dev_Comments'])); + if (deviceData['devFavorite'] == 1) {$('#chkFavorite').iCheck('check');} else {$('#chkFavorite').iCheck('uncheck');} + $('#txtGroup').val (deviceData['devGroup']); + $('#txtLocation').val (deviceData['devLocation']); + $('#txtComments').val (decodeSpecialChars(deviceData['devComments'])); $('#txtNetworkNodeMac').val ( networkParentMacName) ; - $('#txtNetworkNodeMac').attr ('data-mynodemac', deviceData['dev_Network_Node_MAC_ADDR']); - $('#txtNetworkPort').val (deviceData['dev_Network_Node_port']); - $('#txtNetworkSite').val (deviceData['dev_NetworkSite']); - $('#txtSSID').val (deviceData['dev_SSID']); + $('#txtNetworkNodeMac').attr ('data-mynodemac', deviceData['devParentMAC']); + $('#txtNetworkPort').val (deviceData['devParentPort']); + $('#txtNetworkSite').val (deviceData['devSite']); + $('#txtSSID').val (deviceData['devSSID']); // disabling network node configuration if root Internet node toggleNetworkConfiguration(mac == 'Internet') - $('#txtFirstConnection').val (deviceData['dev_FirstConnection']); - $('#txtLastConnection').val (deviceData['dev_LastConnection']); - $('#txtLastIP').val (deviceData['dev_LastIP']); - $('#txtStatus').val (deviceData['dev_Status'].replace('-', '')); - if (deviceData['dev_StaticIP'] == 1) {$('#chkStaticIP').iCheck('check');} else {$('#chkStaticIP').iCheck('uncheck');} + $('#txtFirstConnection').val (deviceData['devFirstConnection']); + $('#txtLastConnection').val (deviceData['devLastConnection']); + $('#txtLastIP').val (deviceData['devLastIP']); + $('#txtStatus').val (deviceData['devStatus'].replace('-', '')); + if (deviceData['devStaticIP'] == 1) {$('#chkStaticIP').iCheck('check');} else {$('#chkStaticIP').iCheck('uncheck');} - $('#txtScanCycle').val (deviceData['dev_ScanCycle'] == "1" ? "yes" : "no"); - if (deviceData['dev_AlertEvents'] == 1) {$('#chkAlertEvents').iCheck('check');} else {$('#chkAlertEvents').iCheck('uncheck');} - if (deviceData['dev_AlertDeviceDown'] == 1) {$('#chkAlertDown').iCheck('check');} else {$('#chkAlertDown').iCheck('uncheck');} - $('#txtSkipRepeated').val (findSkipRepeated (deviceData['dev_SkipRepeated'])); - if (deviceData['dev_NewDevice'] == 1) {$('#chkNewDevice').iCheck('check');} else {$('#chkNewDevice').iCheck('uncheck');} - if (deviceData['dev_Archived'] == 1) {$('#chkArchived').iCheck('check');} else {$('#chkArchived').iCheck('uncheck');} + $('#txtScanCycle').val (deviceData['devScan'] == "1" ? "yes" : "no"); + if (deviceData['devAlertEvents'] == 1) {$('#chkAlertEvents').iCheck('check');} else {$('#chkAlertEvents').iCheck('uncheck');} + if (deviceData['devAlertDown'] == 1) {$('#chkAlertDown').iCheck('check');} else {$('#chkAlertDown').iCheck('uncheck');} + $('#txtSkipRepeated').val (findSkipRepeated (deviceData['devSkipRepeated'])); + if (deviceData['devIsNew'] == 1) {$('#chkNewDevice').iCheck('check');} else {$('#chkNewDevice').iCheck('uncheck');} + if (deviceData['devIsArchived'] == 1) {$('#chkArchived').iCheck('check');} else {$('#chkArchived').iCheck('uncheck');} - if (deviceData['dev_RandomMAC'] == 1) {$('#iconRandomMACactive').removeClass ('hidden'); + if (deviceData['devRandomMAC'] == 1) {$('#iconRandomMACactive').removeClass ('hidden'); $('#iconRandomMACinactive').addClass ('hidden'); } else {$('#iconRandomMACactive').addClass ('hidden'); $('#iconRandomMACinactive').removeClass ('hidden'); }; @@ -1329,7 +1329,7 @@ function getDeviceData (readAllData=false) { pos = devicesList.findIndex(item => item.rowid == deviceData['rowid']); if (pos == -1) { - devicesList.push({"rowid" : deviceData['rowid'], "mac" : deviceData['dev_MAC'], "name": deviceData['dev_Name'], "type": deviceData['dev_DeviceType']}); + devicesList.push({"rowid" : deviceData['rowid'], "mac" : deviceData['devMac'], "name": deviceData['devName'], "type": deviceData['devType']}); pos=0; } } @@ -1401,7 +1401,7 @@ function performSwitch(direction) // get new mac from the devicesList. Don't change to the commented out line below, the mac query string in the URL isn't updated yet! // mac = params.mac; - mac = devicesList[pos].dev_MAC.toString(); + mac = devicesList[pos].devMac.toString(); setCache("piaDeviceDetailsMac", mac); @@ -1728,7 +1728,7 @@ function setTextValue (textElement, textValue) { if(textElement == "txtNetworkNodeMac") { $('#'+textElement).attr ('data-mynodemac', textValue); - $('#'+textElement).val (getDeviceDataByMac(textValue, "dev_Name")); + $('#'+textElement).val (getDeviceDataByMac(textValue, "devName")); } else { $('#'+textElement).attr ('data-myvalue', textValue); diff --git a/front/deviceDetailsTools.php b/front/deviceDetailsTools.php index 21350b4d..3f132756 100755 --- a/front/deviceDetailsTools.php +++ b/front/deviceDetailsTools.php @@ -77,16 +77,16 @@ - - - - @@ -155,7 +155,7 @@ $( "#tracerouteoutput" ).empty(); $.ajax({ method: "GET", - url: "./php/server/traceroute.php?action=get&ip=" + getDeviceDataByMac(getMac(), 'dev_LastIP') + "", + url: "./php/server/traceroute.php?action=get&ip=" + getDeviceDataByMac(getMac(), 'devLastIP') + "", beforeSend: function() { $('#tracerouteoutput').addClass("ajax_scripts_loading"); }, complete: function() { $('#tracerouteoutput').removeClass("ajax_scripts_loading"); }, success: function(data, textStatus) { @@ -170,7 +170,7 @@ $( "#nslookupoutput" ).empty(); $.ajax({ method: "GET", - url: "./php/server/nslookup.php?action=get&ip=" + getDeviceDataByMac(getMac(), 'dev_LastIP') + "", + url: "./php/server/nslookup.php?action=get&ip=" + getDeviceDataByMac(getMac(), 'devLastIP') + "", beforeSend: function() { $('#nslookupoutput').addClass("ajax_scripts_loading"); }, complete: function() { $('#nslookupoutput').removeClass("ajax_scripts_loading"); }, success: function(data, textStatus) { diff --git a/front/devices.php b/front/devices.php index 7b4a980d..3bb2f18c 100755 --- a/front/devices.php +++ b/front/devices.php @@ -207,11 +207,7 @@ function main () { $('#tableDevices tr').html(html); - // Hide UI elements as per settings - // setTimeout(() => { - hideUIelements("UI_DEV_SECTIONS") - - // }, 10); + hideUIelements("UI_DEV_SECTIONS") // Initialize components with parameters initializeDatatable(getUrlAnchor('my_devices')); @@ -320,31 +316,31 @@ function filterDataByStatus(data, status) { let result = true; - if (!to_display.includes('down') && item.dev_PresentLastScan === 0 && item.dev_AlertDeviceDown !== 0) { + if (!to_display.includes('down') && item.devPresentLastScan === 0 && item.devAlertDown !== 0) { result = false; - } else if (!to_display.includes('new') && item.dev_NewDevice === 1) { + } else if (!to_display.includes('new') && item.devIsNew === 1) { result = false; - } else if (!to_display.includes('archived') && item.dev_Archived === 1) { + } else if (!to_display.includes('archived') && item.devIsArchived === 1) { result = false; - } else if (!to_display.includes('offline') && item.dev_PresentLastScan === 0) { + } else if (!to_display.includes('offline') && item.devPresentLastScan === 0) { result = false; - } else if (!to_display.includes('online') && item.dev_PresentLastScan === 1) { + } else if (!to_display.includes('online') && item.devPresentLastScan === 1) { result = false; } return result; // Include all items for 'my_devices' status case 'connected': - return item.dev_PresentLastScan === 1; + return item.devPresentLastScan === 1; case 'favorites': - return item.dev_Favorite === 1; + return item.devFavorite === 1; case 'new': - return item.dev_NewDevice === 1; + return item.devIsNew === 1; case 'offline': - return item.dev_PresentLastScan === 0; + return item.devPresentLastScan === 0; case 'down': - return (item.dev_PresentLastScan === 0 && item.dev_AlertDeviceDown !== 0); + return (item.devPresentLastScan === 0 && item.devAlertDown !== 0); case 'archived': - return item.dev_Archived === 1; + return item.devIsArchived === 1; default: return true; // Include all items for unknown statuses } @@ -355,23 +351,23 @@ function filterDataByStatus(data, status) { function getDeviceStatus(item) { - if(item.dev_NewDevice === 1) + if(item.devIsNew === 1) { return 'New'; } - else if(item.dev_PresentLastScan === 1) + else if(item.devPresentLastScan === 1) { return 'On-line'; } - else if(item.dev_PresentLastScan === 0 && item.dev_AlertDeviceDown !== 0) + else if(item.devPresentLastScan === 0 && item.devAlertDown !== 0) { return 'Down'; } - else if(item.dev_Archived === 1) + else if(item.devIsArchived === 1) { return 'Archived'; } - else if(item.dev_PresentLastScan === 0) + else if(item.devPresentLastScan === 0) { return 'Off-line'; } @@ -380,6 +376,164 @@ function getDeviceStatus(item) } // ----------------------------------------------------------------------------- +function initializeDatatable_new (status) { + +// Build GraphQL query dynamically based on tableColumnVisible +let columnsToFetch = [ + 'devMac', 'devName', 'devLastConnection', 'devIsArchived', 'devOwner', 'devType', + 'devIcon', 'devFavorite', 'devGroup', 'devFirstConnection', 'devLastIP', 'devNetworkNodeMAC', + 'devLocation', 'devVendor', 'devNetworkNodePort', 'devGUID', 'devSyncHubNodeName', + 'devNetworkSite', 'devSSID', 'devSourcePlugin' +]; + +let selectedColumns = columnsToFetch.filter(col => tableColumnVisible.includes(col)); + +// Construct the GraphQL query +let graphqlQuery = ` + query { + devices { + ${selectedColumns.join('\n')} + } + } +`; + +console.log(graphqlQuery); + + +$.ajax({ + url: 'php/server/query_graphql.php', // PHP endpoint that proxies to the GraphQL server + type: 'POST', + contentType: 'application/json', + data: JSON.stringify({ + query: graphqlQuery, + variables: {} // Optional: pass variables if needed + }), + success: function(response) { + console.log('GraphQL Response:', response); + + // Handle the GraphQL response + let devicesListAll_JSON = response.data.devices; + let devicesListAll_JSON_str = JSON.stringify(devicesListAll_JSON); + setCache('devicesListAll_JSON', devicesListAll_JSON_str); + + // Query data + getDevicesTotals(devicesListAll_JSON); + + // Filter the data based on deviceStatus + var filteredData = filterDataByStatus(devicesListAll_JSON, deviceStatus); + + // Convert JSON data into the desired format + var dataArray = { + data: filteredData.map(function(item) { + var originalRow = selectedColumns.map(function(column) { + return item[column] || ""; + }); + + var newRow = []; + + // Reorder data based on user-defined columns order + for (index = 0; index < tableColumnOrder.length; index++) { + newRow.push(originalRow[tableColumnOrder[index]]); + } + + return newRow; + }) + }; + + // Initialize DataTable + if ($.fn.dataTable.isDataTable('#tableDevices')) { + var table = $('#tableDevices').DataTable(); + table.clear().destroy(); + } + + var table = $('#tableDevices').DataTable({ + 'data': dataArray["data"], + 'paging': true, + 'lengthChange': true, + 'lengthMenu': [[10, 25, 50, 100, 500, 100000], [10, 25, 50, 100, 500, getString('Device_Tablelenght_all')]], + 'searching': true, + 'ordering': true, + 'info': true, + 'autoWidth': false, + 'pageLength': tableRows, + 'order': tableOrder, + 'select': true, + 'columnDefs': [ + { visible: false, targets: tableColumnHide }, + { className: 'text-center', targets: [mapIndx(3), mapIndx(4), mapIndx(9), mapIndx(10), mapIndx(15), mapIndx(18)] }, + { width: '80px', targets: [mapIndx(6), mapIndx(7), mapIndx(15)] }, + { width: '30px', targets: [mapIndx(10), mapIndx(13), mapIndx(18)] }, + { orderData: [mapIndx(12)], targets: mapIndx(8) }, + + // Device Name + { targets: [mapIndx(0)], 'createdCell': function (td, cellData, rowData) { + $(td).html (''+ cellData +''); + }}, + + // Handle other column customizations (similar to the original code) + // Example for IP address formatting: + { targets: [mapIndx(8)], 'createdCell': function (td, cellData) { + if (!emptyArr.includes(cellData)) { + $(td).html (` + ${cellData} + + `); + } else { + $(td).html(''); + } + }}, + + // Other columns (Status, MAC, Date, etc.) can be similarly customized. + ], + 'processing': true, + 'language': { + processing: '
Loading...
', + emptyTable: 'No data', + "lengthMenu": "", + "search": ": ", + "paginate": { + "next": "", + "previous": "" + }, + "info": "", + } + }); + + // Event listeners for row selection and saving parameters (same as the original code) + $('#tableDevices').on('length.dt', function (e, settings, len) { + setCookie ("nax_parTableRows", len, 129600); // save for 90 days + }); + + $('#tableDevices').on('order.dt', function () { + setCookie ("nax_parTableOrder", JSON.stringify(table.order()), 129600); // save for 90 days + }); + + // Add multi-edit button and row selection functionality + $('#multiEditPlc').append( + `` + ); + + $('#tableDevices').on('click', 'tr', function () { + setTimeout(function(){ + var anyRowSelected = $('#tableDevices tr.selected').length > 0; + $('#multiEdit').toggle(anyRowSelected); + }, 200); + }); + + hideSpinner(); // Hide the loading spinner + }, + error: function(xhr, status, error) { + console.error('AJAX Error:', error); + } +}); + + +} + + + function initializeDatatable (status) { if(!status) @@ -435,31 +589,31 @@ function initializeDatatable (status) { var dataArray = { data: filteredData.map(function(item) { var originalRow = [ - item.dev_Name || "", - item.dev_Owner || "", - item.dev_DeviceType || "", - item.dev_Icon || "", - item.dev_Favorite || "", - item.dev_Group || "", + item.devName || "", + item.devOwner || "", + item.devType || "", + item.devIcon || "", + item.devFavorite || "", + item.devGroup || "", // --- - item.dev_FirstConnection || "", - item.dev_LastConnection || "", - item.dev_LastIP || "", - (isRandomMAC(item.dev_MAC)) || "", // Check if randomized MAC + item.devFirstConnection || "", + item.devLastConnection || "", + item.devLastIP || "", + (isRandomMAC(item.devMac)) || "", // Check if randomized MAC getDeviceStatus(item) || "", - item.dev_MAC || "", // hidden - formatIPlong(item.dev_LastIP) || "", // IP orderable + item.devMac || "", // hidden + formatIPlong(item.devLastIP) || "", // IP orderable item.rowid || "", - item.dev_Network_Node_MAC_ADDR || "", - getNumberOfChildren(item.dev_MAC, result.data) || 0, - item.dev_Location || "", - item.dev_Vendor || "", - item.dev_Network_Node_port || 0, - item.dev_GUID || "", - item.dev_SyncHubNodeName || "", - item.dev_NetworkSite || "", - item.dev_SSID || "", - item.dev_SourcePlugin || "" + item.devParentMAC || "", + getNumberOfChildren(item.devMac, result.data) || 0, + item.devLocation || "", + item.devVendor || "", + item.devParentPort || 0, + item.devGUID || "", + item.devSyncHubNode || "", + item.devSite || "", + item.devSSID || "", + item.devSourcePlugin || "" ]; var newRow = []; @@ -612,15 +766,15 @@ function initializeDatatable (status) { devData = getDeviceDataByMac(rowData[mapIndx(11)]) - if (devData.dev_PresentLastScan == 1) + if (devData.devPresentLastScan == 1) { css = "green text-white statusOnline" icon = '' - } else if (devData.dev_PresentLastScan != 1 && devData.dev_AlertDeviceDown == 1) + } else if (devData.devPresentLastScan != 1 && devData.devAlertDown == 1) { css = "red text-white statusDown" icon = '' - } else if(devData.dev_PresentLastScan != 1) + } else if(devData.devPresentLastScan != 1) { css = "gray text-white statusOffline" icon = '' @@ -656,11 +810,6 @@ function initializeDatatable (status) { $('#tableDevices').on( 'order.dt', function () { setCookie ("nax_parTableOrder", JSON.stringify (table.order()), 129600); // save for 90 days - setCache ('devicesList', getDevicesFromTable(table) ); - } ); - - $('#tableDevices').on( 'search.dt', function () { - setCache ('devicesList', getDevicesFromTable(table) ); } ); // add multi-edit button @@ -679,7 +828,6 @@ function initializeDatatable (status) { $('#multiEdit').toggle(anyRowSelected); }, 200); - }); @@ -689,35 +837,6 @@ function initializeDatatable (status) { }; -// ----------------------------------------------------------------------------- -// Gets a JSON list of rowID and mac from the displayed table in the UI -function getDevicesFromTable(table) -{ - rowIDs = table.column(mapIndx(13), { 'search': 'applied' }).data().toArray() // - rowMACs = table.column(mapIndx(11), { 'search': 'applied' }).data().toArray() // - rowNames = table.column(mapIndx(0), { 'search': 'applied' }).data().toArray() // - rowTypes = table.column(mapIndx(2), { 'search': 'applied' }).data().toArray() // - rowIcons = table.column(mapIndx(3), { 'search': 'applied' }).data().toArray() // - rowParentMAC = table.column(mapIndx(14), { 'search': 'applied' }).data().toArray() // - rowStatus = table.column(mapIndx(10), { 'search': 'applied' }).data().toArray() // - - result = [] - - rowIDs.map(function(rowID, index){ - result.push({ - "rowid": rowID, - "mac" : rowMACs[index], - "name" : rowNames[index], - "type" : rowTypes[index], - "icon" : rowIcons[index], - "parentMac" : rowParentMAC[index], - "status" : rowStatus[index] }) - }) - - return JSON.stringify (result) -} - - // ----------------------------------------------------------------------------- function getNumberOfChildren(mac, devices) { @@ -725,7 +844,7 @@ function getNumberOfChildren(mac, devices) $.each(devices, function(index, dev) { - if(dev.dev_Network_Node_MAC_ADDR != null && dev.dev_Network_Node_MAC_ADDR.trim() == mac.trim()) + if(dev.devParentMAC != null && dev.devParentMAC.trim() == mac.trim()) { childrenCount++; } diff --git a/front/js/common.js b/front/js/common.js index 5b30cabc..6f440381 100755 --- a/front/js/common.js +++ b/front/js/common.js @@ -701,9 +701,9 @@ function navigateToDeviceWithIp (ip) { $.each(devices, function(index, obj) { - if(obj.dev_LastIP.trim() == ip.trim()) + if(obj.devLastIP.trim() == ip.trim()) { - mac = obj.dev_MAC; + mac = obj.devMac; window.open(window.location.origin +'/deviceDetails.php?mac=' + mac , "_blank"); } @@ -714,7 +714,7 @@ function navigateToDeviceWithIp (ip) { // ----------------------------------------------------------------------------- function getNameByMacAddress(macAddress) { - return getDeviceDataByMac(macAddress, "dev_Name") + return getDeviceDataByMac(macAddress, "devName") } // ----------------------------------------------------------------------------- @@ -880,7 +880,7 @@ function getDeviceDataByMac(macAddress, dbColumn) { const devices = JSON.parse(devicesCache); for (const device of devices) { - if (device["dev_MAC"].toLowerCase() === macAddress.toLowerCase()) { + if (device["devMac"].toLowerCase() === macAddress.toLowerCase()) { if(dbColumn) { diff --git a/front/js/ui_components.js b/front/js/ui_components.js index 81aa9245..529a963e 100755 --- a/front/js/ui_components.js +++ b/front/js/ui_components.js @@ -23,7 +23,7 @@ function initDeviceSelectors(devicesListAll_JSON) { // Loop through the devices list devicesList.forEach(function(device) { - selectorFieldsHTML += ``; + selectorFieldsHTML += ``; }); selector = `
diff --git a/front/multiEditCore.php b/front/multiEditCore.php index a3d29187..d9e5fe75 100755 --- a/front/multiEditCore.php +++ b/front/multiEditCore.php @@ -77,11 +77,11 @@ settingsData = res["data"]; - excludedColumns = ["NEWDEV_dev_MAC", "NEWDEV_dev_FirstConnection", "NEWDEV_dev_LastConnection", "NEWDEV_dev_LastNotification", "NEWDEV_dev_LastIP", "NEWDEV_dev_StaticIP", "NEWDEV_dev_ScanCycle", "NEWDEV_dev_PresentLastScan" ] + excludedColumns = ["NEWDEV_devMac", "NEWDEV_devFirstConnection", "NEWDEV_devLastConnection", "NEWDEV_devLastNotification", "NEWDEV_devLastIP", "NEWDEV_devStaticIP", "NEWDEV_devScan", "NEWDEV_devPresentLastScan" ] const relevantColumns = settingsData.filter(set => set.Group === "NEWDEV" && - set.Code_Name.includes("_dev_") && + set.Code_Name.includes("_dev") && !excludedColumns.includes(set.Code_Name) && !set.Code_Name.includes("__metadata") ); @@ -143,13 +143,13 @@ console.log(columns[j].Code_Name) // Handle Icons as they need a preview - if(columns[j].Code_Name == 'NEWDEV_dev_Icon') + if(columns[j].Code_Name == 'NEWDEV_devIcon') { input = ` - +