import sys import json # Register NetAlertX directories INSTALL_PATH="/app" sys.path.extend([f"{INSTALL_PATH}/server"]) import conf from const import fullConfFolder import workflows.actions from logger import mylog, Logger from helper import get_setting_value # Make sure log level is initialized correctly Logger(get_setting_value('LOG_LEVEL')) from workflows.triggers import Trigger from workflows.conditions import ConditionGroup from workflows.actions import * class WorkflowManager: def __init__(self, db): self.db = db self.workflows = self.load_workflows() self.update_api = False def load_workflows(self): """Load workflows from workflows.json.""" try: workflows_json_path = fullConfFolder + '/workflows.json' with open(workflows_json_path, 'r') as f: workflows = json.load(f) return workflows except (FileNotFoundError, json.JSONDecodeError): mylog('none', ['[WF] Failed to load workflows.json']) return [] def get_new_app_events(self): """Get new unprocessed events from the AppEvents table.""" result = self.db.sql.execute(""" SELECT * FROM AppEvents WHERE AppEventProcessed = 0 ORDER BY DateTimeCreated ASC """).fetchall() mylog('none', [f'[WF] get_new_app_events - new events count: {len(result)}']) return result def process_event(self, event): """Process the events. Check if events match a workflow trigger""" evGuid = event["GUID"] mylog('verbose', [f"[WF] Processing event with GUID {evGuid}"]) # Check if the trigger conditions match for workflow in self.workflows: # Ensure workflow is enabled before proceeding if workflow.get("enabled", "No").lower() == "yes": wfName = workflow["name"] mylog('debug', [f"[WF] Checking if '{evGuid}' triggers the workflow '{wfName}'"]) # construct trigger object which also evaluates if the current event triggers it trigger = Trigger(workflow["trigger"], event, self.db) if trigger.triggered: mylog('verbose', [f"[WF] Event with GUID '{evGuid}' triggered the workflow '{wfName}'"]) self.execute_workflow(workflow, trigger) # After processing the event, mark the event as processed (set AppEventProcessed to 1) self.db.sql.execute(""" UPDATE AppEvents SET AppEventProcessed = 1 WHERE "Index" = ? """, (event['Index'],)) # Pass the event's unique identifier self.db.commitDB() def execute_workflow(self, workflow, trigger): """Execute the actions in the given workflow if conditions are met.""" wfName = workflow["name"] # Ensure conditions exist if not isinstance(workflow.get("conditions"), list): m = f"[WF] workflow['conditions'] must be a list" mylog('none', [m]) raise ValueError(m) # Evaluate each condition group separately for condition_group in workflow["conditions"]: evaluator = ConditionGroup(condition_group) if evaluator.evaluate(trigger): # If any group evaluates to True mylog('none', [f"[WF] Workflow {wfName} will be executed - conditions were evaluated as TRUE"]) mylog('debug', [f"[WF] Workflow condition_group: {condition_group}"]) self.execute_actions(workflow["actions"], trigger) return # Stop if a condition group succeeds mylog('none', ["[WF] No condition group matched. Actions not executed."]) def execute_actions(self, actions, trigger): """Execute the actions defined in a workflow.""" for action in actions: if action["type"] == "update_field": field = action["field"] value = action["value"] action_instance = UpdateFieldAction(self.db, field, value, trigger) # indicate if the api has to be updated self.update_api = True elif action["type"] == "run_plugin": plugin_name = action["plugin"] params = action["params"] action_instance = RunPluginAction(self.db, plugin_name, params, trigger) elif action["type"] == "delete_device": action_instance = DeleteObjectAction(self.db, trigger) # elif action["type"] == "send_notification": # method = action["method"] # message = action["message"] # action_instance = SendNotificationAction(method, message, trigger) else: m = f"[WF] Unsupported action type: {action['type']}" mylog('none', [m]) raise ValueError(m) action_instance.execute() # Execute the action # if result: # # Iterate through actions and execute them # for action in workflow["actions"]: # if action["type"] == "update_field": # # Action type is "update_field", so map to UpdateFieldAction # field = action["field"] # value = action["value"] # action_instance = UpdateFieldAction(field, value) # action_instance.execute(trigger.event) # elif action["type"] == "run_plugin": # # Action type is "run_plugin", so map to RunPluginAction # plugin_name = action["plugin"] # params = action["params"] # action_instance = RunPluginAction(plugin_name, params) # action_instance.execute(trigger.event) # elif action["type"] == "send_notification": # # Action type is "send_notification", so map to SendNotificationAction # method = action["method"] # message = action["message"] # action_instance = SendNotificationAction(method, message) # action_instance.execute(trigger.event) # else: # # Handle unsupported action types # raise ValueError(f"Unsupported action type: {action['type']}")