wf work + docs

This commit is contained in:
jokob-sk
2025-03-31 18:04:56 +11:00
parent 8a07f7067b
commit 2c445ccaeb
11 changed files with 180 additions and 19 deletions

View File

@@ -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"
```

View File

@@ -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 🔌
![Plugin notification settings](./img/NOTIFICATIONS/Plugin-notification-settings.png)
@@ -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.

View File

@@ -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.
---

View File

@@ -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
View File

View 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
View File

0
front/php/templates/language/uk_ua.json Normal file → Executable file
View File

View 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");

View File

@@ -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)))

View File

@@ -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: