mirror of
https://github.com/jokob-sk/NetAlertX.git
synced 2025-12-07 09:36:05 -08:00
GraphQl 0.11.17.1 - better api_token initialization + device tiles
This commit is contained in:
@@ -171,6 +171,9 @@ function main () {
|
|||||||
|
|
||||||
showSpinner();
|
showSpinner();
|
||||||
|
|
||||||
|
// render tiles
|
||||||
|
getDevicesTotals();
|
||||||
|
|
||||||
//initialize the table headers in the correct order
|
//initialize the table headers in the correct order
|
||||||
var availableColumns = getSettingOptions("UI_device_columns").split(",");
|
var availableColumns = getSettingOptions("UI_device_columns").split(",");
|
||||||
headersDefaultOrder = availableColumns.map(val => getString(val));
|
headersDefaultOrder = availableColumns.map(val => getString(val));
|
||||||
@@ -223,60 +226,77 @@ function mapIndx(oldIndex)
|
|||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
// Query total numbers of Devices by status
|
// Query total numbers of Devices by status
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
function getDevicesTotals(devicesData) {
|
function getDevicesTotals() {
|
||||||
|
// Check cache first
|
||||||
|
let resultJSON = getCache("getDevicesTotals");
|
||||||
|
|
||||||
let resultJSON = "";
|
if (resultJSON !== "") {
|
||||||
|
resultJSON = JSON.parse(resultJSON);
|
||||||
if (getCache("getDevicesTotals") !== "") {
|
processDeviceTotals(resultJSON);
|
||||||
resultJSON = getCache("getDevicesTotals");
|
|
||||||
} else {
|
} else {
|
||||||
|
// Fetch data via AJAX
|
||||||
|
$.ajax({
|
||||||
|
url: "/api/table_devices_tiles.json",
|
||||||
|
type: "GET",
|
||||||
|
dataType: "json",
|
||||||
|
success: function(response) {
|
||||||
|
if (response && response.data) {
|
||||||
|
resultJSON = response.data[0]; // Assuming the structure {"data": [ ... ]}
|
||||||
|
|
||||||
|
// Save the result to cache
|
||||||
|
setCache("getDevicesTotals", JSON.stringify(resultJSON));
|
||||||
|
|
||||||
// Define filter conditions and corresponding objects
|
// Process the fetched data
|
||||||
const filters = [
|
processDeviceTotals(resultJSON);
|
||||||
{ status: 'my_devices', color: 'bg-aqua', label: getString('Device_Shortcut_AllDevices'), icon: 'fa-laptop' },
|
} else {
|
||||||
{ status: 'all', color: 'bg-aqua', label: getString('Gen_All_Devices'), icon: 'fa-laptop' },
|
console.error("Invalid response format from API");
|
||||||
{ status: 'connected', color: 'bg-green', label: getString('Device_Shortcut_Connected'), icon: 'fa-plug' },
|
}
|
||||||
{ status: 'favorites', color: 'bg-yellow', label: getString('Device_Shortcut_Favorites'), icon: 'fa-star' },
|
},
|
||||||
{ status: 'new', color: 'bg-yellow', label: getString('Device_Shortcut_NewDevices'), icon: 'fa-plus' },
|
error: function(xhr, status, error) {
|
||||||
{ status: 'down', color: 'bg-red', label: getString('Device_Shortcut_DownOnly'), icon: 'fa-warning' },
|
console.error("Failed to fetch devices data:", error);
|
||||||
{ status: 'archived', color: 'bg-gray', label: getString('Device_Shortcut_Archived'), icon: 'fa-eye-slash' },
|
|
||||||
{ status: 'offline', color: 'bg-gray', label: getString('Gen_Offline'), icon: 'fa-xmark' }
|
|
||||||
];
|
|
||||||
|
|
||||||
// Initialize an empty array to store the final objects
|
|
||||||
let dataArray = [];
|
|
||||||
|
|
||||||
// Loop through each filter condition
|
|
||||||
filters.forEach(filter => {
|
|
||||||
// Calculate count dynamically based on filter condition
|
|
||||||
let count = filterDataByStatus(devicesData, filter.status).length;
|
|
||||||
|
|
||||||
// Check any condition to skip adding the object to dataArray
|
|
||||||
if (
|
|
||||||
(['', 'False'].includes(getSetting('UI_hide_empty')) || (getSetting('UI_hide_empty') == "True" && count > 0)) &&
|
|
||||||
(getSetting('UI_shown_cards') == "" || getSetting('UI_shown_cards').includes(filter.status))
|
|
||||||
) {
|
|
||||||
dataArray.push({
|
|
||||||
onclickEvent: `initializeDatatable('${filter.status}')`,
|
|
||||||
color: filter.color,
|
|
||||||
title: count,
|
|
||||||
label: filter.label,
|
|
||||||
icon: filter.icon
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// render info boxes/tile cards
|
function processDeviceTotals(devicesData) {
|
||||||
renderInfoboxes(
|
// Define filter conditions and corresponding objects
|
||||||
dataArray
|
const filters = [
|
||||||
)
|
{ status: 'my_devices', color: 'bg-aqua', label: getString('Device_Shortcut_AllDevices'), icon: 'fa-laptop' },
|
||||||
|
{ status: 'all', color: 'bg-aqua', label: getString('Gen_All_Devices'), icon: 'fa-laptop' },
|
||||||
|
{ status: 'connected', color: 'bg-green', label: getString('Device_Shortcut_Connected'), icon: 'fa-plug' },
|
||||||
|
{ status: 'favorites', color: 'bg-yellow', label: getString('Device_Shortcut_Favorites'), icon: 'fa-star' },
|
||||||
|
{ status: 'new', color: 'bg-yellow', label: getString('Device_Shortcut_NewDevices'), icon: 'fa-plus' },
|
||||||
|
{ status: 'down', color: 'bg-red', label: getString('Device_Shortcut_DownOnly'), icon: 'fa-warning' },
|
||||||
|
{ status: 'archived', color: 'bg-gray', label: getString('Device_Shortcut_Archived'), icon: 'fa-eye-slash' },
|
||||||
|
{ status: 'offline', color: 'bg-gray', label: getString('Gen_Offline'), icon: 'fa-xmark' }
|
||||||
|
];
|
||||||
|
|
||||||
// save to cache
|
// Initialize an empty array to store the final objects
|
||||||
setCache("getDevicesTotals", resultJSON);
|
let dataArray = [];
|
||||||
}
|
|
||||||
|
|
||||||
// console.log(resultJSON);
|
// Loop through each filter condition
|
||||||
|
filters.forEach(filter => {
|
||||||
|
// Get count directly from API response data
|
||||||
|
let count = devicesData[filter.status] || 0;
|
||||||
|
|
||||||
|
// Check any condition to skip adding the object to dataArray
|
||||||
|
if (
|
||||||
|
(['', 'False'].includes(getSetting('UI_hide_empty')) || (getSetting('UI_hide_empty') == "True" && count > 0)) &&
|
||||||
|
(getSetting('UI_shown_cards') == "" || getSetting('UI_shown_cards').includes(filter.status))
|
||||||
|
) {
|
||||||
|
dataArray.push({
|
||||||
|
onclickEvent: `initializeDatatable('${filter.status}')`,
|
||||||
|
color: filter.color,
|
||||||
|
title: count,
|
||||||
|
label: filter.label,
|
||||||
|
icon: filter.icon
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Render info boxes/tile cards
|
||||||
|
renderInfoboxes(dataArray);
|
||||||
}
|
}
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
@@ -340,6 +360,7 @@ function filterDataByStatus(data, status) {
|
|||||||
|
|
||||||
// Map column index to column name for GraphQL query
|
// Map column index to column name for GraphQL query
|
||||||
function mapColumnIndexToFieldName(index, tableColumnVisible) {
|
function mapColumnIndexToFieldName(index, tableColumnVisible) {
|
||||||
|
// the order is important, don't change it!
|
||||||
const columnNames = [
|
const columnNames = [
|
||||||
"devName",
|
"devName",
|
||||||
"devOwner",
|
"devOwner",
|
||||||
@@ -367,6 +388,8 @@ function mapColumnIndexToFieldName(index, tableColumnVisible) {
|
|||||||
"devSourcePlugin"
|
"devSourcePlugin"
|
||||||
];
|
];
|
||||||
|
|
||||||
|
console.log("OrderBy: " + columnNames[tableColumnOrder[index]]);
|
||||||
|
|
||||||
return columnNames[tableColumnOrder[index]] || null;
|
return columnNames[tableColumnOrder[index]] || null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -522,7 +545,7 @@ function initializeDatatable (status) {
|
|||||||
device.devParentChildrenCount || 0,
|
device.devParentChildrenCount || 0,
|
||||||
device.devLocation || "",
|
device.devLocation || "",
|
||||||
device.devVendor || "",
|
device.devVendor || "",
|
||||||
device.devParentPort || 0,
|
device.devParentPort || "",
|
||||||
device.devGUID || "",
|
device.devGUID || "",
|
||||||
device.devSyncHubNode || "",
|
device.devSyncHubNode || "",
|
||||||
device.devSite || "",
|
device.devSite || "",
|
||||||
@@ -708,6 +731,7 @@ function initializeDatatable (status) {
|
|||||||
},
|
},
|
||||||
initComplete: function (settings, devices) {
|
initComplete: function (settings, devices) {
|
||||||
// Handle any additional interactions or event listeners as required
|
// Handle any additional interactions or event listeners as required
|
||||||
|
|
||||||
// Save cookie Rows displayed, and Parameters rows & order
|
// Save cookie Rows displayed, and Parameters rows & order
|
||||||
$('#tableDevices').on( 'length.dt', function ( e, settings, len ) {
|
$('#tableDevices').on( 'length.dt', function ( e, settings, len ) {
|
||||||
setCookie ("nax_parTableRows", len, 129600); // save for 90 days
|
setCookie ("nax_parTableRows", len, 129600); // save for 90 days
|
||||||
|
|||||||
@@ -185,12 +185,12 @@ def main ():
|
|||||||
db.commitDB()
|
db.commitDB()
|
||||||
|
|
||||||
# Footer
|
# Footer
|
||||||
updateState("Process: Wait")
|
|
||||||
mylog('verbose', ['[MAIN] Process: Wait'])
|
mylog('verbose', ['[MAIN] Process: Wait'])
|
||||||
else:
|
else:
|
||||||
# do something
|
# do something
|
||||||
# mylog('verbose', ['[MAIN] Waiting to start next loop'])
|
# mylog('verbose', ['[MAIN] Waiting to start next loop'])
|
||||||
dummyVariable = 1
|
updateState("Process: Wait")
|
||||||
|
|
||||||
|
|
||||||
#loop
|
#loop
|
||||||
|
|||||||
@@ -3,9 +3,9 @@ import json
|
|||||||
|
|
||||||
# Register NetAlertX modules
|
# Register NetAlertX modules
|
||||||
import conf
|
import conf
|
||||||
from const import (apiPath, sql_appevents, sql_devices_all, sql_events_pending_alert, sql_settings, sql_plugins_events, sql_plugins_history, sql_plugins_objects,sql_language_strings, sql_notifications_all, sql_online_history)
|
from const import (apiPath, sql_appevents, sql_devices_all, sql_events_pending_alert, sql_settings, sql_plugins_events, sql_plugins_history, sql_plugins_objects,sql_language_strings, sql_notifications_all, sql_online_history, sql_devices_tiles)
|
||||||
from logger import mylog
|
from logger import mylog
|
||||||
from helper import write_file, get_setting_value
|
from helper import write_file, get_setting_value, updateState
|
||||||
|
|
||||||
# Import the start_server function
|
# Import the start_server function
|
||||||
from graphql_server.graphql_server_start import start_server
|
from graphql_server.graphql_server_start import start_server
|
||||||
@@ -17,8 +17,10 @@ apiEndpoints = []
|
|||||||
#===============================================================================
|
#===============================================================================
|
||||||
def update_api(db, all_plugins, isNotification = False, updateOnlyDataSources = []):
|
def update_api(db, all_plugins, isNotification = False, updateOnlyDataSources = []):
|
||||||
mylog('debug', ['[API] Update API starting'])
|
mylog('debug', ['[API] Update API starting'])
|
||||||
# return
|
|
||||||
|
|
||||||
|
# update app_state.json and retrieve app_state to chjeck if GraphQL server is running
|
||||||
|
app_state = updateState("Update: API", None, None, None, None)
|
||||||
|
|
||||||
folder = apiPath
|
folder = apiPath
|
||||||
|
|
||||||
# Save plugins
|
# Save plugins
|
||||||
@@ -36,6 +38,7 @@ def update_api(db, all_plugins, isNotification = False, updateOnlyDataSources =
|
|||||||
["plugins_language_strings", sql_language_strings],
|
["plugins_language_strings", sql_language_strings],
|
||||||
["notifications", sql_notifications_all],
|
["notifications", sql_notifications_all],
|
||||||
["online_history", sql_online_history],
|
["online_history", sql_online_history],
|
||||||
|
["devices_tiles", sql_devices_tiles],
|
||||||
["custom_endpoint", conf.API_CUSTOM_SQL],
|
["custom_endpoint", conf.API_CUSTOM_SQL],
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -50,15 +53,17 @@ def update_api(db, all_plugins, isNotification = False, updateOnlyDataSources =
|
|||||||
graphql_port_value = get_setting_value("GRAPHQL_PORT")
|
graphql_port_value = get_setting_value("GRAPHQL_PORT")
|
||||||
api_token_value = get_setting_value("API_TOKEN")
|
api_token_value = get_setting_value("API_TOKEN")
|
||||||
|
|
||||||
# Validate and start the server if settings are available
|
# start GraphQL server if not yet running
|
||||||
if graphql_port_value is not None and api_token_value is not None:
|
if app_state.graphQLServerStarted == 0:
|
||||||
try:
|
# Validate if settings are available
|
||||||
graphql_port_value = int(graphql_port_value) # Ensure port is an integer
|
if graphql_port_value is not None and len(api_token_value) > 1:
|
||||||
start_server(graphql_port=graphql_port_value) # Start the server
|
try:
|
||||||
except ValueError:
|
graphql_port_value = int(graphql_port_value) # Ensure port is an integer
|
||||||
mylog('none', [f"[API] Invalid GRAPHQL_PORT value, must be an integer: {graphql_port_value}"])
|
start_server(graphql_port_value, app_state) # Start the server
|
||||||
else:
|
except ValueError:
|
||||||
mylog('none', [f"[API] GRAPHQL_PORT or API_TOKEN is not set, will try later."])
|
mylog('none', [f"[API] Invalid GRAPHQL_PORT value, must be an integer: {graphql_port_value}"])
|
||||||
|
else:
|
||||||
|
mylog('none', [f"[API] GRAPHQL_PORT or API_TOKEN is not set, will try later."])
|
||||||
|
|
||||||
|
|
||||||
#-------------------------------------------------------------------------------
|
#-------------------------------------------------------------------------------
|
||||||
|
|||||||
@@ -42,6 +42,38 @@ sql_devices_all = """
|
|||||||
FROM Devices
|
FROM Devices
|
||||||
"""
|
"""
|
||||||
sql_appevents = """select * from AppEvents"""
|
sql_appevents = """select * from AppEvents"""
|
||||||
|
# The below query calculates counts of devices in various categories:
|
||||||
|
# (connected/online, offline, down, new, archived),
|
||||||
|
# as well as a combined count for devices that match any status listed in the UI_MY_DEVICES setting
|
||||||
|
sql_devices_tiles = """
|
||||||
|
WITH Statuses AS (
|
||||||
|
SELECT Value
|
||||||
|
FROM Settings
|
||||||
|
WHERE Code_Name = 'UI_MY_DEVICES'
|
||||||
|
),
|
||||||
|
MyDevicesFilter AS (
|
||||||
|
SELECT
|
||||||
|
-- Build a dynamic filter for devices matching any status in UI_MY_DEVICES
|
||||||
|
devPresentLastScan, devAlertDown, devIsNew, devIsArchived
|
||||||
|
FROM Devices
|
||||||
|
WHERE
|
||||||
|
(instr((SELECT Value FROM Statuses), 'online') > 0 AND devPresentLastScan = 1) OR
|
||||||
|
(instr((SELECT Value FROM Statuses), 'offline') > 0 AND devPresentLastScan = 0) OR
|
||||||
|
(instr((SELECT Value FROM Statuses), 'down') > 0 AND devPresentLastScan = 0 AND devAlertDown = 1) OR
|
||||||
|
(instr((SELECT Value FROM Statuses), 'new') > 0 AND devIsNew = 1) OR
|
||||||
|
(instr((SELECT Value FROM Statuses), 'archived') > 0 AND devIsArchived = 1)
|
||||||
|
)
|
||||||
|
SELECT
|
||||||
|
-- Counts for each individual status
|
||||||
|
(SELECT COUNT(*) FROM Devices WHERE devPresentLastScan = 1) AS connected,
|
||||||
|
(SELECT COUNT(*) FROM Devices WHERE devPresentLastScan = 0) AS offline,
|
||||||
|
(SELECT COUNT(*) FROM Devices WHERE devPresentLastScan = 0 AND devAlertDown = 1) AS down,
|
||||||
|
(SELECT COUNT(*) FROM Devices WHERE devIsNew = 1) AS new,
|
||||||
|
(SELECT COUNT(*) FROM Devices WHERE devIsArchived = 1) AS archived,
|
||||||
|
-- My Devices count
|
||||||
|
(SELECT COUNT(*) FROM MyDevicesFilter) AS my_devices
|
||||||
|
FROM Statuses;
|
||||||
|
"""
|
||||||
sql_devices_stats = """SELECT Online_Devices as online, Down_Devices as down, All_Devices as 'all', Archived_Devices as archived,
|
sql_devices_stats = """SELECT Online_Devices as online, Down_Devices as down, All_Devices as 'all', Archived_Devices as archived,
|
||||||
(select count(*) from Devices a where devIsNew = 1 ) as new,
|
(select count(*) from Devices a where devIsNew = 1 ) as new,
|
||||||
(select count(*) from Devices a where devName = '(unknown)' or devName = '(name not found)' ) as unknown
|
(select count(*) from Devices a where devName = '(unknown)' or devName = '(name not found)' ) as unknown
|
||||||
|
|||||||
@@ -62,6 +62,7 @@ class Device(ObjectType):
|
|||||||
devIsRandomMac = Int()
|
devIsRandomMac = Int()
|
||||||
devParentChildrenCount = Int()
|
devParentChildrenCount = Int()
|
||||||
devIpLong = Int()
|
devIpLong = Int()
|
||||||
|
devFilterStatus = String()
|
||||||
|
|
||||||
|
|
||||||
class DeviceResult(ObjectType):
|
class DeviceResult(ObjectType):
|
||||||
@@ -92,6 +93,13 @@ class Query(ObjectType):
|
|||||||
|
|
||||||
mylog('none', f'[graphql_schema] devices_data: {devices_data}')
|
mylog('none', f'[graphql_schema] devices_data: {devices_data}')
|
||||||
|
|
||||||
|
# Define static list of searchable fields
|
||||||
|
searchable_fields = [
|
||||||
|
"devName", "devMac", "devOwner", "devType", "devVendor",
|
||||||
|
"devGroup", "devComments", "devLocation", "devStatus",
|
||||||
|
"devSSID", "devSite", "devSourcePlugin", "devSyncHubNode"
|
||||||
|
]
|
||||||
|
|
||||||
# Apply sorting if options are provided
|
# Apply sorting if options are provided
|
||||||
if options:
|
if options:
|
||||||
if options.sort:
|
if options.sort:
|
||||||
@@ -104,9 +112,14 @@ class Query(ObjectType):
|
|||||||
|
|
||||||
# Filter data if a search term is provided
|
# Filter data if a search term is provided
|
||||||
if options.search:
|
if options.search:
|
||||||
|
search_term = options.search.lower()
|
||||||
|
|
||||||
devices_data = [
|
devices_data = [
|
||||||
device for device in devices_data
|
device for device in devices_data
|
||||||
if options.search.lower() in device.get("devName", "").lower()
|
if any(
|
||||||
|
search_term in str(device.get(field, "")).lower()
|
||||||
|
for field in searchable_fields # Search only predefined fields
|
||||||
|
)
|
||||||
]
|
]
|
||||||
|
|
||||||
# Then apply pagination
|
# Then apply pagination
|
||||||
|
|||||||
@@ -38,11 +38,11 @@ def graphql_endpoint():
|
|||||||
# Return the result as JSON
|
# Return the result as JSON
|
||||||
return jsonify(result.data)
|
return jsonify(result.data)
|
||||||
|
|
||||||
def start_server(graphql_port):
|
def start_server(graphql_port, app_state):
|
||||||
"""Start the GraphQL server in a background thread."""
|
"""Start the GraphQL server in a background thread."""
|
||||||
state = updateState("GraphQL: Starting", None, None, None, None)
|
|
||||||
|
|
||||||
if state.graphQLServerStarted == 0:
|
if app_state.graphQLServerStarted == 0:
|
||||||
|
|
||||||
mylog('verbose', [f'[graphql_server] Starting on port: {graphql_port}'])
|
mylog('verbose', [f'[graphql_server] Starting on port: {graphql_port}'])
|
||||||
|
|
||||||
# Start Flask app in a separate thread
|
# Start Flask app in a separate thread
|
||||||
@@ -57,4 +57,4 @@ def start_server(graphql_port):
|
|||||||
thread.start()
|
thread.start()
|
||||||
|
|
||||||
# Update the state to indicate the server has started
|
# Update the state to indicate the server has started
|
||||||
state = updateState("Process: Wait", None, None, None, 1)
|
app_state = updateState("Process: Wait", None, None, None, 1)
|
||||||
Reference in New Issue
Block a user