mirror of
https://github.com/jokob-sk/NetAlertX.git
synced 2026-03-31 23:32:22 -07:00
Compare commits
14 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6bc2de6e24 | ||
|
|
09b42166cc | ||
|
|
dbe490a042 | ||
|
|
5996e70f60 | ||
|
|
15366a7f2e | ||
|
|
d5d1684ef9 | ||
|
|
c1141fc9a8 | ||
|
|
d38dcda35b | ||
|
|
ac5224747e | ||
|
|
5c23bde21c | ||
|
|
8e83d9b67d | ||
|
|
30c004eb77 | ||
|
|
1b6dc94bae | ||
|
|
76d37edc63 |
17
.github/workflows/code-checks.yml
vendored
17
.github/workflows/code-checks.yml
vendored
@@ -17,6 +17,23 @@ jobs:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: 🚨 Ensure DELETE FROM CurrentScan is not commented out
|
||||
run: |
|
||||
echo "🔍 Checking that DELETE FROM CurrentScan is not commented out..."
|
||||
|
||||
MATCHES=$(grep -RInE '^[[:space:]]*#[[:space:]]*db\.sql\.execute\("DELETE FROM CurrentScan"\)' \
|
||||
--include="*.py" .) || true
|
||||
|
||||
if [ -n "$MATCHES" ]; then
|
||||
echo "❌ Found commented-out DELETE FROM CurrentScan call:"
|
||||
echo "$MATCHES"
|
||||
echo
|
||||
echo "This line must NOT be commented out in committed code."
|
||||
exit 1
|
||||
else
|
||||
echo "✅ DELETE FROM CurrentScan is active."
|
||||
fi
|
||||
|
||||
- name: Check for incorrect absolute '/php/' URLs in frontend code
|
||||
run: |
|
||||
echo "🔍 Checking for incorrect absolute '/php/' URLs (should be 'php/' or './php/')..."
|
||||
|
||||
6
.github/workflows/run-all-tests.yml
vendored
6
.github/workflows/run-all-tests.yml
vendored
@@ -3,8 +3,8 @@ name: 🧪 Manual Test Suite Selector
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
run_authoritative:
|
||||
description: '📂 authoritative_fields/ (Logic, Locks, IPs)'
|
||||
run_scan:
|
||||
description: '📂 scan/ (Scan, Logic, Locks, IPs)'
|
||||
type: boolean
|
||||
default: true
|
||||
run_api:
|
||||
@@ -43,7 +43,7 @@ jobs:
|
||||
run: |
|
||||
PATHS=""
|
||||
# Folder Mapping with 'test/' prefix
|
||||
if [ "${{ github.event.inputs.run_authoritative }}" == "true" ]; then PATHS="$PATHS test/authoritative_fields/"; fi
|
||||
if [ "${{ github.event.inputs.scan }}" == "true" ]; then PATHS="$PATHS test/scan/"; fi
|
||||
if [ "${{ github.event.inputs.run_api }}" == "true" ]; then PATHS="$PATHS test/api_endpoints/ test/server/"; fi
|
||||
if [ "${{ github.event.inputs.run_backend }}" == "true" ]; then PATHS="$PATHS test/backend/"; fi
|
||||
if [ "${{ github.event.inputs.run_docker_env }}" == "true" ]; then PATHS="$PATHS test/docker_tests/"; fi
|
||||
|
||||
@@ -56,14 +56,14 @@ docker run -d \
|
||||
--tmpfs /tmp:uid=20211,gid=20211,mode=1700 \
|
||||
-e PORT=20211 \
|
||||
-e APP_CONF_OVERRIDE='{"GRAPHQL_PORT":"20214"}' \
|
||||
ghcr.io/jokob-sk/netalertx:latest
|
||||
ghcr.io/netalertx/netalertx:latest
|
||||
```
|
||||
|
||||
Note: Your `/local_data_dir` should contain a `config` and `db` folder.
|
||||
|
||||
To deploy a containerized instance directly from the source repository, execute the following BASH sequence:
|
||||
```bash
|
||||
git clone https://github.com/jokob-sk/NetAlertX.git
|
||||
git clone https://github.com/netalertx/NetAlertX.git
|
||||
cd NetAlertX
|
||||
docker compose up --force-recreate --build
|
||||
# To customize: edit docker-compose.yaml and run that last command again
|
||||
|
||||
@@ -21,7 +21,7 @@ docker run \
|
||||
--tmpfs /tmp:uid=20211,gid=20211,mode=1700 \
|
||||
-e PORT=20211 \
|
||||
-e APP_CONF_OVERRIDE='{"GRAPHQL_PORT":"20214"}' \
|
||||
ghcr.io/jokob-sk/netalertx:latest
|
||||
ghcr.io/netalertx/netalertx:latest
|
||||
|
||||
```
|
||||
|
||||
@@ -34,7 +34,7 @@ Note: Your `/local_data_dir` should contain a `config` and `db` folder.
|
||||
|
||||
If possible, check if your issue got fixed in the `_dev` image before opening a new issue. The container is:
|
||||
|
||||
`ghcr.io/jokob-sk/netalertx-dev:latest`
|
||||
`ghcr.io/netalertx/netalertx-dev:latest`
|
||||
|
||||
> ⚠ Please backup your DB and config beforehand!
|
||||
|
||||
|
||||
@@ -8,26 +8,26 @@ Before starting development, please review the following guidelines.
|
||||
|
||||
### Priority Order (Highest to Lowest)
|
||||
|
||||
1. 🔼 Fixing core bugs that lack workarounds
|
||||
2. 🔵 Adding core functionality that unlocks other features (e.g., plugins)
|
||||
3. 🔵 Refactoring to enable faster development
|
||||
4. 🔽 UI improvements (PRs welcome, but low priority)
|
||||
1. 🔼 Fixing core bugs that lack workarounds
|
||||
2. 🔵 Adding core functionality that unlocks other features (e.g., plugins)
|
||||
3. 🔵 Refactoring to enable faster development
|
||||
4. 🔽 UI improvements (PRs welcome, but low priority)
|
||||
|
||||
### Design Philosophy
|
||||
|
||||
The application architecture is designed for extensibility and maintainability. It relies heavily on configuration manifests via plugins and settings to dynamically build the UI and populate the application with data from various sources.
|
||||
The application architecture is designed for extensibility and maintainability. It relies heavily on configuration manifests via plugins and settings to dynamically build the UI and populate the application with data from various sources.
|
||||
|
||||
For details, see:
|
||||
- [Plugins Development](PLUGINS_DEV.md) (includes video)
|
||||
- [Settings System](SETTINGS_SYSTEM.md)
|
||||
For details, see:
|
||||
- [Plugins Development](PLUGINS_DEV.md) (includes video)
|
||||
- [Settings System](SETTINGS_SYSTEM.md)
|
||||
|
||||
Focus on **core functionality** and integrate with existing tools rather than reinventing the wheel.
|
||||
Focus on **core functionality** and integrate with existing tools rather than reinventing the wheel.
|
||||
|
||||
Examples:
|
||||
- Using **Apprise** for notifications instead of implementing multiple separate gateways
|
||||
- Implementing **regex-based validation** instead of one-off validation for each setting
|
||||
Examples:
|
||||
- Using **Apprise** for notifications instead of implementing multiple separate gateways
|
||||
- Implementing **regex-based validation** instead of one-off validation for each setting
|
||||
|
||||
> [!NOTE]
|
||||
> [!NOTE]
|
||||
> UI changes have lower priority. PRs are welcome, but please keep them **small and focused**.
|
||||
|
||||
## Development Environment Set Up
|
||||
@@ -43,7 +43,7 @@ The following steps will guide you to set up your environment for local developm
|
||||
### 1. Download the code:
|
||||
|
||||
- `mkdir /development`
|
||||
- `cd /development && git clone https://github.com/jokob-sk/NetAlertX.git`
|
||||
- `cd /development && git clone https://github.com/netalertx/NetAlertX.git`
|
||||
|
||||
### 2. Create a DEV .env_dev file
|
||||
|
||||
@@ -59,13 +59,13 @@ PORT=22222 # make sure this port is unique on your whole network
|
||||
DEV_LOCATION=/development/NetAlertX
|
||||
APP_DATA_LOCATION=/volume/docker_appdata
|
||||
# Make sure your GRAPHQL_PORT setting has a port that is unique on your whole host network
|
||||
APP_CONF_OVERRIDE={"GRAPHQL_PORT":"22223"}
|
||||
APP_CONF_OVERRIDE={"GRAPHQL_PORT":"22223"}
|
||||
# ALWAYS_FRESH_INSTALL=true # uncommenting this will always delete the content of /config and /db dirs on boot to simulate a fresh install
|
||||
```
|
||||
|
||||
### 3. Create /db and /config dirs
|
||||
### 3. Create /db and /config dirs
|
||||
|
||||
Create a folder `netalertx` in the `APP_DATA_LOCATION` (in this example in `/volume/docker_appdata`) with 2 subfolders `db` and `config`.
|
||||
Create a folder `netalertx` in the `APP_DATA_LOCATION` (in this example in `/volume/docker_appdata`) with 2 subfolders `db` and `config`.
|
||||
|
||||
- `mkdir /volume/docker_appdata/netalertx`
|
||||
- `mkdir /volume/docker_appdata/netalertx/db`
|
||||
@@ -82,9 +82,9 @@ You can then modify the python script without restarting/rebuilding the containe
|
||||
|
||||
## Tips
|
||||
|
||||
A quick cheat sheet of useful commands.
|
||||
A quick cheat sheet of useful commands.
|
||||
|
||||
### Removing the container and image
|
||||
### Removing the container and image
|
||||
|
||||
A command to stop, remove the container and the image (replace `netalertx` and `netalertx-netalertx` with the appropriate values)
|
||||
|
||||
@@ -98,23 +98,23 @@ Most code changes can be tested without rebuilding the container. When working o
|
||||
|
||||

|
||||
|
||||
2. If above doesn't work, SSH into the container and kill & restart the main script loop
|
||||
2. If above doesn't work, SSH into the container and kill & restart the main script loop
|
||||
|
||||
- `sudo docker exec -it netalertx /bin/bash`
|
||||
- `pkill -f "python /app/server" && python /app/server & `
|
||||
|
||||
3. If none of the above work, restart the docker container.
|
||||
3. If none of the above work, restart the docker container.
|
||||
|
||||
- This is usually the last resort as sometimes the Docker engine becomes unresponsive and the whole engine needs to be restarted.
|
||||
- This is usually the last resort as sometimes the Docker engine becomes unresponsive and the whole engine needs to be restarted.
|
||||
|
||||
## Contributing & Pull Requests
|
||||
|
||||
### Before submitting a PR, please ensure:
|
||||
|
||||
✔ Changes are **backward-compatible** with existing installs.
|
||||
✔ No unnecessary changes are made.
|
||||
✔ New features are **reusable**, not narrowly scoped.
|
||||
✔ Features are implemented via **plugins** if possible.
|
||||
✔ Changes are **backward-compatible** with existing installs.
|
||||
✔ No unnecessary changes are made.
|
||||
✔ New features are **reusable**, not narrowly scoped.
|
||||
✔ Features are implemented via **plugins** if possible.
|
||||
|
||||
### Mandatory Test Cases
|
||||
|
||||
@@ -122,15 +122,15 @@ Most code changes can be tested without rebuilding the container. When working o
|
||||
- Existing DB/config compatibility.
|
||||
- Notification testing:
|
||||
|
||||
- Email
|
||||
- Apprise (e.g., Telegram)
|
||||
- Webhook (e.g., Discord)
|
||||
- MQTT (e.g., Home Assistant)
|
||||
- Email
|
||||
- Apprise (e.g., Telegram)
|
||||
- Webhook (e.g., Discord)
|
||||
- MQTT (e.g., Home Assistant)
|
||||
|
||||
- Updating Settings and their persistence.
|
||||
- Updating a Device
|
||||
- Plugin functionality.
|
||||
- Error log inspection.
|
||||
|
||||
> [!NOTE]
|
||||
> [!NOTE]
|
||||
> Always run all available tests as per the [Testing documentation](API_TESTS.md).
|
||||
|
||||
@@ -17,7 +17,7 @@ services:
|
||||
netalertx:
|
||||
#use an environmental variable to set host networking mode if needed
|
||||
container_name: netalertx # The name when you docker contiainer ls
|
||||
image: ghcr.io/jokob-sk/netalertx:latest
|
||||
image: ghcr.io/netalertx/netalertx:latest
|
||||
network_mode: ${NETALERTX_NETWORK_MODE:-host} # Use host networking for ARP scanning and other services
|
||||
|
||||
read_only: true # Make the container filesystem read-only
|
||||
|
||||
@@ -31,7 +31,7 @@ docker run -d --rm --network=host \
|
||||
--tmpfs /tmp:uid=${NETALERTX_UID:-20211},gid=${NETALERTX_GID:-20211},mode=1700 \
|
||||
-e PORT=20211 \
|
||||
-e APP_CONF_OVERRIDE={"GRAPHQL_PORT":"20214"} \
|
||||
ghcr.io/jokob-sk/netalertx:latest
|
||||
ghcr.io/netalertx/netalertx:latest
|
||||
```
|
||||
|
||||
> Runtime UID/GID: The image defaults to a service user `netalertx` (UID/GID 20211). A separate readonly lock owner also uses UID/GID 20211 for 004/005 immutability. You can override the runtime UID/GID at build (ARG) or run (`--user` / compose `user:`) but must align writable mounts (`/data`, `/tmp*`) and tmpfs `uid/gid` to that choice.
|
||||
|
||||
@@ -35,9 +35,9 @@ services:
|
||||
netalertx:
|
||||
container_name: netalertx
|
||||
# Use this line for stable release
|
||||
image: "ghcr.io/jokob-sk/netalertx:latest"
|
||||
image: "ghcr.io/netalertx/netalertx:latest"
|
||||
# Or, use this for the latest development build
|
||||
# image: "ghcr.io/jokob-sk/netalertx-dev:latest"
|
||||
# image: "ghcr.io/netalertx/netalertx-dev:latest"
|
||||
network_mode: "host"
|
||||
restart: unless-stopped
|
||||
cap_drop: # Drop all capabilities for enhanced security
|
||||
@@ -48,7 +48,7 @@ services:
|
||||
- NET_BIND_SERVICE
|
||||
- CHOWN
|
||||
- SETUID
|
||||
- SETGID
|
||||
- SETGID
|
||||
volumes:
|
||||
- ${APP_FOLDER}/netalertx/config:/data/config
|
||||
- ${APP_FOLDER}/netalertx/db:/data/db
|
||||
|
||||
@@ -44,7 +44,7 @@ Use the following Compose snippet to deploy NetAlertX with a **static LAN IP** a
|
||||
```yaml
|
||||
services:
|
||||
netalertx:
|
||||
image: ghcr.io/jokob-sk/netalertx:latest
|
||||
image: ghcr.io/netalertx/netalertx:latest
|
||||
...
|
||||
networks:
|
||||
swarm-ipvlan:
|
||||
|
||||
@@ -32,12 +32,22 @@ NetAlertX is a lightweight, flexible platform for monitoring networks, tracking
|
||||

|
||||
|
||||
- **Real-Time Notifications**: Receive immediate alerts for new devices, disconnected devices, or unexpected changes.
|
||||
- **Customizable Triggers**: Define rules based on device type, IP ranges, presence, or other network parameters.
|
||||
- **Customizable Filters and Rules**: Define rules based on device type, IP ranges, presence, or other network parameters.
|
||||
- **Alert Deduplication & Suppression**: Avoid unnecessary noise with smart alert handling.
|
||||
- **Historical Logs**: Maintain a complete timeline of network events for review and reporting.
|
||||
|
||||
---
|
||||
|
||||
## Workflows for implementing Business rules
|
||||
|
||||

|
||||
|
||||
- **Custom rules**: Cretae custom flows and update device information based to scan results.
|
||||
- **Customizable Triggers**: Define rules based on any device data, including device type, IP ranges, presence, or other network parameters.
|
||||
- **Automated Updates**: Automate repetitive tasks, making network management more efficient.
|
||||
|
||||
---
|
||||
|
||||
## Multi-Channel Notification
|
||||
|
||||

|
||||
|
||||
@@ -12,7 +12,7 @@ docker run --rm --network=host \
|
||||
-v /etc/localtime:/etc/localtime:ro \
|
||||
--tmpfs /tmp:uid=20211,gid=20211,mode=1700 \
|
||||
-e PORT=20211 \
|
||||
ghcr.io/jokob-sk/netalertx:latest
|
||||
ghcr.io/netalertx/netalertx:latest
|
||||
```
|
||||
|
||||
> [!WARNING]
|
||||
@@ -70,7 +70,7 @@ If you use a custom `PUID` (e.g. `0`) and `GUID` (e.g. `100`) make sure you also
|
||||
docker run -it --rm --name netalertx --user "0" \
|
||||
-v /local_data_dir:/data \
|
||||
--tmpfs /tmp:uid=20211,gid=20211,mode=1700 \
|
||||
ghcr.io/jokob-sk/netalertx:latest
|
||||
ghcr.io/netalertx/netalertx:latest
|
||||
```
|
||||
|
||||
2. Wait for logs showing **permissions being fixed**. The container will then **hang intentionally**.
|
||||
@@ -95,7 +95,7 @@ docker run -it --rm --name netalertx --user "0" \
|
||||
services:
|
||||
netalertx:
|
||||
container_name: netalertx
|
||||
image: "ghcr.io/jokob-sk/netalertx"
|
||||
image: "ghcr.io/netalertx/netalertx"
|
||||
network_mode: "host"
|
||||
cap_drop: # Drop all capabilities for enhanced security
|
||||
- ALL
|
||||
|
||||
@@ -318,7 +318,7 @@ As per user feedback, we’ve re-introduced the ability to control which user th
|
||||
services:
|
||||
netalertx:
|
||||
container_name: netalertx
|
||||
image: "ghcr.io/jokob-sk/netalertx"
|
||||
image: "ghcr.io/netalertx/netalertx"
|
||||
network_mode: "host"
|
||||
cap_drop:
|
||||
- ALL
|
||||
|
||||
@@ -80,9 +80,9 @@ services:
|
||||
netalertx:
|
||||
container_name: netalertx
|
||||
# Use this line for the stable release
|
||||
image: "ghcr.io/jokob-sk/netalertx:latest"
|
||||
image: "ghcr.io/netalertx/netalertx:latest"
|
||||
# Or use this line for the latest development build
|
||||
# image: "ghcr.io/jokob-sk/netalertx-dev:latest"
|
||||
# image: "ghcr.io/netalertx/netalertx-dev:latest"
|
||||
network_mode: "host"
|
||||
restart: unless-stopped
|
||||
|
||||
|
||||
@@ -39,7 +39,7 @@ You can specify the DNS server in the docker-compose to improve name resolution
|
||||
services:
|
||||
netalertx:
|
||||
container_name: netalertx
|
||||
image: "ghcr.io/jokob-sk/netalertx:latest"
|
||||
image: "ghcr.io/netalertx/netalertx:latest"
|
||||
...
|
||||
dns: # specifying the DNS servers used for the container
|
||||
- 10.8.0.1
|
||||
|
||||
@@ -37,8 +37,8 @@ services:
|
||||
netalertx:
|
||||
container_name: netalertx
|
||||
# use the below line if you want to test the latest dev image
|
||||
# image: "ghcr.io/jokob-sk/netalertx-dev:latest"
|
||||
image: "ghcr.io/jokob-sk/netalertx:latest"
|
||||
# image: "ghcr.io/netalertx/netalertx-dev:latest"
|
||||
image: "ghcr.io/netalertx/netalertx:latest"
|
||||
network_mode: "host"
|
||||
restart: unless-stopped
|
||||
cap_drop: # Drop all capabilities for enhanced security
|
||||
|
||||
@@ -538,6 +538,7 @@ body
|
||||
font-size: larger;
|
||||
float: right;
|
||||
text-align: center;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
hr
|
||||
@@ -929,6 +930,14 @@ height: 50px;
|
||||
.nav-tabs-custom .tab-content {
|
||||
overflow: scroll;
|
||||
}
|
||||
.infobox_label
|
||||
{
|
||||
display: none;
|
||||
}
|
||||
.small-box .icon
|
||||
{
|
||||
display: block !important;
|
||||
}
|
||||
}
|
||||
|
||||
.top_small_box_gray_text {
|
||||
|
||||
@@ -572,7 +572,7 @@ function purgeAllExecute() {
|
||||
data: JSON.stringify({
|
||||
dbtable: dbTable,
|
||||
columnName: 'Plugin',
|
||||
id: plugPrefix
|
||||
id: [plugPrefix]
|
||||
}),
|
||||
contentType: "application/json",
|
||||
success: function(response, textStatus) {
|
||||
@@ -603,15 +603,18 @@ function deleteListed(plugPrefixArg, dbTableArg) {
|
||||
|
||||
// Ask for confirmation
|
||||
showModalWarning(`${getString('Gen_Purge')} ${plugPrefix} ${dbTable}`, `${getString('Gen_AreYouSure')} (${idArr.length})`,
|
||||
`${getString('Gen_Cancel')}`, `${getString('Gen_Okay')}`, "deleteListedExecute");
|
||||
`${getString('Gen_Cancel')}`, `${getString('Gen_Okay')}`, () => deleteListedExecute(idArr));
|
||||
}
|
||||
|
||||
// --------------------------------------------------------
|
||||
function deleteListedExecute() {
|
||||
function deleteListedExecute(idArr) {
|
||||
const apiBase = getApiBase();
|
||||
const apiToken = getSetting("API_TOKEN");
|
||||
const url = `${apiBase}/dbquery/delete`;
|
||||
|
||||
console.log(idArr);
|
||||
|
||||
|
||||
$.ajax({
|
||||
method: "POST",
|
||||
url: url,
|
||||
@@ -619,7 +622,7 @@ function deleteListedExecute() {
|
||||
data: JSON.stringify({
|
||||
dbtable: dbTable,
|
||||
columnName: 'Index',
|
||||
id: idArr.toString()
|
||||
id: idArr
|
||||
}),
|
||||
contentType: "application/json",
|
||||
success: function(response, textStatus) {
|
||||
|
||||
@@ -57,7 +57,7 @@
|
||||
virtualisation.oci-containers = {
|
||||
containers = {
|
||||
netalertx = {
|
||||
image = "ghcr.io/jokob-sk/netalertx:${cfg.imageTag}";
|
||||
image = "ghcr.io/netalertx/netalertx:${cfg.imageTag}";
|
||||
autoStart = true;
|
||||
extraOptions = [
|
||||
"--network=host"
|
||||
|
||||
@@ -185,7 +185,7 @@ printf "%b\n" "${GREEN}[INSTALLING] ${RESET}Cloning app
|
||||
printf "%b\n" "--------------------------------------------------------------------------"
|
||||
|
||||
mkdir -p "$INSTALL_DIR"
|
||||
git clone https://github.com/jokob-sk/NetAlertX.git "$INSTALL_DIR/"
|
||||
git clone https://github.com/netalertx/NetAlertX.git "$INSTALL_DIR/"
|
||||
|
||||
if [ ! -f "$INSTALL_DIR/front/buildtimestamp.txt" ]; then
|
||||
date +%s > "$INSTALL_DIR/front/buildtimestamp.txt"
|
||||
|
||||
@@ -1287,14 +1287,22 @@ def dbquery_update(payload=None):
|
||||
def dbquery_delete(payload=None):
|
||||
data = request.get_json() or {}
|
||||
required = ["columnName", "id", "dbtable"]
|
||||
if not all(data.get(k) for k in required):
|
||||
return jsonify({"success": False, "message": "ERROR: Missing parameters", "error": "Missing required 'columnName', 'id', or 'dbtable' query parameter"}), 400
|
||||
if not all(k in data and data[k] for k in required):
|
||||
return jsonify({
|
||||
"success": False,
|
||||
"message": "ERROR: Missing parameters",
|
||||
"error": "Missing required 'columnName', 'id', or 'dbtable' query parameter"
|
||||
}), 400
|
||||
|
||||
return delete_query(
|
||||
column_name=data["columnName"],
|
||||
ids=data["id"],
|
||||
dbtable=data["dbtable"],
|
||||
)
|
||||
dbtable = data["dbtable"]
|
||||
column_name = data["columnName"]
|
||||
ids = data["id"]
|
||||
|
||||
# Ensure ids is a list
|
||||
if not isinstance(ids, list):
|
||||
ids = [ids]
|
||||
|
||||
return delete_query(column_name, ids, dbtable)
|
||||
|
||||
|
||||
# --------------------------
|
||||
|
||||
@@ -11,6 +11,7 @@ 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 logger import mylog # noqa: E402 [flake8 lint suppression]
|
||||
|
||||
|
||||
def read_query(raw_sql_b64):
|
||||
@@ -82,17 +83,18 @@ def delete_query(column_name, ids, dbtable):
|
||||
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} = ?"
|
||||
# Wrap table and column in quotes to handle reserved words
|
||||
sql = f'DELETE FROM "{dbtable}" WHERE "{column_name}" = ?'
|
||||
mylog("debug", f"[delete_query] sql {sql} with id={id_val}")
|
||||
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
|
||||
|
||||
|
||||
@@ -44,7 +44,7 @@ ALLOWED_NMAP_MODES = Literal[
|
||||
|
||||
NOTIFICATION_LEVELS = Literal["info", "warning", "error", "alert", "interrupt"]
|
||||
|
||||
ALLOWED_TABLES = Literal["Devices", "Events", "Sessions", "Settings", "CurrentScan", "Online_History", "Plugins_Objects"]
|
||||
ALLOWED_TABLES = Literal["Devices", "Events", "Sessions", "Settings", "CurrentScan", "Online_History", "Plugins_Objects", "Plugins_History"]
|
||||
|
||||
ALLOWED_LOG_FILES = Literal[
|
||||
"app.log", "app_front.log", "IP_changes.log", "stdout.log", "stderr.log",
|
||||
|
||||
@@ -103,7 +103,7 @@ def process_scan(db):
|
||||
|
||||
# Clear current scan as processed
|
||||
# 🐛 CurrentScan DEBUG: comment out below when debugging to keep the CurrentScan table after restarts/scan finishes
|
||||
# db.sql.execute("DELETE FROM CurrentScan")
|
||||
db.sql.execute("DELETE FROM CurrentScan")
|
||||
|
||||
# re-broadcast unread notifiation count to update FE
|
||||
update_unread_notifications_count()
|
||||
|
||||
@@ -41,6 +41,8 @@ def scan_db():
|
||||
devIsNew INTEGER DEFAULT 1,
|
||||
devFavorite INTEGER DEFAULT 0,
|
||||
devScan INTEGER DEFAULT 1,
|
||||
devAlertDown INTEGER DEFAULT 0,
|
||||
devAlertEvents INTEGER DEFAULT 1,
|
||||
|
||||
-- Authoritative Metadata Columns
|
||||
devMacSource TEXT,
|
||||
@@ -81,6 +83,41 @@ def scan_db():
|
||||
)
|
||||
""")
|
||||
|
||||
# 3. Events Table
|
||||
cur.execute("""
|
||||
CREATE TABLE Events (
|
||||
eve_MAC TEXT,
|
||||
eve_IP TEXT,
|
||||
eve_DateTime TEXT,
|
||||
eve_EventType TEXT,
|
||||
eve_AdditionalInfo TEXT,
|
||||
eve_PendingAlertEmail INTEGER
|
||||
)
|
||||
""")
|
||||
|
||||
# 4. LatestEventsPerMAC View
|
||||
cur.execute("""DROP VIEW IF EXISTS LatestEventsPerMAC;""")
|
||||
cur.execute("""
|
||||
CREATE VIEW LatestEventsPerMAC AS
|
||||
WITH RankedEvents AS (
|
||||
SELECT
|
||||
e.*,
|
||||
ROW_NUMBER() OVER (PARTITION BY e.eve_MAC ORDER BY e.eve_DateTime DESC) AS row_num
|
||||
FROM Events AS e
|
||||
)
|
||||
SELECT
|
||||
e.eve_MAC,
|
||||
e.eve_EventType,
|
||||
e.eve_DateTime,
|
||||
e.eve_PendingAlertEmail,
|
||||
d.devPresentLastScan,
|
||||
c.scanLastIP
|
||||
FROM RankedEvents AS e
|
||||
LEFT JOIN Devices AS d ON e.eve_MAC = d.devMac
|
||||
LEFT JOIN CurrentScan AS c ON e.eve_MAC = c.scanMac
|
||||
WHERE e.row_num = 1;
|
||||
""")
|
||||
|
||||
# 3. LatestDeviceScan View (Inner Join for Online Devices)
|
||||
cur.execute("""
|
||||
CREATE VIEW LatestDeviceScan AS
|
||||
@@ -104,4 +104,3 @@ def test_primary_ipv4_is_set_and_ipv6_preserved(scan_db, mock_device_handling):
|
||||
assert row["devLastIP"] == "10.0.0.5"
|
||||
assert row["devPrimaryIPv4"] == "10.0.0.5"
|
||||
assert row["devPrimaryIPv6"] == "2001:db8::2"
|
||||
|
||||
Reference in New Issue
Block a user