Frontend user events rewrite v0.1

This commit is contained in:
Jokob-sk
2023-10-25 08:11:57 +11:00
parent e434a686c6
commit 0ed24dac0a
9 changed files with 175 additions and 169 deletions

View File

@@ -548,6 +548,15 @@ function isEmpty(value)
return emptyArr.includes(value) return emptyArr.includes(value)
} }
// -----------------------------------------------------------------------------
// Generate a GUID
function getGuid() {
return "10000000-1000-4000-8000-100000000000".replace(/[018]/g, c =>
(c ^ crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> c / 4).toString(16)
);
}
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// Loading Spinner overlay // Loading Spinner overlay
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------

0
front/js/settings_utils.js Normal file → Executable file
View File

View File

@@ -760,7 +760,7 @@ function performLogManage() {
showModalOk ('Result', data ); showModalOk ('Result', data );
} }
}) })
} }
// -------------------------------------------------------- // --------------------------------------------------------
function scrollDown() function scrollDown()

View File

@@ -13,6 +13,7 @@ require dirname(__FILE__).'/../templates/skinUI.php';
$FUNCTION = []; $FUNCTION = [];
$SETTINGS = []; $SETTINGS = [];
$ACTION = "";
// init request params // init request params
if(array_key_exists('function', $_REQUEST) != FALSE) if(array_key_exists('function', $_REQUEST) != FALSE)
@@ -20,21 +21,39 @@ if(array_key_exists('function', $_REQUEST) != FALSE)
$FUNCTION = $_REQUEST['function']; $FUNCTION = $_REQUEST['function'];
} }
if(array_key_exists('settings', $_REQUEST) != FALSE)
{
$SETTINGS = $_REQUEST['settings'];
}
// call functions based on requested params // call functions based on requested params
if ($FUNCTION == 'savesettings') switch ($FUNCTION) {
{ case 'savesettings':
saveSettings(); saveSettings();
} break;
elseif ($FUNCTION == 'cleanLog')
{ case 'cleanLog':
if(array_key_exists('settings', $_REQUEST) != FALSE)
{
$SETTINGS = $_REQUEST['settings'];
}
cleanLog($SETTINGS); cleanLog($SETTINGS);
break;
case 'addToExecutionQueue':
if(array_key_exists('action', $_REQUEST) != FALSE)
{
$ACTION = $_REQUEST['action'];
}
addToExecutionQueue($ACTION);
break;
default:
// Handle any other cases or errors if needed
break;
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// Formatting data functions // Formatting data functions
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
@@ -195,6 +214,25 @@ function displayMessage($message, $logAlert = FALSE, $logConsole = TRUE, $logFil
} }
// Adds an action to perform into the execution_queue.log file
function addToExecutionQueue($action)
{
global $logFolderPath, $timestamp;
$logFile = 'execution_queue.log';
$fullPath = $logFolderPath . $logFile;
// Open the file or skip if it can't be opened
if ($file = fopen($fullPath, 'a')) {
fwrite($file, "[" . $timestamp . "]|" . $action . PHP_EOL);
fclose($file);
displayMessage('Action "'.$action.'" added to the execution queue.', false, true, true, true);
} else {
displayMessage('Log file not found or couldn\'t be created.', false, true, true, true);
}
}
// ---------------------------------------------------------------------------------------- // ----------------------------------------------------------------------------------------
function cleanLog($logFile) function cleanLog($logFile)
{ {

View File

@@ -489,7 +489,7 @@ while ($row = $result -> fetchArray (SQLITE3_ASSOC)) {
data-myparam="${codeName}" data-myparam="${codeName}"
data-myparam-plugin="${group}" data-myparam-plugin="${group}"
data-myevent="${event}" data-myevent="${event}"
onclick="handleEvent(this)" onclick="addToExecutionQueue(this)"
> >
<i title="${getString(event + "_event_tooltip")}" class="fa ${getString(event + "_event_icon")}"> <i title="${getString(event + "_event_tooltip")}" class="fa ${getString(event + "_event_icon")}">
</i> </i>
@@ -856,22 +856,73 @@ while ($row = $result -> fetchArray (SQLITE3_ASSOC)) {
} }
function updateModalState(){ // function updateModalState(){
setTimeout(function(){ // setTimeout(function(){
displayedEvent = $('#'+modalEventStatusId).html() // displayedEvent = $('#'+modalEventStatusId).html()
// loop until finished // // loop until finished
if(displayedEvent.indexOf('finished') == -1) // if the message is different from finished, check again in 2s // if(displayedEvent.indexOf('finished') == -1) // if the message is different from finished, check again in 2s
{ // {
getParam(modalEventStatusId,"Front_Event", true) // getParam(modalEventStatusId,"Front_Event", true)
updateModalState() // updateModalState()
// }
// }, 2000);
// }
// --------------------------------------------------------
// Calls a backend function to add a front-end event (specified by the attributes 'data-myevent' and 'data-myparam-plugin' on the passed element) to an execution queue
function addToExecutionQueue(element)
{
// value has to be in format event|param. e.g. run|ARPSCAN
action = `${getGuid()}|${$(element).attr('data-myevent')}|${$(element).attr('data-myparam-plugin')}`
// addToExecutionQueue(action)
$.ajax({
method: "POST",
url: "php/server/util.php",
data: { function: "addToExecutionQueue", action: action },
success: function(data, textStatus) {
showModalOk ('Result', data );
} }
})
}
// TODO
function updateModalState() {
setTimeout(function() {
// Fetch the content from the log file using an AJAX request
$.ajax({
url: '~/log/execution_queue.log',
type: 'GET',
success: function(data) {
// Update the content of the HTML element (e.g., a div with id 'logContent')
$('#logContent').html(data);
// Check if the displayed content contains 'finished'
if (data.indexOf('finished') === -1) {
// If not finished, continue to update
updateModalState();
}
},
error: function() {
// Handle error, such as the file not being found
$('#logContent').html('Error: Log file not found.');
}
});
}, 2000); }, 2000);
} }
// Call the function to start the periodic updates
updateModalState();
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------

View File

@@ -729,35 +729,66 @@ class plugin_object_class:
#=============================================================================== #===============================================================================
#------------------------------------------------------------------------------- #-------------------------------------------------------------------------------
# def check_and_run_user_event(db, pluginsState):
# sql = db.sql # TO-DO
# sql.execute(""" select * from Parameters where par_ID = "Front_Event" """)
# rows = sql.fetchall()
# event, param = ['','']
# if len(rows) > 0 and rows[0]['par_Value'] != 'finished':
# keyValue = rows[0]['par_Value'].split('|')
# if len(keyValue) == 2:
# event = keyValue[0]
# param = keyValue[1]
# else:
# return pluginsState
# if event == 'test':
# pluginsState = handle_test(param, db, pluginsState)
# if event == 'run':
# pluginsState = handle_run(param, db, pluginsState)
# # clear event execution flag
# sql.execute ("UPDATE Parameters SET par_Value='finished' WHERE par_ID='Front_Event'")
# # commit to DB
# db.commitDB()
# return pluginsState
def check_and_run_user_event(db, pluginsState): def check_and_run_user_event(db, pluginsState):
# Check if the log file exists
logFile = os.path.join(logPath, "execution_queue.log")
sql = db.sql # TO-DO if not os.path.exists(logFile):
sql.execute(""" select * from Parameters where par_ID = "Front_Event" """)
rows = sql.fetchall()
event, param = ['','']
if len(rows) > 0 and rows[0]['par_Value'] != 'finished':
keyValue = rows[0]['par_Value'].split('|')
if len(keyValue) == 2:
event = keyValue[0]
param = keyValue[1]
else:
return pluginsState return pluginsState
with open(logFile, "r") as file:
lines = file.readlines()
for line in lines:
# Split the line by '|', and take the third and fourth columns (indices 2 and 3)
columns = line.strip().split('|')[2:4]
if len(columns) != 2:
continue
event, param = columns
if event == 'test': if event == 'test':
pluginsState = handle_test(param, db, pluginsState) pluginsState = handle_test(param, db, pluginsState)
if event == 'run': if event == 'run':
pluginsState = handle_run(param, db, pluginsState) pluginsState = handle_run(param, db, pluginsState)
# clear event execution flag # Clear the log file
sql.execute ("UPDATE Parameters SET par_Value='finished' WHERE par_ID='Front_Event'") open(logFile, "w").close()
# commit to DB
db.commitDB()
return pluginsState return pluginsState
#------------------------------------------------------------------------------- #-------------------------------------------------------------------------------
def handle_run(runType, db, pluginsState): def handle_run(runType, db, pluginsState):

View File

@@ -1,8 +0,0 @@
""" Publishers for Pi.Alert """
"""
each publisher exposes:
- check_config () returning True / False
- send (message) returning True / Fasle
"""

View File

@@ -1,111 +0,0 @@
import json
import subprocess
import hashlib
import hmac
import conf
from const import logPath
from helper import noti_obj, write_file
from logger import logResult, mylog
#-------------------------------------------------------------------------------
def check_config():
if conf.WEBHOOK_URL == '':
mylog('none', ['[Check Config] Error: Webhook service not set up correctly. Check your pialert.conf WEBHOOK_* variables.'])
return False
else:
return True
#-------------------------------------------------------------------------------
def send (msg: noti_obj):
# limit = 1024 * 1024 # 1MB limit (1024 bytes * 1024 bytes = 1MB)
limit = conf.WEBHOOK_SIZE
# use data type based on specified payload type
if conf.WEBHOOK_PAYLOAD == 'json':
# In this code, the truncate_json function is used to recursively traverse the JSON object
# and remove nodes that exceed the size limit. It checks the size of each node's JSON representation
# using json.dumps and includes only the nodes that are within the limit.
json_data = msg.json
json_str = json.dumps(json_data)
if len(json_str) <= limit:
payloadData = json_data
else:
def truncate_json(obj):
if isinstance(obj, dict):
return {
key: truncate_json(value)
for key, value in obj.items()
if len(json.dumps(value)) <= limit
}
elif isinstance(obj, list):
return [
truncate_json(item)
for item in obj
if len(json.dumps(item)) <= limit
]
else:
return obj
payloadData = truncate_json(json_data)
if conf.WEBHOOK_PAYLOAD == 'html':
if len(msg.html) > limit:
payloadData = msg.html[:limit] + " <h1>(text was truncated)</h1>"
else:
payloadData = msg.html
if conf.WEBHOOK_PAYLOAD == 'text':
if len(msg.text) > limit:
payloadData = msg.text[:limit] + " (text was truncated)"
else:
payloadData = msg.text
# Define slack-compatible payload
_json_payload = { "text": payloadData } if conf.WEBHOOK_PAYLOAD == 'text' else {
"username": "Pi.Alert",
"text": "There are new notifications",
"attachments": [{
"title": "Pi.Alert Notifications",
"title_link": conf.REPORT_DASHBOARD_URL,
"text": payloadData
}]
}
# DEBUG - Write the json payload into a log file for debugging
write_file (logPath + '/webhook_payload.json', json.dumps(_json_payload))
# Using the Slack-Compatible Webhook endpoint for Discord so that the same payload can be used for both
# Consider: curl has the ability to load in data to POST from a file + piping
if(conf.WEBHOOK_URL.startswith('https://discord.com/api/webhooks/') and not conf.WEBHOOK_URL.endswith("/slack")):
_WEBHOOK_URL = f"{conf.WEBHOOK_URL}/slack"
curlParams = ["curl","-i","-H", "Content-Type:application/json" ,"-d", json.dumps(_json_payload), _WEBHOOK_URL]
else:
_WEBHOOK_URL = conf.WEBHOOK_URL
curlParams = ["curl","-i","-X", conf.WEBHOOK_REQUEST_METHOD , "-H", "Content-Type:application/json", "-d", json.dumps(_json_payload), _WEBHOOK_URL]
# Add HMAC signature if configured
if(conf.WEBHOOK_SECRET != ''):
h = hmac.new(conf.WEBHOOK_SECRET.encode("UTF-8"), json.dumps(_json_payload, separators=(',', ':')).encode(), hashlib.sha256).hexdigest()
curlParams.insert(4,"-H")
curlParams.insert(5,f"X-Webhook-Signature: sha256={h}")
try:
# Execute CURL call
mylog('debug', ['[send_webhook] curlParams: ', curlParams])
result = subprocess.run(curlParams, capture_output=True, text=True)
stdout = result.stdout
stderr = result.stderr
# Write stdout and stderr into .log files for debugging if needed
mylog('debug', ['[send_webhook] stdout: ', stdout])
mylog('debug', ['[send_webhook] stderr: ', stderr])
# logResult(stdout, stderr) # TO-DO should be changed to mylog
except subprocess.CalledProcessError as e:
# An error occurred, handle it
mylog('none', ['[send_webhook] Error: ', e.output])

View File

@@ -24,10 +24,6 @@ from const import pialertPath, logPath, apiPath
from helper import noti_obj, generate_mac_links, removeDuplicateNewLines, timeNowTZ, hide_email, updateState, get_file_content, write_file from helper import noti_obj, generate_mac_links, removeDuplicateNewLines, timeNowTZ, hide_email, updateState, get_file_content, write_file
from logger import logResult, mylog, print_log from logger import logResult, mylog, print_log
from publishers.webhook import (check_config as webhook_check_config,
send as send_webhook)
#=============================================================================== #===============================================================================
# REPORTING # REPORTING
@@ -178,7 +174,7 @@ def get_notifications (db):
notiStruc = construct_notifications(db, sqlQuery, "New devices") notiStruc = construct_notifications(db, sqlQuery, "New devices")
# collect "new_devices" for the webhook json # collect "new_devices" for the json
json_new_devices = notiStruc.json["data"] json_new_devices = notiStruc.json["data"]
mail_text = mail_text.replace ('<NEW_DEVICES_TABLE>', notiStruc.text + '\n') mail_text = mail_text.replace ('<NEW_DEVICES_TABLE>', notiStruc.text + '\n')
@@ -194,7 +190,7 @@ def get_notifications (db):
notiStruc = construct_notifications(db, sqlQuery, "Down devices") notiStruc = construct_notifications(db, sqlQuery, "Down devices")
# collect "down_devices" for the webhook json # collect "down_devices" for the json
json_down_devices = notiStruc.json["data"] json_down_devices = notiStruc.json["data"]
mail_text = mail_text.replace ('<DOWN_DEVICES_TABLE>', notiStruc.text + '\n') mail_text = mail_text.replace ('<DOWN_DEVICES_TABLE>', notiStruc.text + '\n')
@@ -211,7 +207,7 @@ def get_notifications (db):
notiStruc = construct_notifications(db, sqlQuery, "Events") notiStruc = construct_notifications(db, sqlQuery, "Events")
# collect "events" for the webhook json # collect "events" for the json
json_events = notiStruc.json["data"] json_events = notiStruc.json["data"]
mail_text = mail_text.replace ('<EVENTS_TABLE>', notiStruc.text + '\n') mail_text = mail_text.replace ('<EVENTS_TABLE>', notiStruc.text + '\n')
@@ -224,7 +220,7 @@ def get_notifications (db):
notiStruc = construct_notifications(db, sqlQuery, "Plugins") notiStruc = construct_notifications(db, sqlQuery, "Plugins")
# collect "plugins" for the webhook json # collect "plugins" for the json
json_plugins = notiStruc.json["data"] json_plugins = notiStruc.json["data"]
mail_text = mail_text.replace ('<PLUGINS_TABLE>', notiStruc.text + '\n') mail_text = mail_text.replace ('<PLUGINS_TABLE>', notiStruc.text + '\n')