mirror of
https://github.com/jokob-sk/NetAlertX.git
synced 2025-12-06 17:15:38 -08:00
wf work + docs
This commit is contained in:
@@ -103,3 +103,42 @@ DEV_LOCATION=/path/to/local/source/code
|
||||
```
|
||||
|
||||
To run the container execute: `sudo docker-compose --env-file /path/to/.env up`
|
||||
|
||||
|
||||
## #Example 4: Docker swarm
|
||||
|
||||
Notice how the host network is defined in a swarm setup:
|
||||
|
||||
```yaml
|
||||
services:
|
||||
netalertx:
|
||||
# Use the below line if you want to test the latest dev image
|
||||
# image: "jokobsk/netalertx-dev:latest"
|
||||
image: "ghcr.io/jokob-sk/netalertx:latest"
|
||||
volumes:
|
||||
- /mnt/MYSERVER/netalertx/config:/config:rw
|
||||
- /mnt/MYSERVER/netalertx/db:/netalertx/db:rw
|
||||
- /mnt/MYSERVER/netalertx/logs:/netalertx/front/log:rw
|
||||
environment:
|
||||
- TZ=Europe/London
|
||||
- PORT=20211
|
||||
# network_mode: host
|
||||
networks:
|
||||
- outside
|
||||
deploy:
|
||||
mode: replicated
|
||||
replicas: 1
|
||||
restart_policy:
|
||||
condition: on-failure
|
||||
# placement: # ✅ Placement is now correctly inside deploy
|
||||
# constraints:
|
||||
# - node.role == manager
|
||||
# - node.labels.device == NUC2
|
||||
|
||||
networks:
|
||||
outside:
|
||||
external:
|
||||
name: "host"
|
||||
|
||||
|
||||
```
|
||||
|
||||
@@ -21,6 +21,9 @@ There are 4 settings on the device for influencing notifications. You can:
|
||||
2. **Alert Down** - Alerts when a device goes down. This setting overrides a disabled **Alert Events** setting, so you will get a notification of a device going down even if you don't have **Alert Events** ticked. Disabling this will disable down and down reconnected notifications on the device.
|
||||
3. **Skip repeated notifications**, if for example you know there is a temporary issue and want to pause the same notification for this device for a given time.
|
||||
|
||||
> [!NOTE]
|
||||
> Please read through the [NTFPRCS plugin](https://github.com/jokob-sk/NetAlertX/blob/main/front/plugins/notification_processing/README.md) documentation to understand how device and global settings influence the notification processing.
|
||||
|
||||
## Plugin settings 🔌
|
||||
|
||||

|
||||
@@ -38,7 +41,7 @@ Click the **Read more in the docs.** Link at the top of each plugin to get more
|
||||
|
||||
In Notification Processing settings, you can specify blanket rules. These allow you to specify exceptions to the Plugin and Device settings and will override those.
|
||||
|
||||
1. Notify on (`NTFPRCS_INCLUDED_SECTIONS`) allows you to specify which events trigger notifications. Usual setups will have `new_devices`, `down_devices`, and possibly `down_reconnected` set. Including `plugin` (dependenton the Plugin `<plugin>_WATCH` and `<plugin>_REPORT_ON` settings) and `events` (dependent on the on-device **Alert Events** setting) might be too noisy for most setups. More info in the [NTFPRCS plugin](/front/plugins/notification_processing/README.md) on what events these selections include.
|
||||
1. Notify on (`NTFPRCS_INCLUDED_SECTIONS`) allows you to specify which events trigger notifications. Usual setups will have `new_devices`, `down_devices`, and possibly `down_reconnected` set. Including `plugin` (dependenton the Plugin `<plugin>_WATCH` and `<plugin>_REPORT_ON` settings) and `events` (dependent on the on-device **Alert Events** setting) might be too noisy for most setups. More info in the [NTFPRCS plugin](https://github.com/jokob-sk/NetAlertX/blob/main/front/plugins/notification_processing/README.md) on what events these selections include.
|
||||
2. Alert down after (`NTFPRCS_alert_down_time`) is useful if you want to wait for some time before the system sends out a down notification for a device. This is related to the on-device **Alert down** setting and only devices with this checked will trigger a down notification.
|
||||
3. A filter to allow you to set device-specific exceptions to New devices being added to the app.
|
||||
4. A filter to allow you to set device-specific exceptions to generated Events.
|
||||
|
||||
@@ -54,9 +54,11 @@ Below you can find a couple of configuration examples.
|
||||
|
||||
## Example 1: Assign Device to Network Node Based on IP
|
||||
|
||||
This workflow assigns newly added devices with IP addresses in the `192.168.1.*` range to the device with the MAC address `6c:6d:6d:6c:6c:6c`.
|
||||
|
||||
### Trigger:
|
||||
- **Object Type**: `Devices`
|
||||
- **Event Type**: `create`
|
||||
- **Event Type**: `insert`
|
||||
|
||||
### Conditions:
|
||||
- **Logic**: `AND`
|
||||
@@ -71,12 +73,12 @@ Below you can find a couple of configuration examples.
|
||||
- **Field**: `devNetworkNode`
|
||||
- **Value**: `6c:6d:6d:6c:6c:6c`
|
||||
|
||||
This workflow assigns newly added devices with IP addresses in the `192.168.1.*` range to the device with the MAC address `6c:6d:6d:6c:6c:6c`.
|
||||
|
||||
---
|
||||
|
||||
## Example 2: Mark Device as Not New and Delete If from Google Vendor
|
||||
|
||||
This workflow automates the process of marking Google devices as not new and deleting them if they meet the criteria.
|
||||
|
||||
### Trigger:
|
||||
- **Object Type**: `Devices`
|
||||
- **Event Type**: `update`
|
||||
@@ -107,7 +109,7 @@ This workflow assigns newly added devices with IP addresses in the `192.168.1.*`
|
||||
|
||||
This action deletes the device after it is marked as not new.
|
||||
|
||||
This workflow automates the process of marking Google devices as not new and deleting them if they meet the criteria.
|
||||
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -68,11 +68,13 @@ function showModalWarning(
|
||||
callbackFunction = null,
|
||||
triggeredBy = null
|
||||
) {
|
||||
prefix = "modal-warning";
|
||||
|
||||
// set captions
|
||||
$("#modal-warning-title").html(title);
|
||||
$("#modal-warning-message").html(message);
|
||||
$("#modal-warning-cancel").html(btnCancel);
|
||||
$("#modal-warning-OK").html(btnOK);
|
||||
$(`#${prefix}-title`).html(title);
|
||||
$(`#${prefix}-message`).html(message);
|
||||
$(`#${prefix}-cancel`).html(btnCancel);
|
||||
$(`#${prefix}-OK`).html(btnOK);
|
||||
|
||||
if (callbackFunction != null) {
|
||||
modalCallbackFunction = callbackFunction;
|
||||
@@ -83,7 +85,7 @@ function showModalWarning(
|
||||
}
|
||||
|
||||
// Show modal
|
||||
$("#modal-warning").modal("show");
|
||||
$(`#${prefix}`).modal("show");
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
@@ -93,7 +95,8 @@ function showModalInput(
|
||||
btnCancel = getString("Gen_Cancel"),
|
||||
btnOK = getString("Gen_Okay"),
|
||||
callbackFunction = null,
|
||||
triggeredBy = null
|
||||
triggeredBy = null,
|
||||
defaultValue = ""
|
||||
) {
|
||||
prefix = "modal-input";
|
||||
|
||||
@@ -102,6 +105,7 @@ function showModalInput(
|
||||
$(`#${prefix}-message`).html(message);
|
||||
$(`#${prefix}-cancel`).html(btnCancel);
|
||||
$(`#${prefix}-OK`).html(btnOK);
|
||||
$(`#${prefix}-textarea`).val(defaultValue);
|
||||
|
||||
if (callbackFunction != null) {
|
||||
modalCallbackFunction = callbackFunction;
|
||||
|
||||
0
front/php/templates/language/de_de.json
Normal file → Executable file
0
front/php/templates/language/de_de.json
Normal file → Executable file
@@ -681,11 +681,17 @@
|
||||
"WF_Condition_field": "Field",
|
||||
"WF_Condition_operator": "Operator",
|
||||
"WF_Condition_value": "Value",
|
||||
"WF_Duplicate": "Duplicate Workflow",
|
||||
"WF_Import": "Import Workflow",
|
||||
"WF_Import_Copy": "Paste in the workflow you copied previously.",
|
||||
"WF_Export": "Export Workflow",
|
||||
"WF_Export_Copy": "Copy the below workflow and import it where needed.",
|
||||
"WF_Conditions": "Conditions",
|
||||
"WF_Conditions_logic_rules": "Logic rules",
|
||||
"WF_Enabled": "Workflow enabled",
|
||||
"WF_Name": "Workflow name",
|
||||
"WF_Remove": "Remove Workflow",
|
||||
"WF_Remove_Copy": "Do you want to remove this workflow?",
|
||||
"WF_Save": "Save Workflows",
|
||||
"WF_Trigger": "Trigger",
|
||||
"WF_Trigger_event_type": "Event type",
|
||||
|
||||
0
front/php/templates/language/fr_fr.json
Normal file → Executable file
0
front/php/templates/language/fr_fr.json
Normal file → Executable file
0
front/php/templates/language/uk_ua.json
Normal file → Executable file
0
front/php/templates/language/uk_ua.json
Normal file → Executable file
@@ -16,9 +16,9 @@
|
||||
<i class="fa fa-fw fa-plus"></i> <?= lang('WF_Add');?>
|
||||
</button>
|
||||
</div>
|
||||
<div class="save-workflows col-sm-4 col-xs-12">
|
||||
<button type="button" class="btn btn-primary col-sm-12 col-xs-12" id="save" onclick="saveWorkflows()">
|
||||
<i class="fa fa-fw fa-floppy-disk"></i> <?= lang('WF_Save');?>
|
||||
<div class="import-wf col-sm-4 col-xs-12">
|
||||
<button type="button" class="btn btn-primary col-sm-12 col-xs-12" id="import">
|
||||
<i class="fa fa-fw fa-file-import"></i> <?= lang('WF_Import');?>
|
||||
</button>
|
||||
</div>
|
||||
<div class="restart-app col-sm-4 col-xs-12">
|
||||
@@ -26,6 +26,11 @@
|
||||
<i class="fa fa-fw fa-arrow-rotate-right"></i> <?= lang('Maint_RestartServer');?>
|
||||
</button>
|
||||
</div>
|
||||
<div class="save-workflows col-xs-12">
|
||||
<button type="button" class="btn btn-primary bg-green col-sm-12 col-xs-12" id="save" onclick="saveWorkflows()">
|
||||
<i class="fa fa-fw fa-floppy-disk"></i> <?= lang('WF_Save');?>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@@ -45,6 +50,9 @@ let fieldOptions = [
|
||||
let triggerTypes = [
|
||||
"Devices"
|
||||
];
|
||||
let triggerEvents = [
|
||||
"update", "insert", "delete"
|
||||
];
|
||||
|
||||
let wfEnabledOptions = [
|
||||
"Yes", "No"
|
||||
@@ -208,7 +216,7 @@ function generateWorkflowUI(wf, wfIndex) {
|
||||
let $eventTypeDropdown = createEditableDropdown(
|
||||
`[${wfIndex}].trigger.event_type`,
|
||||
getString("WF_Trigger_event_type"),
|
||||
["update", "create", "delete"],
|
||||
triggerEvents,
|
||||
wf.trigger.event_type,
|
||||
`wf-${wfIndex}-trigger-event-type`
|
||||
);
|
||||
@@ -360,7 +368,7 @@ function generateWorkflowUI(wf, wfIndex) {
|
||||
$actionsContainer.append($actionAddButtonWrap)
|
||||
|
||||
|
||||
let $wfRemoveButtonWrap = $("<div>", { class: "button-container col-sm-12 col-sx-12" });
|
||||
let $wfRemoveButtonWrap = $("<div>", { class: "button-container col-sm-4 col-sx-12" });
|
||||
|
||||
let $wfRemoveIcon = $("<i>", {
|
||||
class: "fa-solid fa-trash"
|
||||
@@ -372,10 +380,40 @@ function generateWorkflowUI(wf, wfIndex) {
|
||||
})
|
||||
.append($wfRemoveIcon) // Add icon
|
||||
.append(` ${getString("WF_Remove")}`); // Add text
|
||||
|
||||
|
||||
let $wfDuplicateButtonWrap = $("<div>", { class: "button-container col-sm-4 col-sx-12" });
|
||||
|
||||
let $wfDuplicateIcon = $("<i>", {
|
||||
class: "fa-solid fa-copy"
|
||||
});
|
||||
|
||||
let $wfDuplicateButton = $("<div>", {
|
||||
class: "pointer duplicate-wf green-hover-text",
|
||||
wfIndex: wfIndex
|
||||
})
|
||||
.append($wfDuplicateIcon) // Add icon
|
||||
.append(` ${getString("WF_Duplicate")}`); // Add text
|
||||
|
||||
let $wfExportButtonWrap = $("<div>", { class: "button-container col-sm-4 col-sx-12" });
|
||||
|
||||
let $wfExportIcon = $("<i>", {
|
||||
class: "fa-solid fa-file-export"
|
||||
});
|
||||
|
||||
let $wfExportButton = $("<div>", {
|
||||
class: "pointer export-wf green-hover-text",
|
||||
wfIndex: wfIndex
|
||||
})
|
||||
.append($wfExportIcon) // Add icon
|
||||
.append(` ${getString("WF_Export")}`); // Add text
|
||||
|
||||
$wfCollapsiblePanel.append($actionsContainer);
|
||||
|
||||
$wfCollapsiblePanel.append($wfDuplicateButtonWrap.append($wfDuplicateButton))
|
||||
$wfCollapsiblePanel.append($wfExportButtonWrap.append($wfExportButton))
|
||||
$wfCollapsiblePanel.append($wfRemoveButtonWrap.append($wfRemoveButton))
|
||||
|
||||
|
||||
$wfContainer.append($wfCollapsiblePanel)
|
||||
|
||||
@@ -765,7 +803,62 @@ function addWorkflow(workflows) {
|
||||
// Function to remove a Workflow
|
||||
function removeWorkflow(workflows, wfIndex) {
|
||||
|
||||
workflows.splice(wfIndex, 1);
|
||||
showModalWarning ('<?= lang('WF_Remove');?>', '<?= lang('WF_Remove_Copy');?>',
|
||||
'<?= lang('Gen_Cancel');?>', '<?= lang('Gen_Delete');?>', `executeRemoveWorkflow`, wfIndex);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------
|
||||
// Function to execute the remove of a Workflow
|
||||
function executeRemoveWorkflow() {
|
||||
|
||||
workflows = getWorkflowsJson()
|
||||
|
||||
workflows.splice($('#modal-warning').attr("data-myparam-triggered-by"), 1);
|
||||
|
||||
updateWorkflowsJson(workflows)
|
||||
|
||||
// Re-render the UI
|
||||
renderWorkflows();
|
||||
}
|
||||
|
||||
// ---------------------------------------------------
|
||||
// Function to duplicate a Workflow
|
||||
function duplicateWorkflow(workflows, wfIndex) {
|
||||
|
||||
workflows.push(workflows[wfIndex])
|
||||
|
||||
updateWorkflowsJson(workflows)
|
||||
|
||||
// Re-render the UI
|
||||
renderWorkflows();
|
||||
}
|
||||
|
||||
// ---------------------------------------------------
|
||||
// Function to export a Workflow
|
||||
function exportWorkflow(workflows, wfIndex) {
|
||||
|
||||
// Add new icon as base64 string
|
||||
showModalInput ('<i class="fa fa-copy pointer"></i> <?= lang('WF_Export');?>', '<?= lang('WF_Export_Copy');?>',
|
||||
'<?= lang('Gen_Cancel');?>', '<?= lang('Gen_Okay');?>', null, null, JSON.stringify(workflows[wfIndex], null, 2));
|
||||
}
|
||||
|
||||
// ---------------------------------------------------
|
||||
// Function to import a Workflow
|
||||
function importWorkflow(workflows, wfIndex) {
|
||||
|
||||
// Add new icon as base64 string
|
||||
showModalInput ('<i class="fa fa-arrow-up pointer"></i> <?= lang('WF_Import');?>', '<?= lang('WF_Import_Copy');?>',
|
||||
'<?= lang('Gen_Cancel');?>', '<?= lang('Gen_Okay');?>', 'importWorkflowExecute', null, "" );
|
||||
|
||||
}
|
||||
|
||||
function importWorkflowExecute()
|
||||
{
|
||||
var json = JSON.parse($('#modal-input-textarea').val());
|
||||
|
||||
workflows = getWorkflowsJson()
|
||||
|
||||
workflows.push(json);
|
||||
|
||||
updateWorkflowsJson(workflows)
|
||||
|
||||
@@ -1019,6 +1112,21 @@ $(document).on("click", ".remove-wf", function () {
|
||||
removeWorkflow(getWorkflowsJson(), wfIndex);
|
||||
});
|
||||
|
||||
$(document).on("click", ".duplicate-wf", function () {
|
||||
let wfIndex = $(this).attr("wfindex");
|
||||
duplicateWorkflow(getWorkflowsJson(), wfIndex);
|
||||
});
|
||||
|
||||
$(document).on("click", ".export-wf", function () {
|
||||
let wfIndex = $(this).attr("wfindex");
|
||||
exportWorkflow(getWorkflowsJson(), wfIndex);
|
||||
});
|
||||
|
||||
$(document).on("click", ".import-wf", function () {
|
||||
let wfIndex = $(this).attr("wfindex");
|
||||
importWorkflow(getWorkflowsJson(), wfIndex);
|
||||
});
|
||||
|
||||
$(document).on("click", ".add-condition", function () {
|
||||
let wfIndex = $(this).attr("wfindex");
|
||||
let parentIndexPath = $(this).attr("parentIndexPath");
|
||||
|
||||
@@ -819,7 +819,6 @@ class plugin_object_class:
|
||||
|
||||
# Check if self.status is valid
|
||||
if self.status not in ["exists", "watched-changed", "watched-not-changed", "new", "not-processed", "missing-in-last-scan"]:
|
||||
mylog('none', [f'[plugin_object_class] ERROR on objDbRow: {objDbRow}'])
|
||||
raise ValueError(f"Invalid status value for plugin object ({self.pluginPref}|{self.primaryId}|{self.watched1}) invalid status: {self.status} on objDbRow:", objDbRow)
|
||||
|
||||
self.idsHash = str(hash(str(self.primaryId) + str(self.secondaryId)))
|
||||
|
||||
@@ -41,7 +41,7 @@ class Condition:
|
||||
if self.operator == "equals":
|
||||
result = str(obj_value) == str(self.value)
|
||||
elif self.operator == "contains":
|
||||
result = str(self.value) in str(obj_value)
|
||||
result = str(self.value).lower() in str(obj_value).lower()
|
||||
elif self.operator == "regex":
|
||||
result = bool(re.match(self.value, str(obj_value)))
|
||||
else:
|
||||
|
||||
Reference in New Issue
Block a user