mirror of
https://github.com/jokob-sk/NetAlertX.git
synced 2025-12-06 17:15:38 -08:00
345 lines
11 KiB
Python
Executable File
345 lines
11 KiB
Python
Executable File
# !/usr/bin/env python
|
|
|
|
import os
|
|
import sys
|
|
from flask import jsonify, request
|
|
|
|
# Register NetAlertX directories
|
|
INSTALL_PATH = os.getenv("NETALERTX_APP", "/app")
|
|
sys.path.extend([f"{INSTALL_PATH}/front/plugins", f"{INSTALL_PATH}/server"])
|
|
|
|
from database import get_temp_db_connection # noqa: E402 [flake8 lint suppression]
|
|
from helper import is_random_mac, get_setting_value # noqa: E402 [flake8 lint suppression]
|
|
from utils.datetime_utils import timeNowDB, format_date # noqa: E402 [flake8 lint suppression]
|
|
from db.db_helper import row_to_json, get_date_from_period # noqa: E402 [flake8 lint suppression]
|
|
|
|
# --------------------------
|
|
# Device Endpoints Functions
|
|
# --------------------------
|
|
|
|
|
|
def get_device_data(mac):
|
|
"""Fetch device info with children, event stats, and presence calculation."""
|
|
|
|
# Open temporary connection for this request
|
|
conn = get_temp_db_connection()
|
|
cur = conn.cursor()
|
|
|
|
now = timeNowDB()
|
|
|
|
# Special case for new device
|
|
if mac.lower() == "new":
|
|
|
|
device_data = {
|
|
"devMac": "",
|
|
"devName": "",
|
|
"devOwner": "",
|
|
"devType": "",
|
|
"devVendor": "",
|
|
"devFavorite": 0,
|
|
"devGroup": "",
|
|
"devComments": "",
|
|
"devFirstConnection": now,
|
|
"devLastConnection": now,
|
|
"devLastIP": "",
|
|
"devStaticIP": 0,
|
|
"devScan": 0,
|
|
"devLogEvents": 0,
|
|
"devAlertEvents": 0,
|
|
"devAlertDown": 0,
|
|
"devParentRelType": "default",
|
|
"devReqNicsOnline": 0,
|
|
"devSkipRepeated": 0,
|
|
"devLastNotification": "",
|
|
"devPresentLastScan": 0,
|
|
"devIsNew": 1,
|
|
"devLocation": "",
|
|
"devIsArchived": 0,
|
|
"devParentMAC": "",
|
|
"devParentPort": "",
|
|
"devIcon": "",
|
|
"devGUID": "",
|
|
"devSite": "",
|
|
"devSSID": "",
|
|
"devSyncHubNode": "",
|
|
"devSourcePlugin": "",
|
|
"devCustomProps": "",
|
|
"devStatus": "Unknown",
|
|
"devIsRandomMAC": False,
|
|
"devSessions": 0,
|
|
"devEvents": 0,
|
|
"devDownAlerts": 0,
|
|
"devPresenceHours": 0,
|
|
"devFQDN": "",
|
|
}
|
|
return jsonify(device_data)
|
|
|
|
# Compute period date for sessions/events
|
|
period = request.args.get("period", "") # e.g., '7 days', '1 month', etc.
|
|
period_date_sql = get_date_from_period(period)
|
|
|
|
# Fetch device info + computed fields
|
|
sql = f"""
|
|
SELECT
|
|
d.*,
|
|
CASE
|
|
WHEN d.devAlertDown != 0 AND d.devPresentLastScan = 0 THEN 'Down'
|
|
WHEN d.devPresentLastScan = 1 THEN 'On-line'
|
|
ELSE 'Off-line'
|
|
END AS devStatus,
|
|
|
|
(SELECT COUNT(*) FROM Sessions
|
|
WHERE ses_MAC = d.devMac AND (
|
|
ses_DateTimeConnection >= {period_date_sql} OR
|
|
ses_DateTimeDisconnection >= {period_date_sql} OR
|
|
ses_StillConnected = 1
|
|
)) AS devSessions,
|
|
|
|
(SELECT COUNT(*) FROM Events
|
|
WHERE eve_MAC = d.devMac AND eve_DateTime >= {period_date_sql}
|
|
AND eve_EventType NOT IN ('Connected','Disconnected')) AS devEvents,
|
|
|
|
(SELECT COUNT(*) FROM Events
|
|
WHERE eve_MAC = d.devMac AND eve_DateTime >= {period_date_sql}
|
|
AND eve_EventType = 'Device Down') AS devDownAlerts,
|
|
|
|
(SELECT CAST(MAX(0, SUM(
|
|
julianday(IFNULL(ses_DateTimeDisconnection,'{now}')) -
|
|
julianday(CASE WHEN ses_DateTimeConnection < {period_date_sql}
|
|
THEN {period_date_sql} ELSE ses_DateTimeConnection END)
|
|
) * 24) AS INT)
|
|
FROM Sessions
|
|
WHERE ses_MAC = d.devMac
|
|
AND ses_DateTimeConnection IS NOT NULL
|
|
AND (ses_DateTimeDisconnection IS NOT NULL OR ses_StillConnected = 1)
|
|
AND (ses_DateTimeConnection >= {period_date_sql}
|
|
OR ses_DateTimeDisconnection >= {period_date_sql} OR ses_StillConnected = 1)
|
|
) AS devPresenceHours
|
|
|
|
FROM Devices d
|
|
WHERE d.devMac = ? OR CAST(d.rowid AS TEXT) = ?
|
|
"""
|
|
# Fetch device
|
|
cur.execute(sql, (mac, mac))
|
|
row = cur.fetchone()
|
|
if not row:
|
|
return jsonify({"error": "Device not found"}), 404
|
|
|
|
device_data = row_to_json(list(row.keys()), row)
|
|
device_data["devFirstConnection"] = format_date(device_data["devFirstConnection"])
|
|
device_data["devLastConnection"] = format_date(device_data["devLastConnection"])
|
|
device_data["devIsRandomMAC"] = is_random_mac(device_data["devMac"])
|
|
|
|
# Fetch children
|
|
cur.execute(
|
|
"SELECT * FROM Devices WHERE devParentMAC = ? ORDER BY devPresentLastScan DESC",
|
|
(device_data["devMac"],),
|
|
)
|
|
children_rows = cur.fetchall()
|
|
children = [row_to_json(list(r.keys()), r) for r in children_rows]
|
|
children_nics = [c for c in children if c.get("devParentRelType") == "nic"]
|
|
|
|
device_data["devChildrenDynamic"] = children
|
|
device_data["devChildrenNicsDynamic"] = children_nics
|
|
|
|
conn.close()
|
|
|
|
return jsonify(device_data)
|
|
|
|
|
|
def set_device_data(mac, data):
|
|
"""Update or create a device."""
|
|
if data.get("createNew", False):
|
|
sql = """
|
|
INSERT INTO Devices (
|
|
devMac, devName, devOwner, devType, devVendor, devIcon,
|
|
devFavorite, devGroup, devLocation, devComments,
|
|
devParentMAC, devParentPort, devSSID, devSite,
|
|
devStaticIP, devScan, devAlertEvents, devAlertDown,
|
|
devParentRelType, devReqNicsOnline, devSkipRepeated,
|
|
devIsNew, devIsArchived, devLastConnection,
|
|
devFirstConnection, devLastIP, devGUID, devCustomProps,
|
|
devSourcePlugin
|
|
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
"""
|
|
|
|
values = (
|
|
mac,
|
|
data.get("devName", ""),
|
|
data.get("devOwner", ""),
|
|
data.get("devType", ""),
|
|
data.get("devVendor", ""),
|
|
data.get("devIcon", ""),
|
|
data.get("devFavorite", 0),
|
|
data.get("devGroup", ""),
|
|
data.get("devLocation", ""),
|
|
data.get("devComments", ""),
|
|
data.get("devParentMAC", ""),
|
|
data.get("devParentPort", ""),
|
|
data.get("devSSID", ""),
|
|
data.get("devSite", ""),
|
|
data.get("devStaticIP", 0),
|
|
data.get("devScan", 0),
|
|
data.get("devAlertEvents", 0),
|
|
data.get("devAlertDown", 0),
|
|
data.get("devParentRelType", "default"),
|
|
data.get("devReqNicsOnline", 0),
|
|
data.get("devSkipRepeated", 0),
|
|
data.get("devIsNew", 0),
|
|
data.get("devIsArchived", 0),
|
|
data.get("devLastConnection", timeNowDB()),
|
|
data.get("devFirstConnection", timeNowDB()),
|
|
data.get("devLastIP", ""),
|
|
data.get("devGUID", ""),
|
|
data.get("devCustomProps", ""),
|
|
data.get("devSourcePlugin", "DUMMY"),
|
|
)
|
|
|
|
else:
|
|
sql = """
|
|
UPDATE Devices SET
|
|
devName=?, devOwner=?, devType=?, devVendor=?, devIcon=?,
|
|
devFavorite=?, devGroup=?, devLocation=?, devComments=?,
|
|
devParentMAC=?, devParentPort=?, devSSID=?, devSite=?,
|
|
devStaticIP=?, devScan=?, devAlertEvents=?, devAlertDown=?,
|
|
devParentRelType=?, devReqNicsOnline=?, devSkipRepeated=?,
|
|
devIsNew=?, devIsArchived=?, devCustomProps=?
|
|
WHERE devMac=?
|
|
"""
|
|
values = (
|
|
data.get("devName", ""),
|
|
data.get("devOwner", ""),
|
|
data.get("devType", ""),
|
|
data.get("devVendor", ""),
|
|
data.get("devIcon", ""),
|
|
data.get("devFavorite", 0),
|
|
data.get("devGroup", ""),
|
|
data.get("devLocation", ""),
|
|
data.get("devComments", ""),
|
|
data.get("devParentMAC", ""),
|
|
data.get("devParentPort", ""),
|
|
data.get("devSSID", ""),
|
|
data.get("devSite", ""),
|
|
data.get("devStaticIP", 0),
|
|
data.get("devScan", 0),
|
|
data.get("devAlertEvents", 0),
|
|
data.get("devAlertDown", 0),
|
|
data.get("devParentRelType", "default"),
|
|
data.get("devReqNicsOnline", 0),
|
|
data.get("devSkipRepeated", 0),
|
|
data.get("devIsNew", 0),
|
|
data.get("devIsArchived", 0),
|
|
data.get("devCustomProps", ""),
|
|
mac,
|
|
)
|
|
|
|
conn = get_temp_db_connection()
|
|
cur = conn.cursor()
|
|
cur.execute(sql, values)
|
|
conn.commit()
|
|
conn.close()
|
|
return jsonify({"success": True})
|
|
|
|
|
|
def delete_device(mac):
|
|
"""Delete a device by MAC."""
|
|
conn = get_temp_db_connection()
|
|
cur = conn.cursor()
|
|
cur.execute("DELETE FROM Devices WHERE devMac=?", (mac,))
|
|
conn.commit()
|
|
conn.close()
|
|
return jsonify({"success": True})
|
|
|
|
|
|
def delete_device_events(mac):
|
|
"""Delete all events for a device."""
|
|
conn = get_temp_db_connection()
|
|
cur = conn.cursor()
|
|
cur.execute("DELETE FROM Events WHERE eve_MAC=?", (mac,))
|
|
conn.commit()
|
|
conn.close()
|
|
return jsonify({"success": True})
|
|
|
|
|
|
def reset_device_props(mac, data=None):
|
|
"""Reset device custom properties to default."""
|
|
default_props = get_setting_value("NEWDEV_devCustomProps")
|
|
conn = get_temp_db_connection()
|
|
cur = conn.cursor()
|
|
cur.execute(
|
|
"UPDATE Devices SET devCustomProps=? WHERE devMac=?",
|
|
(default_props, mac),
|
|
)
|
|
conn.commit()
|
|
conn.close()
|
|
return jsonify({"success": True})
|
|
|
|
|
|
def update_device_column(mac, column_name, column_value):
|
|
"""
|
|
Update a specific column for a given device.
|
|
Example: update_device_column("AA:BB:CC:DD:EE:FF", "devParentMAC", "Internet")
|
|
"""
|
|
|
|
conn = get_temp_db_connection()
|
|
cur = conn.cursor()
|
|
|
|
# Build safe SQL with column name whitelisted
|
|
sql = f"UPDATE Devices SET {column_name}=? WHERE devMac=?"
|
|
cur.execute(sql, (column_value, mac))
|
|
conn.commit()
|
|
|
|
if cur.rowcount > 0:
|
|
return jsonify({"success": True})
|
|
else:
|
|
return jsonify({"success": False, "error": "Device not found"}), 404
|
|
|
|
conn.close()
|
|
|
|
return jsonify({"success": True})
|
|
|
|
|
|
def copy_device(mac_from, mac_to):
|
|
"""
|
|
Copy a device entry from one MAC to another.
|
|
If a device already exists with mac_to, it will be replaced.
|
|
"""
|
|
conn = get_temp_db_connection()
|
|
cur = conn.cursor()
|
|
|
|
try:
|
|
# Drop temporary table if exists
|
|
cur.execute("DROP TABLE IF EXISTS temp_devices")
|
|
|
|
# Create temporary table with source device
|
|
cur.execute(
|
|
"CREATE TABLE temp_devices AS SELECT * FROM Devices WHERE devMac = ?",
|
|
(mac_from,),
|
|
)
|
|
|
|
# Update temporary table to target MAC
|
|
cur.execute("UPDATE temp_devices SET devMac = ?", (mac_to,))
|
|
|
|
# Delete previous entry with target MAC
|
|
cur.execute("DELETE FROM Devices WHERE devMac = ?", (mac_to,))
|
|
|
|
# Insert new entry from temporary table
|
|
cur.execute(
|
|
"INSERT INTO Devices SELECT * FROM temp_devices WHERE devMac = ?", (mac_to,)
|
|
)
|
|
|
|
# Drop temporary table
|
|
cur.execute("DROP TABLE temp_devices")
|
|
|
|
conn.commit()
|
|
return jsonify(
|
|
{"success": True, "message": f"Device copied from {mac_from} to {mac_to}"}
|
|
)
|
|
|
|
except Exception as e:
|
|
conn.rollback()
|
|
return jsonify({"success": False, "error": str(e)})
|
|
|
|
finally:
|
|
conn.close()
|