Compare commits

...

4 Commits

Author SHA1 Message Date
jokob-sk
7dba186e39 Docker swarm guide #1031
Some checks are pending
Code checks / check-url-paths (push) Waiting to run
docker / docker_dev (push) Waiting to run
Deploy MkDocs / deploy (push) Waiting to run
2025-05-10 15:43:46 +10:00
jokob-sk
7443c52021 DateTime format based on TIMEZONE #1044 2025-05-10 15:28:54 +10:00
jokob-sk
03822ac8fa DateTime format based on TIMEZONE #1044 2025-05-10 15:23:02 +10:00
jokob-sk
01f7a18dce Auto delete in-app notifications #1052 2025-05-10 14:33:27 +10:00
39 changed files with 342 additions and 120 deletions

79
docs/DOCKER_SWARM.md Executable file
View File

@@ -0,0 +1,79 @@
# Docker Swarm Deployment Guide (IPvlan)
This guide describes how to deploy **NetAlertX** in a **Docker Swarm** environment using an `ipvlan` network. This enables the container to receive a LAN IP address directly, which is ideal for network monitoring.
---
## ⚙️ Step 1: Create an IPvlan Config-Only Network on All Nodes
> Run this command on **each node** in the Swarm.
```bash
docker network create -d ipvlan \
--subnet=192.168.1.0/24 \ # 🔧 Replace with your LAN subnet
--gateway=192.168.1.1 \ # 🔧 Replace with your LAN gateway
-o ipvlan_mode=l2 \
-o parent=eno1 \ # 🔧 Replace with your network interface (e.g., eth0, eno1)
--config-only \
ipvlan-swarm-config
```
---
## 🖥️ Step 2: Create the Swarm-Scoped IPvlan Network (One-Time Setup)
> Run this on **one Swarm manager node only**.
```bash
docker network create -d ipvlan \
--scope swarm \
--config-from ipvlan-swarm-config \
swarm-ipvlan
```
---
## 🧾 Step 3: Deploy NetAlertX with Docker Compose
Use the following Compose snippet to deploy NetAlertX with a **static LAN IP** assigned via the `swarm-ipvlan` network.
```yaml
services:
netalertx:
image: ghcr.io/jokob-sk/netalertx:latest
ports:
- 20211:20211
volumes:
- /mnt/YOUR_SERVER/netalertx/config:/app/config:rw
- /mnt/YOUR_SERVER/netalertx/db:/netalertx/app/db:rw
- /mnt/YOUR_SERVER/netalertx/logs:/netalertx/app/log:rw
environment:
- TZ=Europe/London
- PORT=20211
networks:
swarm-ipvlan:
ipv4_address: 192.168.1.240 # ⚠️ Choose a free IP from your LAN
deploy:
mode: replicated
replicas: 1
restart_policy:
condition: on-failure
placement:
constraints:
- node.role == manager # 🔄 Or use: node.labels.netalertx == true
networks:
swarm-ipvlan:
external: true
```
---
## ✅ Notes
* The `ipvlan` setup allows **NetAlertX** to have a direct IP on your LAN.
* Replace `eno1` with your interface, IP addresses, and volume paths to match your environment.
* Make sure the assigned IP (`192.168.1.240` above) is not in use or managed by DHCP.
* You may also use a node label constraint instead of `node.role == manager` for more control.

View File

@@ -1,4 +1,4 @@
# Overview
# Home Assistant integration overview
NetAlertX comes with MQTT support, allowing you to show all detected devices as devices in Home Assistant. It also supplies a collection of stats, such as number of online devices.

View File

@@ -173,7 +173,7 @@
// Additional form elements like the random MAC address button for devMac
let inlineControl = "";
// handle rendom mac
// handle random mac
if (setting.setKey == "NEWDEV_devMac" && deviceData["devIsRandomMAC"] == true) {
inlineControl += `<span class="input-group-addon pointer"
title="${getString("RandomMAC_hover")}">

View File

@@ -47,9 +47,28 @@
{visible: false, targets: [0]},
// Replace HTML codes
{targets: [1,2,3,5],
{targets: [3,5],
'createdCell': function (td, cellData, rowData, row, col) {
$(td).html (translateHTMLcodes (cellData));
} },
// Date
{targets: [1,2],
"createdCell": function (td, cellData, rowData, row, col) {
console.log(cellData);
if (!cellData.includes("missing event") && !cellData.includes("..."))
{
if (cellData.includes("+")) { // Check if timezone offset is present
cellData = cellData.split('+')[0]; // Remove timezone offset
}
console.log(cellData);
result = localizeTimestamp(cellData);
} else
{
result = translateHTMLcodes(cellData)
}
$(td).html (result);
} }
],

View File

@@ -216,7 +216,7 @@ function initializeDatatable () {
} },
// Replace HTML codes
{targets: [3,4,5,6,7],
{targets: [4,5,6,7],
"createdCell": function (td, cellData, rowData, row, col) {
$(td).html (translateHTMLcodes (cellData));
} },
@@ -226,6 +226,12 @@ function initializeDatatable () {
"createdCell": function (td, cellData, rowData, row, col) {
// console.log(cellData);
$(td).html (cellData);
} },
// Date
{targets: [3],
"createdCell": function (td, cellData, rowData, row, col) {
// console.log(cellData);
$(td).html (localizeTimestamp(cellData));
} }
],

View File

@@ -353,6 +353,28 @@ function getLangCode() {
// String utilities
// -----------------------------------------------------------------------------
function localizeTimestamp(result)
{
// contains TZ in format Europe/Berlin
tz = getSetting("TIMEZONE")
const date = new Date(result); // Assumes result is a timestamp or ISO string
const formatter = new Intl.DateTimeFormat('default', {
timeZone: tz,
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit',
second: '2-digit',
hour12: false // change to true if you want AM/PM format
});
return formatter.format(date);
}
// ----------------------------------------------------
/**
* Replaces double quotes within single-quoted strings, then converts all single quotes to double quotes,

View File

@@ -120,34 +120,8 @@
let timeZone = "<?php echo $timeZone ?>";
let now = new Date();
// Convert to the specified time zone
let formatter = new Intl.DateTimeFormat("en-UK", {
timeZone: timeZone,
day: "2-digit",
month: "short",
year: "numeric",
hour: "2-digit",
minute: "2-digit",
second: "2-digit",
hour12: false, // Use 24-hour format
});
let parts = formatter.formatToParts(now);
// Extract date components
let day = parts.find(p => p.type === "day").value;
let month = parts.find(p => p.type === "month").value;
let year = parts.find(p => p.type === "year").value;
// Extract time components
let hour = parts.find(p => p.type === "hour").value;
let minute = parts.find(p => p.type === "minute").value;
let second = parts.find(p => p.type === "second").value;
// Construct the date and time in DD-MMM-YYYY HH:MM:SS format
let formattedDateTime = `${day}-${month}-${year} ${hour}:${minute}:${second}`;
if (document.getElementById) {
document.getElementById("NAX_Servertime_plc").innerHTML = '(' + formattedDateTime + ')';
document.getElementById("NAX_Servertime_plc").innerHTML = '(' + localizeTimestamp(now) + ')';
document.getElementById("NAX_TZ").innerHTML = timeZone;
}

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

View File

@@ -16,7 +16,8 @@ from plugin_utils import get_plugins_configs
from logger import mylog, Logger
from const import pluginsPath, fullDbPath, logPath
from helper import timeNowTZ, get_setting_value
from notification import write_notification
from messaging.in_app import write_notification
import conf
# Make sure the TIMEZONE for logging is correct

View File

@@ -24,7 +24,7 @@ from plugin_utils import getPluginObject
from plugin_helper import Plugin_Objects
from logger import mylog, Logger, append_line_to_file
from helper import timeNowTZ, get_setting_value, bytes_to_string, sanitize_string, cleanDeviceName
from notification import Notification_obj
from models.notification_instance import NotificationInstance
from database import DB, get_device_stats
pluginName = 'TESTONLY'

View File

@@ -17,7 +17,7 @@ from const import confFileName, logPath
from plugin_helper import Plugin_Objects
from logger import mylog, Logger, append_line_to_file
from helper import timeNowTZ, get_setting_value
from notification import Notification_obj
from models.notification_instance import NotificationInstance
from database import DB
from pytz import timezone
@@ -50,8 +50,8 @@ def main():
# Initialize the Plugin obj output file
plugin_objects = Plugin_Objects(RESULT_FILE)
# Create a Notification_obj instance
notifications = Notification_obj(db)
# Create a NotificationInstance instance
notifications = NotificationInstance(db)
# Retrieve new notifications
new_notifications = notifications.getNew()

View File

@@ -26,7 +26,7 @@ from const import confFileName, logPath
from plugin_helper import Plugin_Objects
from logger import mylog, Logger, append_line_to_file
from helper import timeNowTZ, get_setting_value, hide_email
from notification import Notification_obj
from models.notification_instance import NotificationInstance
from database import DB
from pytz import timezone
@@ -59,8 +59,8 @@ def main():
# Initialize the Plugin obj output file
plugin_objects = Plugin_Objects(RESULT_FILE)
# Create a Notification_obj instance
notifications = Notification_obj(db)
# Create a NotificationInstance instance
notifications = NotificationInstance(db)
# Retrieve new notifications
new_notifications = notifications.getNew()

View File

@@ -28,7 +28,7 @@ from plugin_utils import getPluginObject
from plugin_helper import Plugin_Objects
from logger import mylog, Logger, append_line_to_file
from helper import timeNowTZ, get_setting_value, bytes_to_string, sanitize_string, normalize_string
from notification import Notification_obj
from models.notification_instance import NotificationInstance
from database import DB, get_device_stats
from pytz import timezone

View File

@@ -20,7 +20,7 @@ from const import confFileName, logPath
from plugin_helper import Plugin_Objects, handleEmpty
from logger import mylog, Logger, append_line_to_file
from helper import timeNowTZ, get_setting_value
from notification import Notification_obj
from models.notification_instance import NotificationInstance
from database import DB
from pytz import timezone
@@ -53,8 +53,8 @@ def main():
# Initialize the Plugin obj output file
plugin_objects = Plugin_Objects(RESULT_FILE)
# Create a Notification_obj instance
notifications = Notification_obj(db)
# Create a NotificationInstance instance
notifications = NotificationInstance(db)
# Retrieve new notifications
new_notifications = notifications.getNew()

View File

@@ -12,7 +12,7 @@ sys.path.extend([f"{INSTALL_PATH}/front/plugins", f"{INSTALL_PATH}/server"])
from plugin_helper import Plugin_Objects, handleEmpty # noqa: E402
from logger import mylog, Logger # noqa: E402
from helper import timeNowTZ, get_setting_value, hide_string # noqa: E402
from notification import Notification_obj # noqa: E402
from models.notification_instance import NotificationInstance # noqa: E402
from database import DB # noqa: E402
import conf
from const import confFileName, logPath
@@ -49,8 +49,8 @@ def main():
# Initialize the Plugin obj output file
plugin_objects = Plugin_Objects(RESULT_FILE)
# Create a Notification_obj instance
notifications = Notification_obj(db)
# Create a NotificationInstance instance
notifications = NotificationInstance(db)
# Retrieve new notifications
new_notifications = notifications.getNew()

View File

@@ -20,7 +20,7 @@ from const import confFileName, logPath
from plugin_helper import Plugin_Objects, handleEmpty
from logger import mylog, Logger, append_line_to_file
from helper import timeNowTZ, get_setting_value, hide_string
from notification import Notification_obj
from models.notification_instance import NotificationInstance
from database import DB
from pytz import timezone
@@ -53,8 +53,8 @@ def main():
# Initialize the Plugin obj output file
plugin_objects = Plugin_Objects(RESULT_FILE)
# Create a Notification_obj instance
notifications = Notification_obj(db)
# Create a NotificationInstance instance
notifications = NotificationInstance(db)
# Retrieve new notifications
new_notifications = notifications.getNew()

View File

@@ -17,7 +17,7 @@ from const import confFileName, logPath
from plugin_helper import Plugin_Objects
from logger import mylog, Logger, append_line_to_file
from helper import timeNowTZ, get_setting_value
from notification import Notification_obj
from models.notification_instance import NotificationInstance
from database import DB
from pytz import timezone
@@ -51,8 +51,8 @@ def main():
# Initialize the Plugin obj output file
plugin_objects = Plugin_Objects(RESULT_FILE)
# Create a Notification_obj instance
notifications = Notification_obj(db)
# Create a NotificationInstance instance
notifications = NotificationInstance(db)
# Retrieve new notifications
new_notifications = notifications.getNew()

View File

@@ -23,7 +23,7 @@ from const import logPath, confFileName
from plugin_helper import Plugin_Objects, handleEmpty
from logger import mylog, Logger, append_line_to_file
from helper import timeNowTZ, get_setting_value, hide_string, write_file
from notification import Notification_obj
from models.notification_instance import NotificationInstance
from database import DB
from pytz import timezone
@@ -56,8 +56,8 @@ def main():
# Initialize the Plugin obj output file
plugin_objects = Plugin_Objects(RESULT_FILE)
# Create a Notification_obj instance
notifications = Notification_obj(db)
# Create a NotificationInstance instance
notifications = NotificationInstance(db)
# Retrieve new notifications
new_notifications = notifications.getNew()

View File

@@ -16,7 +16,7 @@ from plugin_utils import get_plugins_configs
from logger import mylog, Logger
from const import pluginsPath, fullDbPath, logPath
from helper import timeNowTZ, get_setting_value
from notification import write_notification
from messaging.in_app import write_notification
from database import DB
from models.device_instance import DeviceInstance
import conf

View File

@@ -25,7 +25,7 @@ from plugin_utils import get_plugins_configs
from logger import mylog, Logger
from const import pluginsPath, fullDbPath, logPath
from helper import timeNowTZ, get_setting_value
from notification import write_notification
from messaging.in_app import write_notification
import conf
# Make sure the TIMEZONE for logging is correct

View File

@@ -19,7 +19,7 @@ from plugin_utils import get_plugins_configs
from logger import mylog, Logger
from const import pluginsPath, fullDbPath, logPath
from helper import timeNowTZ, get_setting_value
from notification import write_notification
from messaging.in_app import write_notification
import conf
# Make sure the TIMEZONE for logging is correct

View File

@@ -243,6 +243,34 @@
"string": "How many last <code>app.log</code> lines to keep. If <code>LOG_LEVEL</code> is set to <code>debug</code> the app generates about 10000 lines per hour, so when debugging an issue the recommended setting should cover the bug occurence timeframe. For example for a bug with a 3 day periodical appearence the value <code>1000000</code> should be sufficient. Setting this value to <code>1000000</code> generates approximatelly a 50MB <code>app.log</code> file. Set to <code>0</code> to disable log purging."
}
]
},
{
"function": "NOTI_LENGTH",
"type": {
"dataType": "integer",
"elements": [
{
"elementType": "input",
"elementOptions": [{ "type": "number" }],
"transformers": []
}
]
},
"default_value": 0,
"options": [],
"localized": ["name", "description"],
"name": [
{
"language_code": "en_us",
"string": "Notifications history"
}
],
"description": [
{
"language_code": "en_us",
"string": "How many latest in-app Notification entries to keep. Set to <code>0</code> to disable."
}
]
}
],

View File

@@ -19,6 +19,7 @@ from plugin_helper import Plugin_Object, Plugin_Objects, decodeBase64
from logger import mylog, Logger, append_line_to_file
from helper import timeNowTZ, get_setting_value
from const import logPath, applicationPath
from messaging.in_app import remove_old
import conf
from pytz import timezone
@@ -41,6 +42,7 @@ def main():
mylog('verbose', [f'[{pluginName}] In script'])
MAINT_LOG_LENGTH = int(get_setting_value('MAINT_LOG_LENGTH'))
MAINT_NOTI_LENGTH = int(get_setting_value('MAINT_NOTI_LENGTH'))
# Check if set
if MAINT_LOG_LENGTH != 0:
@@ -63,7 +65,10 @@ def main():
mylog('verbose', [f'[{pluginName}] Cleanup finished'])
# Check if set
if MAINT_NOTI_LENGTH != 0:
mylog('verbose', [f'[{pluginName}] Cleaning in-app notification history'])
remove_old(MAINT_NOTI_LENGTH)
return 0

View File

@@ -16,7 +16,7 @@ from plugin_utils import get_plugins_configs
from logger import mylog, Logger
from const import pluginsPath, fullDbPath, logPath
from helper import timeNowTZ, get_setting_value
from notification import write_notification
from messaging.in_app import write_notification
from database import DB
from models.device_instance import DeviceInstance
import conf

View File

@@ -45,7 +45,7 @@ from plugin_utils import get_plugins_configs
from logger import mylog, Logger
from const import pluginsPath, fullDbPath, logPath
from helper import timeNowTZ, get_setting_value
from notification import write_notification
from messaging.in_app import write_notification
from pytz import timezone
import conf

View File

@@ -20,7 +20,7 @@ from logger import mylog, Logger
from const import pluginsPath, fullDbPath, logPath
from helper import timeNowTZ, get_setting_value
from crypto_utils import encrypt_data
from notification import write_notification
from messaging.in_app import write_notification
import conf
from pytz import timezone

View File

@@ -17,7 +17,7 @@ from plugin_utils import get_plugins_configs
from logger import mylog, Logger
from const import pluginsPath, fullDbPath, logPath
from helper import timeNowTZ, get_setting_value
from notification import write_notification
from messaging.in_app import write_notification
from database import DB
from models.device_instance import DeviceInstance
import conf

View File

@@ -78,6 +78,9 @@ require 'php/templates/header.php';
if (result.includes("+")) { // Check if timezone offset is present
result = result.split('+')[0]; // Remove timezone offset
}
result = localizeTimestamp(result);
return result;
}
},

View File

@@ -43,6 +43,7 @@ nav:
- Reverse Proxy: REVERSE_PROXY.md
- Webhooks (n8n): WEBHOOK_N8N.md
- Workflows: WORKFLOWS.md
- Docker Swarm: DOCKER_SWARM.md
- Help:
- Common issues: COMMON_ISSUES.md
- Random MAC: RANDOM_MAC.md

View File

@@ -31,8 +31,8 @@ from api import update_api
from scan.session_events import process_scan
from initialise import importConfigs
from database import DB
from reporting import get_notifications
from notification import Notification_obj
from messaging.reporting import get_notifications
from models.notification_instance import NotificationInstance
from plugin import plugin_manager
from scan.device_handling import update_devices_names
from workflows.manager import WorkflowManager
@@ -172,7 +172,7 @@ def main ():
final_json = get_notifications(db)
# Write the notifications into the DB
notification = Notification_obj(db)
notification = NotificationInstance(db)
notificationObj = notification.create(final_json, "")
# run all enabled publisher gateways

View File

@@ -9,8 +9,8 @@ from const import (apiPath, sql_appevents, sql_devices_all, sql_events_pending_a
from logger import mylog
from helper import write_file, get_setting_value, timeNowTZ
from app_state import updateState
from user_events_queue import UserEventsQueue
from notification import write_notification
from models.user_events_queue_instance import UserEventsQueueInstance
from messaging.in_app import write_notification
# Import the start_server function
from graphql_server.graphql_server_start import start_server
@@ -147,7 +147,7 @@ class api_endpoint_class:
# Update user event execution log
# mylog('verbose', [f'[API] api_endpoint_class: is_ad_hoc_user_event {self.is_ad_hoc_user_event}'])
if self.is_ad_hoc_user_event:
execution_log = UserEventsQueue()
execution_log = UserEventsQueueInstance()
execution_log.finalize_event("update_api")
self.is_ad_hoc_user_event = False

View File

@@ -11,7 +11,7 @@ sys.path.extend([f"{INSTALL_PATH}/server"])
from logger import mylog
from helper import get_setting_value, timeNowTZ
from app_state import updateState
from notification import write_notification
from messaging.in_app import write_notification
# Flask application
app = Flask(__name__)

View File

@@ -19,7 +19,7 @@ from api import update_api
from scheduler import schedule_class
from plugin import plugin_manager, print_plugin_info
from plugin_utils import get_plugins_configs, get_set_value_for_init
from notification import write_notification
from messaging.in_app import write_notification
from crypto_utils import get_random_bytes
#===============================================================================

105
server/messaging/in_app.py Executable file
View File

@@ -0,0 +1,105 @@
import datetime
import os
import sys
import _io
import json
import uuid
import socket
import subprocess
import requests
from yattag import indent
from json2table import convert
# Register NetAlertX directories
INSTALL_PATH="/app"
sys.path.extend([f"{INSTALL_PATH}/server"])
# Register NetAlertX modules
import conf
from const import applicationPath, logPath, apiPath, confFileName, reportTemplatesPath
from logger import logResult, mylog
from helper import generate_mac_links, removeDuplicateNewLines, timeNowTZ, get_file_content, write_file, get_setting_value, get_timezone_offset
NOTIFICATION_API_FILE = apiPath + 'user_notifications.json'
# Show Frontend User Notification
def write_notification(content, level, timestamp):
# Generate GUID
guid = str(uuid.uuid4())
# Prepare notification dictionary
notification = {
'timestamp': str(timestamp),
'guid': guid,
'read': 0,
'level': level,
'content': content
}
# If file exists, load existing data, otherwise initialize as empty list
if os.path.exists(NOTIFICATION_API_FILE):
with open(NOTIFICATION_API_FILE, 'r') as file:
# Check if the file object is of type _io.TextIOWrapper
if isinstance(file, _io.TextIOWrapper):
file_contents = file.read() # Read file contents
if file_contents == '':
file_contents = '[]' # If file is empty, initialize as empty list
# mylog('debug', ['[Notification] User Notifications file: ', file_contents])
notifications = json.loads(file_contents) # Parse JSON data
else:
mylog('none', '[Notification] File is not of type _io.TextIOWrapper')
notifications = []
else:
notifications = []
# Append new notification
notifications.append(notification)
# Write updated data back to file
with open(NOTIFICATION_API_FILE, 'w') as file:
json.dump(notifications, file, indent=4)
# Trim notifications
def remove_old(keepNumberOfEntries):
# Check if file exists
if not os.path.exists(NOTIFICATION_API_FILE):
mylog('info', '[Notification] No notifications file to clean.')
return
# Load existing notifications
try:
with open(NOTIFICATION_API_FILE, 'r') as file:
file_contents = file.read().strip()
if file_contents == '':
notifications = []
else:
notifications = json.loads(file_contents)
except Exception as e:
mylog('none', f'[Notification] Error reading notifications file: {e}')
return
if not isinstance(notifications, list):
mylog('none', '[Notification] Invalid format: not a list')
return
# Sort by timestamp descending
try:
notifications.sort(key=lambda x: x['timestamp'], reverse=True)
except KeyError:
mylog('none', '[Notification] Missing timestamp in one or more entries')
return
# Trim to the latest entries
trimmed = notifications[:keepNumberOfEntries]
# Write back the trimmed list
try:
with open(NOTIFICATION_API_FILE, 'w') as file:
json.dump(trimmed, file, indent=4)
mylog('verbose', f'[Notification] Trimmed notifications to latest {keepNumberOfEntries}')
except Exception as e:
mylog('none', f'Error writing trimmed notifications file: {e}')

View File

@@ -12,6 +12,11 @@
import datetime
import json
import sys
# Register NetAlertX directories
INSTALL_PATH="/app"
sys.path.extend([f"{INSTALL_PATH}/server"])
import conf
from const import applicationPath, logPath, apiPath, confFileName

View File

@@ -2,6 +2,7 @@ import datetime
import os
import _io
import json
import sys
import uuid
import socket
import subprocess
@@ -9,16 +10,22 @@ import requests
from yattag import indent
from json2table import convert
# Register NetAlertX directories
INSTALL_PATH="/app"
sys.path.extend([f"{INSTALL_PATH}/server"])
# Register NetAlertX modules
import conf
from const import applicationPath, logPath, apiPath, confFileName, reportTemplatesPath
from logger import logResult, mylog
from helper import generate_mac_links, removeDuplicateNewLines, timeNowTZ, get_file_content, write_file, get_setting_value, get_timezone_offset
from messaging.in_app import write_notification
#-------------------------------------------------------------------------------
# Notification object handling
#-------------------------------------------------------------------------------
class Notification_obj:
class NotificationInstance:
def __init__(self, db):
self.db = db
@@ -290,45 +297,7 @@ class Notification_obj:
# Reporting
#-------------------------------------------------------------------------------
# Handle Frontend User Notifications
def write_notification(content, level, timestamp):
NOTIFICATION_API_FILE = apiPath + 'user_notifications.json'
# Generate GUID
guid = str(uuid.uuid4())
# Prepare notification dictionary
notification = {
'timestamp': str(timestamp),
'guid': guid,
'read': 0,
'level': level,
'content': content
}
# If file exists, load existing data, otherwise initialize as empty list
if os.path.exists(NOTIFICATION_API_FILE):
with open(NOTIFICATION_API_FILE, 'r') as file:
# Check if the file object is of type _io.TextIOWrapper
if isinstance(file, _io.TextIOWrapper):
file_contents = file.read() # Read file contents
if file_contents == '':
file_contents = '[]' # If file is empty, initialize as empty list
# mylog('debug', ['[Notification] User Notifications file: ', file_contents])
notifications = json.loads(file_contents) # Parse JSON data
else:
mylog('error', 'File is not of type _io.TextIOWrapper')
notifications = []
else:
notifications = []
# Append new notification
notifications.append(notification)
# Write updated data back to file
with open(NOTIFICATION_API_FILE, 'w') as file:
json.dump(notifications, file, indent=4)
#-------------------------------------------------------------------------------
def construct_notifications(JSON, section):

View File

@@ -1,10 +1,15 @@
import os
import sys
# Register NetAlertX directories
INSTALL_PATH="/app"
sys.path.extend([f"{INSTALL_PATH}/server"])
# Register NetAlertX modules
from const import pluginsPath, logPath, applicationPath, reportTemplatesPath
from logger import mylog
class UserEventsQueue:
class UserEventsQueueInstance:
"""
Handles the execution queue log file, allowing reading, writing,
and removing processed events.
@@ -30,7 +35,7 @@ class UserEventsQueue:
Returns an empty list if the file doesn't exist.
"""
if not os.path.exists(self.log_file):
mylog('none', ['[UserEventsQueue] Log file not found: ', self.log_file])
mylog('none', ['[UserEventsQueueInstance] Log file not found: ', self.log_file])
return [] # No log file, return empty list
with open(self.log_file, "r") as file:
return file.readlines()
@@ -72,7 +77,7 @@ class UserEventsQueue:
self.write_log(updated_lines)
mylog('minimal', ['[UserEventsQueue] Processed event: ', event])
mylog('minimal', ['[UserEventsQueueInstance] Processed event: ', event])
return removed

View File

@@ -16,11 +16,11 @@ from helper import timeNowTZ, get_file_content, write_file, get_setting, get_set
from app_state import updateState
from api import update_api
from plugin_utils import logEventStatusCounts, get_plugin_string, get_plugin_setting_obj, print_plugin_info, list_to_csv, combine_plugin_objects, resolve_wildcards_arr, handle_empty, custom_plugin_decoder, decode_and_rename_files
from notification import Notification_obj, write_notification
from user_events_queue import UserEventsQueue
from models.notification_instance import NotificationInstance
from messaging.in_app import write_notification
from models.user_events_queue_instance import UserEventsQueueInstance
from crypto_utils import generate_deterministic_guid
#-------------------------------------------------------------------------------
class plugin_manager:
def __init__(self, db, all_plugins):
@@ -79,7 +79,7 @@ class plugin_manager:
"""
Process user events from the execution queue log file and notify the user about executed events.
"""
execution_log = UserEventsQueue()
execution_log = UserEventsQueueInstance()
# Track whether to show notification for executed events
executed_events = []
@@ -151,7 +151,7 @@ class plugin_manager:
sample_json = json.loads(get_file_content(reportTemplatesPath + 'webhook_json_sample.json'))[0]["body"]["attachments"][0]["text"]
# Create fake notification
notification = Notification_obj(self.db)
notification = NotificationInstance(self.db)
notificationObj = notification.create(sample_json, "")
# Run test
@@ -562,7 +562,7 @@ def execute_plugin(db, all_plugins, plugin ):
endpoints = ["plugins_events","plugins_objects", "plugins_history", "appevents"]
# check if we need to update devices api endpoint as well to prevent long user waits on Loading...
userUpdatedDevices = UserEventsQueue().has_update_devices
userUpdatedDevices = UserEventsQueueInstance().has_update_devices
if userUpdatedDevices:
endpoints += ["devices"]

View File

@@ -8,7 +8,7 @@ import conf
from scan.device_handling import create_new_devices, print_scan_stats, save_scanned_devices, update_devices_data_from_scan, exclude_ignored_devices
from helper import timeNowTZ
from logger import mylog
from reporting import skip_repeated_notifications
from messaging.reporting import skip_repeated_notifications
#===============================================================================