mirror of
https://github.com/jokob-sk/NetAlertX.git
synced 2026-04-02 00:02:19 -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
|
- name: Checkout code
|
||||||
uses: actions/checkout@v4
|
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
|
- name: Check for incorrect absolute '/php/' URLs in frontend code
|
||||||
run: |
|
run: |
|
||||||
echo "🔍 Checking for incorrect absolute '/php/' URLs (should be 'php/' or './php/')..."
|
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:
|
on:
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
inputs:
|
inputs:
|
||||||
run_authoritative:
|
run_scan:
|
||||||
description: '📂 authoritative_fields/ (Logic, Locks, IPs)'
|
description: '📂 scan/ (Scan, Logic, Locks, IPs)'
|
||||||
type: boolean
|
type: boolean
|
||||||
default: true
|
default: true
|
||||||
run_api:
|
run_api:
|
||||||
@@ -43,7 +43,7 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
PATHS=""
|
PATHS=""
|
||||||
# Folder Mapping with 'test/' prefix
|
# 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_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_backend }}" == "true" ]; then PATHS="$PATHS test/backend/"; fi
|
||||||
if [ "${{ github.event.inputs.run_docker_env }}" == "true" ]; then PATHS="$PATHS test/docker_tests/"; 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 \
|
--tmpfs /tmp:uid=20211,gid=20211,mode=1700 \
|
||||||
-e PORT=20211 \
|
-e PORT=20211 \
|
||||||
-e APP_CONF_OVERRIDE='{"GRAPHQL_PORT":"20214"}' \
|
-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.
|
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:
|
To deploy a containerized instance directly from the source repository, execute the following BASH sequence:
|
||||||
```bash
|
```bash
|
||||||
git clone https://github.com/jokob-sk/NetAlertX.git
|
git clone https://github.com/netalertx/NetAlertX.git
|
||||||
cd NetAlertX
|
cd NetAlertX
|
||||||
docker compose up --force-recreate --build
|
docker compose up --force-recreate --build
|
||||||
# To customize: edit docker-compose.yaml and run that last command again
|
# 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 \
|
--tmpfs /tmp:uid=20211,gid=20211,mode=1700 \
|
||||||
-e PORT=20211 \
|
-e PORT=20211 \
|
||||||
-e APP_CONF_OVERRIDE='{"GRAPHQL_PORT":"20214"}' \
|
-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:
|
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!
|
> ⚠ Please backup your DB and config beforehand!
|
||||||
|
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ The following steps will guide you to set up your environment for local developm
|
|||||||
### 1. Download the code:
|
### 1. Download the code:
|
||||||
|
|
||||||
- `mkdir /development`
|
- `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
|
### 2. Create a DEV .env_dev file
|
||||||
|
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ services:
|
|||||||
netalertx:
|
netalertx:
|
||||||
#use an environmental variable to set host networking mode if needed
|
#use an environmental variable to set host networking mode if needed
|
||||||
container_name: netalertx # The name when you docker contiainer ls
|
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
|
network_mode: ${NETALERTX_NETWORK_MODE:-host} # Use host networking for ARP scanning and other services
|
||||||
|
|
||||||
read_only: true # Make the container filesystem read-only
|
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 \
|
--tmpfs /tmp:uid=${NETALERTX_UID:-20211},gid=${NETALERTX_GID:-20211},mode=1700 \
|
||||||
-e PORT=20211 \
|
-e PORT=20211 \
|
||||||
-e APP_CONF_OVERRIDE={"GRAPHQL_PORT":"20214"} \
|
-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.
|
> 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:
|
netalertx:
|
||||||
container_name: netalertx
|
container_name: netalertx
|
||||||
# Use this line for stable release
|
# 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
|
# 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"
|
network_mode: "host"
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
cap_drop: # Drop all capabilities for enhanced security
|
cap_drop: # Drop all capabilities for enhanced security
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ Use the following Compose snippet to deploy NetAlertX with a **static LAN IP** a
|
|||||||
```yaml
|
```yaml
|
||||||
services:
|
services:
|
||||||
netalertx:
|
netalertx:
|
||||||
image: ghcr.io/jokob-sk/netalertx:latest
|
image: ghcr.io/netalertx/netalertx:latest
|
||||||
...
|
...
|
||||||
networks:
|
networks:
|
||||||
swarm-ipvlan:
|
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.
|
- **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.
|
- **Alert Deduplication & Suppression**: Avoid unnecessary noise with smart alert handling.
|
||||||
- **Historical Logs**: Maintain a complete timeline of network events for review and reporting.
|
- **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
|
## Multi-Channel Notification
|
||||||
|
|
||||||

|

|
||||||
|
|||||||
@@ -12,7 +12,7 @@ docker run --rm --network=host \
|
|||||||
-v /etc/localtime:/etc/localtime:ro \
|
-v /etc/localtime:/etc/localtime:ro \
|
||||||
--tmpfs /tmp:uid=20211,gid=20211,mode=1700 \
|
--tmpfs /tmp:uid=20211,gid=20211,mode=1700 \
|
||||||
-e PORT=20211 \
|
-e PORT=20211 \
|
||||||
ghcr.io/jokob-sk/netalertx:latest
|
ghcr.io/netalertx/netalertx:latest
|
||||||
```
|
```
|
||||||
|
|
||||||
> [!WARNING]
|
> [!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" \
|
docker run -it --rm --name netalertx --user "0" \
|
||||||
-v /local_data_dir:/data \
|
-v /local_data_dir:/data \
|
||||||
--tmpfs /tmp:uid=20211,gid=20211,mode=1700 \
|
--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**.
|
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:
|
services:
|
||||||
netalertx:
|
netalertx:
|
||||||
container_name: netalertx
|
container_name: netalertx
|
||||||
image: "ghcr.io/jokob-sk/netalertx"
|
image: "ghcr.io/netalertx/netalertx"
|
||||||
network_mode: "host"
|
network_mode: "host"
|
||||||
cap_drop: # Drop all capabilities for enhanced security
|
cap_drop: # Drop all capabilities for enhanced security
|
||||||
- ALL
|
- ALL
|
||||||
|
|||||||
@@ -318,7 +318,7 @@ As per user feedback, we’ve re-introduced the ability to control which user th
|
|||||||
services:
|
services:
|
||||||
netalertx:
|
netalertx:
|
||||||
container_name: netalertx
|
container_name: netalertx
|
||||||
image: "ghcr.io/jokob-sk/netalertx"
|
image: "ghcr.io/netalertx/netalertx"
|
||||||
network_mode: "host"
|
network_mode: "host"
|
||||||
cap_drop:
|
cap_drop:
|
||||||
- ALL
|
- ALL
|
||||||
|
|||||||
@@ -80,9 +80,9 @@ services:
|
|||||||
netalertx:
|
netalertx:
|
||||||
container_name: netalertx
|
container_name: netalertx
|
||||||
# Use this line for the stable release
|
# 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
|
# 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"
|
network_mode: "host"
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ You can specify the DNS server in the docker-compose to improve name resolution
|
|||||||
services:
|
services:
|
||||||
netalertx:
|
netalertx:
|
||||||
container_name: 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
|
dns: # specifying the DNS servers used for the container
|
||||||
- 10.8.0.1
|
- 10.8.0.1
|
||||||
|
|||||||
@@ -37,8 +37,8 @@ services:
|
|||||||
netalertx:
|
netalertx:
|
||||||
container_name: netalertx
|
container_name: netalertx
|
||||||
# use the below line if you want to test the latest dev image
|
# use the below line if you want to test the latest dev image
|
||||||
# image: "ghcr.io/jokob-sk/netalertx-dev:latest"
|
# image: "ghcr.io/netalertx/netalertx-dev:latest"
|
||||||
image: "ghcr.io/jokob-sk/netalertx:latest"
|
image: "ghcr.io/netalertx/netalertx:latest"
|
||||||
network_mode: "host"
|
network_mode: "host"
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
cap_drop: # Drop all capabilities for enhanced security
|
cap_drop: # Drop all capabilities for enhanced security
|
||||||
|
|||||||
@@ -538,6 +538,7 @@ body
|
|||||||
font-size: larger;
|
font-size: larger;
|
||||||
float: right;
|
float: right;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
z-index: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
hr
|
hr
|
||||||
@@ -929,6 +930,14 @@ height: 50px;
|
|||||||
.nav-tabs-custom .tab-content {
|
.nav-tabs-custom .tab-content {
|
||||||
overflow: scroll;
|
overflow: scroll;
|
||||||
}
|
}
|
||||||
|
.infobox_label
|
||||||
|
{
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.small-box .icon
|
||||||
|
{
|
||||||
|
display: block !important;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.top_small_box_gray_text {
|
.top_small_box_gray_text {
|
||||||
|
|||||||
@@ -572,7 +572,7 @@ function purgeAllExecute() {
|
|||||||
data: JSON.stringify({
|
data: JSON.stringify({
|
||||||
dbtable: dbTable,
|
dbtable: dbTable,
|
||||||
columnName: 'Plugin',
|
columnName: 'Plugin',
|
||||||
id: plugPrefix
|
id: [plugPrefix]
|
||||||
}),
|
}),
|
||||||
contentType: "application/json",
|
contentType: "application/json",
|
||||||
success: function(response, textStatus) {
|
success: function(response, textStatus) {
|
||||||
@@ -603,15 +603,18 @@ function deleteListed(plugPrefixArg, dbTableArg) {
|
|||||||
|
|
||||||
// Ask for confirmation
|
// Ask for confirmation
|
||||||
showModalWarning(`${getString('Gen_Purge')} ${plugPrefix} ${dbTable}`, `${getString('Gen_AreYouSure')} (${idArr.length})`,
|
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 apiBase = getApiBase();
|
||||||
const apiToken = getSetting("API_TOKEN");
|
const apiToken = getSetting("API_TOKEN");
|
||||||
const url = `${apiBase}/dbquery/delete`;
|
const url = `${apiBase}/dbquery/delete`;
|
||||||
|
|
||||||
|
console.log(idArr);
|
||||||
|
|
||||||
|
|
||||||
$.ajax({
|
$.ajax({
|
||||||
method: "POST",
|
method: "POST",
|
||||||
url: url,
|
url: url,
|
||||||
@@ -619,7 +622,7 @@ function deleteListedExecute() {
|
|||||||
data: JSON.stringify({
|
data: JSON.stringify({
|
||||||
dbtable: dbTable,
|
dbtable: dbTable,
|
||||||
columnName: 'Index',
|
columnName: 'Index',
|
||||||
id: idArr.toString()
|
id: idArr
|
||||||
}),
|
}),
|
||||||
contentType: "application/json",
|
contentType: "application/json",
|
||||||
success: function(response, textStatus) {
|
success: function(response, textStatus) {
|
||||||
|
|||||||
@@ -57,7 +57,7 @@
|
|||||||
virtualisation.oci-containers = {
|
virtualisation.oci-containers = {
|
||||||
containers = {
|
containers = {
|
||||||
netalertx = {
|
netalertx = {
|
||||||
image = "ghcr.io/jokob-sk/netalertx:${cfg.imageTag}";
|
image = "ghcr.io/netalertx/netalertx:${cfg.imageTag}";
|
||||||
autoStart = true;
|
autoStart = true;
|
||||||
extraOptions = [
|
extraOptions = [
|
||||||
"--network=host"
|
"--network=host"
|
||||||
|
|||||||
@@ -185,7 +185,7 @@ printf "%b\n" "${GREEN}[INSTALLING] ${RESET}Cloning app
|
|||||||
printf "%b\n" "--------------------------------------------------------------------------"
|
printf "%b\n" "--------------------------------------------------------------------------"
|
||||||
|
|
||||||
mkdir -p "$INSTALL_DIR"
|
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
|
if [ ! -f "$INSTALL_DIR/front/buildtimestamp.txt" ]; then
|
||||||
date +%s > "$INSTALL_DIR/front/buildtimestamp.txt"
|
date +%s > "$INSTALL_DIR/front/buildtimestamp.txt"
|
||||||
|
|||||||
@@ -1287,14 +1287,22 @@ def dbquery_update(payload=None):
|
|||||||
def dbquery_delete(payload=None):
|
def dbquery_delete(payload=None):
|
||||||
data = request.get_json() or {}
|
data = request.get_json() or {}
|
||||||
required = ["columnName", "id", "dbtable"]
|
required = ["columnName", "id", "dbtable"]
|
||||||
if not all(data.get(k) for k in required):
|
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 jsonify({
|
||||||
|
"success": False,
|
||||||
|
"message": "ERROR: Missing parameters",
|
||||||
|
"error": "Missing required 'columnName', 'id', or 'dbtable' query parameter"
|
||||||
|
}), 400
|
||||||
|
|
||||||
return delete_query(
|
dbtable = data["dbtable"]
|
||||||
column_name=data["columnName"],
|
column_name = data["columnName"]
|
||||||
ids=data["id"],
|
ids = data["id"]
|
||||||
dbtable=data["dbtable"],
|
|
||||||
)
|
# 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"])
|
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 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):
|
def read_query(raw_sql_b64):
|
||||||
@@ -82,17 +83,18 @@ def delete_query(column_name, ids, dbtable):
|
|||||||
conn = get_temp_db_connection()
|
conn = get_temp_db_connection()
|
||||||
cur = conn.cursor()
|
cur = conn.cursor()
|
||||||
|
|
||||||
if not isinstance(ids, list):
|
|
||||||
ids = [ids]
|
|
||||||
|
|
||||||
deleted_count = 0
|
deleted_count = 0
|
||||||
for id_val in ids:
|
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,))
|
cur.execute(sql, (id_val,))
|
||||||
deleted_count += cur.rowcount
|
deleted_count += cur.rowcount
|
||||||
|
|
||||||
conn.commit()
|
conn.commit()
|
||||||
conn.close()
|
conn.close()
|
||||||
return jsonify({"success": True, "deleted_count": deleted_count})
|
return jsonify({"success": True, "deleted_count": deleted_count})
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return jsonify({"success": False, "error": str(e)}), 400
|
return jsonify({"success": False, "error": str(e)}), 400
|
||||||
|
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ ALLOWED_NMAP_MODES = Literal[
|
|||||||
|
|
||||||
NOTIFICATION_LEVELS = Literal["info", "warning", "error", "alert", "interrupt"]
|
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[
|
ALLOWED_LOG_FILES = Literal[
|
||||||
"app.log", "app_front.log", "IP_changes.log", "stdout.log", "stderr.log",
|
"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
|
# Clear current scan as processed
|
||||||
# 🐛 CurrentScan DEBUG: comment out below when debugging to keep the CurrentScan table after restarts/scan finishes
|
# 🐛 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
|
# re-broadcast unread notifiation count to update FE
|
||||||
update_unread_notifications_count()
|
update_unread_notifications_count()
|
||||||
|
|||||||
@@ -41,6 +41,8 @@ def scan_db():
|
|||||||
devIsNew INTEGER DEFAULT 1,
|
devIsNew INTEGER DEFAULT 1,
|
||||||
devFavorite INTEGER DEFAULT 0,
|
devFavorite INTEGER DEFAULT 0,
|
||||||
devScan INTEGER DEFAULT 1,
|
devScan INTEGER DEFAULT 1,
|
||||||
|
devAlertDown INTEGER DEFAULT 0,
|
||||||
|
devAlertEvents INTEGER DEFAULT 1,
|
||||||
|
|
||||||
-- Authoritative Metadata Columns
|
-- Authoritative Metadata Columns
|
||||||
devMacSource TEXT,
|
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)
|
# 3. LatestDeviceScan View (Inner Join for Online Devices)
|
||||||
cur.execute("""
|
cur.execute("""
|
||||||
CREATE VIEW LatestDeviceScan AS
|
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["devLastIP"] == "10.0.0.5"
|
||||||
assert row["devPrimaryIPv4"] == "10.0.0.5"
|
assert row["devPrimaryIPv4"] == "10.0.0.5"
|
||||||
assert row["devPrimaryIPv6"] == "2001:db8::2"
|
assert row["devPrimaryIPv6"] == "2001:db8::2"
|
||||||
|
|
||||||
Reference in New Issue
Block a user