api layer v0.3.1 - /dbquery
Some checks failed
Code checks / check-url-paths (push) Has been cancelled
docker / docker_dev (push) Has been cancelled
Deploy MkDocs / deploy (push) Has been cancelled

Signed-off-by: jokob-sk <jokob-sk@gmail.com>
This commit is contained in:
jokob-sk
2025-08-28 08:12:23 +10:00
parent ae12195439
commit b1b67c268f
3 changed files with 265 additions and 0 deletions

View File

@@ -9,6 +9,7 @@ from .history_endpoint import delete_online_history
from .prometheus_endpoint import get_metric_stats
from .sessions_endpoint import get_sessions, delete_session, create_session, get_sessions_calendar, get_device_sessions, get_session_events
from .nettools_endpoint import wakeonlan, traceroute, speedtest, nslookup, nmap_scan, internet_info
from .dbquery_endpoint import read_query, write_query, update_query, delete_query
from .sync_endpoint import handle_sync_post, handle_sync_get
import sys
@@ -33,6 +34,7 @@ CORS(
r"/history/*": {"origins": "*"},
r"/nettools/*": {"origins": "*"},
r"/sessions/*": {"origins": "*"},
r"/dbquery/*": {"origins": "*"},
r"/events/*": {"origins": "*"}
},
supports_credentials=True,
@@ -263,6 +265,73 @@ def api_internet_info():
return jsonify({"success": False, "error": "Forbidden"}), 403
return internet_info()
# --------------------------
# DB query
# --------------------------
@app.route("/dbquery/read", methods=["POST"])
def dbquery_read():
if not is_authorized():
return jsonify({"error": "Forbidden"}), 403
data = request.get_json() or {}
raw_sql_b64 = data.get("rawSql")
if not raw_sql_b64:
return jsonify({"error": "rawSql is required"}), 400
return read_query(raw_sql_b64)
@app.route("/dbquery/write", methods=["POST"])
def dbquery_write():
if not is_authorized():
return jsonify({"error": "Forbidden"}), 403
data = request.get_json() or {}
raw_sql_b64 = data.get("rawSql")
if not raw_sql_b64:
return jsonify({"error": "rawSql is required"}), 400
return write_query(raw_sql_b64)
@app.route("/dbquery/update", methods=["POST"])
def dbquery_update():
if not is_authorized():
return jsonify({"error": "Forbidden"}), 403
data = request.get_json() or {}
required = ["columnName", "id", "dbtable", "columns", "values"]
if not all(data.get(k) for k in required):
return jsonify({"error": "Missing required parameters"}), 400
return update_query(
column_name=data["columnName"],
ids=data["id"],
dbtable=data["dbtable"],
columns=data["columns"],
values=data["values"],
)
@app.route("/dbquery/delete", methods=["POST"])
def dbquery_delete():
if not is_authorized():
return jsonify({"error": "Forbidden"}), 403
data = request.get_json() or {}
required = ["columnName", "id", "dbtable"]
if not all(data.get(k) for k in required):
return jsonify({"error": "Missing required parameters"}), 400
return delete_query(
column_name=data["columnName"],
ids=data["id"],
dbtable=data["dbtable"],
)
# --------------------------
# Online history
# --------------------------

View File

@@ -0,0 +1,103 @@
#!/usr/bin/env python
import json
import argparse
import os
import pathlib
import base64
import re
import sys
from datetime import datetime
from flask import jsonify, request, Response
import csv
import io
from io import StringIO
# Register NetAlertX directories
INSTALL_PATH="/app"
sys.path.extend([f"{INSTALL_PATH}/front/plugins", f"{INSTALL_PATH}/server"])
from database import get_temp_db_connection
def read_query(raw_sql_b64):
"""Execute a read-only query (SELECT)."""
try:
raw_sql = base64.b64decode(raw_sql_b64).decode("utf-8")
conn = get_temp_db_connection()
cur = conn.cursor()
cur.execute(raw_sql)
rows = cur.fetchall()
# Convert rows → dict list
columns = [col[0] for col in cur.description] if cur.description else []
results = [dict(zip(columns, row)) for row in rows]
conn.close()
return jsonify({"success": True, "results": results})
except Exception as e:
return jsonify({"success": False, "error": str(e)}), 400
def write_query(raw_sql_b64):
"""Execute a write query (INSERT/UPDATE/DELETE)."""
try:
raw_sql = base64.b64decode(raw_sql_b64).decode("utf-8")
conn = get_temp_db_connection()
cur = conn.cursor()
cur.execute(raw_sql)
conn.commit()
affected = cur.rowcount
conn.close()
return jsonify({"success": True, "affected_rows": affected})
except Exception as e:
return jsonify({"success": False, "error": str(e)}), 400
def update_query(column_name, ids, dbtable, columns, values):
"""Update rows in dbtable based on column_name + ids."""
try:
conn = get_temp_db_connection()
cur = conn.cursor()
if not isinstance(ids, list):
ids = [ids]
updated_count = 0
for i in range(len(ids)):
set_clause = ", ".join([f"{col} = ?" for col in columns])
sql = f"UPDATE {dbtable} SET {set_clause} WHERE {column_name} = ?"
params = list(values) + [ids[i]]
cur.execute(sql, params)
updated_count += cur.rowcount
conn.commit()
conn.close()
return jsonify({"success": True, "updated_count": updated_count})
except Exception as e:
return jsonify({"success": False, "error": str(e)}), 400
def delete_query(column_name, ids, dbtable):
"""Delete rows in dbtable based on column_name + ids."""
try:
conn = get_temp_db_connection()
cur = conn.cursor()
if not isinstance(ids, list):
ids = [ids]
deleted_count = 0
for id_val in ids:
sql = f"DELETE FROM {dbtable} WHERE {column_name} = ?"
cur.execute(sql, (id_val,))
deleted_count += cur.rowcount
conn.commit()
conn.close()
return jsonify({"success": True, "deleted_count": deleted_count})
except Exception as e:
return jsonify({"success": False, "error": str(e)}), 400