mirror of
https://github.com/jokob-sk/NetAlertX.git
synced 2025-12-07 09:36:05 -08:00
Plugins 0.4 - ForeignKey support added
This commit is contained in:
@@ -40,8 +40,8 @@
|
||||
$expireMinutes = $_REQUEST['expireMinutes'];
|
||||
}
|
||||
|
||||
if (isset ($_REQUEST['key'])) {
|
||||
$key = $_REQUEST['key'];
|
||||
if (isset ($_REQUEST['columnName'])) {
|
||||
$columnName = $_REQUEST['columnName'];
|
||||
}
|
||||
|
||||
if (isset ($_REQUEST['id'])) {
|
||||
@@ -66,8 +66,8 @@
|
||||
switch ($action) {
|
||||
case 'create': create($skipCache, $defaultValue, $expireMinutes, $dbtable, $columns, $values ); break;
|
||||
// case 'read' : read($skipCache, $defaultValue, $expireMinutes, $dbtable, $columns, $values); break;
|
||||
case 'update': update($key, $id, $skipCache, $defaultValue, $expireMinutes, $dbtable, $columns, $values); break;
|
||||
case 'delete': delete($key, $id, $dbtable); break;
|
||||
case 'update': update($columnName, $id, $skipCache, $defaultValue, $expireMinutes, $dbtable, $columns, $values); break;
|
||||
case 'delete': delete($columnName, $id, $dbtable); break;
|
||||
default: logServerConsole ('Action: '. $action); break;
|
||||
}
|
||||
}
|
||||
@@ -76,7 +76,7 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// update
|
||||
//------------------------------------------------------------------------------
|
||||
function update($key, $id, $skipCache, $defaultValue, $expireMinutes, $dbtable, $columns, $values) {
|
||||
function update($columnName, $id, $skipCache, $defaultValue, $expireMinutes, $dbtable, $columns, $values) {
|
||||
|
||||
global $db;
|
||||
|
||||
@@ -111,7 +111,7 @@ function update($key, $id, $skipCache, $defaultValue, $expireMinutes, $dbtable,
|
||||
|
||||
// Update value
|
||||
$sql = 'UPDATE '.$dbtable.' SET '. $columnValues .'
|
||||
WHERE "'. $key .'"="'. $id.'"';
|
||||
WHERE "'. $columnName .'"="'. $id.'"';
|
||||
$result = $db->query($sql);
|
||||
|
||||
if (! $result == TRUE) {
|
||||
@@ -155,26 +155,37 @@ function create($skipCache, $defaultValue, $expireMinutes, $dbtable, $columns, $
|
||||
//------------------------------------------------------------------------------
|
||||
// delete
|
||||
//------------------------------------------------------------------------------
|
||||
function delete($key, $id, $dbtable)
|
||||
function delete($columnName, $id, $dbtable)
|
||||
{
|
||||
global $db;
|
||||
|
||||
// handle one or multiple ids
|
||||
if(strpos($id, ',') !== false)
|
||||
{
|
||||
$idsArr = explode(",", $id);
|
||||
}else
|
||||
{
|
||||
$idsArr = array($id);
|
||||
}
|
||||
// handle one or multiple ids
|
||||
if(strpos($id, ',') !== false)
|
||||
{
|
||||
$idsArr = explode(",", $id);
|
||||
}else
|
||||
{
|
||||
$idsArr = array($id);
|
||||
}
|
||||
|
||||
$idsStr = "";
|
||||
|
||||
foreach ($idsArr as $item)
|
||||
{
|
||||
$idsStr = $idsStr . '"' .$item.'"';
|
||||
}
|
||||
|
||||
// Insert new value
|
||||
$sql = 'DELETE FROM '.$dbtable.' WHERE "'.$key.'" IN ('. $id .')';
|
||||
$sql = 'DELETE FROM '.$dbtable.' WHERE "'.$columnName.'" IN ('. $idsStr .')';
|
||||
$result = $db->query($sql);
|
||||
|
||||
if (! $result == TRUE) {
|
||||
echo "Error deleting entry\n\n$sql \n\n". $db->lastErrorMsg();
|
||||
return;
|
||||
} else
|
||||
{
|
||||
echo lang('Gen_DataUpdatedUITakesTime');
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ $lang['en_us'] = array(
|
||||
// General
|
||||
//////////////////////////////////////////////////////////////////
|
||||
'Gen_Delete' => 'Delete',
|
||||
'Gen_DeleteAll' => 'Delete all',
|
||||
'Gen_Cancel' => 'Cancel',
|
||||
'Gen_Okay' => 'Ok',
|
||||
'Gen_Save' => 'Save',
|
||||
@@ -669,6 +670,8 @@ The arp-scan time itself depends on the number of IP addresses to check so set t
|
||||
'NMAP_ARGS_description' => 'Arguments used to run the Nmap scan. Be careful to specify <a href="https://linux.die.net/man/1/nmap" target="_blank">the arguments</a> correctly. For example <code>-p -10000</code> scans ports from 1 to 10000.',
|
||||
|
||||
// API
|
||||
'API_display_name' => 'API',
|
||||
'API_icon' => '<i class="fa fa-arrow-down-up-across-line"></i>',
|
||||
'API_CUSTOM_SQL_name' => 'Custom endpoint',
|
||||
'API_CUSTOM_SQL_description' => 'You can specify a custom SQL query which will generate a JSON file and then expose it via the <a href="/api/table_custom_endpoint.json" target="_blank"><code>table_custom_endpoint.json</code> file endpoint</a>.',
|
||||
|
||||
|
||||
@@ -63,6 +63,9 @@ function getFormControl(dbColumnDef, value, index) {
|
||||
case 'url':
|
||||
result = `<span><a href="${value}" target="_blank">${value}</a><span>`;
|
||||
break;
|
||||
case 'devicemac':
|
||||
result = `<span><a href="/deviceDetails.php?mac=${value}" target="_blank">${value}</a><span>`;
|
||||
break;
|
||||
case 'threshold':
|
||||
$.each(dbColumnDef.options, function(index, obj) {
|
||||
if(Number(value) < obj.maximum && result == '')
|
||||
@@ -94,7 +97,7 @@ function saveData (id) {
|
||||
index = $(`#${id}`).attr('data-my-index')
|
||||
columnValue = $(`#${id}`).val()
|
||||
|
||||
$.get(`php/server/dbHelper.php?action=update&dbtable=Plugins_Objects&key=Index&id=${index}&columns=UserData&values=${columnValue}`, function(data) {
|
||||
$.get(`php/server/dbHelper.php?action=update&dbtable=Plugins_Objects&columnName=Index&id=${index}&columns=UserData&values=${columnValue}`, function(data) {
|
||||
|
||||
// var result = JSON.parse(data);
|
||||
console.log(data)
|
||||
@@ -242,18 +245,25 @@ function generateTabs()
|
||||
|
||||
// Generate the history rows
|
||||
var histCount = 0
|
||||
var histCountDisplayed = 0
|
||||
|
||||
for(i=0;i<pluginHistory.length;i++)
|
||||
{
|
||||
if(pluginHistory[i].Plugin == obj.unique_prefix)
|
||||
{
|
||||
clm = ""
|
||||
if(histCount < 50) // only display 50 entries to optimize performance
|
||||
{
|
||||
clm = ""
|
||||
|
||||
for(j=0;j<colDefinitions.length;j++)
|
||||
{
|
||||
clm += '<td>'+ pluginHistory[i][colDefinitions[j].column] +'</td>'
|
||||
}
|
||||
hiRows += `<tr data-my-index="${pluginHistory[i]["Index"]}" >${clm}</tr>`
|
||||
histCount++;
|
||||
for(j=0;j<colDefinitions.length;j++)
|
||||
{
|
||||
clm += '<td>'+ pluginHistory[i][colDefinitions[j].column] +'</td>'
|
||||
}
|
||||
hiRows += `<tr data-my-index="${pluginHistory[i]["Index"]}" >${clm}</tr>`
|
||||
|
||||
histCountDisplayed++;
|
||||
}
|
||||
histCount++; // count and display the total
|
||||
}
|
||||
}
|
||||
|
||||
@@ -299,7 +309,7 @@ function generateTabs()
|
||||
<li >
|
||||
<a href="#historyTarget_${obj.unique_prefix}" data-toggle="tab" >
|
||||
|
||||
<i class="fa fa-clock"></i> <?= lang('Plugins_History');?> (${histCount})
|
||||
<i class="fa fa-clock"></i> <?= lang('Plugins_History');?> (${histCountDisplayed} out of ${histCount} )
|
||||
|
||||
</a>
|
||||
</li>
|
||||
@@ -320,7 +330,7 @@ function generateTabs()
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="plugin-obj-purge">
|
||||
<button class="btn btn-primary" onclick="purgeAll('${obj.unique_prefix}', 'Plugins_Objects' )"><?= lang('Gen_Purge');?></button>
|
||||
<button class="btn btn-primary" onclick="purgeAll('${obj.unique_prefix}', 'Plugins_Objects' )"><?= lang('Gen_DeleteAll');?></button>
|
||||
</div>
|
||||
</div>
|
||||
<div id="eventsTarget_${obj.unique_prefix}" class="tab-pane">
|
||||
@@ -334,7 +344,7 @@ function generateTabs()
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="plugin-obj-purge">
|
||||
<button class="btn btn-primary" onclick="purgeAll('${obj.unique_prefix}', 'Plugins_Events' )"><?= lang('Gen_Purge');?></button>
|
||||
<button class="btn btn-primary" onclick="purgeAll('${obj.unique_prefix}', 'Plugins_Events' )"><?= lang('Gen_DeleteAll');?></button>
|
||||
</div>
|
||||
</div>
|
||||
<div id="historyTarget_${obj.unique_prefix}" class="tab-pane">
|
||||
@@ -348,7 +358,7 @@ function generateTabs()
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="plugin-obj-purge">
|
||||
<button class="btn btn-primary" onclick="purgeAll('${obj.unique_prefix}', 'Plugins_History' )"><?= lang('Gen_Purge');?></button>
|
||||
<button class="btn btn-primary" onclick="purgeAll('${obj.unique_prefix}', 'Plugins_History' )"><?= lang('Gen_DeleteAll');?></button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -373,8 +383,6 @@ function generateTabs()
|
||||
|
||||
// --------------------------------------------------------
|
||||
// handle first tab (objectsTarget_) display
|
||||
var lastPrefix = ''
|
||||
|
||||
function initTabs()
|
||||
{
|
||||
// events on tab change
|
||||
@@ -384,26 +392,28 @@ function initTabs()
|
||||
// save the last prefix
|
||||
if(target.includes('_') == false )
|
||||
{
|
||||
lastPrefix = target.split('#')[1]
|
||||
pref = target.split('#')[1]
|
||||
} else
|
||||
{
|
||||
pref = target.split('_')[1]
|
||||
}
|
||||
|
||||
|
||||
everythingHidden = false;
|
||||
|
||||
if($('#objectsTarget_'+ lastPrefix) && $('#historyTarget_'+ lastPrefix) && $('#eventsTarget_'+ lastPrefix))
|
||||
if($('#objectsTarget_'+ pref) != undefined && $('#historyTarget_'+ pref) != undefined && $('#eventsTarget_'+ pref) != undefined)
|
||||
{
|
||||
everythingHidden = $('#objectsTarget_'+ lastPrefix).attr('class').includes('active') == false && $('#historyTarget_'+ lastPrefix).attr('class').includes('active') == false && $('#eventsTarget_'+ lastPrefix).attr('class').includes('active') == false;
|
||||
everythingHidden = $('#objectsTarget_'+ pref).attr('class').includes('active') == false && $('#historyTarget_'+ pref).attr('class').includes('active') == false && $('#eventsTarget_'+ pref).attr('class').includes('active') == false;
|
||||
}
|
||||
|
||||
// show the objectsTarget if no specific pane selected or if selected is hidden
|
||||
if((target == '#'+lastPrefix ) && everythingHidden) //|| target == '#objectsTarget_'+ lastPrefix
|
||||
if((target == '#'+pref ) && everythingHidden)
|
||||
{
|
||||
var classTmp = $('#objectsTarget_'+ lastPrefix).attr('class');
|
||||
var classTmp = $('#objectsTarget_'+ pref).attr('class');
|
||||
|
||||
if($('#objectsTarget_'+ lastPrefix).attr('class').includes('active') == false)
|
||||
{
|
||||
console.log('show')
|
||||
if($('#objectsTarget_'+ pref).attr('class').includes('active') == false)
|
||||
{
|
||||
classTmp += ' active';
|
||||
$('#objectsTarget_'+ lastPrefix).attr('class', classTmp)
|
||||
$('#objectsTarget_'+ pref).attr('class', classTmp)
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -422,20 +432,27 @@ function purgeAll(callback) {
|
||||
}
|
||||
|
||||
// --------------------------------------------------------
|
||||
dbIndexes = ''
|
||||
|
||||
function purgeAllExecute() {
|
||||
$.ajax({
|
||||
method: "POST",
|
||||
url: "php/server/dbHelper.php",
|
||||
data: { action: "delete", dbtable: dbTable, columnName: 'Plugin', id:plugPrefix },
|
||||
success: function(data, textStatus) {
|
||||
showModalOk ('Result', data );
|
||||
}
|
||||
})
|
||||
|
||||
// Execute
|
||||
// console.log("targetLogFile:" + targetLogFile)
|
||||
// console.log("logFileAction:" + logFileAction)
|
||||
}
|
||||
|
||||
idArr = $('#NMAPSRV table[data-my-dbtable="Plugins_Objects"] tr[data-my-index]').map(function(){return $(this).attr("data-my-index");}).get();
|
||||
// --------------------------------------------------------
|
||||
function purgeVisible() {
|
||||
|
||||
idArr = $(`#${plugPrefix} table[data-my-dbtable="${dbTable}"] tr[data-my-index]`).map(function(){return $(this).attr("data-my-index");}).get();
|
||||
|
||||
$.ajax({
|
||||
method: "POST",
|
||||
url: "php/server/dbHelper.php",
|
||||
data: { action: "delete", dbtable: 'Plugins_Objects', key: 'Index', id:idArr.toString() },
|
||||
data: { action: "delete", dbtable: dbTable, columnName: 'Index', id:idArr.toString() },
|
||||
success: function(data, textStatus) {
|
||||
showModalOk ('Result', data );
|
||||
}
|
||||
|
||||
@@ -1,12 +1,22 @@
|
||||
# ⚠ Disclaimer
|
||||
|
||||
Highly experimental feature. Follow the below very carefully and check example plugin(s). Plugin UI is not my priority right now, happy to approve PRs if you are interested in extending/improvintg the UI experience.
|
||||
Highly experimental feature. Follow the below very carefully and check example plugin(s). Plugin UI is not my priority right now, happy to approve PRs if you are interested in extending/improvintg the UI experience (e.g. making the tables sortable/filterable).
|
||||
|
||||
## ❗ Known issues:
|
||||
|
||||
These issues will be hopefully fixed with time, so please don't report them. Instead, if you know how, feel free to investigate and submit a PR to fix the below. Keep the PRs small as it's easier to approve them:
|
||||
|
||||
* Existing plugin objects sometimes not interpreted correctly and a new object is created instead, resulting in dupliucat entries.
|
||||
* Occasional (experienced twice) hanging of processing plugin script file.
|
||||
* UI displaying outdated values until the API endpoints get refreshed.
|
||||
|
||||
## Overview
|
||||
|
||||
PiAlert comes with a simple plugin system to feed events from third-party scripts into the UI and then send notifications if desired.
|
||||
PiAlert comes with a plugin system to feed events from third-party scripts into the UI and then send notifications if desired.
|
||||
|
||||
If you wish to develop a plugin, please check the existing plugin structure. Once the settings are saved by the user they need to be removed from the `pialert.conf` file manually if you want to re-initialize them from the `config.json` of teh plugin.
|
||||
If you wish to develop a plugin, please check the existing plugin structure. Once the settings are saved by the user they need to be removed from the `pialert.conf` file manually if you want to re-initialize them from the `config.json` of the plugin.
|
||||
|
||||
Again, please read the below carefully if you'd like to contribute with a plugin yourself. This documentation file might be outdated, so double check the sample plugins as well.
|
||||
|
||||
## Plugin file structure overview
|
||||
|
||||
@@ -35,6 +45,8 @@ You need to set the `data_source` to either `pialert-db-query` or `python-script
|
||||
```json
|
||||
"data_source": "pialert-db-query"
|
||||
```
|
||||
Any of the above datasources have to return a "table" of the exact structure as outlined below.
|
||||
|
||||
### Column order and values
|
||||
|
||||
| Order | Represented Column | Required | Description |
|
||||
@@ -47,6 +59,7 @@ You need to set the `data_source` to either `pialert-db-query` or `python-script
|
||||
| 5 | `Watched_Value3` | no | As above |
|
||||
| 6 | `Watched_Value4` | no | As above |
|
||||
| 7 | `Extra` | no | Any other data you want to pass and display in PiAlert and the notifications |
|
||||
| 8 | `ForeignKey` | no | A foreign key that can be used to link to the parent object (usually a MAC address) |
|
||||
|
||||
### "data_source": "python-script"
|
||||
|
||||
@@ -65,8 +78,8 @@ Valid CSV:
|
||||
|
||||
```csv
|
||||
|
||||
https://www.google.com|null|2023-01-02 15:56:30|200|0.7898|null|null|null
|
||||
https://www.duckduckgo.com|192.168.0.1|2023-01-02 15:56:30|200|0.9898|null|null|Best search engine
|
||||
https://www.google.com|null|2023-01-02 15:56:30|200|0.7898|null|null|null|null
|
||||
https://www.duckduckgo.com|192.168.0.1|2023-01-02 15:56:30|200|0.9898|null|null|Best search engine|ff:ee:ff:11:ff:11
|
||||
|
||||
```
|
||||
|
||||
@@ -74,9 +87,9 @@ Invalid CSV with different errors on each line:
|
||||
|
||||
```csv
|
||||
|
||||
https://www.google.com|null|2023-01-02 15:56:30|200|0.7898||null|null
|
||||
https://www.google.com|null|2023-01-02 15:56:30|200|0.7898||null|null|null
|
||||
https://www.duckduckgo.com|null|2023-01-02 15:56:30|200|0.9898|null|null|Best search engine|
|
||||
|https://www.duckduckgo.com|null|2023-01-02 15:56:30|200|0.9898|null|null|Best search engine
|
||||
|https://www.duckduckgo.com|null|2023-01-02 15:56:30|200|0.9898|null|null|Best search engine|null
|
||||
null|192.168.1.1|2023-01-02 15:56:30|200|0.9898|null|null|Best search engine
|
||||
https://www.duckduckgo.com|192.168.1.1|2023-01-02 15:56:30|null|0.9898|null|null|Best search engine
|
||||
https://www.google.com|null|2023-01-02 15:56:30|200|0.7898|||
|
||||
@@ -100,7 +113,8 @@ SELECT dv.dev_Name as Object_PrimaryID,
|
||||
ns.State as Watched_Value2,
|
||||
'null' as Watched_Value3,
|
||||
'null' as Watched_Value4,
|
||||
ns.Extra as Extra
|
||||
ns.Extra as Extra,
|
||||
dv.dev_MAC as ForeignKey
|
||||
FROM
|
||||
(SELECT * FROM Nmap_Scan) ns
|
||||
LEFT JOIN
|
||||
@@ -221,7 +235,7 @@ Example:
|
||||
```
|
||||
##### UI settings in database_column_definitions
|
||||
|
||||
The UI will adjust how columns are displayed in the UI based on teh definition of the `database_column_definitions` object.
|
||||
The UI will adjust how columns are displayed in the UI based on the definition of the `database_column_definitions` object. Thease are the supported form controls and related functionality:
|
||||
|
||||
- Only columns with `"show": true` and also with at least an english translation will be shown in the UI.
|
||||
- Supported types: `label`, `text`, `threshold`, `replace`
|
||||
@@ -231,6 +245,8 @@ The UI will adjust how columns are displayed in the UI based on teh definition o
|
||||
- The `options` property is used in conjunction with these types:
|
||||
- `threshold` - The `options` array contains objects from lowest `maximum` to highest with corresponding `hexColor` 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, that is compared to the "value" and if the values are the same, the string in `replacement` is displayed in the UI instead of the actual "value"
|
||||
- `devicemac` - The value is considered to be a mac adress and a link pointing to the device with teh given mac address is generated.
|
||||
- `url` - The value is considered to be a url so a link is generated.
|
||||
|
||||
|
||||
```json
|
||||
|
||||
@@ -149,6 +149,18 @@
|
||||
"language_code":"en_us",
|
||||
"string" : "Extra"
|
||||
}]
|
||||
},
|
||||
{
|
||||
"column": "ForeignKey",
|
||||
"show": true,
|
||||
"type": "devicemac",
|
||||
"default_value":"",
|
||||
"options": [],
|
||||
"localized": ["name"],
|
||||
"name":[{
|
||||
"language_code":"en_us",
|
||||
"string" : "MAC"
|
||||
}]
|
||||
},
|
||||
{
|
||||
"column": "Status",
|
||||
@@ -195,7 +207,7 @@
|
||||
{
|
||||
"function": "CMD",
|
||||
"type": "text",
|
||||
"default_value":"SELECT dv.dev_Name as Object_PrimaryID, cast('http://' || 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.dev_Name as Object_PrimaryID, cast('http://' || 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, dv.dev_MAC 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",
|
||||
"options": [],
|
||||
"localized": ["name", "description"],
|
||||
"name" : [{
|
||||
|
||||
@@ -51,8 +51,8 @@ def service_monitoring_log(site, status, latency):
|
||||
)
|
||||
)
|
||||
with open(last_run, 'a') as last_run_logfile:
|
||||
# https://www.duckduckgo.com|192.168.0.1|2023-01-02 15:56:30|200|0.9898|null|null|Best search engine
|
||||
last_run_logfile.write("{}|{}|{}|{}|{}|{}|{}|{}\n".format(
|
||||
# https://www.duckduckgo.com|192.168.0.1|2023-01-02 15:56:30|200|0.9898|null|null|Best search engine|null
|
||||
last_run_logfile.write("{}|{}|{}|{}|{}|{}|{}|{}|{}\n".format(
|
||||
site,
|
||||
'null',
|
||||
strftime("%Y-%m-%d %H:%M:%S"),
|
||||
@@ -61,6 +61,7 @@ def service_monitoring_log(site, status, latency):
|
||||
'null',
|
||||
'null',
|
||||
'null',
|
||||
'null',
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user