Merge branch 'main' into devving-devcontainer

This commit is contained in:
Adam Outler
2025-09-20 18:42:34 -04:00
committed by GitHub
7 changed files with 178 additions and 133 deletions

View File

@@ -62,7 +62,7 @@ function renderLogArea($params) {
'</textarea> '</textarea>
</div> </div>
<div class="row logs-row"> <div class="row logs-row">
<div class="log-file col-sm-6 col-xs-12">' . htmlspecialchars($fileName) . ' <div class="log-file col-sm-6 col-xs-12">' . htmlspecialchars($filePath) . '
<div class="logs-size">' . number_format((filesize($filePath) / 1000000), 2, ",", ".") . ' MB' <div class="logs-size">' . number_format((filesize($filePath) / 1000000), 2, ",", ".") . ' MB'
. $downloadButtonHtml . . $downloadButtonHtml .
'</div> '</div>

View File

@@ -82,8 +82,7 @@ class CustomDatabaseWrapper {
private $maxRetries; private $maxRetries;
private $retryDelay; private $retryDelay;
public function __construct($filename, $flags = SQLITE3_OPEN_READWRITE | SQLITE3_OPEN_CREATE, public function __construct($filename, $flags = SQLITE3_OPEN_READWRITE | SQLITE3_OPEN_CREATE, $maxRetries = 3, $retryDelay = 1000, $encryptionKey = "") {
$maxRetries = 3, $retryDelay = 1000, $encryptionKey = "") {
$this->sqlite = new SQLite3($filename, $flags, $encryptionKey); $this->sqlite = new SQLite3($filename, $flags, $encryptionKey);
$this->maxRetries = $maxRetries; $this->maxRetries = $maxRetries;
$this->retryDelay = $retryDelay; $this->retryDelay = $retryDelay;

View File

@@ -48,7 +48,7 @@ if (!empty($_REQUEST['action']) && $_REQUEST['action'] == 'logout') {
// Load configuration // Load configuration
if (!file_exists(CONFIG_PATH)) { if (!file_exists(CONFIG_PATH)) {
die("Configuration file not found."); die("Configuration file not found in " . $_SERVER['DOCUMENT_ROOT'] . "/../config/app.conf");
} }
$configLines = file(CONFIG_PATH); $configLines = file(CONFIG_PATH);

View File

@@ -14,7 +14,8 @@ echo "---------------------------------------------------------"
# Set environment variables # Set environment variables
INSTALL_DIR=/app # Specify the installation directory here INSTALL_DIR=/app # Specify the installation directory here
INSTALLER_DIR=$INSTALL_DIR/install/ubuntu24 INSTALL_SYSTEM_NAME=ubuntu24
INSTALLER_DIR=$INSTALL_DIR/install/$INSTALL_SYSTEM_NAME
# Check if script is run as root # Check if script is run as root
if [[ $EUID -ne 0 ]]; then if [[ $EUID -ne 0 ]]; then
@@ -101,5 +102,5 @@ fi
# This is where we setup the virtual environment and install dependencies # This is where we setup the virtual environment and install dependencies
cd "$INSTALLER_DIR" || { echo "Failed to change directory to $INSTALLER_DIR"; exit 1; } cd "$INSTALLER_DIR" || { echo "Failed to change directory to $INSTALLER_DIR"; exit 1; }
chmod +x "$INSTALLER_DIR/start.ubuntu24.sh" chmod +x "$INSTALLER_DIR/start.$INSTALL_SYSTEM_NAME.sh"
"$INSTALLER_DIR/start.ubuntu24.sh" "$INSTALLER_DIR/start.$INSTALL_SYSTEM_NAME.sh"

View File

@@ -10,7 +10,8 @@ echo "This script will set up and start NetAlertX on your Ubuntu24 system."
INSTALL_DIR=/app INSTALL_DIR=/app
# DO NOT CHANGE ANYTHING BELOW THIS LINE! # DO NOT CHANGE ANYTHING BELOW THIS LINE!
INSTALLER_DIR=$INSTALL_DIR/install/ubuntu24 INSTALL_SYSTEM_NAME=ubuntu24
INSTALLER_DIR=$INSTALL_DIR/install/$INSTALL_SYSTEM_NAME
CONF_FILE=app.conf CONF_FILE=app.conf
DB_FILE=app.db DB_FILE=app.db
NGINX_CONF_FILE=netalertx.conf NGINX_CONF_FILE=netalertx.conf
@@ -50,11 +51,12 @@ echo
# Install dependencies # Install dependencies
apt-get install -y \ apt-get install -y \
tini snmp ca-certificates curl libwww-perl arp-scan perl apt-utils cron \ tini snmp ca-certificates curl libwww-perl arp-scan perl apt-utils cron \
nginx-light php php-cgi php-fpm php-sqlite3 php-curl sqlite3 dnsutils net-tools \ sqlite3 dnsutils net-tools mtr \
python3 python3-dev iproute2 nmap python3-pip zip usbutils traceroute nbtscan avahi-daemon avahi-utils build-essential python3 python3-dev iproute2 nmap python3-pip zip usbutils traceroute nbtscan avahi-daemon avahi-utils build-essential
# alternate dependencies # alternate dependencies
apt-get install nginx nginx-core mtr php-fpm php${PHPVERSION}-fpm php-cli php${PHPVERSION} php${PHPVERSION}-sqlite3 -y # nginx-core install nginx and nginx-common as dependencies
apt-get install nginx-core php${PHPVERSION} php${PHPVERSION}-sqlite3 php php-cgi php-fpm php-sqlite3 php-curl php-fpm php${PHPVERSION}-fpm php-cli -y
phpenmod -v ${PHPVERSION} sqlite3 phpenmod -v ${PHPVERSION} sqlite3
update-alternatives --install /usr/bin/python python /usr/bin/python3 10 update-alternatives --install /usr/bin/python python /usr/bin/python3 10
@@ -138,22 +140,31 @@ else
fi fi
fi fi
# create log and api mounts echo "---------------------------------------------------------"
echo "[INSTALL] Create log and api mounts" echo "[INSTALL] Create log and api mounts"
mkdir -p "${INSTALL_DIR}/log" "${INSTALL_DIR}/api" echo "---------------------------------------------------------"
umount "${INSTALL_DIR}/log" 2>/dev/null || true echo
umount "${INSTALL_DIR}/api" 2>/dev/null || true
mount -t tmpfs -o size=32m,noexec,nosuid,nodev tmpfs "${INSTALL_DIR}/log"
mount -t tmpfs -o size=16m,noexec,nosuid,nodev tmpfs "${INSTALL_DIR}/api"
# Create an empty log files
# Create the execution_queue.log file if it doesn't exist echo "[INSTALL] Cleaning up old mounts if any"
umount "${INSTALL_DIR}/log"
umount "${INSTALL_DIR}/api"
echo "[INSTALL] Creating log and api folders if they don't exist"
mkdir -p "${INSTALL_DIR}/log" "${INSTALL_DIR}/api"
echo "[INSTALL] Mounting log and api folders as tmpfs"
mount -t tmpfs -o noexec,nosuid,nodev tmpfs "${INSTALL_DIR}/log"
mount -t tmpfs -o noexec,nosuid,nodev tmpfs "${INSTALL_DIR}/api"
# Create log files if they don't exist
echo "[INSTALL] Creating log files if they don't exist"
touch "${INSTALL_DIR}"/log/{app.log,execution_queue.log,app_front.log,app.php_errors.log,stderr.log,stdout.log,db_is_locked.log} touch "${INSTALL_DIR}"/log/{app.log,execution_queue.log,app_front.log,app.php_errors.log,stderr.log,stdout.log,db_is_locked.log}
touch "${INSTALL_DIR}"/api/user_notifications.json touch "${INSTALL_DIR}"/api/user_notifications.json
# Create plugins sub-directory if it doesn't exist in case a custom log folder is used # Create plugins sub-directory if it doesn't exist in case a custom log folder is used
mkdir -p "${INSTALL_DIR}"/log/plugins mkdir -p "${INSTALL_DIR}"/log/plugins
# Fixing file permissions # Fixing file permissions
echo "[INSTALL] Fixing file permissions" echo "[INSTALL] Fixing file permissions"
chown root:www-data "${INSTALL_DIR}"/api/user_notifications.json chown root:www-data "${INSTALL_DIR}"/api/user_notifications.json
@@ -182,8 +193,8 @@ fi
# Copy starter $DB_FILE and $CONF_FILE if they don't exist # Copy starter $DB_FILE and $CONF_FILE if they don't exist
cp --update=none "${INSTALL_PATH}/back/$CONF_FILE" "${INSTALL_PATH}/config/$CONF_FILE" cp -u "${INSTALL_PATH}/back/$CONF_FILE" "${INSTALL_PATH}/config/$CONF_FILE"
cp --update=none "${INSTALL_PATH}/back/$DB_FILE" "$FILEDB" cp -u "${INSTALL_PATH}/back/$DB_FILE" "$FILEDB"
echo "[INSTALL] Fixing permissions after copied starter config & DB" echo "[INSTALL] Fixing permissions after copied starter config & DB"

View File

@@ -186,9 +186,15 @@ def main ():
pm.run_plugin_scripts('on_notification') pm.run_plugin_scripts('on_notification')
notification.setAllProcessed() notification.setAllProcessed()
# clear pending email flag
# and the plugin events
notification.clearPendingEmailFlag() notification.clearPendingEmailFlag()
else: else:
# If there are no notifications to process,
# we still need to clear all plugin events
notification.clearPluginEvents()
mylog('verbose', ['[Notification] No changes to report']) mylog('verbose', ['[Notification] No changes to report'])
# Commit SQL # Commit SQL

View File

@@ -1,30 +1,31 @@
import datetime
import os
import _io
import json import json
import sys import sys
import uuid import uuid
import socket import socket
import subprocess import subprocess
import requests
from yattag import indent from yattag import indent
from json2table import convert from json2table import convert
# Register NetAlertX directories # Register NetAlertX directories
INSTALL_PATH="/app" INSTALL_PATH = "/app"
sys.path.extend([f"{INSTALL_PATH}/server"]) sys.path.extend([f"{INSTALL_PATH}/server"])
# Register NetAlertX modules # Register NetAlertX modules
import conf import conf
from const import applicationPath, logPath, apiPath, confFileName, reportTemplatesPath from const import applicationPath, logPath, apiPath, reportTemplatesPath
from logger import logResult, mylog from logger import mylog
from helper import generate_mac_links, removeDuplicateNewLines, timeNowTZ, get_file_content, write_file, get_setting_value, get_timezone_offset from helper import generate_mac_links, \
removeDuplicateNewLines, \
timeNowTZ, \
write_file, \
get_setting_value, \
get_timezone_offset
from messaging.in_app import write_notification from messaging.in_app import write_notification
#------------------------------------------------------------------------------- # -----------------------------------------------------------------------------
# Notification object handling # Notification object handling
#------------------------------------------------------------------------------- # -----------------------------------------------------------------------------
class NotificationInstance: class NotificationInstance:
def __init__(self, db): def __init__(self, db):
self.db = db self.db = db
@@ -52,14 +53,13 @@ class NotificationInstance:
return JSON, Extra return JSON, Extra
# Create a new DB entry if new notifications available, otherwise skip # Create a new DB entry if new notifications available, otherwise skip
def create(self, JSON, Extra=""): def create(self, JSON, Extra=""):
JSON, Extra = self.on_before_create(JSON, Extra) JSON, Extra = self.on_before_create(JSON, Extra)
# Write output data for debug # Write output data for debug
write_file (logPath + '/report_output.json', json.dumps(JSON)) write_file(logPath + '/report_output.json', json.dumps(JSON))
# Check if nothing to report, end # Check if nothing to report, end
if JSON["new_devices"] == [] and JSON["down_devices"] == [] and JSON["events"] == [] and JSON["plugins"] == [] and JSON["down_reconnected"] == []: if JSON["new_devices"] == [] and JSON["down_devices"] == [] and JSON["events"] == [] and JSON["plugins"] == [] and JSON["down_reconnected"] == []:
@@ -78,8 +78,6 @@ class NotificationInstance:
self.Extra = Extra self.Extra = Extra
if self.HasNotifications: if self.HasNotifications:
# if not notiStruc.json['data'] and not notiStruc.text and not notiStruc.html: # if not notiStruc.json['data'] and not notiStruc.text and not notiStruc.html:
# mylog('debug', '[Notification] notiStruc is empty') # mylog('debug', '[Notification] notiStruc is empty')
# else: # else:
@@ -89,7 +87,6 @@ class NotificationInstance:
HTML = "" HTML = ""
template_file_path = reportTemplatesPath + 'report_template.html' template_file_path = reportTemplatesPath + 'report_template.html'
# Open text Template # Open text Template
mylog('verbose', ['[Notification] Open text Template']) mylog('verbose', ['[Notification] Open text Template'])
template_file = open(reportTemplatesPath + 'report_template.txt', 'r') template_file = open(reportTemplatesPath + 'report_template.txt', 'r')
@@ -105,38 +102,54 @@ class NotificationInstance:
# prepare new version text # prepare new version text
newVersionText = '' newVersionText = ''
if conf.newVersionAvailable : if conf.newVersionAvailable:
newVersionText = '🚀A new version is available.' newVersionText = '🚀A new version is available.'
mail_text = mail_text.replace ('<NEW_VERSION>', newVersionText) mail_text = mail_text.replace('<NEW_VERSION>', newVersionText)
mail_html = mail_html.replace ('<NEW_VERSION>', newVersionText) mail_html = mail_html.replace('<NEW_VERSION>', newVersionText)
# Report "REPORT_DATE" in Header & footer # Report "REPORT_DATE" in Header & footer
timeFormated = timeNowTZ().strftime ('%Y-%m-%d %H:%M') timeFormated = timeNowTZ().strftime('%Y-%m-%d %H:%M')
mail_text = mail_text.replace ('<REPORT_DATE>', timeFormated) mail_text = mail_text.replace('<REPORT_DATE>', timeFormated)
mail_html = mail_html.replace ('<REPORT_DATE>', timeFormated) mail_html = mail_html.replace('<REPORT_DATE>', timeFormated)
# Report "SERVER_NAME" in Header & footer # Report "SERVER_NAME" in Header & footer
mail_text = mail_text.replace ('<SERVER_NAME>', socket.gethostname() ) mail_text = mail_text.replace('<SERVER_NAME>', socket.gethostname())
mail_html = mail_html.replace ('<SERVER_NAME>', socket.gethostname() ) mail_html = mail_html.replace('<SERVER_NAME>', socket.gethostname())
# Report "VERSION" in Header & footer # Report "VERSION" in Header & footer
VERSIONFILE = subprocess.check_output(['php', applicationPath + '/front/php/templates/version.php']).decode('utf-8') try:
mail_text = mail_text.replace ('<BUILD_VERSION>', VERSIONFILE) VERSIONFILE = subprocess.check_output(
mail_html = mail_html.replace ('<BUILD_VERSION>', VERSIONFILE) ['php', applicationPath + '/front/php/templates/version.php'],
timeout=5
).decode('utf-8')
except Exception as e:
mylog('debug', [f'[Notification] Unable to read version.php: {e}'])
VERSIONFILE = 'unknown'
mail_text = mail_text.replace('<BUILD_VERSION>', VERSIONFILE)
mail_html = mail_html.replace('<BUILD_VERSION>', VERSIONFILE)
# Report "BUILD" in Header & footer # Report "BUILD" in Header & footer
BUILDFILE = subprocess.check_output(['php', applicationPath + '/front/php/templates/build.php']).decode('utf-8') try:
mail_text = mail_text.replace ('<BUILD_DATE>', BUILDFILE) BUILDFILE = subprocess.check_output(
mail_html = mail_html.replace ('<BUILD_DATE>', BUILDFILE) ['php', applicationPath + '/front/php/templates/build.php'],
timeout=5
).decode('utf-8')
except Exception as e:
mylog('debug', [f'[Notification] Unable to read build.php: {e}'])
BUILDFILE = 'unknown'
mail_text = mail_text.replace('<BUILD_DATE>', BUILDFILE)
mail_html = mail_html.replace('<BUILD_DATE>', BUILDFILE)
# Start generating the TEXT & HTML notification messages # Start generating the TEXT & HTML notification messages
# new_devices # new_devices
# --- # ---
html, text = construct_notifications(self.JSON, "new_devices") html, text = construct_notifications(self.JSON, "new_devices")
mail_text = mail_text.replace ('<NEW_DEVICES_TABLE>', text + '\n') mail_text = mail_text.replace('<NEW_DEVICES_TABLE>', text + '\n')
mail_html = mail_html.replace ('<NEW_DEVICES_TABLE>', html) mail_html = mail_html.replace('<NEW_DEVICES_TABLE>', html)
mylog('verbose', ['[Notification] New Devices sections done.']) mylog('verbose', ['[Notification] New Devices sections done.'])
# down_devices # down_devices
@@ -144,8 +157,8 @@ class NotificationInstance:
html, text = construct_notifications(self.JSON, "down_devices") html, text = construct_notifications(self.JSON, "down_devices")
mail_text = mail_text.replace ('<DOWN_DEVICES_TABLE>', text + '\n') mail_text = mail_text.replace('<DOWN_DEVICES_TABLE>', text + '\n')
mail_html = mail_html.replace ('<DOWN_DEVICES_TABLE>', html) mail_html = mail_html.replace('<DOWN_DEVICES_TABLE>', html)
mylog('verbose', ['[Notification] Down Devices sections done.']) mylog('verbose', ['[Notification] Down Devices sections done.'])
# down_reconnected # down_reconnected
@@ -153,8 +166,8 @@ class NotificationInstance:
html, text = construct_notifications(self.JSON, "down_reconnected") html, text = construct_notifications(self.JSON, "down_reconnected")
mail_text = mail_text.replace ('<DOWN_RECONNECTED_TABLE>', text + '\n') mail_text = mail_text.replace('<DOWN_RECONNECTED_TABLE>', text + '\n')
mail_html = mail_html.replace ('<DOWN_RECONNECTED_TABLE>', html) mail_html = mail_html.replace('<DOWN_RECONNECTED_TABLE>', html)
mylog('verbose', ['[Notification] Reconnected Down Devices sections done.']) mylog('verbose', ['[Notification] Reconnected Down Devices sections done.'])
@@ -163,8 +176,8 @@ class NotificationInstance:
html, text = construct_notifications(self.JSON, "events") html, text = construct_notifications(self.JSON, "events")
mail_text = mail_text.replace ('<EVENTS_TABLE>', text + '\n') mail_text = mail_text.replace('<EVENTS_TABLE>', text + '\n')
mail_html = mail_html.replace ('<EVENTS_TABLE>', html) mail_html = mail_html.replace('<EVENTS_TABLE>', html)
mylog('verbose', ['[Notification] Events sections done.']) mylog('verbose', ['[Notification] Events sections done.'])
@@ -172,28 +185,28 @@ class NotificationInstance:
# --- # ---
html, text = construct_notifications(self.JSON, "plugins") html, text = construct_notifications(self.JSON, "plugins")
mail_text = mail_text.replace ('<PLUGINS_TABLE>', text + '\n') mail_text = mail_text.replace('<PLUGINS_TABLE>', text + '\n')
mail_html = mail_html.replace ('<PLUGINS_TABLE>', html) mail_html = mail_html.replace('<PLUGINS_TABLE>', html)
mylog('verbose', ['[Notification] Plugins sections done.']) mylog('verbose', ['[Notification] Plugins sections done.'])
final_text = removeDuplicateNewLines(mail_text) final_text = removeDuplicateNewLines(mail_text)
# Create clickable MAC links # Create clickable MAC links
mail_html = generate_mac_links (mail_html, conf.REPORT_DASHBOARD_URL + '/deviceDetails.php?mac=') mail_html = generate_mac_links(mail_html, conf.REPORT_DASHBOARD_URL + '/deviceDetails.php?mac=')
final_html = indent( final_html = indent(
mail_html, mail_html,
indentation = ' ', indentation=' ',
newline = '\r\n', newline='\r\n',
indent_text = True indent_text=True
) )
send_api(self.JSON, final_text, final_html) send_api(self.JSON, final_text, final_html)
# Write output data for debug # Write output data for debug
write_file (logPath + '/report_output.txt', final_text) write_file(logPath + '/report_output.txt', final_text)
write_file (logPath + '/report_output.html', final_html) write_file(logPath + '/report_output.html', final_html)
mylog('minimal', ['[Notification] Udating API files']) mylog('minimal', ['[Notification] Udating API files'])
@@ -201,7 +214,7 @@ class NotificationInstance:
self.HTML = final_html self.HTML = final_html
# Notify frontend # Notify frontend
write_notification(f'Report:{self.GUID}', "alert", self.DateTimeCreated ) write_notification(f'Report:{self.GUID}', "alert", self.DateTimeCreated)
self.upsert() self.upsert()
@@ -256,57 +269,63 @@ class NotificationInstance:
self.save() self.save()
# Clear the Pending Email flag from all events and devices
def clearPendingEmailFlag(self): def clearPendingEmailFlag(self):
# Clean Pending Alert Events # Clean Pending Alert Events
self.db.sql.execute ("""UPDATE Devices SET devLastNotification = ? self.db.sql.execute("""
UPDATE Devices SET devLastNotification = ?
WHERE devMac IN ( WHERE devMac IN (
SELECT eve_MAC FROM Events SELECT eve_MAC FROM Events
WHERE eve_PendingAlertEmail = 1 WHERE eve_PendingAlertEmail = 1
) )
""", (timeNowTZ(),) ) """, (timeNowTZ(),))
self.db.sql.execute ("""UPDATE Events SET eve_PendingAlertEmail = 0 self.db.sql.execute("""
UPDATE Events SET eve_PendingAlertEmail = 0
WHERE eve_PendingAlertEmail = 1 WHERE eve_PendingAlertEmail = 1
AND eve_EventType !='Device Down' """) AND eve_EventType !='Device Down' """)
# Clear down events flag after the reporting window passed # Clear down events flag after the reporting window passed
self.db.sql.execute (f"""UPDATE Events SET eve_PendingAlertEmail = 0 minutes = int(get_setting_value('NTFPRCS_alert_down_time') or 0)
tz_offset = get_timezone_offset()
self.db.sql.execute("""
UPDATE Events
SET eve_PendingAlertEmail = 0
WHERE eve_PendingAlertEmail = 1 WHERE eve_PendingAlertEmail = 1
AND eve_EventType =='Device Down' AND eve_EventType = 'Device Down'
AND eve_DateTime < datetime('now', '-{get_setting_value('NTFPRCS_alert_down_time')} minutes', '{get_timezone_offset()}') AND eve_DateTime < datetime('now', ?, ?)
""") """, (f"-{minutes} minutes", tz_offset))
mylog('minimal', ['[Notification] Notifications changes: ',
self.db.sql.rowcount])
# clear plugin events # clear plugin events
self.db.sql.execute ("DELETE FROM Plugins_Events") self.clearPluginEvents()
# DEBUG - print number of rows updated
mylog('minimal', ['[Notification] Notifications changes: ', self.db.sql.rowcount])
def clearPluginEvents(self):
# clear plugin events table
self.db.sql.execute("DELETE FROM Plugins_Events")
self.save() self.save()
def save(self): def save(self):
# Commit changes # Commit changes
self.db.commitDB() self.db.commitDB()
#-------------------------------------------------------------------------------
# -----------------------------------------------------------------------------
# Reporting # Reporting
#------------------------------------------------------------------------------- # -----------------------------------------------------------------------------
# ------------------------------------------------------------------------------
#-------------------------------------------------------------------------------
def construct_notifications(JSON, section): def construct_notifications(JSON, section):
jsn = JSON[section] jsn = JSON[section]
# Return if empty # Return if empty
if jsn == []: if jsn == []:
return '','' return '', ''
tableTitle = JSON[section + "_meta"]["title"] tableTitle = JSON[section + "_meta"]["title"]
headers = JSON[section + "_meta"]["columnNames"] headers = JSON[section + "_meta"]["columnNames"]
@@ -314,14 +333,20 @@ def construct_notifications(JSON, section):
html = '' html = ''
text = '' text = ''
table_attributes = {"style" : "border-collapse: collapse; font-size: 12px; color:#70707", "width" : "100%", "cellspacing" : 0, "cellpadding" : "3px", "bordercolor" : "#C0C0C0", "border":"1"} table_attributes = {
"style": "border-collapse: collapse; font-size: 12px; color:#70707",
"width": "100%",
"cellspacing": 0,
"cellpadding": "3px",
"bordercolor": "#C0C0C0",
"border": "1"
}
headerProps = "width='120px' style='color:white; font-size: 16px;' bgcolor='#64a0d6' " headerProps = "width='120px' style='color:white; font-size: 16px;' bgcolor='#64a0d6' "
thProps = "width='120px' style='color:#F0F0F0' bgcolor='#64a0d6' " thProps = "width='120px' style='color:#F0F0F0' bgcolor='#64a0d6' "
build_direction = "TOP_TO_BOTTOM" build_direction = "TOP_TO_BOTTOM"
text_line = '{}\t{}\n' text_line = '{}\t{}\n'
if len(jsn) > 0: if len(jsn) > 0:
text = tableTitle + "\n---------\n" text = tableTitle + "\n---------\n"
@@ -329,7 +354,13 @@ def construct_notifications(JSON, section):
html = convert({"data": jsn}, build_direction=build_direction, table_attributes=table_attributes) html = convert({"data": jsn}, build_direction=build_direction, table_attributes=table_attributes)
# Cleanup the generated HTML table notification # Cleanup the generated HTML table notification
html = format_table(html, "data", headerProps, tableTitle).replace('<ul>','<ul style="list-style:none;padding-left:0">').replace("<td>null</td>", "<td></td>") html = format_table(html,
"data",
headerProps,
tableTitle).replace('<ul>',
'<ul style="list-style:none;padding-left:0">'
).replace("<td>null</td>",
"<td></td>")
# prepare text-only message # prepare text-only message
for device in jsn: for device in jsn:
@@ -337,7 +368,7 @@ def construct_notifications(JSON, section):
padding = "" padding = ""
if len(header) < 4: if len(header) < 4:
padding = "\t" padding = "\t"
text += text_line.format ( header + ': ' + padding, device[header]) text += text_line.format(header + ': ' + padding, device[header])
text += '\n' text += '\n'
# Format HTML table headers # Format HTML table headers
@@ -346,24 +377,21 @@ def construct_notifications(JSON, section):
return html, text return html, text
#-------------------------------------------------------------------------------
# -----------------------------------------------------------------------------
def send_api(json_final, mail_text, mail_html): def send_api(json_final, mail_text, mail_html):
mylog('verbose', ['[Send API] Updating notification_* files in ', apiPath]) mylog('verbose', ['[Send API] Updating notification_* files in ', apiPath])
write_file(apiPath + 'notification_text.txt' , mail_text) write_file(apiPath + 'notification_text.txt', mail_text)
write_file(apiPath + 'notification_text.html' , mail_html) write_file(apiPath + 'notification_text.html', mail_html)
write_file(apiPath + 'notification_json_final.json' , json.dumps(json_final)) write_file(apiPath + 'notification_json_final.json', json.dumps(json_final))
# -----------------------------------------------------------------------------
#-------------------------------------------------------------------------------
# Replacing table headers # Replacing table headers
def format_table (html, thValue, props, newThValue = ''): def format_table(html, thValue, props, newThValue=''):
if newThValue == '': if newThValue == '':
newThValue = thValue newThValue = thValue
return html.replace("<th>"+thValue+"</th>", "<th "+props+" >"+newThValue+"</th>" ) return html.replace("<th>"+thValue+"</th>", "<th "+props+" >"+newThValue+"</th>")