mirror of
https://github.com/jokob-sk/NetAlertX.git
synced 2025-12-06 17:15:38 -08:00
@@ -46,14 +46,16 @@ ARG INSTALL_DIR=/app
|
||||
|
||||
# NetAlertX app directories
|
||||
ENV NETALERTX_APP=${INSTALL_DIR}
|
||||
ENV NETALERTX_CONFIG=${NETALERTX_APP}/config
|
||||
ENV NETALERTX_DATA=/data
|
||||
ENV NETALERTX_CONFIG=${NETALERTX_DATA}/config
|
||||
ENV NETALERTX_FRONT=${NETALERTX_APP}/front
|
||||
ENV NETALERTX_PLUGINS=${NETALERTX_FRONT}/plugins
|
||||
ENV NETALERTX_SERVER=${NETALERTX_APP}/server
|
||||
ENV NETALERTX_API=${NETALERTX_APP}/api
|
||||
ENV NETALERTX_DB=${NETALERTX_APP}/db
|
||||
ENV NETALERTX_API=/tmp/api
|
||||
ENV NETALERTX_DB=${NETALERTX_DATA}/db
|
||||
ENV NETALERTX_DB_FILE=${NETALERTX_DB}/app.db
|
||||
ENV NETALERTX_BACK=${NETALERTX_APP}/back
|
||||
ENV NETALERTX_LOG=${NETALERTX_APP}/log
|
||||
ENV NETALERTX_LOG=/tmp/log
|
||||
ENV NETALERTX_PLUGINS_LOG=${NETALERTX_LOG}/plugins
|
||||
ENV NETALERTX_CONFIG_FILE=${NETALERTX_CONFIG}/app.conf
|
||||
|
||||
@@ -70,6 +72,7 @@ ENV LOG_EXECUTION_QUEUE=${NETALERTX_LOG}/execution_queue.log
|
||||
ENV LOG_REPORT_OUTPUT_JSON=${NETALERTX_LOG}/report_output.json
|
||||
ENV LOG_STDOUT=${NETALERTX_LOG}/stdout.log
|
||||
ENV LOG_CROND=${NETALERTX_LOG}/crond.log
|
||||
ENV LOG_NGINX_ERROR=${NETALERTX_LOG}/nginx-error.log
|
||||
|
||||
# System Services configuration files
|
||||
ENV ENTRYPOINT_CHECKS=/entrypoint.d
|
||||
@@ -78,25 +81,26 @@ ENV SYSTEM_SERVICES_SCRIPTS=${SYSTEM_SERVICES}/scripts
|
||||
ENV SYSTEM_SERVICES_CONFIG=${SYSTEM_SERVICES}/config
|
||||
ENV SYSTEM_NGINX_CONFIG=${SYSTEM_SERVICES_CONFIG}/nginx
|
||||
ENV SYSTEM_NGINX_CONFIG_FILE=${SYSTEM_NGINX_CONFIG}/nginx.conf
|
||||
ENV SYSTEM_SERVICES_ACTIVE_CONFIG=${SYSTEM_NGINX_CONFIG}/conf.active
|
||||
ENV SYSTEM_SERVICES_ACTIVE_CONFIG=/tmp/nginx/active-config
|
||||
ENV SYSTEM_SERVICES_PHP_FOLDER=${SYSTEM_SERVICES_CONFIG}/php
|
||||
ENV SYSTEM_SERVICES_PHP_FPM_D=${SYSTEM_SERVICES_PHP_FOLDER}/php-fpm.d
|
||||
ENV SYSTEM_SERVICES_CROND=${SYSTEM_SERVICES_CONFIG}/crond
|
||||
ENV SYSTEM_SERVICES_RUN=${SYSTEM_SERVICES}/run
|
||||
ENV SYSTEM_SERVICES_RUN=/tmp/run
|
||||
ENV SYSTEM_SERVICES_RUN_TMP=${SYSTEM_SERVICES_RUN}/tmp
|
||||
ENV SYSTEM_SERVICES_RUN_LOG=${SYSTEM_SERVICES_RUN}/logs
|
||||
ENV PHP_FPM_CONFIG_FILE=${SYSTEM_SERVICES_PHP_FOLDER}/php-fpm.conf
|
||||
ENV READ_ONLY_FOLDERS="${NETALERTX_BACK} ${NETALERTX_FRONT} ${NETALERTX_SERVER} ${SYSTEM_SERVICES} \
|
||||
${SYSTEM_SERVICES_CONFIG} ${ENTRYPOINT_CHECKS}"
|
||||
ENV READ_WRITE_FOLDERS="${NETALERTX_CONFIG} ${NETALERTX_DB} ${NETALERTX_API} ${NETALERTX_LOG} \
|
||||
${NETALERTX_PLUGINS_LOG} ${SYSTEM_SERVICES_RUN} ${SYSTEM_SERVICES_RUN_TMP} \
|
||||
${SYSTEM_SERVICES_RUN_LOG}"
|
||||
ENV READ_WRITE_FOLDERS="${NETALERTX_DATA} ${NETALERTX_CONFIG} ${NETALERTX_DB} ${NETALERTX_API} \
|
||||
${NETALERTX_LOG} ${NETALERTX_PLUGINS_LOG} ${SYSTEM_SERVICES_RUN} \
|
||||
${SYSTEM_SERVICES_RUN_TMP} ${SYSTEM_SERVICES_RUN_LOG} \
|
||||
${SYSTEM_SERVICES_ACTIVE_CONFIG}"
|
||||
|
||||
#Python environment
|
||||
ENV PYTHONUNBUFFERED=1
|
||||
ENV VIRTUAL_ENV=/opt/venv
|
||||
ENV VIRTUAL_ENV_BIN=/opt/venv/bin
|
||||
ENV PYTHONPATH=${NETALERTX_APP}:${NETALERTX_SERVER}:${VIRTUAL_ENV}/lib/python3.12/site-packages
|
||||
ENV PYTHONPATH=${NETALERTX_APP}:${NETALERTX_SERVER}:${NETALERTX_PLUGINS}:${VIRTUAL_ENV}/lib/python3.12/site-packages
|
||||
ENV PATH="${SYSTEM_SERVICES}:${VIRTUAL_ENV_BIN}:$PATH"
|
||||
|
||||
# App Environment
|
||||
@@ -104,7 +108,7 @@ ENV LISTEN_ADDR=0.0.0.0
|
||||
ENV PORT=20211
|
||||
ENV NETALERTX_DEBUG=0
|
||||
ENV VENDORSPATH=/app/back/ieee-oui.txt
|
||||
ENV VENDORSPATH_NEWEST=/services/run/tmp/ieee-oui.txt
|
||||
ENV VENDORSPATH_NEWEST=${SYSTEM_SERVICES_RUN_TMP}/ieee-oui.txt
|
||||
ENV ENVIRONMENT=alpine
|
||||
ENV READ_ONLY_USER=readonly READ_ONLY_GROUP=readonly
|
||||
ENV NETALERTX_USER=netalertx NETALERTX_GROUP=netalertx
|
||||
@@ -128,8 +132,9 @@ COPY --chown=${NETALERTX_USER}:${NETALERTX_GROUP} install/production-filesystem/
|
||||
COPY --chown=${NETALERTX_USER}:${NETALERTX_GROUP} --chmod=755 back ${NETALERTX_BACK}
|
||||
COPY --chown=${NETALERTX_USER}:${NETALERTX_GROUP} --chmod=755 front ${NETALERTX_FRONT}
|
||||
COPY --chown=${NETALERTX_USER}:${NETALERTX_GROUP} --chmod=755 server ${NETALERTX_SERVER}
|
||||
RUN install -d -o ${NETALERTX_USER} -g ${NETALERTX_GROUP} -m 755 ${NETALERTX_API} \
|
||||
${NETALERTX_LOG} ${SYSTEM_SERVICES_RUN_TMP} ${SYSTEM_SERVICES_RUN_LOG} && \
|
||||
|
||||
# Create required folders with correct ownership and permissions
|
||||
RUN install -d -o ${NETALERTX_USER} -g ${NETALERTX_GROUP} -m 700 ${READ_WRITE_FOLDERS} && \
|
||||
sh -c "find ${NETALERTX_APP} -type f \( -name '*.sh' -o -name 'speedtest-cli' \) \
|
||||
-exec chmod 750 {} \;"
|
||||
|
||||
@@ -211,11 +216,14 @@ HEALTHCHECK --interval=30s --timeout=10s --start-period=60s --retries=3 \
|
||||
# .devcontainer/scripts/generate-configs.sh
|
||||
# The generator appends this stage to produce .devcontainer/Dockerfile.
|
||||
# Prefer to place dev-only setup here; use setup.sh only for runtime fixes.
|
||||
# Permissions in devcontainer should be of a brutalist nature. They will be
|
||||
# Open and wide to avoid permission issues during development allowing max
|
||||
# flexibility.
|
||||
|
||||
FROM runner AS netalertx-devcontainer
|
||||
ENV INSTALL_DIR=/app
|
||||
|
||||
ENV PYTHONPATH=/workspaces/NetAlertX/test:/workspaces/NetAlertX/server:/app:/app/server:/opt/venv/lib/python3.12/site-packages:/usr/lib/python3.12/site-packages
|
||||
ENV PYTHONPATH=${PYTHONPATH}:/workspaces/NetAlertX/test:/workspaces/NetAlertX/server:/usr/lib/python3.12/site-packages
|
||||
ENV PATH=/services:${PATH}
|
||||
ENV PHP_INI_SCAN_DIR=/services/config/php/conf.d:/etc/php83/conf.d
|
||||
ENV LISTEN_ADDR=0.0.0.0
|
||||
@@ -226,16 +234,28 @@ COPY .devcontainer/resources/devcontainer-overlay/ /
|
||||
USER root
|
||||
# Install common tools, create user, and set up sudo
|
||||
RUN apk add --no-cache git nano vim jq php83-pecl-xdebug py3-pip nodejs sudo gpgconf pytest \
|
||||
pytest-cov fish shfmt github-cli py3-yaml py3-docker-py docker-cli docker-cli-buildx \
|
||||
pytest-cov zsh alpine-zsh-config shfmt github-cli py3-yaml py3-docker-py docker-cli docker-cli-buildx \
|
||||
docker-cli-compose
|
||||
|
||||
RUN install -d -o netalertx -g netalertx -m 755 /services/php/modules && \
|
||||
cp -a /usr/lib/php83/modules/. /services/php/modules/ && \
|
||||
echo "${NETALERTX_USER} ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers
|
||||
RUN mkdir /workspaces && \
|
||||
install -d -o netalertx -g netalertx -m 777 /services/run/logs && \
|
||||
install -d -o netalertx -g netalertx -m 777 /app/run/tmp/client_body && \
|
||||
sed -i -e 's|:/app:|:/workspaces:|' /etc/passwd && \
|
||||
ENV SHELL=/bin/zsh
|
||||
|
||||
RUN mkdir -p /workspaces && \
|
||||
install -d -m 777 /data /data/config /data/db && \
|
||||
install -d -m 777 /tmp/log /tmp/log/plugins /tmp/api /tmp/run /tmp/nginx && \
|
||||
install -d -m 777 /tmp/nginx/active-config /tmp/nginx/client_body /tmp/nginx/config && \
|
||||
install -d -m 777 /tmp/nginx/fastcgi /tmp/nginx/proxy /tmp/nginx/scgi /tmp/nginx/uwsgi && \
|
||||
install -d -m 777 /tmp/run/tmp /tmp/run/logs && \
|
||||
chmod 777 /workspaces && \
|
||||
chown -R netalertx:netalertx /data && \
|
||||
chmod 666 /data/config/app.conf /data/db/app.db && \
|
||||
chmod 1777 /tmp && \
|
||||
install -d -o root -g root -m 1777 /tmp/.X11-unix && \
|
||||
mkdir -p /home/netalertx && \
|
||||
chown netalertx:netalertx /home/netalertx && \
|
||||
sed -i -e 's#/app:#/workspaces:#' /etc/passwd && \
|
||||
find /opt/venv -type d -exec chmod o+rwx {} \;
|
||||
|
||||
USER netalertx
|
||||
|
||||
37
.devcontainer/NetAlertX.code-workspace
Normal file
37
.devcontainer/NetAlertX.code-workspace
Normal file
@@ -0,0 +1,37 @@
|
||||
{
|
||||
"folders": [
|
||||
{
|
||||
"name": "NetAlertX Source",
|
||||
"path": "/workspaces/NetAlertX"
|
||||
},
|
||||
{
|
||||
"name": "💾 NetAlertX Data",
|
||||
"path": "/data"
|
||||
},
|
||||
{
|
||||
"name": "🔍 Active NetAlertX log",
|
||||
"path": "/tmp/log"
|
||||
},
|
||||
{
|
||||
"name": "🌐 Active NetAlertX nginx",
|
||||
"path": "/tmp/nginx"
|
||||
},
|
||||
{
|
||||
"name": "📊 Active NetAlertX api",
|
||||
"path": "/tmp/api"
|
||||
},
|
||||
{
|
||||
"name": "⚙️ Active NetAlertX run",
|
||||
"path": "/tmp/run"
|
||||
}
|
||||
],
|
||||
"settings": {
|
||||
"terminal.integrated.suggest.enabled": true,
|
||||
"terminal.integrated.defaultProfile.linux": "zsh",
|
||||
"terminal.integrated.profiles.linux": {
|
||||
"zsh": {
|
||||
"path": "/usr/bin/fish"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -19,6 +19,17 @@ Common workflows (F1->Tasks: Run Task)
|
||||
- Backend (GraphQL/Flask): `.devcontainer/scripts/restart-backend.sh` starts it under debugpy and logs to `/app/log/app.log`
|
||||
- Frontend (nginx + PHP-FPM): Started via setup.sh; can be restarted by the task "Start Frontend (nginx and PHP-FPM)".
|
||||
|
||||
Production Container Evaulation
|
||||
1. F1 → Tasks: Shutdown services ([Dev Container] Stop Frontend & Backend Services)
|
||||
2. F1 → Tasks: Docker system and build prune ([Any] Docker system and build Prune)
|
||||
3. F1 → Remote: Close Unused Forwarded Ports (VS Code command)
|
||||
4. F1 → Tasks: Build & Launch Production (Build & Launch Prodcution Docker
|
||||
5. visit http://localhost:20211
|
||||
|
||||
Unit tests
|
||||
1. F1 → Tasks: Rebuild test container ([Any] Build Unit Test Docker image)
|
||||
2. F1 → Test: Run all tests
|
||||
|
||||
Testing
|
||||
- pytest is installed via Alpine packages (py3-pytest, py3-pytest-cov).
|
||||
- PYTHONPATH includes workspace and venv site-packages so tests can import `server/*` modules and third-party libs.
|
||||
|
||||
26
.devcontainer/WORKSPACE.md
Normal file
26
.devcontainer/WORKSPACE.md
Normal file
@@ -0,0 +1,26 @@
|
||||
# NetAlertX Multi-Folder Workspace
|
||||
|
||||
This repository uses a multi-folder workspace configuration to provide easy access to runtime directories.
|
||||
|
||||
## Opening the Multi-Folder Workspace
|
||||
|
||||
After the devcontainer builds, open the workspace file to access all folders:
|
||||
|
||||
1. **File** → **Open Workspace from File**
|
||||
2. Select `NetAlertX.code-workspace`
|
||||
|
||||
Or use Command Palette (Ctrl+Shift+P / Cmd+Shift+P):
|
||||
- Type: `Workspaces: Open Workspace from File`
|
||||
- Select `NetAlertX.code-workspace`
|
||||
|
||||
## Workspace Folders
|
||||
|
||||
The workspace includes:
|
||||
- **NetAlertX** - Main source code
|
||||
- **/tmp** - Runtime temporary files
|
||||
- **/tmp/api** - API response cache (JSON files)
|
||||
- **/tmp/log** - Application and plugin logs
|
||||
|
||||
## Testing Configuration
|
||||
|
||||
Pytest is configured to only discover tests in the main `test/` directory, not in `/tmp` folders.
|
||||
@@ -2,6 +2,8 @@
|
||||
"name": "NetAlertX DevContainer",
|
||||
"remoteUser": "netalertx",
|
||||
"workspaceFolder": "/workspaces/NetAlertX",
|
||||
"workspaceMount": "source=${localWorkspaceFolder},target=/workspaces/NetAlertX,type=bind,consistency=cached",
|
||||
"onCreateCommand": "mkdir -p /tmp/api /tmp/log",
|
||||
"build": {
|
||||
"dockerfile": "./Dockerfile", // Dockerfile generated by script
|
||||
"context": "../", // Context is the root of the repository
|
||||
@@ -44,7 +46,8 @@
|
||||
},
|
||||
|
||||
"postCreateCommand": {
|
||||
"Install Pip Requirements": "/opt/venv/bin/pip3 install pytest docker debugpy"
|
||||
"Install Pip Requirements": "/opt/venv/bin/pip3 install pytest docker debugpy",
|
||||
"Workspace Instructions": "printf '\n\n<> DevContainer Ready!\n\n📁 To access /tmp folders in the workspace:\n File → Open Workspace from File → NetAlertX.code-workspace\n\n📖 See .devcontainer/WORKSPACE.md for details\n\n'"
|
||||
},
|
||||
"postStartCommand": {
|
||||
"Start Environment":"${containerWorkspaceFolder}/.devcontainer/scripts/setup.sh",
|
||||
@@ -70,15 +73,25 @@
|
||||
"esbenp.prettier-vscode",
|
||||
"eamodio.gitlens",
|
||||
"alexcvzz.vscode-sqlite",
|
||||
"yzhang.markdown-all-in-one",
|
||||
"mkhl.shfmt"
|
||||
"mkhl.shfmt",
|
||||
"charliermarsh.ruff",
|
||||
"ms-python.flake8"
|
||||
],
|
||||
"settings": {
|
||||
"terminal.integrated.cwd": "${containerWorkspaceFolder}",
|
||||
"terminal.integrated.profiles.linux": {
|
||||
"zsh": {
|
||||
"path": "/bin/zsh",
|
||||
"args": ["-l"]
|
||||
}
|
||||
},
|
||||
"terminal.integrated.defaultProfile.linux": "zsh",
|
||||
|
||||
// Python testing configuration
|
||||
"python.testing.pytestEnabled": true,
|
||||
"python.testing.unittestEnabled": false,
|
||||
"python.testing.pytestArgs": ["test"],
|
||||
"python.testing.cwd": "${containerWorkspaceFolder}",
|
||||
// Make sure we discover tests and import server correctly
|
||||
"python.analysis.extraPaths": [
|
||||
"/workspaces/NetAlertX",
|
||||
|
||||
@@ -3,11 +3,14 @@
|
||||
# .devcontainer/scripts/generate-configs.sh
|
||||
# The generator appends this stage to produce .devcontainer/Dockerfile.
|
||||
# Prefer to place dev-only setup here; use setup.sh only for runtime fixes.
|
||||
# Permissions in devcontainer should be of a brutalist nature. They will be
|
||||
# Open and wide to avoid permission issues during development allowing max
|
||||
# flexibility.
|
||||
|
||||
FROM runner AS netalertx-devcontainer
|
||||
ENV INSTALL_DIR=/app
|
||||
|
||||
ENV PYTHONPATH=/workspaces/NetAlertX/test:/workspaces/NetAlertX/server:/app:/app/server:/opt/venv/lib/python3.12/site-packages:/usr/lib/python3.12/site-packages
|
||||
ENV PYTHONPATH=${PYTHONPATH}:/workspaces/NetAlertX/test:/workspaces/NetAlertX/server:/usr/lib/python3.12/site-packages
|
||||
ENV PATH=/services:${PATH}
|
||||
ENV PHP_INI_SCAN_DIR=/services/config/php/conf.d:/etc/php83/conf.d
|
||||
ENV LISTEN_ADDR=0.0.0.0
|
||||
@@ -18,16 +21,28 @@ COPY .devcontainer/resources/devcontainer-overlay/ /
|
||||
USER root
|
||||
# Install common tools, create user, and set up sudo
|
||||
RUN apk add --no-cache git nano vim jq php83-pecl-xdebug py3-pip nodejs sudo gpgconf pytest \
|
||||
pytest-cov fish shfmt github-cli py3-yaml py3-docker-py docker-cli docker-cli-buildx \
|
||||
pytest-cov zsh alpine-zsh-config shfmt github-cli py3-yaml py3-docker-py docker-cli docker-cli-buildx \
|
||||
docker-cli-compose
|
||||
|
||||
RUN install -d -o netalertx -g netalertx -m 755 /services/php/modules && \
|
||||
cp -a /usr/lib/php83/modules/. /services/php/modules/ && \
|
||||
echo "${NETALERTX_USER} ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers
|
||||
RUN mkdir /workspaces && \
|
||||
install -d -o netalertx -g netalertx -m 777 /services/run/logs && \
|
||||
install -d -o netalertx -g netalertx -m 777 /app/run/tmp/client_body && \
|
||||
sed -i -e 's|:/app:|:/workspaces:|' /etc/passwd && \
|
||||
ENV SHELL=/bin/zsh
|
||||
|
||||
RUN mkdir -p /workspaces && \
|
||||
install -d -m 777 /data /data/config /data/db && \
|
||||
install -d -m 777 /tmp/log /tmp/log/plugins /tmp/api /tmp/run /tmp/nginx && \
|
||||
install -d -m 777 /tmp/nginx/active-config /tmp/nginx/client_body /tmp/nginx/config && \
|
||||
install -d -m 777 /tmp/nginx/fastcgi /tmp/nginx/proxy /tmp/nginx/scgi /tmp/nginx/uwsgi && \
|
||||
install -d -m 777 /tmp/run/tmp /tmp/run/logs && \
|
||||
chmod 777 /workspaces && \
|
||||
chown -R netalertx:netalertx /data && \
|
||||
chmod 666 /data/config/app.conf /data/db/app.db && \
|
||||
chmod 1777 /tmp && \
|
||||
install -d -o root -g root -m 1777 /tmp/.X11-unix && \
|
||||
mkdir -p /home/netalertx && \
|
||||
chown netalertx:netalertx /home/netalertx && \
|
||||
sed -i -e 's#/app:#/workspaces:#' /etc/passwd && \
|
||||
find /opt/venv -type d -exec chmod o+rwx {} \;
|
||||
|
||||
USER netalertx
|
||||
|
||||
@@ -8,7 +8,9 @@ worker_processes auto;
|
||||
pcre_jit on;
|
||||
|
||||
# Configures default error logger.
|
||||
error_log /app/log/nginx-error.log warn;
|
||||
error_log /tmp/log/nginx-error.log warn;
|
||||
|
||||
pid /tmp/run/nginx.pid;
|
||||
|
||||
events {
|
||||
# The maximum number of simultaneous connections that can be opened by
|
||||
@@ -17,13 +19,12 @@ events {
|
||||
}
|
||||
|
||||
http {
|
||||
|
||||
# Mapping of temp paths for various nginx modules.
|
||||
client_body_temp_path /services/run/tmp/client_body;
|
||||
proxy_temp_path /services/run/tmp/proxy;
|
||||
fastcgi_temp_path /services/run/tmp/fastcgi;
|
||||
uwsgi_temp_path /services/run/tmp/uwsgi;
|
||||
scgi_temp_path /services/run/tmp/scgi;
|
||||
client_body_temp_path /tmp/nginx/client_body;
|
||||
proxy_temp_path /tmp/nginx/proxy;
|
||||
fastcgi_temp_path /tmp/nginx/fastcgi;
|
||||
uwsgi_temp_path /tmp/nginx/uwsgi;
|
||||
scgi_temp_path /tmp/nginx/scgi;
|
||||
|
||||
# Includes mapping of file name extensions to MIME types of responses
|
||||
# and defines the default type.
|
||||
@@ -89,7 +90,7 @@ http {
|
||||
'"$http_user_agent" "$http_x_forwarded_for"';
|
||||
|
||||
# Sets the path, format, and configuration for a buffered log write.
|
||||
access_log /app/log/nginx-access.log main;
|
||||
access_log /tmp/log/nginx-access.log main;
|
||||
|
||||
|
||||
# Virtual host config
|
||||
@@ -100,11 +101,10 @@ http {
|
||||
index index.php;
|
||||
add_header X-Forwarded-Prefix "/app" always;
|
||||
|
||||
|
||||
location ~* \.php$ {
|
||||
# Set Cache-Control header to prevent caching on the first load
|
||||
add_header Cache-Control "no-store";
|
||||
fastcgi_pass unix:/services/run/php.sock;
|
||||
fastcgi_pass unix:/tmp/run/php.sock;
|
||||
include /services/config/nginx/fastcgi_params;
|
||||
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
|
||||
fastcgi_param SCRIPT_NAME $fastcgi_script_name;
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
# NetAlertX devcontainer zsh configuration
|
||||
# Keep this lightweight and deterministic so shells behave consistently.
|
||||
|
||||
export PATH="$HOME/.local/bin:$PATH"
|
||||
export EDITOR=vim
|
||||
export SHELL=/bin/zsh
|
||||
|
||||
# Start inside the workspace if it exists
|
||||
if [ -d "/workspaces/NetAlertX" ]; then
|
||||
cd /workspaces/NetAlertX
|
||||
fi
|
||||
|
||||
# Enable basic completion and prompt helpers
|
||||
autoload -Uz compinit promptinit colors
|
||||
colors
|
||||
compinit -u
|
||||
promptinit
|
||||
|
||||
# Friendly prompt with virtualenv awareness
|
||||
setopt PROMPT_SUBST
|
||||
|
||||
_venv_segment() {
|
||||
if [ -n "$VIRTUAL_ENV" ]; then
|
||||
printf '(%s) ' "${VIRTUAL_ENV:t}"
|
||||
fi
|
||||
}
|
||||
|
||||
PROMPT='%F{green}$(_venv_segment)%f%F{cyan}%n@%m%f %F{yellow}%~%f %# '
|
||||
RPROMPT='%F{magenta}$(git rev-parse --abbrev-ref HEAD 2>/dev/null)%f'
|
||||
|
||||
# Sensible defaults
|
||||
setopt autocd
|
||||
setopt correct
|
||||
setopt extendedglob
|
||||
HISTFILE="$HOME/.zsh_history"
|
||||
HISTSIZE=5000
|
||||
SAVEHIST=5000
|
||||
|
||||
alias ll='ls -alF'
|
||||
alias la='ls -A'
|
||||
alias gs='git status -sb'
|
||||
alias gp='git pull --ff-only'
|
||||
|
||||
# Ensure pyenv/virtualenv activate hooks adjust the prompt cleanly
|
||||
if [ -f "$HOME/.zshrc.local" ]; then
|
||||
source "$HOME/.zshrc.local"
|
||||
fi
|
||||
@@ -1,7 +1,11 @@
|
||||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
|
||||
read -r -p "Are you sure you want to destroy your host docker containers and images? Type YES to continue: " reply
|
||||
if [[ -n "${CONFIRM_PRUNE:-}" && "${CONFIRM_PRUNE}" == "YES" ]]; then
|
||||
reply="YES"
|
||||
else
|
||||
read -r -p "Are you sure you want to destroy your host docker containers and images? Type YES to continue: " reply
|
||||
fi
|
||||
|
||||
if [[ "${reply}" == "YES" ]]; then
|
||||
docker system prune -af
|
||||
|
||||
@@ -1,184 +1,110 @@
|
||||
#!/bin/bash
|
||||
# Runtime setup for devcontainer (executed after container starts).
|
||||
# Prefer building setup into resources/devcontainer-Dockerfile when possible.
|
||||
# Use this script for runtime-only adjustments (permissions, sockets, ownership,
|
||||
# and services managed without init) that are difficult at build time.
|
||||
id
|
||||
|
||||
# Define variables (paths, ports, environment)
|
||||
|
||||
export APP_DIR="/app"
|
||||
export APP_COMMAND="/workspaces/NetAlertX/.devcontainer/scripts/restart-backend.sh"
|
||||
export PHP_FPM_BIN="/usr/sbin/php-fpm83"
|
||||
export CROND_BIN="/usr/sbin/crond -f"
|
||||
# NetAlertX Devcontainer Setup Script
|
||||
#
|
||||
# This script forcefully resets all runtime state for a single-user devcontainer.
|
||||
# It is intentionally idempotent: every run wipes and recreates all relevant folders,
|
||||
# symlinks, and files, so the environment is always fresh and predictable.
|
||||
#
|
||||
# - No conditional logic: everything is (re)created, overwritten, or reset unconditionally.
|
||||
# - No security hardening: this is for disposable, local dev use only.
|
||||
# - No checks for existing files, mounts, or processes—just do the work.
|
||||
#
|
||||
# If you add new runtime files or folders, add them to the creation/reset section below.
|
||||
#
|
||||
# Do not add if-then logic or error handling for missing/existing files. Simplicity is the goal.
|
||||
|
||||
|
||||
export ALWAYS_FRESH_INSTALL=false
|
||||
export INSTALL_DIR=/app
|
||||
export LOGS_LOCATION=/app/logs
|
||||
export CONF_FILE="app.conf"
|
||||
export DB_FILE="app.db"
|
||||
export FULL_FILEDB_PATH="${INSTALL_DIR}/db/${DB_FILE}"
|
||||
export OUI_FILE="/usr/share/arp-scan/ieee-oui.txt" # Define the path to ieee-oui.txt and ieee-iab.txt
|
||||
export TZ=Europe/Paris
|
||||
export PORT=20211
|
||||
export SOURCE_DIR="/workspaces/NetAlertX"
|
||||
SOURCE_DIR=${SOURCE_DIR:-/workspaces/NetAlertX}
|
||||
PY_SITE_PACKAGES="${VIRTUAL_ENV:-/opt/venv}/lib/python3.12/site-packages"
|
||||
SOURCE_SERVICES_DIR="${SOURCE_DIR}/install/production-filesystem/services"
|
||||
|
||||
LOG_FILES=(
|
||||
LOG_APP
|
||||
LOG_APP_FRONT
|
||||
LOG_STDOUT
|
||||
LOG_STDERR
|
||||
LOG_EXECUTION_QUEUE
|
||||
LOG_APP_PHP_ERRORS
|
||||
LOG_IP_CHANGES
|
||||
LOG_CROND
|
||||
LOG_REPORT_OUTPUT_TXT
|
||||
LOG_REPORT_OUTPUT_HTML
|
||||
LOG_REPORT_OUTPUT_JSON
|
||||
LOG_DB_IS_LOCKED
|
||||
LOG_NGINX_ERROR
|
||||
)
|
||||
|
||||
ensure_docker_socket_access() {
|
||||
local socket="/var/run/docker.sock"
|
||||
if [ ! -S "${socket}" ]; then
|
||||
echo "docker socket not present; skipping docker group configuration"
|
||||
return
|
||||
fi
|
||||
sudo chmod 666 /var/run/docker.sock 2>/dev/null || true
|
||||
sudo chown "$(id -u)":"$(id -g)" /workspaces
|
||||
sudo chmod 755 /workspaces
|
||||
|
||||
local sock_gid
|
||||
sock_gid=$(stat -c '%g' "${socket}" 2>/dev/null || true)
|
||||
if [ -z "${sock_gid}" ]; then
|
||||
echo "unable to determine docker socket gid; skipping docker group configuration"
|
||||
return
|
||||
fi
|
||||
killall php-fpm83 nginx crond python3 2>/dev/null || true
|
||||
|
||||
local group_entry=""
|
||||
if command -v getent >/dev/null 2>&1; then
|
||||
group_entry=$(getent group "${sock_gid}" 2>/dev/null || true)
|
||||
else
|
||||
group_entry=$(grep -E ":${sock_gid}:" /etc/group 2>/dev/null || true)
|
||||
fi
|
||||
# Mount ramdisks for volatile data
|
||||
sudo mount -t tmpfs -o size=100m,mode=0777 tmpfs /tmp/log 2>/dev/null || true
|
||||
sudo mount -t tmpfs -o size=50m,mode=0777 tmpfs /tmp/api 2>/dev/null || true
|
||||
sudo mount -t tmpfs -o size=50m,mode=0777 tmpfs /tmp/run 2>/dev/null || true
|
||||
sudo mount -t tmpfs -o size=50m,mode=0777 tmpfs /tmp/nginx 2>/dev/null || true
|
||||
|
||||
local group_name=""
|
||||
if [ -n "${group_entry}" ]; then
|
||||
group_name=$(echo "${group_entry}" | cut -d: -f1)
|
||||
else
|
||||
group_name="docker-host"
|
||||
sudo addgroup -g "${sock_gid}" "${group_name}" 2>/dev/null || group_name=$(grep -E ":${sock_gid}:" /etc/group | head -n1 | cut -d: -f1)
|
||||
fi
|
||||
|
||||
if [ -z "${group_name}" ]; then
|
||||
echo "failed to resolve group for docker socket gid ${sock_gid}; skipping docker group configuration"
|
||||
return
|
||||
fi
|
||||
|
||||
if ! id -nG netalertx | tr ' ' '\n' | grep -qx "${group_name}"; then
|
||||
sudo addgroup netalertx "${group_name}" 2>/dev/null || true
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
main() {
|
||||
echo "=== NetAlertX Development Container Setup ==="
|
||||
killall php-fpm83 nginx crond python3 2>/dev/null
|
||||
sleep 1
|
||||
echo "Setting up ${SOURCE_DIR}..."
|
||||
ensure_docker_socket_access
|
||||
sudo chown $(id -u):$(id -g) /workspaces
|
||||
sudo chmod 755 /workspaces
|
||||
configure_source
|
||||
|
||||
echo "--- Starting Development Services ---"
|
||||
configure_php
|
||||
|
||||
|
||||
start_services
|
||||
}
|
||||
|
||||
isRamDisk() {
|
||||
if [ -z "$1" ] || [ ! -d "$1" ]; then
|
||||
echo "Usage: isRamDisk <directory>" >&2
|
||||
return 2
|
||||
fi
|
||||
|
||||
local fstype
|
||||
fstype=$(df -T "$1" | awk 'NR==2 {print $2}')
|
||||
|
||||
if [ "$fstype" = "tmpfs" ] || [ "$fstype" = "ramfs" ]; then
|
||||
return 0 # Success (is a ramdisk)
|
||||
else
|
||||
return 1 # Failure (is not a ramdisk)
|
||||
fi
|
||||
}
|
||||
|
||||
# Setup source directory
|
||||
configure_source() {
|
||||
echo "[1/4] Configuring System..."
|
||||
echo " -> Setting up /services permissions"
|
||||
sudo chown -R netalertx /services
|
||||
|
||||
echo "[2/4] Configuring Source..."
|
||||
echo " -> Cleaning up previous instances"
|
||||
|
||||
test -e ${NETALERTX_LOG} && sudo umount "${NETALERTX_LOG}" 2>/dev/null || true
|
||||
test -e ${NETALERTX_API} && sudo umount "${NETALERTX_API}" 2>/dev/null || true
|
||||
test -e ${NETALERTX_APP} && sudo rm -Rf ${NETALERTX_APP}/
|
||||
|
||||
echo " -> Linking source to ${NETALERTX_APP}"
|
||||
sudo ln -s ${SOURCE_DIR}/ ${NETALERTX_APP}
|
||||
|
||||
echo " -> Mounting ramdisks for /log and /api"
|
||||
mkdir -p ${NETALERTX_LOG} ${NETALERTX_API}
|
||||
sudo mount -o uid=$(id -u netalertx),gid=$(id -g netalertx),mode=775 -t tmpfs -o size=256M tmpfs "${NETALERTX_LOG}"
|
||||
sudo mount -o uid=$(id -u netalertx),gid=$(id -g netalertx),mode=775 -t tmpfs -o size=256M tmpfs "${NETALERTX_API}"
|
||||
mkdir -p ${NETALERTX_PLUGINS_LOG}
|
||||
touch ${NETALERTX_PLUGINS_LOG}/.dockerignore ${NETALERTX_API}/.dockerignore
|
||||
# tmpfs mounts configured with netalertx ownership and 775 permissions above
|
||||
|
||||
touch /app/log/nginx_error.log
|
||||
echo " -> Empty log"|tee ${INSTALL_DIR}/log/app.log \
|
||||
${INSTALL_DIR}/log/app_front.log \
|
||||
${INSTALL_DIR}/log/stdout.log
|
||||
touch ${INSTALL_DIR}/log/stderr.log \
|
||||
${INSTALL_DIR}/log/execution_queue.log
|
||||
echo 0 > ${INSTALL_DIR}/log/db_is_locked.log
|
||||
for f in ${INSTALL_DIR}/log/*.log; do
|
||||
sudo chown netalertx:www-data $f
|
||||
sudo chmod 664 $f
|
||||
echo "" > $f
|
||||
done
|
||||
|
||||
mkdir -p /app/log/plugins
|
||||
sudo chown -R netalertx:www-data ${INSTALL_DIR}
|
||||
|
||||
|
||||
while ps ax | grep -v grep | grep python3 > /dev/null; do
|
||||
killall python3 &>/dev/null
|
||||
sleep 0.2
|
||||
done
|
||||
sudo chmod 777 /opt/venv/lib/python3.12/site-packages/ && \
|
||||
sudo chmod 005 /opt/venv/lib/python3.12/site-packages/
|
||||
sudo chmod 666 /var/run/docker.sock
|
||||
|
||||
echo " -> Updating build timestamp"
|
||||
date +%s > ${NETALERTX_FRONT}/buildtimestamp.txt
|
||||
|
||||
}
|
||||
|
||||
# configure_php: configure PHP-FPM and enable dev debug options
|
||||
configure_php() {
|
||||
echo "[3/4] Configuring PHP-FPM..."
|
||||
sudo chown -R netalertx:netalertx ${SYSTEM_SERVICES_RUN} 2>/dev/null || true
|
||||
|
||||
}
|
||||
|
||||
# start_services: start crond, PHP-FPM, nginx and the application
|
||||
start_services() {
|
||||
echo "[4/4] Starting services"
|
||||
|
||||
sudo chmod +x /entrypoint.sh
|
||||
setsid bash /entrypoint.sh&
|
||||
sleep 1
|
||||
}
|
||||
|
||||
|
||||
sudo chmod 755 /app/
|
||||
echo "Development $(git rev-parse --short=8 HEAD)"| sudo tee /app/.VERSION
|
||||
# Run the main function
|
||||
main
|
||||
|
||||
# create a services readme file
|
||||
echo "This folder is auto-generated by the container and devcontainer setup.sh script." > /services/README.md
|
||||
echo "Any changes here will be lost on rebuild. To make permanent changes, edit files in .devcontainer or production filesystem and rebuild the container." >> /services/README.md
|
||||
echo "Only make temporary/test changes in this folder, then perform a rebuild to reset." >> /services/README.md
|
||||
sudo chmod 777 /tmp/log /tmp/api /tmp/run /tmp/nginx
|
||||
|
||||
|
||||
|
||||
sudo rm -rf "${SYSTEM_NGINX_CONFIG}/conf.active"
|
||||
sudo ln -s "${SYSTEM_SERVICES_ACTIVE_CONFIG}" "${SYSTEM_NGINX_CONFIG}/conf.active"
|
||||
|
||||
sudo rm -rf /entrypoint.d
|
||||
sudo ln -s "${SOURCE_DIR}/install/production-filesystem/entrypoint.d" /entrypoint.d
|
||||
|
||||
sudo rm -rf "${NETALERTX_APP}"
|
||||
sudo ln -s "${SOURCE_DIR}/" "${NETALERTX_APP}"
|
||||
|
||||
for dir in "${NETALERTX_DATA}" "${NETALERTX_CONFIG}" "${NETALERTX_DB}"; do
|
||||
sudo install -d -m 777 "${dir}"
|
||||
done
|
||||
|
||||
for dir in \
|
||||
"${SYSTEM_SERVICES_RUN_LOG}" \
|
||||
"${SYSTEM_SERVICES_ACTIVE_CONFIG}" \
|
||||
"${NETALERTX_PLUGINS_LOG}" \
|
||||
"/tmp/nginx/client_body" \
|
||||
"/tmp/nginx/proxy" \
|
||||
"/tmp/nginx/fastcgi" \
|
||||
"/tmp/nginx/uwsgi" \
|
||||
"/tmp/nginx/scgi"; do
|
||||
sudo install -d -m 777 "${dir}"
|
||||
done
|
||||
|
||||
# Create nginx temp subdirs with permissions
|
||||
sudo mkdir -p "${SYSTEM_SERVICES_RUN_TMP}/client_body" "${SYSTEM_SERVICES_RUN_TMP}/proxy" "${SYSTEM_SERVICES_RUN_TMP}/fastcgi" "${SYSTEM_SERVICES_RUN_TMP}/uwsgi" "${SYSTEM_SERVICES_RUN_TMP}/scgi"
|
||||
sudo chmod -R 777 "${SYSTEM_SERVICES_RUN_TMP}"
|
||||
|
||||
for var in "${LOG_FILES[@]}"; do
|
||||
path=${!var}
|
||||
dir=$(dirname "${path}")
|
||||
sudo install -d -m 777 "${dir}"
|
||||
touch "${path}"
|
||||
done
|
||||
|
||||
printf '0\n' | sudo tee "${LOG_DB_IS_LOCKED}" >/dev/null
|
||||
sudo chmod 777 "${LOG_DB_IS_LOCKED}"
|
||||
|
||||
sudo pkill -f python3 2>/dev/null || true
|
||||
|
||||
sudo chmod 777 "${PY_SITE_PACKAGES}" "${NETALERTX_DATA}" "${NETALERTX_DATA}"/* 2>/dev/null || true
|
||||
|
||||
sudo chmod 005 "${PY_SITE_PACKAGES}" 2>/dev/null || true
|
||||
|
||||
sudo chown -R "${NETALERTX_USER}:${NETALERTX_GROUP}" "${NETALERTX_APP}"
|
||||
date +%s | sudo tee "${NETALERTX_FRONT}/buildtimestamp.txt" >/dev/null
|
||||
|
||||
sudo chmod 755 "${NETALERTX_APP}"
|
||||
|
||||
sudo chmod +x /entrypoint.sh
|
||||
setsid bash /entrypoint.sh &
|
||||
sleep 1
|
||||
|
||||
echo "Development $(git rev-parse --short=8 HEAD)" | sudo tee "${NETALERTX_APP}/.VERSION" >/dev/null
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
.dockerignore
|
||||
**/.dockerignore
|
||||
.env
|
||||
.git
|
||||
.github
|
||||
|
||||
3
.flake8
Normal file
3
.flake8
Normal file
@@ -0,0 +1,3 @@
|
||||
[flake8]
|
||||
max-line-length = 180
|
||||
ignore = E221,E222,E251,E203
|
||||
24
.github/ISSUE_TEMPLATE/i-have-an-issue.yml
vendored
24
.github/ISSUE_TEMPLATE/i-have-an-issue.yml
vendored
@@ -46,7 +46,7 @@ body:
|
||||
attributes:
|
||||
label: app.conf
|
||||
description: |
|
||||
Paste your `app.conf` (remove personal info)
|
||||
Paste relevant `app.conf`settings (remove sensitive info)
|
||||
render: python
|
||||
validations:
|
||||
required: false
|
||||
@@ -70,6 +70,13 @@ body:
|
||||
- Bare-metal (community only support - Check Discord)
|
||||
validations:
|
||||
required: true
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: Debug or Trace enabled
|
||||
description: I confirm I set `LOG_LEVEL` to `debug` or `trace`
|
||||
options:
|
||||
- label: I have read and followed the steps in the wiki link above and provided the required debug logs and the log section covers the time when the issue occurs.
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: app.log
|
||||
@@ -78,13 +85,14 @@ body:
|
||||
***Generally speaking, all bug reports should have logs provided.***
|
||||
Tip: You can attach images or log files by clicking this area to highlight it and then dragging files in.
|
||||
Additionally, any additional info? Screenshots? References? Anything that will give us more context about the issue you are encountering!
|
||||
You can use `tail -100 /app/log/app.log` in the container if you have trouble getting to the log files.
|
||||
You can use `tail -100 /app/log/app.log` in the container if you have trouble getting to the log files or send them to netalertx@gmail.com with the issue number.
|
||||
validations:
|
||||
required: false
|
||||
- type: checkboxes
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Debug enabled
|
||||
description: I confirm I enabled `debug`
|
||||
options:
|
||||
- label: I have read and followed the steps in the wiki link above and provided the required debug logs and the log section covers the time when the issue occurs.
|
||||
required: true
|
||||
label: Docker Logs
|
||||
description: |
|
||||
You can retrieve the logs from Portainer -> Containers -> your NetAlertX container -> Logs or by running `sudo docker logs netalertx`.
|
||||
validations:
|
||||
required: true
|
||||
|
||||
|
||||
19
.github/copilot-instructions.md
vendored
19
.github/copilot-instructions.md
vendored
@@ -18,7 +18,7 @@ Backend loop phases (see `server/__main__.py` and `server/plugin.py`): `once`, `
|
||||
## Plugin patterns that matter
|
||||
- Manifest lives at `front/plugins/<code_name>/config.json`; `code_name` == folder, `unique_prefix` drives settings and filenames (e.g., `ARPSCAN`).
|
||||
- Control via settings: `<PREF>_RUN` (phase), `<PREF>_RUN_SCHD` (cron-like), `<PREF>_CMD` (script path), `<PREF>_RUN_TIMEOUT`, `<PREF>_WATCH` (diff columns).
|
||||
- Data contract: scripts write `/app/log/plugins/last_result.<PREF>.log` (pipe‑delimited: 9 required cols + optional 4). Use `front/plugins/plugin_helper.py`’s `Plugin_Objects` to sanitize text and normalize MACs, then `write_result_file()`.
|
||||
- Data contract: scripts write `/tmp/log/plugins/last_result.<PREF>.log` (pipe‑delimited: 9 required cols + optional 4). Use `front/plugins/plugin_helper.py`’s `Plugin_Objects` to sanitize text and normalize MACs, then `write_result_file()`.
|
||||
- Device import: define `database_column_definitions` when creating/updating devices; watched fields trigger notifications.
|
||||
|
||||
### Standard Plugin Formats
|
||||
@@ -30,6 +30,7 @@ Backend loop phases (see `server/__main__.py` and `server/plugin.py`): `once`, `
|
||||
* other: Miscellaneous plugins. Runs at various times. Data source: self / Template.
|
||||
|
||||
### Plugin logging & outputs
|
||||
- Always check relevant logs first.
|
||||
- Use logging as shown in other plugins.
|
||||
- Collect results with `Plugin_Objects.add_object(...)` during processing and call `plugin_objects.write_result_file()` exactly once at the end of the script.
|
||||
- Prefer to log a brief summary before writing (e.g., total objects added) to aid troubleshooting; keep logs concise at `info` level and use `verbose` or `debug` for extra context.
|
||||
@@ -42,22 +43,32 @@ Backend loop phases (see `server/__main__.py` and `server/plugin.py`): `once`, `
|
||||
## Conventions & helpers to reuse
|
||||
- Settings: add/modify via `ccd()` in `server/initialise.py` or per‑plugin manifest. Never hardcode ports or secrets; use `get_setting_value()`.
|
||||
- Logging: use `logger.mylog(level, [message])`; levels: none/minimal/verbose/debug/trace.
|
||||
- Time/MAC/strings: `helper.py` (`timeNowTZ`, `normalize_mac`, sanitizers). Validate MACs before DB writes.
|
||||
- Time/MAC/strings: `helper.py` (`timeNowDB`, `normalize_mac`, sanitizers). Validate MACs before DB writes.
|
||||
- DB helpers: prefer `server/db/db_helper.py` functions (e.g., `get_table_json`, device condition helpers) over raw SQL in new paths.
|
||||
|
||||
## Dev workflow (devcontainer)
|
||||
- **Devcontainer philosophy: brutal simplicity.** One user, everything writable, completely idempotent. No permission checks, no conditional logic, no sudo needed. If something doesn't work, tear down the wall and rebuild - don't patch. We unit test permissions in the hardened build.
|
||||
- **Permissions:** Never `chmod` or `chown` during operations. Everything is already writable. If you need permissions, the devcontainer setup is broken - fix `.devcontainer/scripts/setup.sh` or `.devcontainer/resources/devcontainer-Dockerfile` instead.
|
||||
- **Files & Paths:** Use environment variables (`NETALERTX_DB`, `NETALERTX_LOG`, etc.) everywhere. `/data` for persistent config/db, `/tmp` for runtime logs/api/nginx state. Never hardcode `/data/db` or relative paths.
|
||||
- **Database reset:** Use the `[Dev Container] Wipe and Regenerate Database` task. Kills backend, deletes `/data/{db,config}/*`, runs first-time setup scripts. Clean slate, no questions.
|
||||
- Services: use tasks to (re)start backend and nginx/PHP-FPM. Backend runs with debugpy on 5678; attach a Python debugger if needed.
|
||||
- Run a plugin manually: `python3 front/plugins/<code_name>/script.py` (ensure `sys.path` includes `/app/front/plugins` and `/app/server` like the template).
|
||||
- Testing: pytest available via Alpine packages. Tests live in `test/`; app code is under `server/`. PYTHONPATH is preconfigured to include workspace and `/opt/venv` site‑packages.
|
||||
- **Subprocess calls:** ALWAYS set explicit timeouts. Default to 60s minimum unless plugin config specifies otherwise. Nested subprocess calls (e.g., plugins calling external tools) need their own timeout - outer plugin timeout won't save you.
|
||||
|
||||
## What “done right” looks like
|
||||
- When adding a plugin, start from `front/plugins/__template`, implement with `plugin_helper`, define manifest settings, and wire phase via `<PREF>_RUN`. Verify logs in `/app/log/plugins/` and data in `api/*.json`.
|
||||
- When adding a plugin, start from `front/plugins/__template`, implement with `plugin_helper`, define manifest settings, and wire phase via `<PREF>_RUN`. Verify logs in `/tmp/log/plugins/` and data in `api/*.json`.
|
||||
- When introducing new config, define it once (core `ccd()` or plugin manifest) and read it via helpers everywhere.
|
||||
- When exposing new server functionality, add endpoints in `server/api_server/*` and keep authorization consistent; update UI by reading/writing JSON cache rather than bypassing the pipeline.
|
||||
|
||||
## Useful references
|
||||
- Docs: `docs/PLUGINS_DEV.md`, `docs/SETTINGS_SYSTEM.md`, `docs/API_*.md`, `docs/DEBUG_*.md`
|
||||
- Logs: backend `/app/log/app.log`, plugin logs under `/app/log/plugins/`, nginx/php logs under `/var/log/*`
|
||||
- Logs: All logs are under `/tmp/log/`. Plugin logs are very shortly under `/tmp/log/plugins/` until picked up by the server.
|
||||
- plugin logs: `/tmp/log/app.log`
|
||||
- backend logs: `/tmp/log/stdout.log` and `/tmp/log/stderr.log`
|
||||
- frontend commands logs: `/tmp/log/app_front.log`
|
||||
- php errors: `/tmp/log/app.php_errors.log`
|
||||
- nginx logs: `/tmp/log/nginx-access.log` and `/tmp/log/nginx-error.log`
|
||||
|
||||
## Assistant expectations:
|
||||
- Be concise, opinionated, and biased toward security and simplicity.
|
||||
|
||||
21
.github/workflows/docker_dev.yml
vendored
21
.github/workflows/docker_dev.yml
vendored
@@ -10,7 +10,7 @@ on:
|
||||
branches:
|
||||
- next_release
|
||||
|
||||
jobs:
|
||||
jobs:
|
||||
docker_dev:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 30
|
||||
@@ -19,7 +19,8 @@ jobs:
|
||||
packages: write
|
||||
if: >
|
||||
contains(github.event.head_commit.message, 'PUSHPROD') != 'True' &&
|
||||
github.repository == 'jokob-sk/NetAlertX'
|
||||
github.repository == 'jokob-sk/NetAlertX'
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
@@ -30,26 +31,36 @@ jobs:
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
# --- Generate timestamped dev version
|
||||
- name: Generate timestamp version
|
||||
id: timestamp
|
||||
run: |
|
||||
ts=$(date -u +'%Y%m%d-%H%M%S')
|
||||
echo "version=dev-${ts}" >> $GITHUB_OUTPUT
|
||||
echo "Generated version: dev-${ts}"
|
||||
|
||||
- name: Set up dynamic build ARGs
|
||||
id: getargs
|
||||
id: getargs
|
||||
run: echo "version=$(cat ./stable/VERSION)" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Get release version
|
||||
id: get_version
|
||||
run: echo "version=Dev" >> $GITHUB_OUTPUT
|
||||
|
||||
# --- Write the timestamped version to .VERSION file
|
||||
- name: Create .VERSION file
|
||||
run: echo "${{ steps.get_version.outputs.version }}" >> .VERSION
|
||||
run: echo "${{ steps.timestamp.outputs.version }}" > .VERSION
|
||||
|
||||
- name: Docker meta
|
||||
id: meta
|
||||
uses: docker/metadata-action@v4
|
||||
uses: docker/metadata-action@v5
|
||||
with:
|
||||
images: |
|
||||
ghcr.io/jokob-sk/netalertx-dev
|
||||
jokobsk/netalertx-dev
|
||||
tags: |
|
||||
type=raw,value=latest
|
||||
type=raw,value=${{ steps.timestamp.outputs.version }}
|
||||
type=ref,event=branch
|
||||
type=ref,event=pr
|
||||
type=semver,pattern={{version}}
|
||||
|
||||
31
.github/workflows/docker_prod.yml
vendored
31
.github/workflows/docker_prod.yml
vendored
@@ -6,7 +6,6 @@
|
||||
# GitHub recommends pinning actions to a commit SHA.
|
||||
# To get a newer version, you will need to update the SHA.
|
||||
# You can also reference a tag or branch, but the action may change without warning.
|
||||
|
||||
name: Publish Docker image
|
||||
|
||||
on:
|
||||
@@ -14,6 +13,7 @@ on:
|
||||
types: [published]
|
||||
tags:
|
||||
- '*.[1-9]+[0-9]?.[1-9]+*'
|
||||
|
||||
jobs:
|
||||
docker:
|
||||
runs-on: ubuntu-latest
|
||||
@@ -21,6 +21,7 @@ jobs:
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
@@ -31,42 +32,39 @@ jobs:
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Set up dynamic build ARGs
|
||||
id: getargs
|
||||
run: echo "version=$(cat ./stable/VERSION)" >> $GITHUB_OUTPUT
|
||||
|
||||
# --- Get release version from tag
|
||||
- name: Get release version
|
||||
id: get_version
|
||||
run: echo "::set-output name=version::${GITHUB_REF#refs/tags/}"
|
||||
run: echo "version=${GITHUB_REF#refs/tags/}" >> $GITHUB_OUTPUT
|
||||
|
||||
# --- Write version to .VERSION file
|
||||
- name: Create .VERSION file
|
||||
run: echo "${{ steps.get_version.outputs.version }}" >> .VERSION
|
||||
run: echo "${{ steps.get_version.outputs.version }}" > .VERSION
|
||||
|
||||
# --- Generate Docker metadata and tags
|
||||
- name: Docker meta
|
||||
id: meta
|
||||
uses: docker/metadata-action@v4
|
||||
uses: docker/metadata-action@v5
|
||||
with:
|
||||
# list of Docker images to use as base name for tags
|
||||
images: |
|
||||
ghcr.io/jokob-sk/netalertx
|
||||
jokobsk/netalertx
|
||||
# generate Docker tags based on the following events/attributes
|
||||
jokobsk/netalertx
|
||||
tags: |
|
||||
type=semver,pattern={{version}},value=${{ inputs.version }}
|
||||
type=semver,pattern={{major}}.{{minor}},value=${{ inputs.version }}
|
||||
type=semver,pattern={{major}},value=${{ inputs.version }}
|
||||
type=semver,pattern={{version}},value=${{ steps.get_version.outputs.version }}
|
||||
type=semver,pattern={{major}}.{{minor}},value=${{ steps.get_version.outputs.version }}
|
||||
type=semver,pattern={{major}},value=${{ steps.get_version.outputs.version }}
|
||||
type=ref,event=branch,suffix=-{{ sha }}
|
||||
type=ref,event=pr
|
||||
type=raw,value=latest,enable=${{ github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/') }}
|
||||
|
||||
- name: Log in to Github Container registry
|
||||
- name: Log in to Github Container Registry (GHCR)
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: jokob-sk
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Login to DockerHub
|
||||
- name: Log in to DockerHub
|
||||
if: github.event_name != 'pull_request'
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
@@ -81,6 +79,5 @@ jobs:
|
||||
push: ${{ github.event_name != 'pull_request' }}
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
# # ⚠ disable cache if build is failing to download debian packages
|
||||
# cache-from: type=registry,ref=ghcr.io/jokob-sk/netalertx:buildcache
|
||||
# cache-to: type=registry,ref=ghcr.io/jokob-sk/netalertx:buildcache,mode=max
|
||||
|
||||
2
.github/workflows/docker_rewrite.yml
vendored
2
.github/workflows/docker_rewrite.yml
vendored
@@ -43,7 +43,7 @@ jobs:
|
||||
|
||||
- name: Docker meta
|
||||
id: meta
|
||||
uses: docker/metadata-action@v4
|
||||
uses: docker/metadata-action@v5
|
||||
with:
|
||||
images: |
|
||||
ghcr.io/jokob-sk/netalertx-dev-rewrite
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
import sys, importlib
|
||||
mods = [
|
||||
'json', 'simplejson',
|
||||
'httplib', 'http.client',
|
||||
'urllib2', 'urllib.request',
|
||||
'Queue', 'queue',
|
||||
'cStringIO', 'StringIO', 'io',
|
||||
'md5', 'hashlib',
|
||||
'ssl'
|
||||
]
|
||||
print('PYTHON_EXE:' + sys.executable)
|
||||
print('PYTHON_VER:' + sys.version.replace('\n', ' '))
|
||||
for m in mods:
|
||||
try:
|
||||
mod = importlib.import_module(m)
|
||||
ver = getattr(mod, '__version__', None)
|
||||
if ver is None:
|
||||
# try common attributes
|
||||
ver = getattr(mod, 'version', None)
|
||||
info = (' version=' + str(ver)) if ver is not None else ''
|
||||
print('OK %s%s' % (m, info))
|
||||
except Exception as e:
|
||||
print('MISSING %s %s: %s' % (m, e.__class__.__name__, e))
|
||||
8
.vscode/launch.json
vendored
8
.vscode/launch.json
vendored
@@ -29,6 +29,14 @@
|
||||
"pathMappings": {
|
||||
"/app": "${workspaceFolder}"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Python: Current File",
|
||||
"type": "debugpy",
|
||||
"request": "launch",
|
||||
"program": "${file}",
|
||||
"console": "integratedTerminal",
|
||||
"justMyCode": true
|
||||
}
|
||||
]
|
||||
}
|
||||
18
.vscode/settings.json
vendored
18
.vscode/settings.json
vendored
@@ -11,13 +11,23 @@
|
||||
// Let the Python extension invoke pytest via the interpreter; avoid hardcoded paths
|
||||
// Removed python.testing.pytestPath and legacy pytest.command overrides
|
||||
|
||||
"terminal.integrated.defaultProfile.linux": "fish",
|
||||
"terminal.integrated.defaultProfile.linux": "zsh",
|
||||
"terminal.integrated.profiles.linux": {
|
||||
"fish": {
|
||||
"path": "/usr/bin/fish"
|
||||
"zsh": {
|
||||
"path": "/bin/zsh"
|
||||
}
|
||||
}
|
||||
,
|
||||
// Fallback for older VS Code versions or schema validators that don't accept custom profiles
|
||||
"terminal.integrated.shell.linux": "/usr/bin/fish"
|
||||
"terminal.integrated.shell.linux": "/usr/bin/zsh"
|
||||
,
|
||||
"python.linting.flake8Enabled": true,
|
||||
"python.linting.enabled": true,
|
||||
"python.linting.flake8Args": [
|
||||
"--config=.flake8"
|
||||
],
|
||||
"python.formatting.provider": "black",
|
||||
"python.formatting.blackArgs": [
|
||||
"--line-length=180"
|
||||
]
|
||||
}
|
||||
93
.vscode/tasks.json
vendored
93
.vscode/tasks.json
vendored
@@ -1,16 +1,27 @@
|
||||
{
|
||||
"version": "2.0.0",
|
||||
"inputs": [
|
||||
{
|
||||
"id": "confirmPrune",
|
||||
"type": "promptString",
|
||||
"description": "DANGER! Type YES to confirm pruning all unused Docker resources. This will destroy containers, images, volumes, and networks!",
|
||||
"default": ""
|
||||
}
|
||||
],
|
||||
"tasks": [
|
||||
{
|
||||
"label": "[Any POSIX] Generate Devcontainer Configs",
|
||||
"type": "shell",
|
||||
"command": ".devcontainer/scripts/generate-configs.sh",
|
||||
"detail": "Generates devcontainer configs from the template. This must be run after changes to devcontainer to combine/merge them into the final config used by VS Code. Note- this has no bearing on the production or test image.",
|
||||
"presentation": {
|
||||
"echo": true,
|
||||
"reveal": "always",
|
||||
"panel": "shared",
|
||||
"showReuseMessage": false
|
||||
"showReuseMessage": false,
|
||||
"group": "POSIX Tasks"
|
||||
},
|
||||
|
||||
"problemMatcher": [],
|
||||
"group": {
|
||||
"kind": "build",
|
||||
@@ -24,12 +35,19 @@
|
||||
{
|
||||
"label": "[Any] Docker system and build Prune",
|
||||
"type": "shell",
|
||||
"command": ".devcontainer/scripts/confirm-docker-prune.sh",
|
||||
"command": ".devcontainer/scripts/confirm-docker-prune.sh",
|
||||
"detail": "DANGER! Prunes all unused Docker resources (images, containers, volumes, networks). Any stopped container will be wiped and data will be lost. Use with caution.",
|
||||
"options": {
|
||||
"env": {
|
||||
"CONFIRM_PRUNE": "${input:confirmPrune}"
|
||||
}
|
||||
},
|
||||
"presentation": {
|
||||
"echo": true,
|
||||
"reveal": "always",
|
||||
"panel": "shared",
|
||||
"showReuseMessage": false
|
||||
"showReuseMessage": false,
|
||||
"group": "Any"
|
||||
},
|
||||
"problemMatcher": [],
|
||||
"group": {
|
||||
@@ -45,6 +63,7 @@
|
||||
"label": "[Dev Container] Re-Run Startup Script",
|
||||
"type": "shell",
|
||||
"command": "./isDevContainer.sh || exit 1;/workspaces/NetAlertX/.devcontainer/scripts/setup.sh",
|
||||
"detail": "The startup script runs directly after the container is started. It reprovisions permissions, links folders, and performs other setup tasks. Run this if you have made changes to the setup script or need to reprovision the container.",
|
||||
"options": {
|
||||
"cwd": "/workspaces/NetAlertX/.devcontainer/scripts"
|
||||
},
|
||||
@@ -65,6 +84,7 @@
|
||||
"label": "[Dev Container] Start Backend (Python)",
|
||||
"type": "shell",
|
||||
"command": "./isDevContainer.sh || exit 1; /services/start-backend.sh",
|
||||
"detail": "Restarts the NetAlertX backend (Python) service in the dev container. This may take 5 seconds to be completely ready.",
|
||||
"options": {
|
||||
"cwd": "/workspaces/NetAlertX/.devcontainer/scripts"
|
||||
},
|
||||
@@ -73,7 +93,8 @@
|
||||
"reveal": "always",
|
||||
"panel": "shared",
|
||||
"showReuseMessage": false,
|
||||
"clear": false
|
||||
"clear": false,
|
||||
"group": "Devcontainer"
|
||||
},
|
||||
"problemMatcher": [],
|
||||
"icon": {
|
||||
@@ -85,6 +106,7 @@
|
||||
"label": "[Dev Container] Start CronD (Scheduler)",
|
||||
"type": "shell",
|
||||
"command": "./isDevContainer.sh || exit 1; /services/start-crond.sh",
|
||||
"detail": "Stops and restarts the crond service.",
|
||||
"options": {
|
||||
"cwd": "/workspaces/NetAlertX/.devcontainer/scripts"
|
||||
},
|
||||
@@ -93,7 +115,8 @@
|
||||
"reveal": "always",
|
||||
"panel": "shared",
|
||||
"showReuseMessage": false,
|
||||
"clear": false
|
||||
"clear": false,
|
||||
"group": "Devcontainer"
|
||||
},
|
||||
"problemMatcher": [],
|
||||
"icon": {
|
||||
@@ -105,6 +128,7 @@
|
||||
"label": "[Dev Container] Start Frontend (nginx and PHP-FPM)",
|
||||
"type": "shell",
|
||||
"command": "./isDevContainer.sh || exit 1; /services/start-php-fpm.sh & /services/start-nginx.sh &",
|
||||
"detail": "Stops and restarts the NetAlertX frontend services (nginx and PHP-FPM) in the dev container. This launches almost instantly.",
|
||||
"options": {
|
||||
"cwd": "/workspaces/NetAlertX/.devcontainer/scripts"
|
||||
|
||||
@@ -114,7 +138,8 @@
|
||||
"reveal": "always",
|
||||
"panel": "shared",
|
||||
"showReuseMessage": false,
|
||||
"clear": false
|
||||
"clear": false,
|
||||
"group": "Devcontainer"
|
||||
},
|
||||
"problemMatcher": [],
|
||||
"icon": {
|
||||
@@ -126,6 +151,7 @@
|
||||
"label": "[Dev Container] Stop Frontend & Backend Services",
|
||||
"type": "shell",
|
||||
"command": "./isDevContainer.sh || exit 1; pkill -f 'php-fpm83|nginx|crond|python3' || true",
|
||||
"detail": "Stops all NetAlertX services running in the dev container.",
|
||||
"options": {
|
||||
"cwd": "/workspaces/NetAlertX/.devcontainer/scripts"
|
||||
},
|
||||
@@ -133,7 +159,8 @@
|
||||
"echo": true,
|
||||
"reveal": "always",
|
||||
"panel": "shared",
|
||||
"showReuseMessage": false
|
||||
"showReuseMessage": false,
|
||||
"group": "Devcontainer"
|
||||
},
|
||||
"problemMatcher": [],
|
||||
"icon": {
|
||||
@@ -142,29 +169,55 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"label": "[Dev Container] List NetAlertX Ports",
|
||||
"label": "[Any] Build Unit Test Docker image",
|
||||
"type": "shell",
|
||||
"command": "list-ports.sh",
|
||||
"options": {
|
||||
"cwd": "/workspaces/NetAlertX/.devcontainer/scripts"
|
||||
},
|
||||
"command": "docker buildx build -t netalertx-test . && echo '🧪 Unit Test Docker image built: netalertx-test'",
|
||||
"detail": "This must be run after changes to the container. Unit testing will not register changes until after this image is rebuilt. It takes about 30 seconds to build unless changes to the venv stage are made. venv takes 90s alone.",
|
||||
"presentation": {
|
||||
"echo": true,
|
||||
"reveal": "always",
|
||||
"panel": "shared",
|
||||
"showReuseMessage": false
|
||||
"showReuseMessage": false,
|
||||
"group": "Any"
|
||||
|
||||
},
|
||||
"problemMatcher": [],
|
||||
"group": {
|
||||
"kind": "build",
|
||||
"isDefault": false
|
||||
},
|
||||
"icon": {
|
||||
"id": "beaker",
|
||||
"color": "terminal.ansiBlue"
|
||||
}
|
||||
},
|
||||
{
|
||||
"label": "[Dev Container] Wipe and Regenerate Database",
|
||||
"type": "shell",
|
||||
"command": "killall 'python3' || true && sleep 1 && rm -rf /data/db/* /data/config/* && bash /entrypoint.d/15-first-run-config.sh && bash /entrypoint.d/20-first-run-db.sh && echo '✅ Database and config wiped and regenerated'",
|
||||
"detail": "Wipes devcontainer db and config. Provides a fresh start in devcontainer, run this task, then run the Rerun Startup Task",
|
||||
"options": {},
|
||||
"presentation": {
|
||||
"echo": true,
|
||||
"reveal": "always",
|
||||
"panel": "shared",
|
||||
"showReuseMessage": false,
|
||||
"group": "Devcontainer"
|
||||
},
|
||||
"problemMatcher": [],
|
||||
"icon": {
|
||||
"id": "output",
|
||||
"color": "terminal.ansiBlue"
|
||||
"id": "database",
|
||||
"color": "terminal.ansiRed"
|
||||
}
|
||||
}
|
||||
,
|
||||
},
|
||||
{
|
||||
"label": "[Any] Build Unit Test Docker image",
|
||||
"label": "Build & Launch Prodcution Docker Container",
|
||||
"type": "shell",
|
||||
"command": "docker buildx build -t netalertx-test . && echo '🧪 Unit Test Docker image built: netalertx-test'",
|
||||
"command": "docker compose up -d --build --force-recreate",
|
||||
"detail": "Before launching, ensure VSCode Ports are closed and services are stopped. Tasks: Stop Frontend & Backend Services & Remote: Close Unused Forwarded Ports to ensure proper operation of the new container.",
|
||||
"options": {
|
||||
"cwd": "/workspaces/NetAlertX"
|
||||
},
|
||||
"presentation": {
|
||||
"echo": true,
|
||||
"reveal": "always",
|
||||
@@ -177,7 +230,7 @@
|
||||
"isDefault": false
|
||||
},
|
||||
"icon": {
|
||||
"id": "beaker",
|
||||
"id": "package",
|
||||
"color": "terminal.ansiBlue"
|
||||
}
|
||||
}
|
||||
|
||||
34
Dockerfile
34
Dockerfile
@@ -43,14 +43,16 @@ ARG INSTALL_DIR=/app
|
||||
|
||||
# NetAlertX app directories
|
||||
ENV NETALERTX_APP=${INSTALL_DIR}
|
||||
ENV NETALERTX_CONFIG=${NETALERTX_APP}/config
|
||||
ENV NETALERTX_DATA=/data
|
||||
ENV NETALERTX_CONFIG=${NETALERTX_DATA}/config
|
||||
ENV NETALERTX_FRONT=${NETALERTX_APP}/front
|
||||
ENV NETALERTX_PLUGINS=${NETALERTX_FRONT}/plugins
|
||||
ENV NETALERTX_SERVER=${NETALERTX_APP}/server
|
||||
ENV NETALERTX_API=${NETALERTX_APP}/api
|
||||
ENV NETALERTX_DB=${NETALERTX_APP}/db
|
||||
ENV NETALERTX_API=/tmp/api
|
||||
ENV NETALERTX_DB=${NETALERTX_DATA}/db
|
||||
ENV NETALERTX_DB_FILE=${NETALERTX_DB}/app.db
|
||||
ENV NETALERTX_BACK=${NETALERTX_APP}/back
|
||||
ENV NETALERTX_LOG=${NETALERTX_APP}/log
|
||||
ENV NETALERTX_LOG=/tmp/log
|
||||
ENV NETALERTX_PLUGINS_LOG=${NETALERTX_LOG}/plugins
|
||||
ENV NETALERTX_CONFIG_FILE=${NETALERTX_CONFIG}/app.conf
|
||||
|
||||
@@ -67,6 +69,7 @@ ENV LOG_EXECUTION_QUEUE=${NETALERTX_LOG}/execution_queue.log
|
||||
ENV LOG_REPORT_OUTPUT_JSON=${NETALERTX_LOG}/report_output.json
|
||||
ENV LOG_STDOUT=${NETALERTX_LOG}/stdout.log
|
||||
ENV LOG_CROND=${NETALERTX_LOG}/crond.log
|
||||
ENV LOG_NGINX_ERROR=${NETALERTX_LOG}/nginx-error.log
|
||||
|
||||
# System Services configuration files
|
||||
ENV ENTRYPOINT_CHECKS=/entrypoint.d
|
||||
@@ -75,25 +78,26 @@ ENV SYSTEM_SERVICES_SCRIPTS=${SYSTEM_SERVICES}/scripts
|
||||
ENV SYSTEM_SERVICES_CONFIG=${SYSTEM_SERVICES}/config
|
||||
ENV SYSTEM_NGINX_CONFIG=${SYSTEM_SERVICES_CONFIG}/nginx
|
||||
ENV SYSTEM_NGINX_CONFIG_FILE=${SYSTEM_NGINX_CONFIG}/nginx.conf
|
||||
ENV SYSTEM_SERVICES_ACTIVE_CONFIG=${SYSTEM_NGINX_CONFIG}/conf.active
|
||||
ENV SYSTEM_SERVICES_ACTIVE_CONFIG=/tmp/nginx/active-config
|
||||
ENV SYSTEM_SERVICES_PHP_FOLDER=${SYSTEM_SERVICES_CONFIG}/php
|
||||
ENV SYSTEM_SERVICES_PHP_FPM_D=${SYSTEM_SERVICES_PHP_FOLDER}/php-fpm.d
|
||||
ENV SYSTEM_SERVICES_CROND=${SYSTEM_SERVICES_CONFIG}/crond
|
||||
ENV SYSTEM_SERVICES_RUN=${SYSTEM_SERVICES}/run
|
||||
ENV SYSTEM_SERVICES_RUN=/tmp/run
|
||||
ENV SYSTEM_SERVICES_RUN_TMP=${SYSTEM_SERVICES_RUN}/tmp
|
||||
ENV SYSTEM_SERVICES_RUN_LOG=${SYSTEM_SERVICES_RUN}/logs
|
||||
ENV PHP_FPM_CONFIG_FILE=${SYSTEM_SERVICES_PHP_FOLDER}/php-fpm.conf
|
||||
ENV READ_ONLY_FOLDERS="${NETALERTX_BACK} ${NETALERTX_FRONT} ${NETALERTX_SERVER} ${SYSTEM_SERVICES} \
|
||||
${SYSTEM_SERVICES_CONFIG} ${ENTRYPOINT_CHECKS}"
|
||||
ENV READ_WRITE_FOLDERS="${NETALERTX_CONFIG} ${NETALERTX_DB} ${NETALERTX_API} ${NETALERTX_LOG} \
|
||||
${NETALERTX_PLUGINS_LOG} ${SYSTEM_SERVICES_RUN} ${SYSTEM_SERVICES_RUN_TMP} \
|
||||
${SYSTEM_SERVICES_RUN_LOG} ${SYSTEM_NGINX_CONFIG}"
|
||||
ENV READ_WRITE_FOLDERS="${NETALERTX_DATA} ${NETALERTX_CONFIG} ${NETALERTX_DB} ${NETALERTX_API} \
|
||||
${NETALERTX_LOG} ${NETALERTX_PLUGINS_LOG} ${SYSTEM_SERVICES_RUN} \
|
||||
${SYSTEM_SERVICES_RUN_TMP} ${SYSTEM_SERVICES_RUN_LOG} \
|
||||
${SYSTEM_SERVICES_ACTIVE_CONFIG}"
|
||||
|
||||
#Python environment
|
||||
ENV PYTHONUNBUFFERED=1
|
||||
ENV VIRTUAL_ENV=/opt/venv
|
||||
ENV VIRTUAL_ENV_BIN=/opt/venv/bin
|
||||
ENV PYTHONPATH=${NETALERTX_APP}:${NETALERTX_SERVER}:${VIRTUAL_ENV}/lib/python3.12/site-packages
|
||||
ENV PYTHONPATH=${NETALERTX_APP}:${NETALERTX_SERVER}:${NETALERTX_PLUGINS}:${VIRTUAL_ENV}/lib/python3.12/site-packages
|
||||
ENV PATH="${SYSTEM_SERVICES}:${VIRTUAL_ENV_BIN}:$PATH"
|
||||
|
||||
# App Environment
|
||||
@@ -101,7 +105,7 @@ ENV LISTEN_ADDR=0.0.0.0
|
||||
ENV PORT=20211
|
||||
ENV NETALERTX_DEBUG=0
|
||||
ENV VENDORSPATH=/app/back/ieee-oui.txt
|
||||
ENV VENDORSPATH_NEWEST=/services/run/tmp/ieee-oui.txt
|
||||
ENV VENDORSPATH_NEWEST=${SYSTEM_SERVICES_RUN_TMP}/ieee-oui.txt
|
||||
ENV ENVIRONMENT=alpine
|
||||
ENV READ_ONLY_USER=readonly READ_ONLY_GROUP=readonly
|
||||
ENV NETALERTX_USER=netalertx NETALERTX_GROUP=netalertx
|
||||
@@ -125,11 +129,15 @@ COPY --chown=${NETALERTX_USER}:${NETALERTX_GROUP} install/production-filesystem/
|
||||
COPY --chown=${NETALERTX_USER}:${NETALERTX_GROUP} --chmod=755 back ${NETALERTX_BACK}
|
||||
COPY --chown=${NETALERTX_USER}:${NETALERTX_GROUP} --chmod=755 front ${NETALERTX_FRONT}
|
||||
COPY --chown=${NETALERTX_USER}:${NETALERTX_GROUP} --chmod=755 server ${NETALERTX_SERVER}
|
||||
RUN install -d -o ${NETALERTX_USER} -g ${NETALERTX_GROUP} -m 755 ${NETALERTX_API} \
|
||||
${NETALERTX_LOG} ${SYSTEM_SERVICES_RUN_TMP} ${SYSTEM_SERVICES_RUN_LOG} && \
|
||||
|
||||
# Create required folders with correct ownership and permissions
|
||||
RUN install -d -o ${NETALERTX_USER} -g ${NETALERTX_GROUP} -m 700 ${READ_WRITE_FOLDERS} && \
|
||||
sh -c "find ${NETALERTX_APP} -type f \( -name '*.sh' -o -name 'speedtest-cli' \) \
|
||||
-exec chmod 750 {} \;"
|
||||
|
||||
# Copy version information into the image
|
||||
COPY --chown=${NETALERTX_USER}:${NETALERTX_GROUP} .VERSION ${NETALERTX_APP}/.VERSION
|
||||
|
||||
# Copy the virtualenv from the builder stage
|
||||
COPY --from=builder --chown=20212:20212 ${VIRTUAL_ENV} ${VIRTUAL_ENV}
|
||||
|
||||
|
||||
@@ -49,14 +49,15 @@ FROM debian:bookworm-slim
|
||||
# NetAlertX app directories
|
||||
ENV INSTALL_DIR=/app
|
||||
ENV NETALERTX_APP=${INSTALL_DIR}
|
||||
ENV NETALERTX_CONFIG=${NETALERTX_APP}/config
|
||||
ENV NETALERTX_DATA=/data
|
||||
ENV NETALERTX_CONFIG=${NETALERTX_DATA}/config
|
||||
ENV NETALERTX_FRONT=${NETALERTX_APP}/front
|
||||
ENV NETALERTX_SERVER=${NETALERTX_APP}/server
|
||||
ENV NETALERTX_API=${NETALERTX_APP}/api
|
||||
ENV NETALERTX_DB=${NETALERTX_APP}/db
|
||||
ENV NETALERTX_API=/tmp/api
|
||||
ENV NETALERTX_DB=${NETALERTX_DATA}/db
|
||||
ENV NETALERTX_DB_FILE=${NETALERTX_DB}/app.db
|
||||
ENV NETALERTX_BACK=${NETALERTX_APP}/back
|
||||
ENV NETALERTX_LOG=${NETALERTX_APP}/log
|
||||
ENV NETALERTX_LOG=/tmp/log
|
||||
ENV NETALERTX_PLUGINS_LOG=${NETALERTX_LOG}/plugins
|
||||
|
||||
# NetAlertX log files
|
||||
@@ -72,17 +73,19 @@ ENV LOG_EXECUTION_QUEUE=${NETALERTX_LOG}/execution_queue.log
|
||||
ENV LOG_REPORT_OUTPUT_JSON=${NETALERTX_LOG}/report_output.json
|
||||
ENV LOG_STDOUT=${NETALERTX_LOG}/stdout.log
|
||||
ENV LOG_CROND=${NETALERTX_LOG}/crond.log
|
||||
ENV LOG_NGINX_ERROR=${NETALERTX_LOG}/nginx-error.log
|
||||
|
||||
# System Services configuration files
|
||||
ENV SYSTEM_SERVICES=/services
|
||||
ENV SYSTEM_SERVICES_CONFIG=${SYSTEM_SERVICES}/config
|
||||
ENV SYSTEM_NGINIX_CONFIG=${SYSTEM_SERVICES_CONFIG}/nginx
|
||||
ENV SYSTEM_NGINX_CONFIG_FILE=${SYSTEM_NGINIX_CONFIG}/nginx.conf
|
||||
ENV SYSTEM_SERVICES_ACTIVE_CONFIG=/tmp/nginx/active-config
|
||||
ENV NETALERTX_CONFIG_FILE=${NETALERTX_CONFIG}/app.conf
|
||||
ENV SYSTEM_SERVICES_PHP_FOLDER=${SYSTEM_SERVICES_CONFIG}/php
|
||||
ENV SYSTEM_SERVICES_PHP_FPM_D=${SYSTEM_SERVICES_PHP_FOLDER}/php-fpm.d
|
||||
ENV SYSTEM_SERVICES_CROND=${SYSTEM_SERVICES_CONFIG}/crond
|
||||
ENV SYSTEM_SERVICES_RUN=${SYSTEM_SERVICES}/run
|
||||
ENV SYSTEM_SERVICES_RUN=/tmp/run
|
||||
ENV SYSTEM_SERVICES_RUN_TMP=${SYSTEM_SERVICES_RUN}/tmp
|
||||
ENV SYSTEM_SERVICES_RUN_LOG=${SYSTEM_SERVICES_RUN}/logs
|
||||
ENV PHP_FPM_CONFIG_FILE=${SYSTEM_SERVICES_PHP_FOLDER}/php-fpm.conf
|
||||
@@ -94,7 +97,7 @@ ENV VIRTUAL_ENV=/opt/venv
|
||||
ENV VIRTUAL_ENV_BIN=/opt/venv/bin
|
||||
ENV PATH="${VIRTUAL_ENV}/bin:${PATH}:/services"
|
||||
ENV VENDORSPATH=/app/back/ieee-oui.txt
|
||||
ENV VENDORSPATH_NEWEST=/services/run/tmp/ieee-oui.txt
|
||||
ENV VENDORSPATH_NEWEST=${SYSTEM_SERVICES_RUN_TMP}/ieee-oui.txt
|
||||
|
||||
|
||||
# App Environment
|
||||
|
||||
19
README.md
19
README.md
@@ -6,7 +6,7 @@
|
||||
|
||||
# NetAlertX - Network, presence scanner and alert framework
|
||||
|
||||
Get visibility of what's going on on your WIFI/LAN network and enable presence detection of important devices. Schedule scans for devices, port changes and get alerts if unknown devices or changes are found. Write your own [Plugin](https://github.com/jokob-sk/NetAlertX/tree/main/docs/PLUGINS.md#readme) with auto-generated UI and in-build notification system. Build out and easily maintain your network source of truth (NSoT).
|
||||
Get visibility of what's going on on your WIFI/LAN network and enable presence detection of important devices. Schedule scans for devices, port changes and get alerts if unknown devices or changes are found. Write your own [Plugin](https://github.com/jokob-sk/NetAlertX/tree/main/docs/PLUGINS.md#readme) with auto-generated UI and in-build notification system. Build out and easily maintain your network source of truth (NSoT) and device inventory.
|
||||
|
||||
## 📋 Table of Contents
|
||||
|
||||
@@ -33,13 +33,18 @@ Get visibility of what's going on on your WIFI/LAN network and enable presence d
|
||||
|
||||
## 🚀 Quick Start
|
||||
|
||||
> [!WARNING]
|
||||
> ⚠️ **Important:** The documentation has been recently updated and some instructions may have changed.
|
||||
> If you are using the currently live production image, please follow the instructions on [Docker Hub](https://hub.docker.com/r/jokobsk/netalertx) for building and running the container.
|
||||
> These docs reflect the latest development version and may differ from the production image.
|
||||
|
||||
Start NetAlertX in seconds with Docker:
|
||||
|
||||
```bash
|
||||
docker run -d --rm --network=host \
|
||||
-v local_path/config:/app/config \
|
||||
-v local_path/db:/app/db \
|
||||
--mount type=tmpfs,target=/app/api \
|
||||
-v local_path/config:/data/config \
|
||||
-v local_path/db:/data/db \
|
||||
--mount type=tmpfs,target=/tmp/api \
|
||||
-e PUID=200 -e PGID=300 \
|
||||
-e TZ=Europe/Berlin \
|
||||
-e PORT=20211 \
|
||||
@@ -61,7 +66,7 @@ For Home Assistant users: [Click here to add NetAlertX](https://my.home-assistan
|
||||
For other install methods, check the [installation docs](#-documentation)
|
||||
|
||||
|
||||
| [📑 Docker guide](https://github.com/jokob-sk/NetAlertX/blob/main/dockerfiles/README.md) | [🚀 Releases](https://github.com/jokob-sk/NetAlertX/releases) | [📚 Docs](https://jokob-sk.github.io/NetAlertX/) | [🔌 Plugins](https://github.com/jokob-sk/NetAlertX/blob/main/docs/PLUGINS.md) | [🤖 Ask AI](https://gurubase.io/g/netalertx)
|
||||
| [📑 Docker guide](https://github.com/jokob-sk/NetAlertX/blob/main/docs/DOCKER_INSTALLATION.md) | [🚀 Releases](https://github.com/jokob-sk/NetAlertX/releases) | [📚 Docs](https://jokob-sk.github.io/NetAlertX/) | [🔌 Plugins](https://github.com/jokob-sk/NetAlertX/blob/main/docs/PLUGINS.md) | [🤖 Ask AI](https://gurubase.io/g/netalertx)
|
||||
|----------------------| ----------------------| ----------------------| ----------------------| ----------------------|
|
||||
|
||||
![showcase][showcase]
|
||||
@@ -103,7 +108,7 @@ The [workflows module](https://github.com/jokob-sk/NetAlertX/blob/main/docs/WORK
|
||||
|
||||
Supported browsers: Chrome, Firefox
|
||||
|
||||
- [[Installation] Docker](https://github.com/jokob-sk/NetAlertX/blob/main/dockerfiles/README.md)
|
||||
- [[Installation] Docker](https://github.com/jokob-sk/NetAlertX/blob/main/docs/DOCKER_INSTALLATION.md)
|
||||
- [[Installation] Home Assistant](https://github.com/alexbelgium/hassio-addons/tree/master/netalertx)
|
||||
- [[Installation] Bare metal](https://github.com/jokob-sk/NetAlertX/blob/main/docs/HW_INSTALL.md)
|
||||
- [[Installation] Unraid App](https://unraid.net/community/apps)
|
||||
@@ -140,7 +145,7 @@ A: No. All scans and data remain local, unless you set up cloud-based notificati
|
||||
A: Yes! You can install it bare-metal. See the [bare metal installation guide](https://github.com/jokob-sk/NetAlertX/blob/main/docs/HW_INSTALL.md).
|
||||
|
||||
**Q: Where is the data stored?**
|
||||
A: In the `/config` and `/db` folders, mapped in Docker. Back up these folders regularly.
|
||||
A: In the `/data/config` and `/data/db` folders. Back up these folders regularly.
|
||||
|
||||
|
||||
## 🐞 Known Issues
|
||||
|
||||
2
db/.gitignore
vendored
2
db/.gitignore
vendored
@@ -1,2 +0,0 @@
|
||||
*
|
||||
!.gitignore
|
||||
@@ -17,53 +17,38 @@ services:
|
||||
|
||||
volumes:
|
||||
|
||||
- type: volume # Persistent Docker-managed Named Volume for storage of config files
|
||||
source: netalertx_config # the default name of the volume is netalertx_config
|
||||
target: /app/config # inside the container mounted to /app/config
|
||||
- type: volume # Persistent Docker-managed Named Volume for storage
|
||||
source: netalertx_data # the default name of the volume is netalertx_data
|
||||
target: /data # consolidated configuration and database storage
|
||||
read_only: false # writable volume
|
||||
|
||||
# Example custom local folder called /home/user/netalertx_config
|
||||
# Example custom local folder called /home/user/netalertx_data
|
||||
# - type: bind
|
||||
# source: /home/user/netalertx_config
|
||||
# target: /app/config
|
||||
# source: /home/user/netalertx_data
|
||||
# target: /data
|
||||
# read_only: false
|
||||
# ... or use the alternative format
|
||||
# - /home/user/netalertx_config:/app/config:rw
|
||||
|
||||
- type: volume
|
||||
source: netalertx_db
|
||||
target: /app/db
|
||||
read_only: false
|
||||
# - /home/user/netalertx_data:/data:rw
|
||||
|
||||
- type: bind # Bind mount for timezone consistency
|
||||
source: /etc/localtime
|
||||
target: /etc/localtime
|
||||
read_only: true
|
||||
|
||||
# Use a custom Enterprise-configured nginx config for ldap or other settings
|
||||
# - /custom-enterprise.conf:/services/config/nginx/conf.active/netalertx.conf:ro
|
||||
# Use a custom Enterprise-configured nginx config for ldap or other settings
|
||||
# - /custom-enterprise.conf:/tmp/nginx/active-config/netalertx.conf:ro
|
||||
|
||||
# Test your plugin on the production container
|
||||
# - /path/on/host:/app/front/plugins/custom
|
||||
|
||||
# Retain logs - comment out tmpfs /app/log if you want to retain logs between container restarts
|
||||
# - /path/on/host/log:/app/log
|
||||
# Retain logs - comment out tmpfs /tmp/log if you want to retain logs between container restarts
|
||||
# - /path/on/host/log:/tmp/log
|
||||
|
||||
# Tempfs mounts for writable directories in a read-only container and improve system performance
|
||||
# All mounts have noexec,nosuid,nodev for security purposes no devices, no suid/sgid and no execution of binaries
|
||||
# async where possible for performance, sync where required for correctness
|
||||
# tmpfs mounts for writable directories in a read-only container and improve system performance
|
||||
# All writes now live under /tmp/* subdirectories which are created dynamically by entrypoint.d scripts
|
||||
# uid=20211 and gid=20211 is the netalertx user inside the container
|
||||
# mode=1700 gives rwx------ permissions to the netalertx user only
|
||||
tmpfs:
|
||||
# Speed up logging. This can be commented out to retain logs between container restarts
|
||||
- "/app/log:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
|
||||
# Speed up API access as frontend/backend API is very chatty
|
||||
- "/app/api:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,sync,noatime,nodiratime"
|
||||
# Required for customization of the nginx listen addr/port without rebuilding the container
|
||||
- "/services/config/nginx/conf.active:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
|
||||
# /services/config/nginx/conf.d is required for nginx and php to start
|
||||
- "/services/run:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
|
||||
# /tmp is required by php for session save this should be reworked to /services/run/tmp
|
||||
- "/tmp:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
|
||||
environment:
|
||||
LISTEN_ADDR: ${LISTEN_ADDR:-0.0.0.0} # Listen for connections on all interfaces
|
||||
@@ -86,6 +71,5 @@ services:
|
||||
# Always restart the container unless explicitly stopped
|
||||
restart: unless-stopped
|
||||
|
||||
volumes: # Persistent volumes for configuration and database storage
|
||||
netalertx_config: # Configuration files
|
||||
netalertx_db: # Database files
|
||||
volumes: # Persistent volume for configuration and database storage
|
||||
netalertx_data:
|
||||
|
||||
@@ -64,8 +64,9 @@ http://<server>:<GRAPHQL_PORT>/
|
||||
* [Metrics](API_METRICS.md) – Prometheus metrics and per-device status
|
||||
* [Network Tools](API_NETTOOLS.md) – Utilities like Wake-on-LAN, traceroute, nslookup, nmap, and internet info
|
||||
* [Online History](API_ONLINEHISTORY.md) – Online/offline device records
|
||||
* [GraphQL](API_GRAPHQL.md) – Advanced queries and filtering
|
||||
* [GraphQL](API_GRAPHQL.md) – Advanced queries and filtering for Devices, Settings and Language Strings
|
||||
* [Sync](API_SYNC.md) – Synchronization between multiple NetAlertX instances
|
||||
* [Logs](API_LOGS.md) – Purging of logs and adding to the event execution queue for user triggered events
|
||||
* [DB query](API_DBQUERY.md) (⚠ Internal) - Low level database access - use other endpoints if possible
|
||||
|
||||
See [Testing](API_TESTS.md) for example requests and usage.
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
# GraphQL API Endpoint
|
||||
|
||||
GraphQL queries are **read-optimized for speed**. Data may be slightly out of date until the file system cache refreshes. The GraphQL endpoints allows you to access the following objects:
|
||||
GraphQL queries are **read-optimized for speed**. Data may be slightly out of date until the file system cache refreshes. The GraphQL endpoints allow you to access the following objects:
|
||||
|
||||
- Devices
|
||||
- Settings
|
||||
* Devices
|
||||
* Settings
|
||||
* Language Strings (LangStrings)
|
||||
|
||||
## Endpoints
|
||||
|
||||
@@ -190,11 +191,74 @@ curl 'http://host:GRAPHQL_PORT/graphql' \
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
---
|
||||
|
||||
## LangStrings Query
|
||||
|
||||
The **LangStrings query** provides access to localized strings. Supports filtering by `langCode` and `langStringKey`. If the requested string is missing or empty, you can optionally fallback to `en_us`.
|
||||
|
||||
### Sample Query
|
||||
|
||||
```graphql
|
||||
query GetLangStrings {
|
||||
langStrings(langCode: "de_de", langStringKey: "settings_other_scanners") {
|
||||
langStrings {
|
||||
langCode
|
||||
langStringKey
|
||||
langStringText
|
||||
}
|
||||
count
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Query Parameters
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| ---------------- | ------- | ---------------------------------------------------------------------------------------- |
|
||||
| `langCode` | String | Optional language code (e.g., `en_us`, `de_de`). If omitted, all languages are returned. |
|
||||
| `langStringKey` | String | Optional string key to retrieve a specific entry. |
|
||||
| `fallback_to_en` | Boolean | Optional (default `true`). If `true`, empty or missing strings fallback to `en_us`. |
|
||||
|
||||
### `curl` Example
|
||||
|
||||
```sh
|
||||
curl 'http://host:GRAPHQL_PORT/graphql' \
|
||||
-X POST \
|
||||
-H 'Authorization: Bearer API_TOKEN' \
|
||||
-H 'Content-Type: application/json' \
|
||||
--data '{
|
||||
"query": "query GetLangStrings { langStrings(langCode: \"de_de\", langStringKey: \"settings_other_scanners\") { langStrings { langCode langStringKey langStringText } count } }"
|
||||
}'
|
||||
```
|
||||
|
||||
### Sample Response
|
||||
|
||||
```json
|
||||
{
|
||||
"data": {
|
||||
"langStrings": {
|
||||
"count": 1,
|
||||
"langStrings": [
|
||||
{
|
||||
"langCode": "de_de",
|
||||
"langStringKey": "settings_other_scanners",
|
||||
"langStringText": "Other, non-device scanner plugins that are currently enabled." // falls back to en_us if empty
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Notes
|
||||
|
||||
* Device and settings queries can be combined in one request since GraphQL supports batching.
|
||||
* Device, settings, and LangStrings queries can be combined in **one request** since GraphQL supports batching.
|
||||
* The `fallback_to_en` feature ensures UI always has a value even if a translation is missing.
|
||||
* Data is **cached in memory** per JSON file; changes to language or plugin files will only refresh after the cache detects a file modification.
|
||||
* The `setOverriddenByEnv` flag helps identify setting values that are locked at container runtime.
|
||||
* The schema is **read-only** — updates must be performed through other APIs or configuration management. See the other [API](API.md) endpoints for details.
|
||||
|
||||
|
||||
179
docs/API_LOGS.md
Normal file
179
docs/API_LOGS.md
Normal file
@@ -0,0 +1,179 @@
|
||||
# Logs API Endpoints
|
||||
|
||||
Manage or purge application log files stored under `/app/log` and manage the execution queue. These endpoints are primarily used for maintenance tasks such as clearing accumulated logs or adding system actions without restarting the container.
|
||||
|
||||
Only specific, pre-approved log files can be purged for security and stability reasons.
|
||||
|
||||
---
|
||||
|
||||
## Delete (Purge) a Log File
|
||||
|
||||
* **DELETE** `/logs?file=<log_file>` → Purge the contents of an allowed log file.
|
||||
|
||||
**Query Parameter:**
|
||||
|
||||
* `file` → The name of the log file to purge (e.g., `app.log`, `stdout.log`)
|
||||
|
||||
**Allowed Files:**
|
||||
|
||||
```
|
||||
app.log
|
||||
app_front.log
|
||||
IP_changes.log
|
||||
stdout.log
|
||||
stderr.log
|
||||
app.php_errors.log
|
||||
execution_queue.log
|
||||
db_is_locked.log
|
||||
```
|
||||
|
||||
**Authorization:**
|
||||
Requires a valid API token in the `Authorization` header.
|
||||
|
||||
---
|
||||
|
||||
### `curl` Example (Success)
|
||||
|
||||
```sh
|
||||
curl -X DELETE 'http://<server_ip>:<GRAPHQL_PORT>/logs?file=app.log' \
|
||||
-H 'Authorization: Bearer <API_TOKEN>' \
|
||||
-H 'Accept: application/json'
|
||||
```
|
||||
|
||||
**Response:**
|
||||
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"message": "[clean_log] File app.log purged successfully"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### `curl` Example (Not Allowed)
|
||||
|
||||
```sh
|
||||
curl -X DELETE 'http://<server_ip>:<GRAPHQL_PORT>/logs?file=not_allowed.log' \
|
||||
-H 'Authorization: Bearer <API_TOKEN>' \
|
||||
-H 'Accept: application/json'
|
||||
```
|
||||
|
||||
**Response:**
|
||||
|
||||
```json
|
||||
{
|
||||
"success": false,
|
||||
"message": "[clean_log] File not_allowed.log is not allowed to be purged"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### `curl` Example (Unauthorized)
|
||||
|
||||
```sh
|
||||
curl -X DELETE 'http://<server_ip>:<GRAPHQL_PORT>/logs?file=app.log' \
|
||||
-H 'Accept: application/json'
|
||||
```
|
||||
|
||||
**Response:**
|
||||
|
||||
```json
|
||||
{
|
||||
"error": "Forbidden"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Add an Action to the Execution Queue
|
||||
|
||||
* **POST** `/logs/add-to-execution-queue` → Add a system action to the execution queue.
|
||||
|
||||
**Request Body (JSON):**
|
||||
|
||||
```json
|
||||
{
|
||||
"action": "update_api|devices"
|
||||
}
|
||||
```
|
||||
|
||||
**Authorization:**
|
||||
Requires a valid API token in the `Authorization` header.
|
||||
|
||||
---
|
||||
|
||||
### `curl` Example (Success)
|
||||
|
||||
The below will update the API cache for Devices
|
||||
|
||||
```sh
|
||||
curl -X POST 'http://<server_ip>:<GRAPHQL_PORT>/logs/add-to-execution-queue' \
|
||||
-H 'Authorization: Bearer <API_TOKEN>' \
|
||||
-H 'Content-Type: application/json' \
|
||||
--data '{"action": "update_api|devices"}'
|
||||
```
|
||||
|
||||
**Response:**
|
||||
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"message": "[UserEventsQueueInstance] Action \"update_api|devices\" added to the execution queue."
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### `curl` Example (Missing Parameter)
|
||||
|
||||
```sh
|
||||
curl -X POST 'http://<server_ip>:<GRAPHQL_PORT>/logs/add-to-execution-queue' \
|
||||
-H 'Authorization: Bearer <API_TOKEN>' \
|
||||
-H 'Content-Type: application/json' \
|
||||
--data '{}'
|
||||
```
|
||||
|
||||
**Response:**
|
||||
|
||||
```json
|
||||
{
|
||||
"success": false,
|
||||
"message": "Missing parameters",
|
||||
"error": "Missing required 'action' field in JSON body"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### `curl` Example (Unauthorized)
|
||||
|
||||
```sh
|
||||
curl -X POST 'http://<server_ip>:<GRAPHQL_PORT>/logs/add-to-execution-queue' \
|
||||
-H 'Content-Type: application/json' \
|
||||
--data '{"action": "update_api|devices"}'
|
||||
```
|
||||
|
||||
**Response:**
|
||||
|
||||
```json
|
||||
{
|
||||
"error": "Forbidden"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Notes
|
||||
|
||||
* Only predefined files in `/app/log` can be purged — arbitrary paths are **not permitted**.
|
||||
* When a log file is purged:
|
||||
|
||||
* Its content is replaced with a short marker text: `"File manually purged"`.
|
||||
* A backend log entry is created via `mylog()`.
|
||||
* A frontend notification is generated via `write_notification()`.
|
||||
* Execution queue actions are appended to `execution_queue.log` and can be processed asynchronously by background tasks or workflows.
|
||||
* Unauthorized or invalid attempts are safely logged and rejected.
|
||||
* For advanced log retrieval, analysis, or structured querying, use the frontend log viewer.
|
||||
* Always ensure that sensitive or production logs are handled carefully — purging cannot be undone.
|
||||
@@ -141,7 +141,7 @@ The endpoints are updated when objects in the API endpoints are changed.
|
||||
|
||||
### Location of the endpoints
|
||||
|
||||
In the container, these files are located under the `/app/api/` folder. You can access them via the `/php/server/query_json.php?file=user_notifications.json` endpoint.
|
||||
In the container, these files are located under the API directory (default: `/tmp/api/`, configurable via `NETALERTX_API` environment variable). You can access them via the `/php/server/query_json.php?file=user_notifications.json` endpoint.
|
||||
|
||||
### Available endpoints
|
||||
|
||||
@@ -332,7 +332,7 @@ Grafana template sample: [Download json](./samples/API/Grafana_Dashboard.json)
|
||||
|
||||
## API Endpoint: /log files
|
||||
|
||||
This API endpoint retrieves files from the `/app/log` folder.
|
||||
This API endpoint retrieves files from the `/tmp/log` folder.
|
||||
|
||||
- Endpoint URL: `php/server/query_logs.php?file=<file name>`
|
||||
- Host: `same as front end (web ui)`
|
||||
@@ -357,7 +357,7 @@ This API endpoint retrieves files from the `/app/log` folder.
|
||||
|
||||
## API Endpoint: /config files
|
||||
|
||||
To retrieve files from the `/app/config` folder.
|
||||
To retrieve files from the `/data/config` folder.
|
||||
|
||||
- Endpoint URL: `php/server/query_config.php?file=<file name>`
|
||||
- Host: `same as front end (web ui)`
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# Backing Things Up
|
||||
|
||||
> [!NOTE]
|
||||
> To back up 99% of your configuration, back up at least the `/app/config` folder.
|
||||
> To back up 99% of your configuration, back up at least the `/data/config` folder.
|
||||
> Database definitions can change between releases, so the safest method is to restore backups using the **same app version** they were taken from, then upgrade incrementally.
|
||||
|
||||
---
|
||||
@@ -25,7 +25,7 @@ Understanding where your data is stored helps you plan your backup strategy.
|
||||
|
||||
### Core Configuration
|
||||
|
||||
Stored in `/app/config/app.conf`.
|
||||
Stored in `/data/config/app.conf`.
|
||||
This includes settings for:
|
||||
|
||||
* Notifications
|
||||
@@ -37,7 +37,7 @@ This includes settings for:
|
||||
|
||||
### Device Data
|
||||
|
||||
Stored in `/app/config/devices_<timestamp>.csv` or `/app/config/devices.csv`, created by the [CSV Backup `CSVBCKP` Plugin](https://github.com/jokob-sk/NetAlertX/tree/main/front/plugins/csv_backup).
|
||||
Stored in `/data/config/devices_<timestamp>.csv` or `/data/config/devices.csv`, created by the [CSV Backup `CSVBCKP` Plugin](https://github.com/jokob-sk/NetAlertX/tree/main/front/plugins/csv_backup).
|
||||
Contains:
|
||||
|
||||
* Device names, icons, and categories
|
||||
@@ -46,7 +46,7 @@ Contains:
|
||||
|
||||
### Historical Data
|
||||
|
||||
Stored in `/app/db/app.db` (see [Database Overview](./DATABASE.md)).
|
||||
Stored in `/data/db/app.db` (see [Database Overview](./DATABASE.md)).
|
||||
Contains:
|
||||
|
||||
* Plugin data and historical entries
|
||||
@@ -77,13 +77,13 @@ You can also download the `app.conf` and `devices.csv` files from the **Maintena
|
||||
|
||||
### 💾 What to Back Up
|
||||
|
||||
* `/app/db/app.db` (uncorrupted)
|
||||
* `/app/config/app.conf`
|
||||
* `/app/config/workflows.json`
|
||||
* `/data/db/app.db` (uncorrupted)
|
||||
* `/data/config/app.conf`
|
||||
* `/data/config/workflows.json`
|
||||
|
||||
### 📥 How to Restore
|
||||
|
||||
Map these files into your container as described in the [Setup documentation](https://github.com/jokob-sk/NetAlertX/blob/main/dockerfiles/README.md#docker-paths).
|
||||
Map these files into your container as described in the [Setup documentation](./DOCKER_INSTALLATION.md).
|
||||
|
||||
---
|
||||
|
||||
@@ -93,14 +93,14 @@ Map these files into your container as described in the [Setup documentation](ht
|
||||
|
||||
### 💾 What to Back Up
|
||||
|
||||
* `/app/config/app.conf`
|
||||
* `/app/config/workflows.json`
|
||||
* `/app/config/devices_<timestamp>.csv` (rename to `devices.csv` during restore)
|
||||
* `/data/config/app.conf`
|
||||
* `/data/config/workflows.json`
|
||||
* `/data/config/devices_<timestamp>.csv` (rename to `devices.csv` during restore)
|
||||
|
||||
### 📥 How to Restore
|
||||
|
||||
1. Copy `app.conf` and `workflows.json` into `/app/config/`
|
||||
2. Rename and place `devices_<timestamp>.csv` → `/app/config/devices.csv`
|
||||
1. Copy `app.conf` and `workflows.json` into `/data/config/`
|
||||
2. Rename and place `devices_<timestamp>.csv` → `/data/config/devices.csv`
|
||||
3. Restore via the **Maintenance** section under *Devices → Bulk Editing*
|
||||
|
||||
This recovers nearly all configuration, workflows, and device metadata.
|
||||
@@ -157,6 +157,6 @@ For users running NetAlertX via Docker, you can back up or restore directly from
|
||||
|
||||
## Summary
|
||||
|
||||
* Back up `/app/config` for configuration and devices; `/app/db` for history
|
||||
* Back up `/data/config` for configuration and devices; `/data/db` for history
|
||||
* Keep regular backups, especially before upgrades
|
||||
* For Docker setups, use the lightweight `alpine`-based backup method for consistency and portability
|
||||
|
||||
@@ -14,9 +14,9 @@ The app uses the MAC address as an unique identifier for devices. If a new MAC i
|
||||
|
||||
Make sure you [File permissions](./FILE_PERMISSIONS.md) are set correctly.
|
||||
|
||||
* If facing issues (AJAX errors, can't write to DB, empty screen, etc,) make sure permissions are set correctly, and check the logs under `/app/log`.
|
||||
* To solve permission issues you can try setting the owner and group of the `app.db` by executing the following on the host system: `docker exec netalertx chown -R www-data:www-data /app/db/app.db`.
|
||||
* If still facing issues, try to map the app.db file (⚠ not folder) to `:/app/db/app.db` (see [docker-compose Examples](https://github.com/jokob-sk/NetAlertX/blob/main/dockerfiles/README.md#-docker-composeyml-examples) for details)
|
||||
* If facing issues (AJAX errors, can't write to DB, empty screen, etc,) make sure permissions are set correctly, and check the logs under `/tmp/log`.
|
||||
* To solve permission issues you can try setting the owner and group of the `app.db` by executing the following on the host system: `docker exec netalertx chown -R www-data:www-data /data/db/app.db`.
|
||||
* If still facing issues, try to map the app.db file (⚠ not folder) to `:/data/db/app.db` (see [docker-compose Examples](https://github.com/jokob-sk/NetAlertX/blob/main/dockerfiles/README.md#-docker-composeyml-examples) for details)
|
||||
|
||||
### Container restarts / crashes
|
||||
|
||||
@@ -49,7 +49,7 @@ Make sure that the subnet and interface in `SCAN_SUBNETS` are correct. If your d
|
||||
|
||||
### Losing my settings and devices after an update
|
||||
|
||||
If you lose your devices and/or settings after an update that means you don't have the `/app/db` and `/app/config` folders mapped to a permanent storage. That means every time you update these folders are re-created. Make sure you have the [volumes specified correctly](./DOCKER_COMPOSE.md) in your `docker-compose.yml` or run command.
|
||||
If you lose your devices and/or settings after an update that means you don't have the `/data/db` and `/data/config` folders mapped to a permanent storage. That means every time you update these folders are re-created. Make sure you have the [volumes specified correctly](./DOCKER_COMPOSE.md) in your `docker-compose.yml` or run command.
|
||||
|
||||
|
||||
### The application is slow
|
||||
|
||||
@@ -27,7 +27,7 @@ Sometimes, the UI might not be accessible. In that case, you can access the logs
|
||||
3. **Check the PHP application error log:**
|
||||
|
||||
```bash
|
||||
cat /app/log/app.php_errors.log
|
||||
cat /tmp/log/app.php_errors.log
|
||||
```
|
||||
|
||||
These logs will help identify syntax issues, fatal errors, or startup problems when the UI fails to load properly.
|
||||
|
||||
@@ -14,8 +14,8 @@ Start the container via the **terminal** with a command similar to this one:
|
||||
|
||||
```bash
|
||||
docker run --rm --network=host \
|
||||
-v local/path/netalertx/config:/app/config \
|
||||
-v local/path/netalertx/db:/app/db \
|
||||
-v local/path/netalertx/config:/data/config \
|
||||
-v local/path/netalertx/db:/data/db \
|
||||
-e TZ=Europe/Berlin \
|
||||
-e PORT=20211 \
|
||||
ghcr.io/jokob-sk/netalertx:latest
|
||||
|
||||
@@ -31,61 +31,46 @@ services:
|
||||
- NET_BIND_SERVICE # Required to bind to privileged ports (nbtscan)
|
||||
|
||||
volumes:
|
||||
- type: volume # Persistent Docker-managed Named Volume for storage of config files
|
||||
source: netalertx_config # the default name of the volume is netalertx_config
|
||||
target: /app/config # inside the container mounted to /app/config
|
||||
read_only: false # writable volume
|
||||
|
||||
# Example custom local folder called /home/user/netalertx_config
|
||||
# - type: bind
|
||||
# source: /home/user/netalertx_config
|
||||
# target: /app/config
|
||||
# read_only: false
|
||||
# ... or use the alternative format
|
||||
# - /home/user/netalertx_config:/app/config:rw
|
||||
|
||||
- type: volume # NetAlertX Database partiton
|
||||
source: netalertx_db
|
||||
target: /app/db
|
||||
- type: volume # Persistent Docker-managed named volume for config + database
|
||||
source: netalertx_data
|
||||
target: /data # `/data/config` and `/data/db` live inside this mount
|
||||
read_only: false
|
||||
|
||||
- type: volume # Future proof mount. During the migration to a
|
||||
source: netalertx_data # future version, app and db will be migrated to
|
||||
target: /data # the /data partition. This will reduce the
|
||||
read_only: false # overhead and pain in the upcoming migration.
|
||||
# Example custom local folder called /home/user/netalertx_data
|
||||
# - type: bind
|
||||
# source: /home/user/netalertx_data
|
||||
# target: /data
|
||||
# read_only: false
|
||||
# ... or use the alternative format
|
||||
# - /home/user/netalertx_data:/data:rw
|
||||
|
||||
- type: bind # Bind mount for timezone consistency
|
||||
source: /etc/localtime
|
||||
source: /etc/localtime # Alternatively add environment TZ: America/New York
|
||||
target: /etc/localtime
|
||||
read_only: true
|
||||
|
||||
# Mount your DHCP server file into NetAlertX for a plugin to access
|
||||
# - path/on/host/to/dhcp.file:/resources/dhcp.file
|
||||
|
||||
# Retain logs - comment out tmpfs /app/log if you want to retain logs between container restarts
|
||||
# - /path/on/host/log:/app/log
|
||||
|
||||
# Tempfs mounts for writable directories in a read-only container and improve system performance
|
||||
# All mounts have noexec,nosuid,nodev for security purposes no devices, no suid/sgid and no execution of binaries
|
||||
# async where possible for performance, sync where required for correctness
|
||||
# tmpfs mount consolidates writable state for a read-only container and improves performance
|
||||
# uid=20211 and gid=20211 is the netalertx user inside the container
|
||||
# mode=1700 gives rwx------ permissions to the netalertx user only
|
||||
# mode=1700 grants rwx------ permissions to the netalertx user only
|
||||
tmpfs:
|
||||
# Speed up logging. This can be commented out to retain logs between container restarts
|
||||
- "/app/log:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
|
||||
# Speed up API access as frontend/backend API is very chatty
|
||||
- "/app/api:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,sync,noatime,nodiratime"
|
||||
# Required for customization of the nginx listen addr/port without rebuilding the container
|
||||
- "/services/config/nginx/conf.active:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
|
||||
# /services/config/nginx/conf.d is required for nginx and php to start
|
||||
- "/services/run:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
|
||||
# /tmp is required by php for session save this should be reworked to /services/run/tmp
|
||||
# Comment out to retain logs between container restarts - this has a server performance impact.
|
||||
- "/tmp:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
|
||||
|
||||
# Retain logs - comment out tmpfs /tmp if you want to retain logs between container restarts
|
||||
# Please note if you remove the /tmp mount, you must create and maintain sub-folder mounts.
|
||||
# - /path/on/host/log:/tmp/log
|
||||
# - "/tmp/api:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
|
||||
# - "/tmp/nginx:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
|
||||
# - "/tmp/run:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
|
||||
|
||||
environment:
|
||||
LISTEN_ADDR: ${LISTEN_ADDR:-0.0.0.0} # Listen for connections on all interfaces
|
||||
PORT: ${PORT:-20211} # Application port
|
||||
GRAPHQL_PORT: ${GRAPHQL_PORT:-20212} # GraphQL API port (passed into APP_CONF_OVERRIDE at runtime)
|
||||
NETALERTX_DEBUG: ${NETALERTX_DEBUG:-0} # 0=kill all services and restart if any dies. 1 keeps running dead services.
|
||||
# NETALERTX_DEBUG: ${NETALERTX_DEBUG:-0} # 0=kill all services and restart if any dies. 1 keeps running dead services.
|
||||
|
||||
# Resource limits to prevent resource exhaustion
|
||||
mem_limit: 2048m # Maximum memory usage
|
||||
@@ -101,10 +86,8 @@ services:
|
||||
# Always restart the container unless explicitly stopped
|
||||
restart: unless-stopped
|
||||
|
||||
volumes: # Persistent volumes for configuration and database storage
|
||||
netalertx_config: # Configuration files
|
||||
netalertx_db: # Database files
|
||||
netalertx_data: # For future config/db upgrade
|
||||
volumes: # Persistent volume for configuration and database storage
|
||||
netalertx_data:
|
||||
```
|
||||
|
||||
Run or re-run it:
|
||||
@@ -163,8 +146,8 @@ However, if you prefer to have direct, file-level access to your configuration f
|
||||
```yaml
|
||||
...
|
||||
volumes:
|
||||
- netalertx_config:/app/config:rw #short-form volume (no /path is a short volume)
|
||||
- netalertx_db:/app/db:rw
|
||||
- netalertx_config:/data/config:rw #short-form volume (no /path is a short volume)
|
||||
- netalertx_db:/data/db:rw
|
||||
...
|
||||
```
|
||||
|
||||
@@ -174,14 +157,14 @@ Make sure to replace `/home/adam/netalertx-files` with your actual path. The for
|
||||
```yaml
|
||||
...
|
||||
volumes:
|
||||
# - netalertx_config:/app/config:rw
|
||||
# - netalertx_db:/app/db:rw
|
||||
- /home/adam/netalertx-files/config:/app/config:rw
|
||||
- /home/adam/netalertx-files/db:/app/db:rw
|
||||
# - netalertx_config:/data/config:rw
|
||||
# - netalertx_db:/data/db:rw
|
||||
- /home/adam/netalertx-files/config:/data/config:rw
|
||||
- /home/adam/netalertx-files/db:/data/db:rw
|
||||
...
|
||||
```
|
||||
|
||||
Now, any files created by NetAlertX in `/app/config` will appear in your `/home/adam/netalertx-files/config` folder.
|
||||
Now, any files created by NetAlertX in `/data/config` will appear in your `/home/adam/netalertx-files/config` folder.
|
||||
|
||||
This same method works for mounting other things, like custom plugins or enterprise NGINX files, as shown in the commented-out examples in the baseline file.
|
||||
|
||||
|
||||
@@ -25,9 +25,9 @@ Head to [https://netalertx.com/](https://netalertx.com/) for more gifs and scree
|
||||
|
||||
```yaml
|
||||
docker run -d --rm --network=host \
|
||||
-v local_path/config:/app/config \
|
||||
-v local_path/db:/app/db \
|
||||
--mount type=tmpfs,target=/app/api \
|
||||
-v local_path/config:/data/config \
|
||||
-v local_path/db:/data/db \
|
||||
--mount type=tmpfs,target=/tmp/api \
|
||||
-e PUID=200 -e PGID=300 \
|
||||
-e TZ=Europe/Berlin \
|
||||
-e PORT=20211 \
|
||||
@@ -58,10 +58,10 @@ See alternative [docked-compose examples](https://github.com/jokob-sk/NetAlertX/
|
||||
|
||||
| Required | Path | Description |
|
||||
| :------------- | :------------- | :-------------|
|
||||
| ✅ | `:/app/config` | Folder which will contain the `app.conf` & `devices.csv` ([read about devices.csv](https://github.com/jokob-sk/NetAlertX/blob/main/docs/DEVICES_BULK_EDITING.md)) files |
|
||||
| ✅ | `:/app/db` | Folder which will contain the `app.db` database file |
|
||||
| | `:/app/log` | Logs folder useful for debugging if you have issues setting up the container |
|
||||
| | `:/app/api` | A simple [API endpoint](https://github.com/jokob-sk/NetAlertX/blob/main/docs/API.md) containing static (but regularly updated) json and other files. |
|
||||
| ✅ | `:/data/config` | Folder which will contain the `app.conf` & `devices.csv` ([read about devices.csv](https://github.com/jokob-sk/NetAlertX/blob/main/docs/DEVICES_BULK_EDITING.md)) files |
|
||||
| ✅ | `:/data/db` | Folder which will contain the `app.db` database file |
|
||||
| | `:/tmp/log` | Logs folder useful for debugging if you have issues setting up the container |
|
||||
| | `:/tmp/api` | A simple [API endpoint](https://github.com/jokob-sk/NetAlertX/blob/main/docs/API.md) containing static (but regularly updated) json and other files. Path configurable via `NETALERTX_API` environment variable. |
|
||||
| | `:/app/front/plugins/<plugin>/ignore_plugin` | Map a file `ignore_plugin` to ignore a plugin. Plugins can be soft-disabled via settings. More in the [Plugin docs](https://github.com/jokob-sk/NetAlertX/blob/main/docs/PLUGINS.md). |
|
||||
| | `:/etc/resolv.conf` | Use a custom `resolv.conf` file for [better name resolution](https://github.com/jokob-sk/NetAlertX/blob/main/docs/REVERSE_DNS.md). |
|
||||
|
||||
@@ -70,7 +70,7 @@ See alternative [docked-compose examples](https://github.com/jokob-sk/NetAlertX/
|
||||
### Initial setup
|
||||
|
||||
- If unavailable, the app generates a default `app.conf` and `app.db` file on the first run.
|
||||
- The preferred way is to manage the configuration via the Settings section in the UI, if UI is inaccessible you can modify [app.conf](https://github.com/jokob-sk/NetAlertX/tree/main/back) in the `/app/config/` folder directly
|
||||
- The preferred way is to manage the configuration via the Settings section in the UI, if UI is inaccessible you can modify [app.conf](https://github.com/jokob-sk/NetAlertX/tree/main/back) in the `/data/config/` folder directly
|
||||
|
||||
#### Setting up scanners
|
||||
|
||||
|
||||
@@ -51,13 +51,13 @@ You want to edit your `app.conf` and other configuration files directly from you
|
||||
volumes:
|
||||
# - type: volume
|
||||
# source: netalertx_config
|
||||
# target: /app/config
|
||||
# target: /data/config
|
||||
# read_only: false
|
||||
...
|
||||
# Example custom local folder called /data/netalertx_config
|
||||
- type: bind
|
||||
source: /data/netalertx_config
|
||||
target: /app/config
|
||||
target: /data/config
|
||||
read_only: false
|
||||
...
|
||||
```
|
||||
@@ -70,7 +70,7 @@ You want to edit your `app.conf` and other configuration files directly from you
|
||||
|
||||
### About This Method
|
||||
|
||||
This replaces the Docker-managed volume with a "bind mount." This is a direct mapping between a folder on your host computer (`/data/netalertx_config`) and a folder inside the container (`/app/config`), allowing you to edit the files directly.
|
||||
This replaces the Docker-managed volume with a "bind mount." This is a direct mapping between a folder on your host computer (`/data/netalertx_config`) and a folder inside the container (`/data/config`), allowing you to edit the files directly.
|
||||
|
||||
---
|
||||
|
||||
@@ -97,13 +97,13 @@ You are currently using a local folder (bind mount) for your configuration (e.g.
|
||||
volumes:
|
||||
- type: volume
|
||||
source: netalertx_config
|
||||
target: /app/config
|
||||
target: /data/config
|
||||
read_only: false
|
||||
...
|
||||
# Example custom local folder called /data/netalertx_config
|
||||
# - type: bind
|
||||
# source: /data/netalertx_config
|
||||
# target: /app/config
|
||||
# target: /data/config
|
||||
# read_only: false
|
||||
...
|
||||
```
|
||||
@@ -149,7 +149,7 @@ You need to override the default Nginx configuration to add features like LDAP,
|
||||
```yaml
|
||||
...
|
||||
# Use a custom Enterprise-configured nginx config for ldap or other settings
|
||||
- /data/my-netalertx.conf:/services/config/nginx/conf.active/netalertx.conf:ro
|
||||
- /data/my-netalertx.conf:/tmp/nginx/active-config/netalertx.conf:ro
|
||||
...
|
||||
```
|
||||
4. Restart the container:
|
||||
|
||||
@@ -45,18 +45,18 @@ services:
|
||||
restart: unless-stopped
|
||||
|
||||
volumes:
|
||||
- ${APP_FOLDER}/netalertx/config:/app/config
|
||||
- ${APP_FOLDER}/netalertx/db:/app/db
|
||||
- ${APP_FOLDER}/netalertx/config:/data/config
|
||||
- ${APP_FOLDER}/netalertx/db:/data/db
|
||||
# Optional: logs (useful for debugging setup issues, comment out for performance)
|
||||
- ${APP_FOLDER}/netalertx/log:/app/log
|
||||
- ${APP_FOLDER}/netalertx/log:/tmp/log
|
||||
|
||||
# API storage options:
|
||||
# (Option 1) tmpfs (default, best performance)
|
||||
- type: tmpfs
|
||||
target: /app/api
|
||||
target: /tmp/api
|
||||
|
||||
# (Option 2) bind mount (useful for debugging)
|
||||
# - ${APP_FOLDER}/netalertx/api:/app/api
|
||||
# - ${APP_FOLDER}/netalertx/api:/tmp/api
|
||||
|
||||
environment:
|
||||
- TZ=${TZ}
|
||||
|
||||
@@ -44,9 +44,9 @@ services:
|
||||
ports:
|
||||
- 20211:20211
|
||||
volumes:
|
||||
- /mnt/YOUR_SERVER/netalertx/config:/app/config:rw
|
||||
- /mnt/YOUR_SERVER/netalertx/db:/netalertx/app/db:rw
|
||||
- /mnt/YOUR_SERVER/netalertx/logs:/netalertx/app/log:rw
|
||||
- /mnt/YOUR_SERVER/netalertx/config:/data/config:rw
|
||||
- /mnt/YOUR_SERVER/netalertx/db:/netalertx/data/db:rw
|
||||
- /mnt/YOUR_SERVER/netalertx/logs:/netalertx/tmp/log:rw
|
||||
environment:
|
||||
- TZ=Europe/London
|
||||
- PORT=20211
|
||||
|
||||
@@ -11,13 +11,15 @@ NetAlertX requires certain paths to be writable at runtime. These paths should b
|
||||
|
||||
| Path | Purpose | Notes |
|
||||
| ------------------------------------ | ----------------------------------- | ------------------------------------------------------ |
|
||||
| `/app/config` | Application configuration | Persistent volume recommended |
|
||||
| `/app/db` | Database files | Persistent volume recommended |
|
||||
| `/app/log` | Logs | Can be `tmpfs` for speed or host volume to retain logs |
|
||||
| `/app/api` | API cache | Use `tmpfs` for faster access |
|
||||
| `/services/config/nginx/conf.active` | Active nginx configuration override | `tmpfs` recommended or customized file mounted |
|
||||
| `/services/run` | Runtime directories for nginx & PHP | `tmpfs` required |
|
||||
| `/tmp` | PHP session save directory | `tmpfs` required |
|
||||
| `/data/config` | Application configuration | Persistent volume recommended |
|
||||
| `/data/db` | Database files | Persistent volume recommended |
|
||||
| `/tmp/log` | Logs | Lives under `/tmp`; optional host bind to retain logs |
|
||||
| `/tmp/api` | API cache | Subdirectory of `/tmp` |
|
||||
| `/tmp/nginx/active-config` | Active nginx configuration override | Mount `/tmp` (or override specific file) |
|
||||
| `/tmp/run` | Runtime directories for nginx & PHP | Subdirectory of `/tmp` |
|
||||
| `/tmp` | PHP session save directory | Backed by `tmpfs` for runtime writes |
|
||||
|
||||
> Mounting `/tmp` as `tmpfs` automatically covers all of its subdirectories (`log`, `api`, `run`, `nginx/active-config`, etc.).
|
||||
|
||||
> All these paths will have **UID 20211 / GID 20211** inside the container. Files on the host will appear owned by `20211:20211`.
|
||||
|
||||
@@ -33,8 +35,8 @@ Sometimes, permission issues arise if your existing host directories were create
|
||||
|
||||
```bash
|
||||
docker run -it --rm --name netalertx --user "0" \
|
||||
-v local/path/config:/app/config \
|
||||
-v local/path/db:/app/db \
|
||||
-v local/path/config:/data/config \
|
||||
-v local/path/db:/data/db \
|
||||
ghcr.io/jokob-sk/netalertx:latest
|
||||
```
|
||||
|
||||
@@ -60,16 +62,12 @@ services:
|
||||
- NET_BIND_SERVICE
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- local/path/config:/app/config
|
||||
- local/path/db:/app/db
|
||||
- local/path/config:/data/config
|
||||
- local/path/db:/data/db
|
||||
environment:
|
||||
- TZ=Europe/Berlin
|
||||
- PORT=20211
|
||||
tmpfs:
|
||||
- "/app/log:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
|
||||
- "/app/api:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,sync,noatime,nodiratime"
|
||||
- "/services/config/nginx/conf.active:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
|
||||
- "/services/run:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
|
||||
tmpfs:
|
||||
- "/tmp:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
|
||||
```
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
NetAlertX can be installed several ways. The best supported option is Docker, followed by a supervised Home Assistant instance, as an Unraid app, and lastly, on bare metal.
|
||||
|
||||
- [[Installation] Docker (recommended)](https://github.com/jokob-sk/NetAlertX/blob/main/dockerfiles/README.md)
|
||||
- [[Installation] Docker (recommended)](https://github.com/jokob-sk/NetAlertX/blob/main/docs/DOCKER_INSTALLATION.md)
|
||||
- [[Installation] Home Assistant](https://github.com/alexbelgium/hassio-addons/tree/master/netalertx)
|
||||
- [[Installation] Unraid App](https://unraid.net/community/apps)
|
||||
- [[Installation] Bare metal (experimental - looking for maintainers)](https://github.com/jokob-sk/NetAlertX/blob/main/docs/HW_INSTALL.md)
|
||||
|
||||
@@ -9,7 +9,7 @@ NetAlertX comes with several logs that help to identify application issues. Thes
|
||||
|
||||
You can find most of the logs exposed in the UI under _Maintenance -> Logs_.
|
||||
|
||||
If the UI is inaccessible, you can access them under `/app/log`.
|
||||
If the UI is inaccessible, you can access them under `/tmp/log`.
|
||||
|
||||

|
||||
|
||||
@@ -52,18 +52,18 @@ The default logs are erased every time the container restarts because they are s
|
||||
|
||||
2. Edit your `docker-compose.yml` file:
|
||||
|
||||
* **Comment out** the `/app/log` line under the `tmpfs:` section.
|
||||
* **Comment out** the `/tmp/log` line under the `tmpfs:` section.
|
||||
* **Uncomment** the "Retain logs" line under the `volumes:` section and set your desired host path.
|
||||
|
||||
```yaml
|
||||
...
|
||||
tmpfs:
|
||||
# - "/app/log:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
|
||||
# - "/tmp/log:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
|
||||
...
|
||||
volumes:
|
||||
...
|
||||
# Retain logs - comment out tmpfs /app/log if you want to retain logs between container restarts
|
||||
- /home/adam/netalertx_logs:/app/log
|
||||
# Retain logs - comment out tmpfs /tmp/log if you want to retain logs between container restarts
|
||||
- /home/adam/netalertx_logs:/tmp/log
|
||||
...
|
||||
```
|
||||
3. Restart the container:
|
||||
@@ -72,4 +72,4 @@ The default logs are erased every time the container restarts because they are s
|
||||
docker-compose up -d
|
||||
```
|
||||
|
||||
This change stops Docker from mounting a temporary in-memory volume at `/app/log`. Instead, it "bind mounts" a persistent folder from your host computer (e.g., `/data/netalertx_logs`) to that *same location* inside the container.
|
||||
This change stops Docker from mounting a temporary in-memory volume at `/tmp/log`. Instead, it "bind mounts" a persistent folder from your host computer (e.g., `/data/netalertx_logs`) to that *same location* inside the container.
|
||||
|
||||
@@ -43,7 +43,7 @@ A banner message will appear at the top of the web UI reminding you to update yo
|
||||
|
||||
|
||||
> [!TIP]
|
||||
> If you have trouble accessing past backups, config or database files you can copy them into the newly mapped directories, for example by running this command in the container: `cp -r /app/config /home/pi/pialert/config/old_backup_files`. This should create a folder in the `config` directory called `old_backup_files` containing all the files in that location. Another approach is to map the old location and the new one at the same time to copy things over.
|
||||
> If you have trouble accessing past backups, config or database files you can copy them into the newly mapped directories, for example by running this command in the container: `cp -r /data/config /home/pi/pialert/config/old_backup_files`. This should create a folder in the `config` directory called `old_backup_files` containing all the files in that location. Another approach is to map the old location and the new one at the same time to copy things over.
|
||||
|
||||
#### New Docker mount locations
|
||||
|
||||
@@ -51,8 +51,8 @@ The internal application path in the container has changed from `/home/pi/pialer
|
||||
|
||||
| Old mount point | New mount point |
|
||||
|----------------------|---------------|
|
||||
| `/home/pi/pialert/config` | `/app/config` |
|
||||
| `/home/pi/pialert/db` | `/app/db` |
|
||||
| `/home/pi/pialert/config` | `/data/config` |
|
||||
| `/home/pi/pialert/db` | `/data/db` |
|
||||
|
||||
|
||||
If you were mounting files directly, please note the file names have changed:
|
||||
@@ -85,10 +85,10 @@ services:
|
||||
network_mode: "host"
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- local/path/config:/home/pi/pialert/config
|
||||
- local/path/db:/home/pi/pialert/db
|
||||
- /local/path/config:/home/pi/pialert/config
|
||||
- /local/path/db:/home/pi/pialert/db
|
||||
# (optional) useful for debugging if you have issues setting up the container
|
||||
- local/path/logs:/home/pi/pialert/front/log
|
||||
- /local/path/logs:/home/pi/pialert/front/log
|
||||
environment:
|
||||
- TZ=Europe/Berlin
|
||||
- PORT=20211
|
||||
@@ -104,10 +104,10 @@ services:
|
||||
network_mode: "host"
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- local/path/config:/app/config # 🆕 This has changed
|
||||
- local/path/db:/app/db # 🆕 This has changed
|
||||
- /local/path/config:/data/config # 🆕 This has changed
|
||||
- /local/path/db:/data/db # 🆕 This has changed
|
||||
# (optional) useful for debugging if you have issues setting up the container
|
||||
- local/path/logs:/app/log # 🆕 This has changed
|
||||
- /local/path/logs:/tmp/log # 🆕 This has changed
|
||||
environment:
|
||||
- TZ=Europe/Berlin
|
||||
- PORT=20211
|
||||
@@ -131,10 +131,10 @@ services:
|
||||
network_mode: "host"
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- local/path/config/pialert.conf:/home/pi/pialert/config/pialert.conf
|
||||
- local/path/db/pialert.db:/home/pi/pialert/db/pialert.db
|
||||
- /local/path/config/pialert.conf:/home/pi/pialert/config/pialert.conf
|
||||
- /local/path/db/pialert.db:/home/pi/pialert/db/pialert.db
|
||||
# (optional) useful for debugging if you have issues setting up the container
|
||||
- local/path/logs:/home/pi/pialert/front/log
|
||||
- /local/path/logs:/home/pi/pialert/front/log
|
||||
environment:
|
||||
- TZ=Europe/Berlin
|
||||
- PORT=20211
|
||||
@@ -150,10 +150,10 @@ services:
|
||||
network_mode: "host"
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- local/path/config/app.conf:/app/config/app.conf # 🆕 This has changed
|
||||
- local/path/db/app.db:/app/db/app.db # 🆕 This has changed
|
||||
- /local/path/config/app.conf:/data/config/app.conf # 🆕 This has changed
|
||||
- /local/path/db/app.db:/data/db/app.db # 🆕 This has changed
|
||||
# (optional) useful for debugging if you have issues setting up the container
|
||||
- local/path/logs:/app/log # 🆕 This has changed
|
||||
- /local/path/logs:/tmp/log # 🆕 This has changed
|
||||
environment:
|
||||
- TZ=Europe/Berlin
|
||||
- PORT=20211
|
||||
@@ -190,10 +190,10 @@ services:
|
||||
network_mode: "host"
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- local/path/config:/app/config
|
||||
- local/path/db:/app/db
|
||||
- /local/path/config:/data/config
|
||||
- /local/path/db:/data/db
|
||||
# (optional) useful for debugging if you have issues setting up the container
|
||||
- local/path/logs:/app/log
|
||||
- /local/path/logs:/tmp/log
|
||||
environment:
|
||||
- TZ=Europe/Berlin
|
||||
- PORT=20211
|
||||
@@ -207,10 +207,10 @@ services:
|
||||
network_mode: "host"
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- local/path/config:/app/config
|
||||
- local/path/db:/app/db
|
||||
- /local/path/config:/data/config
|
||||
- /local/path/db:/data/db
|
||||
# (optional) useful for debugging if you have issues setting up the container
|
||||
- local/path/logs:/app/log
|
||||
- /local/path/logs:/tmp/log
|
||||
environment:
|
||||
- TZ=Europe/Berlin
|
||||
- PORT=20211
|
||||
@@ -234,10 +234,10 @@ services:
|
||||
network_mode: "host"
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- local/path/config:/app/config
|
||||
- local/path/db:/app/db
|
||||
- /local/path/config:/data/config
|
||||
- /local/path/db:/data/db
|
||||
# (optional) useful for debugging if you have issues setting up the container
|
||||
- local/path/logs:/app/log
|
||||
- /local/path/logs:/tmp/log
|
||||
environment:
|
||||
- TZ=Europe/Berlin
|
||||
- PORT=20211
|
||||
@@ -253,11 +253,19 @@ services:
|
||||
|
||||
```sh
|
||||
docker run -it --rm --name netalertx --user "0" \
|
||||
-v local/path/config:/app/config \
|
||||
-v local/path/db:/app/db \
|
||||
-v /local/path/config:/data/config \
|
||||
-v /local/path/db:/data/db \
|
||||
ghcr.io/jokob-sk/netalertx:latest
|
||||
```
|
||||
|
||||
..or alternatively execute:
|
||||
|
||||
```bash
|
||||
sudo chown -R 20211:20211 /local/path/config
|
||||
sudo chown -R 20211:20211 /local/path/db
|
||||
sudo chmod -R a+rwx /local/path/
|
||||
```
|
||||
|
||||
7. Stop the container
|
||||
8. Update the `docker-compose.yml` as per example below.
|
||||
|
||||
@@ -273,24 +281,16 @@ services:
|
||||
- NET_BIND_SERVICE # 🆕 New line
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- local/path/config:/app/config
|
||||
- local/path/db:/app/db
|
||||
- /local/path/config:/data/config
|
||||
- /local/path/db:/data/db
|
||||
# (optional) useful for debugging if you have issues setting up the container
|
||||
#- local/path/logs:/app/log
|
||||
#- /local/path/logs:/tmp/log
|
||||
environment:
|
||||
- TZ=Europe/Berlin
|
||||
- PORT=20211
|
||||
# 🆕 New "tmpfs" section START 🔽
|
||||
tmpfs:
|
||||
# Speed up logging. This can be commented out to retain logs between container restarts
|
||||
- "/app/log:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
|
||||
# Speed up API access as frontend/backend API is very chatty
|
||||
- "/app/api:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,sync,noatime,nodiratime"
|
||||
# Required for customization of the nginx listen addr/port without rebuilding the container
|
||||
- "/services/config/nginx/conf.active:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
|
||||
# /services/config/nginx/conf.d is required for nginx and php to start
|
||||
- "/services/run:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
|
||||
# /tmp is required by php for session save this should be reworked to /services/run/tmp
|
||||
tmpfs:
|
||||
# All writable runtime state resides under /tmp; comment out to persist logs between restarts
|
||||
- "/tmp:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
|
||||
# 🆕 New "tmpfs" section END 🔼
|
||||
```
|
||||
|
||||
@@ -44,14 +44,19 @@ In Notification Processing settings, you can specify blanket rules. These allow
|
||||
|
||||
1. Notify on (`NTFPRCS_INCLUDED_SECTIONS`) allows you to specify which events trigger notifications. Usual setups will have `new_devices`, `down_devices`, and possibly `down_reconnected` set. Including `plugin` (dependenton the Plugin `<plugin>_WATCH` and `<plugin>_REPORT_ON` settings) and `events` (dependent on the on-device **Alert Events** setting) might be too noisy for most setups. More info in the [NTFPRCS plugin](https://github.com/jokob-sk/NetAlertX/blob/main/front/plugins/notification_processing/README.md) on what events these selections include.
|
||||
2. Alert down after (`NTFPRCS_alert_down_time`) is useful if you want to wait for some time before the system sends out a down notification for a device. This is related to the on-device **Alert down** setting and only devices with this checked will trigger a down notification.
|
||||
3. A filter to allow you to set device-specific exceptions to New devices being added to the app.
|
||||
4. A filter to allow you to set device-specific exceptions to generated Events.
|
||||
|
||||
## Ignoring devices 🔕
|
||||
You can filter out unwanted notifications globally. This could be because of a misbehaving device (GoogleNest/GoogleHub (See also [ARPSAN docs and the `--exclude-broadcast` flag](https://github.com/jokob-sk/NetAlertX/tree/main/front/plugins/arp_scan#ip-flipping-on-google-nest-devices))) which flips between IP addresses, or because you want to ignore new device notifications of a certain pattern.
|
||||
|
||||
1. Events Filter (`NTFPRCS_event_condition`) - Filter out Events from notifications.
|
||||
2. New Devices Filter (`NTFPRCS_new_dev_condition`) - Filter out New Devices from notifications, but log and keep a new device in the system.
|
||||
|
||||
## Ignoring devices 💻
|
||||
|
||||

|
||||
|
||||
You can completely ignore detected devices globally. This could be because your instance detects docker containers, you want to ignore devices from a specific manufacturer via MAC rules or you want to ignore devices on a specific IP range.
|
||||
|
||||
1. Ignored MACs (`NEWDEV_ignored_MACs`) - List of MACs to ignore.
|
||||
2. Ignored IPs (`NEWDEV_ignored_IPs`) - List of IPs to ignore.
|
||||
2. Ignored IPs (`NEWDEV_ignored_IPs`) - List of IPs to ignore.
|
||||
|
||||
|
||||
|
||||
@@ -62,7 +62,7 @@ For example, the **ICMP plugin** allows you to specify a regular expression to s
|
||||
|
||||
## Storing Temporary Files in Memory
|
||||
|
||||
On systems with slower I/O speeds, you can optimize performance by storing temporary files in memory. This primarily applies to the `/app/api` and `/app/log` folders.
|
||||
On systems with slower I/O speeds, you can optimize performance by storing temporary files in memory. This primarily applies to the API directory (default: `/tmp/api`, configurable via `NETALERTX_API`) and `/tmp/log` folders.
|
||||
|
||||
Using `tmpfs` reduces disk writes and improves performance. However, it should be **disabled** if persistent logs or API data storage are required.
|
||||
|
||||
@@ -80,15 +80,15 @@ services:
|
||||
network_mode: "host"
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- local/path/config:/app/config
|
||||
- local/path/db:/app/db
|
||||
- local/path/config:/data/config
|
||||
- local/path/db:/data/db
|
||||
# (Optional) Useful for debugging setup issues
|
||||
- local/path/logs:/app/log
|
||||
- local/path/logs:/tmp/log
|
||||
# (API: OPTION 1) Store temporary files in memory (recommended for performance)
|
||||
- type: tmpfs # ◀ 🔺
|
||||
target: /app/api # ◀ 🔺
|
||||
target: /tmp/api # ◀ 🔺
|
||||
# (API: OPTION 2) Store API data on disk (useful for debugging)
|
||||
# - local/path/api:/app/api
|
||||
# - local/path/api:/tmp/api
|
||||
environment:
|
||||
- TZ=Europe/Berlin
|
||||
- PORT=20211
|
||||
|
||||
@@ -1,146 +1,192 @@
|
||||
## config.json Lifecycle in NetAlertX
|
||||
# Plugins Implementation Details
|
||||
|
||||
This document describes on a high level how `config.json` is read, processed, and used by the NetAlertX core and plugins. It also outlines the plugin output contract and the main plugin types.
|
||||
Plugins provide data to the NetAlertX core, which processes it to detect changes, discover new devices, raise alerts, and apply heuristics.
|
||||
|
||||
> [!NOTE]
|
||||
> For a deep-dive on the specific configuration options and sections of the `config.json` plugin manifest, consult the [Plugins Development Guide](PLUGINS_DEV.md).
|
||||
---
|
||||
|
||||
## Overview: Plugin Data Flow
|
||||
|
||||
1. Each plugin runs on a defined schedule.
|
||||
2. Aligning all plugin schedules is recommended so they execute in the same loop.
|
||||
3. During execution, all plugins write their collected data into the **`CurrentScan`** table.
|
||||
4. After all plugins complete, the `CurrentScan` table is evaluated to detect **new devices**, **changes**, and **triggers**.
|
||||
|
||||
Although plugins run independently, they contribute to the shared `CurrentScan` table.
|
||||
To inspect its contents, set `LOG_LEVEL=trace` and check for the log section:
|
||||
|
||||
```
|
||||
================ CurrentScan table content ================
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## `config.json` Lifecycle
|
||||
|
||||
This section outlines how each plugin’s `config.json` manifest is read, validated, and used by the core and plugins.
|
||||
It also describes plugin output expectations and the main plugin categories.
|
||||
|
||||
> [!TIP]
|
||||
> For detailed schema and examples, see the [Plugin Development Guide](PLUGINS_DEV.md).
|
||||
|
||||
---
|
||||
|
||||
### 1. Loading
|
||||
|
||||
* On startup, the app core loads `config.json` for each plugin.
|
||||
* The `config.json` represents a plugin manifest, that contains metadata and runtime settings.
|
||||
* On startup, the core loads `config.json` for each plugin.
|
||||
* The file acts as a **plugin manifest**, defining metadata, runtime configuration, and database mappings.
|
||||
|
||||
---
|
||||
|
||||
### 2. Validation
|
||||
|
||||
* The core checks that each required settings key (such as `RUN`) for a plugin exists.
|
||||
* Invalid or missing values may be replaced with defaults, or the plugin may be disabled.
|
||||
* The core validates required keys (for example, `RUN`).
|
||||
* Missing or invalid entries may be replaced with defaults or cause the plugin to be disabled.
|
||||
|
||||
---
|
||||
|
||||
### 3. Preparation
|
||||
|
||||
* The plugin’s settings (paths, commands, parameters) are prepared.
|
||||
* Database mappings (`mapped_to_table`, `database_column_definitions`) for data ingestion into the core app are parsed.
|
||||
* Plugin parameters (paths, commands, and options) are prepared for execution.
|
||||
* Database mappings (`mapped_to_table`, `database_column_definitions`) are parsed to define how data integrates with the main app.
|
||||
|
||||
---
|
||||
|
||||
### 4. Execution
|
||||
|
||||
* Plugins can be run at different core app execution points, such as on schedule, once on start, after a notification, etc.
|
||||
* At runtime, the scheduler triggers plugins according to their `interval`.
|
||||
* The plugin executes its command or script.
|
||||
* Plugins may run:
|
||||
|
||||
* On a fixed schedule.
|
||||
* Once at startup.
|
||||
* After a notification or other trigger.
|
||||
* The scheduler executes plugins according to their `interval`.
|
||||
|
||||
---
|
||||
|
||||
### 5. Parsing
|
||||
|
||||
* Plugin output is expected in **pipe (`|`)-delimited format**.
|
||||
* The core parses lines into fields, matching the **plugin interface contract**.
|
||||
* Plugin output must be **pipe-delimited (`|`)**.
|
||||
* The core parses each output line following the **Plugin Interface Contract**, splitting and mapping fields accordingly.
|
||||
|
||||
---
|
||||
|
||||
### 6. Mapping
|
||||
|
||||
* Each parsed field is moved into the `Plugins_` database tables and can be mapped into a configured database table.
|
||||
* Controlled by `database_column_definitions` and `mapped_to_table`.
|
||||
* Example: `Object_PrimaryID → Devices.MAC`.
|
||||
* Parsed fields are inserted into the plugin’s `Plugins_*` table.
|
||||
* Data can be mapped into other tables (e.g., `Devices`, `CurrentScan`) as defined by:
|
||||
|
||||
* `database_column_definitions`
|
||||
* `mapped_to_table`
|
||||
|
||||
**Example:** `Object_PrimaryID → devMAC`
|
||||
|
||||
---
|
||||
|
||||
### 6a. Plugin Output Contract
|
||||
|
||||
Each plugin must output results in the **plugin interface contract format**, pipe (`|`)-delimited values, in the column order described under [Plugin Interface Contract](PLUGINS_DEV.md)
|
||||
All plugins must follow the **Plugin Interface Contract** defined in `PLUGINS_DEV.md`.
|
||||
Output values are pipe-delimited in a fixed order.
|
||||
|
||||
#### IDs
|
||||
#### Identifiers
|
||||
|
||||
* `Object_PrimaryID` and `Object_SecondaryID` identify the record (e.g. `MAC|IP`).
|
||||
* `Object_PrimaryID` and `Object_SecondaryID` uniquely identify records (for example, `MAC|IP`).
|
||||
|
||||
#### **Watched values (`Watched_Value1–4`)**
|
||||
#### Watched Values (`Watched_Value1–4`)
|
||||
|
||||
* Used by the core to detect changes between runs.
|
||||
* Changes here can trigger **notifications**.
|
||||
* Used by the core to detect changes between runs.
|
||||
* Changes in these fields can trigger notifications.
|
||||
|
||||
#### **Extra value (`Extra`)**
|
||||
#### Extra Field (`Extra`)
|
||||
|
||||
* Optional, extra field.
|
||||
* Stored in the database but **not used for alerts**.
|
||||
* Optional additional value.
|
||||
* Stored in the database but not used for alerts.
|
||||
|
||||
#### **Helper values (`Helper_Value1–3`)**
|
||||
#### Helper Values (`Helper_Value1–3`)
|
||||
|
||||
* Added for cases where more than IDs + watched + extra are needed.
|
||||
* Can be made visible in the UI.
|
||||
* Stored in the database but **not used for alerts**.
|
||||
* Optional auxiliary data (for display or plugin logic).
|
||||
* Stored but not alert-triggering.
|
||||
|
||||
#### **Mapping matters**
|
||||
#### Mapping
|
||||
|
||||
* While the plugin output is free-form, the `database_column_definitions` and `mapped_to_table` settings in `config.json` determine the **target columns and data types** in NetAlertX.
|
||||
* While the output format is flexible, the plugin’s manifest determines the destination and type of each field.
|
||||
|
||||
---
|
||||
|
||||
### 7. Persistence
|
||||
|
||||
* Data is upserted into the database.
|
||||
* Conflicts are resolved using `Object_PrimaryID` + `Object_SecondaryID`.
|
||||
* Parsed data is **upserted** into the database.
|
||||
* Conflicts are resolved using the combined key: `Object_PrimaryID + Object_SecondaryID`.
|
||||
|
||||
---
|
||||
|
||||
### 8. Plugin Types and Expected Outputs
|
||||
## Plugin Categories
|
||||
|
||||
Beyond the `data_source` setting, plugins fall into functional categories. Each has its own input requirements and output expectations:
|
||||
Plugins fall into several functional categories depending on their purpose and expected outputs.
|
||||
|
||||
#### **Device discovery plugins**
|
||||
### 1. Device Discovery Plugins
|
||||
|
||||
* **Inputs:** `N/A`, subnet, or API for discovery service, or similar.
|
||||
* **Outputs:** At minimum `MAC` and `IP` that results in a new or updated device records in the `Devices` table.
|
||||
* **Mapping:** Must be mapped to the `CurrentScan` table via `database_column_definitions` and `data_filters`.
|
||||
* **Examples:** ARP-scan, NMAP device discovery (e.g., `ARPSCAN`, `NMAPDEV`).
|
||||
|
||||
#### **Device-data enrichment plugins**
|
||||
|
||||
* **Inputs:** Device identifier (usually `MAC`, `IP`).
|
||||
* **Outputs:** Additional data for that device (e.g. open ports).
|
||||
* **Mapping:** Controlled via `database_column_definitions` and `data_filters`.
|
||||
* **Examples:** Ports, MQTT messages (e.g., `NMAP`, `MQTT`)
|
||||
|
||||
#### **Name resolver plugins**
|
||||
|
||||
* **Inputs:** Device identifiers (MAC, IP, or hostname).
|
||||
* **Outputs:** Updated `devName` and `devFQDN` fields.
|
||||
* **Mapping:** Not expected.
|
||||
* **Note:** Currently requires **core app modification** to add new plugins, not fully driven by the plugins’ `config.json`.
|
||||
* **Examples:** Avahiscan (e.g., `NBTSCAN`, `NSLOOKUP`).
|
||||
|
||||
#### **Generic plugins**
|
||||
|
||||
* **Inputs:** Whatever the script or query provides.
|
||||
* **Outputs:** Data shown only in **Integrations → Plugins**, not tied to devices.
|
||||
* **Mapping:** Not expected.
|
||||
* **Examples:** External monitoring data (e.g., `INTRSPD`)
|
||||
|
||||
#### **Configuration-only plugins**
|
||||
|
||||
* **Inputs/Outputs:** None at runtime.
|
||||
* **Mapping:** Not expected.
|
||||
* **Examples:** Used to provide additional settings or execute scripts (e.g., `MAINT`, `CSVBCKP`).
|
||||
* **Inputs:** None, subnet, or discovery API.
|
||||
* **Outputs:** `MAC` and `IP` for new or updated device records in `Devices`.
|
||||
* **Mapping:** Required – usually into `CurrentScan`.
|
||||
* **Examples:** `ARPSCAN`, `NMAPDEV`.
|
||||
|
||||
---
|
||||
|
||||
### 9. Post-Processing
|
||||
### 2. Device Data Enrichment Plugins
|
||||
|
||||
* Notifications are generated if watched values change.
|
||||
* UI is updated with new or updated records.
|
||||
* All values that are configured to be shown in teh UI appear in the Plugins section.
|
||||
* **Inputs:** Device identifiers (`MAC`, `IP`).
|
||||
* **Outputs:** Additional metadata (for example, open ports or sensors).
|
||||
* **Mapping:** Controlled via manifest definitions.
|
||||
* **Examples:** `NMAP`, `MQTT`.
|
||||
|
||||
---
|
||||
|
||||
### 10. Summary
|
||||
### 3. Name Resolver Plugins
|
||||
|
||||
The lifecycle of `config.json` entries is:
|
||||
* **Inputs:** Device identifiers (`MAC`, `IP`, hostname`).
|
||||
* **Outputs:** Updated `devName` and `devFQDN`.
|
||||
* **Mapping:** Typically none.
|
||||
* **Note:** Adding new resolvers currently requires a core change.
|
||||
* **Examples:** `NBTSCAN`, `NSLOOKUP`.
|
||||
|
||||
---
|
||||
|
||||
### 4. Generic Plugins
|
||||
|
||||
* **Inputs:** Custom, based on the plugin logic or script.
|
||||
* **Outputs:** Data displayed under **Integrations → Plugins** only.
|
||||
* **Mapping:** Not required.
|
||||
* **Examples:** `INTRSPD`, custom monitoring scripts.
|
||||
|
||||
---
|
||||
|
||||
### 5. Configuration-Only Plugins
|
||||
|
||||
* **Inputs/Outputs:** None at runtime.
|
||||
* **Purpose:** Used for configuration or maintenance tasks.
|
||||
* **Examples:** `MAINT`, `CSVBCKP`.
|
||||
|
||||
---
|
||||
|
||||
## Post-Processing
|
||||
|
||||
After persistence:
|
||||
|
||||
* The core generates notifications for any watched value changes.
|
||||
* The UI updates with new or modified data.
|
||||
* Plugins with UI-enabled data display under **Integrations → Plugins**.
|
||||
|
||||
---
|
||||
|
||||
## Summary
|
||||
|
||||
The lifecycle of a plugin configuration is:
|
||||
|
||||
**Load → Validate → Prepare → Execute → Parse → Map → Persist → Post-process**
|
||||
|
||||
Plugins must follow the **output contract**, and their category (discovery, specific, resolver, generic, config-only) defines what inputs they require and what outputs are expected.
|
||||
Each plugin must:
|
||||
|
||||
* Follow the **output contract**.
|
||||
* Declare its type and expected output structure.
|
||||
* Define mappings and watched values clearly in `config.json`.
|
||||
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ There is also an in-app Help / FAQ section that should be answering frequently a
|
||||
|
||||
#### 🐳 Docker (Fully supported)
|
||||
|
||||
- The main installation method is as a [docker container - follow these instructions here](https://github.com/jokob-sk/NetAlertX/blob/main/dockerfiles/README.md).
|
||||
- The main installation method is as a [docker container - follow these instructions here](./DOCKER_INSTALLATION.md).
|
||||
|
||||
#### 💻 Bare-metal / On-server (Experimental/community supported 🧪)
|
||||
|
||||
|
||||
@@ -42,9 +42,9 @@ services:
|
||||
image: "ghcr.io/jokob-sk/netalertx:latest"
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- /home/netalertx/config:/app/config
|
||||
- /home/netalertx/db:/app/db
|
||||
- /home/netalertx/log:/app/log
|
||||
- /home/netalertx/config:/data/config
|
||||
- /home/netalertx/db:/data/db
|
||||
- /home/netalertx/log:/tmp/log
|
||||
environment:
|
||||
- TZ=Europe/Berlin
|
||||
- PORT=20211
|
||||
@@ -68,9 +68,9 @@ services:
|
||||
image: "ghcr.io/jokob-sk/netalertx:latest"
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- ./config/app.conf:/app/config/app.conf
|
||||
- ./db:/app/db
|
||||
- ./log:/app/log
|
||||
- ./config/app.conf:/data/config/app.conf
|
||||
- ./db:/data/db
|
||||
- ./log:/tmp/log
|
||||
- ./config/resolv.conf:/etc/resolv.conf # Mapping the /resolv.conf file for better name resolution
|
||||
environment:
|
||||
- TZ=Europe/Berlin
|
||||
|
||||
@@ -499,8 +499,8 @@ Mapping the updated file (on the local filesystem at `/appl/docker/netalertx/def
|
||||
```bash
|
||||
docker run -d --rm --network=host \
|
||||
--name=netalertx \
|
||||
-v /appl/docker/netalertx/config:/app/config \
|
||||
-v /appl/docker/netalertx/db:/app/db \
|
||||
-v /appl/docker/netalertx/config:/data/config \
|
||||
-v /appl/docker/netalertx/db:/data/db \
|
||||
-v /appl/docker/netalertx/default:/etc/nginx/sites-available/default \
|
||||
-e TZ=Europe/Amsterdam \
|
||||
-e PORT=20211 \
|
||||
|
||||
@@ -48,7 +48,7 @@ Here’s a breakdown of the defensive layers you get, right out of the box using
|
||||
|
||||
**Methodology:** All writable locations are treated as untrusted, temporary, and non-executable by default.
|
||||
|
||||
* **In-Memory Volatile Storage:** The `docker-compose.yml` configuration maps all temporary directories (e.g., `/app/log`, `/app/api`, `/tmp`) to in-memory `tmpfs` filesystems. They do not exist on the host's disk.
|
||||
* **In-Memory Volatile Storage:** The `docker-compose.yml` configuration maps all temporary directories (e.g., `/tmp/log`, `/tmp/api`, `/tmp`) to in-memory `tmpfs` filesystems. They do not exist on the host's disk.
|
||||
|
||||
* **Volatile Data:** Because these locations exist only in RAM, their contents are **instantly and irrevocably erased** when the container is stopped. This provides a "self-cleaning" mechanism that purges any attacker-dropped files or payloads on every single restart.
|
||||
|
||||
|
||||
@@ -40,10 +40,10 @@ services:
|
||||
network_mode: "host"
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- local/path/config:/app/config
|
||||
- local/path/db:/app/db
|
||||
- local/path/config:/data/config
|
||||
- local/path/db:/data/db
|
||||
# (optional) useful for debugging if you have issues setting up the container
|
||||
- local/path/logs:/app/log
|
||||
- local/path/logs:/tmp/log
|
||||
environment:
|
||||
- TZ=Europe/Berlin
|
||||
- PORT=20211
|
||||
@@ -57,10 +57,10 @@ services:
|
||||
|
||||
```yaml
|
||||
volumes:
|
||||
- /volume1/app_storage/netalertx/config:/app/config
|
||||
- /volume1/app_storage/netalertx/db:/app/db
|
||||
- /volume1/app_storage/netalertx/config:/data/config
|
||||
- /volume1/app_storage/netalertx/db:/data/db
|
||||
# (optional) useful for debugging if you have issues setting up the container
|
||||
# - local/path/logs:/app/log <- commented out with # ⚠
|
||||
# - local/path/logs:/tmp/log <- commented out with # ⚠
|
||||
```
|
||||
|
||||

|
||||
|
||||
@@ -15,7 +15,7 @@ The **Web UI** is served by an **nginx** server, while the **API backend** runs
|
||||
APP_CONF_OVERRIDE={"GRAPHQL_PORT":"20212"}
|
||||
```
|
||||
|
||||
For more information, check the [Docker installation guide](https://github.com/jokob-sk/NetAlertX/blob/main/dockerfiles/README.md).
|
||||
For more information, check the [Docker installation guide](./DOCKER_INSTALLATION.md).
|
||||
|
||||
## Possible issues and troubleshooting
|
||||
|
||||
@@ -62,11 +62,11 @@ In the container execute and investigate:
|
||||
|
||||
`cat /var/log/nginx/error.log`
|
||||
|
||||
`cat /app/log/app.php_errors.log`
|
||||
`cat /tmp/log/app.php_errors.log`
|
||||
|
||||
### 8. Make sure permissions are correct
|
||||
|
||||
> [!TIP]
|
||||
> You can try to start the container without mapping the `/app/config` and `/app/db` dirs and if the UI shows up then the issue is most likely related to your file system permissions or file ownership.
|
||||
> You can try to start the container without mapping the `/data/config` and `/data/db` dirs and if the UI shows up then the issue is most likely related to your file system permissions or file ownership.
|
||||
|
||||
Please read the [Permissions troubleshooting guide](./FILE_PERMISSIONS.md) and provide a screesnhot of the permissions and ownership in the `/app/db` and `app/config` directories.
|
||||
Please read the [Permissions troubleshooting guide](./FILE_PERMISSIONS.md) and provide a screesnhot of the permissions and ownership in the `/data/db` and `app/config` directories.
|
||||
@@ -23,7 +23,7 @@ Limit capabilities to only those required:
|
||||
- NET_ADMIN
|
||||
- NET_BIND_SERVICE
|
||||
```
|
||||
- Remove any unnecessary `--cap-add` flags from docker run commands
|
||||
- Remove any unnecessary `--cap-add` or `--privileged` flags from docker run commands
|
||||
|
||||
## Additional Resources
|
||||
|
||||
|
||||
@@ -24,9 +24,9 @@ Review and correct your volume mounts in docker-compose.yml:
|
||||
Example volume configuration:
|
||||
```yaml
|
||||
volumes:
|
||||
- ./data/db:/app/db
|
||||
- ./data/config:/app/config
|
||||
- ./data/log:/app/log
|
||||
- ./data/db:/data/db
|
||||
- ./data/config:/data/config
|
||||
- ./data/log:/tmp/log
|
||||
```
|
||||
|
||||
## Additional Resources
|
||||
|
||||
@@ -20,7 +20,7 @@ If you want to use a custom port, create a bind mount for the nginx configuratio
|
||||
- Add to your docker-compose.yml:
|
||||
```yaml
|
||||
volumes:
|
||||
- /path/to/nginx-config:/app/system/services/active/config
|
||||
- /path/to/nginx-config:/tmp/nginx/active-config
|
||||
environment:
|
||||
- PORT=your_custom_port
|
||||
```
|
||||
|
||||
@@ -21,7 +21,7 @@ The app can be installed different ways, with the best support of the docker-bas
|
||||
|
||||
NetAlertX is fully supported in Docker environments, allowing for easy setup and configuration. Follow the official guide to get started:
|
||||
|
||||
- [Docker Installation Guide](https://github.com/jokob-sk/NetAlertX/blob/main/dockerfiles/README.md)
|
||||
- [Docker Installation Guide](./DOCKER_INSTALLATION.md)
|
||||
|
||||
This guide will take you through the process of setting up NetAlertX using Docker Compose or standalone Docker commands.
|
||||
|
||||
|
||||
@@ -15,9 +15,22 @@
|
||||
<?php
|
||||
|
||||
require 'php/templates/header.php';
|
||||
|
||||
// check permissions
|
||||
$dbPath = "../db/app.db";
|
||||
$confPath = "../config/app.conf";
|
||||
// Use environment-aware paths with fallback to legacy locations
|
||||
$dbFolderPath = rtrim(getenv('NETALERTX_DB') ?: '/data/db', '/');
|
||||
$configFolderPath = rtrim(getenv('NETALERTX_CONFIG') ?: '/data/config', '/');
|
||||
|
||||
$dbPath = $dbFolderPath . '/app.db';
|
||||
$confPath = $configFolderPath . '/app.conf';
|
||||
|
||||
// Fallback to legacy paths if new locations don't exist
|
||||
if (!file_exists($dbPath) && file_exists('../db/app.db')) {
|
||||
$dbPath = '../db/app.db';
|
||||
}
|
||||
if (!file_exists($confPath) && file_exists('../config/app.conf')) {
|
||||
$confPath = '../config/app.conf';
|
||||
}
|
||||
|
||||
checkPermissions([$dbPath, $confPath]);
|
||||
?>
|
||||
|
||||
@@ -17,11 +17,12 @@
|
||||
|
||||
// Size and last mod of DB ------------------------------------------------------
|
||||
|
||||
$nax_db = str_replace('front', 'db', getcwd()).'/app.db';
|
||||
$nax_wal = str_replace('front', 'db', getcwd()).'/app.db-wal';
|
||||
$nax_db_size = number_format((filesize($nax_db) / 1000000),2,",",".") . ' MB';
|
||||
$nax_wal_size = number_format((filesize($nax_wal) / 1000000),2,",",".") . ' MB';
|
||||
$nax_db_mod = date ("F d Y H:i:s", filemtime($nax_db));
|
||||
$dbBasePath = rtrim(getenv('NETALERTX_DB') ?: '/data/db', '/');
|
||||
$nax_db = $dbBasePath . '/app.db';
|
||||
$nax_wal = $dbBasePath . '/app.db-wal';
|
||||
$nax_db_size = file_exists($nax_db) ? number_format((filesize($nax_db) / 1000000),2,",",".") . ' MB' : '0 MB';
|
||||
$nax_wal_size = file_exists($nax_wal) ? number_format((filesize($nax_wal) / 1000000),2,",",".") . ' MB' : '0 MB';
|
||||
$nax_db_mod = file_exists($nax_db) ? date ("F d Y H:i:s", filemtime($nax_db)) : 'N/A';
|
||||
|
||||
|
||||
// Table sizes -----------------------------------------------------------------
|
||||
@@ -334,7 +335,7 @@ $db->close();
|
||||
var emptyArr = ['undefined', "", undefined, null];
|
||||
var selectedTab = 'tab_DBTools_id';
|
||||
|
||||
initializeTabs();
|
||||
// initializeTabs() is called in window.onload
|
||||
|
||||
// -----------------------------------------------------------
|
||||
// delete devices with emty macs
|
||||
@@ -704,7 +705,7 @@ function renderLogs(customData) {
|
||||
window.onload = function asyncFooter() {
|
||||
renderLogs();
|
||||
|
||||
// initializeTabs();
|
||||
initializeTabs();
|
||||
|
||||
try {
|
||||
$("#lastCommit").append('<a href="https://github.com/jokob-sk/NetAlertX/commits" target="_blank"><img alt="GitHub last commit" src="https://img.shields.io/github/last-commit/jokob-sk/netalertx/main?logo=github"></a>');
|
||||
|
||||
@@ -2,30 +2,60 @@
|
||||
|
||||
require '../server/init.php';
|
||||
|
||||
$logBasePath = rtrim(getenv('NETALERTX_LOG') ?: '/tmp/log', '/');
|
||||
|
||||
function resolveLogPath($path)
|
||||
{
|
||||
global $logBasePath;
|
||||
|
||||
if ($path === null || $path === '') {
|
||||
return $path;
|
||||
}
|
||||
|
||||
$placeholder = '__NETALERTX_LOG__';
|
||||
if (strpos($path, $placeholder) === 0) {
|
||||
return $logBasePath . substr($path, strlen($placeholder));
|
||||
}
|
||||
|
||||
return $path;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// check if authenticated
|
||||
require_once $_SERVER['DOCUMENT_ROOT'] . '/php/templates/security.php';
|
||||
|
||||
// Function to render the log area component
|
||||
function renderLogArea($params) {
|
||||
global $logBasePath;
|
||||
|
||||
$fileName = isset($params['fileName']) ? $params['fileName'] : '';
|
||||
$filePath = isset($params['filePath']) ? $params['filePath'] : '';
|
||||
$textAreaCssClass = isset($params['textAreaCssClass']) ? $params['textAreaCssClass'] : '';
|
||||
$buttons = isset($params['buttons']) ? $params['buttons'] : [];
|
||||
$content = "";
|
||||
$fileSize = 0;
|
||||
|
||||
if (filesize($filePath) > 2000000) {
|
||||
$filePath = resolveLogPath($filePath);
|
||||
|
||||
if (!is_file($filePath)) {
|
||||
$content = "";
|
||||
$fileSizeMb = 0.0;
|
||||
} elseif (filesize($filePath) > 2000000) {
|
||||
$content = file_get_contents($filePath, false, null, -2000000);
|
||||
$fileSizeMb = filesize($filePath) / 1000000;
|
||||
} else {
|
||||
$content = file_get_contents($filePath);
|
||||
$fileSizeMb = filesize($filePath) / 1000000;
|
||||
}
|
||||
|
||||
// Prepare the download button HTML if filePath starts with /app
|
||||
// Prepare the download button HTML if filePath resides under the active log base path
|
||||
$downloadButtonHtml = '';
|
||||
if (strpos($filePath, '/app') === 0) {
|
||||
$logPrefix = $logBasePath . '/';
|
||||
if ($logPrefix !== '/' && strpos($filePath, $logPrefix) === 0) {
|
||||
$downloadName = basename($filePath);
|
||||
$downloadButtonHtml = '
|
||||
<span class="span-padding">
|
||||
<a href="' . htmlspecialchars(str_replace('/app/log/', '/php/server/query_logs.php?file=', $filePath)) . '" target="_blank">
|
||||
<a href="' . htmlspecialchars('/php/server/query_logs.php?file=' . rawurlencode($downloadName)) . '" target="_blank">
|
||||
<i class="fa fa-download"></i>
|
||||
</a>
|
||||
</span>';
|
||||
@@ -34,13 +64,7 @@ function renderLogArea($params) {
|
||||
// Prepare buttons HTML
|
||||
$buttonsHtml = '';
|
||||
$totalButtons = count($buttons);
|
||||
if ($totalButtons > 0) {
|
||||
$colClass = 12 / $totalButtons;
|
||||
// Use $colClass in your HTML generation or further logic
|
||||
} else {
|
||||
// Handle case where $buttons array is empty
|
||||
$colClass = 12;
|
||||
}
|
||||
$colClass = $totalButtons > 0 ? (12 / $totalButtons) : 12;
|
||||
|
||||
foreach ($buttons as $button) {
|
||||
$labelStringCode = isset($button['labelStringCode']) ? $button['labelStringCode'] : '';
|
||||
@@ -52,8 +76,7 @@ function renderLogArea($params) {
|
||||
</div>';
|
||||
}
|
||||
|
||||
|
||||
// Render the log area HTML
|
||||
// Render HTML
|
||||
$html = '
|
||||
<div class="log-area box box-solid box-primary">
|
||||
<div class="row logs-row col-sm-12 col-xs-12">
|
||||
@@ -63,7 +86,7 @@ function renderLogArea($params) {
|
||||
</div>
|
||||
<div class="row logs-row">
|
||||
<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($fileSizeMb, 2, ",", ".") . ' MB'
|
||||
. $downloadButtonHtml .
|
||||
'</div>
|
||||
</div>
|
||||
|
||||
@@ -10,8 +10,8 @@
|
||||
"event": "askRestartBackend()"
|
||||
}
|
||||
],
|
||||
"fileName": "app.log",
|
||||
"filePath": "/app/log/app.log",
|
||||
"fileName": "app.log",
|
||||
"filePath": "__NETALERTX_LOG__/app.log",
|
||||
"textAreaCssClass": "logs"
|
||||
|
||||
},
|
||||
@@ -22,8 +22,8 @@
|
||||
"event": "logManage('app_front.log', 'cleanLog')"
|
||||
}
|
||||
],
|
||||
"fileName": "app_front.log",
|
||||
"filePath": "/app/log/app_front.log",
|
||||
"fileName": "app_front.log",
|
||||
"filePath": "__NETALERTX_LOG__/app_front.log",
|
||||
"textAreaCssClass": "logs logs-small"
|
||||
},
|
||||
{
|
||||
@@ -33,8 +33,8 @@
|
||||
"event": "logManage('app.php_errors.log', 'cleanLog')"
|
||||
}
|
||||
],
|
||||
"fileName": "app.php_errors.log",
|
||||
"filePath": "/app/log/app.php_errors.log",
|
||||
"fileName": "app.php_errors.log",
|
||||
"filePath": "__NETALERTX_LOG__/app.php_errors.log",
|
||||
"textAreaCssClass": "logs logs-small"
|
||||
},
|
||||
{
|
||||
@@ -44,15 +44,19 @@
|
||||
"event": "logManage('execution_queue.log', 'cleanLog')"
|
||||
}
|
||||
],
|
||||
"fileName": "execution_queue.log",
|
||||
"filePath": "/app/log/execution_queue.log",
|
||||
"fileName": "execution_queue.log",
|
||||
"filePath": "__NETALERTX_LOG__/execution_queue.log",
|
||||
"textAreaCssClass": "logs logs-small"
|
||||
},
|
||||
{
|
||||
"buttons": [
|
||||
{
|
||||
"labelStringCode": "Maint_PurgeLog",
|
||||
"event": "logManage('nginx-error.log', 'cleanLog')"
|
||||
}
|
||||
],
|
||||
"fileName": "nginx/error.log",
|
||||
"filePath": "/var/log/nginx/error.log",
|
||||
"fileName": "nginx-error.log",
|
||||
"filePath": "__NETALERTX_LOG__/nginx-error.log",
|
||||
"textAreaCssClass": "logs logs-small"
|
||||
},
|
||||
{
|
||||
@@ -62,8 +66,8 @@
|
||||
"event": "logManage('db_is_locked.log', 'cleanLog')"
|
||||
}
|
||||
],
|
||||
"fileName": "db_is_locked.log",
|
||||
"filePath": "/app/log/db_is_locked.log",
|
||||
"fileName": "db_is_locked.log",
|
||||
"filePath": "__NETALERTX_LOG__/db_is_locked.log",
|
||||
"textAreaCssClass": "logs logs-small"
|
||||
},
|
||||
{
|
||||
@@ -73,8 +77,8 @@
|
||||
"event": "logManage('stdout.log', 'cleanLog')"
|
||||
}
|
||||
],
|
||||
"fileName": "stdout.log",
|
||||
"filePath": "/app/log/stdout.log",
|
||||
"fileName": "stdout.log",
|
||||
"filePath": "__NETALERTX_LOG__/stdout.log",
|
||||
"textAreaCssClass": "logs logs-small"
|
||||
},
|
||||
{
|
||||
@@ -84,8 +88,30 @@
|
||||
"event": "logManage('stderr.log', 'cleanLog')"
|
||||
}
|
||||
],
|
||||
"fileName": "stderr.log",
|
||||
"filePath": "/app/log/stderr.log",
|
||||
"fileName": "stderr.log",
|
||||
"filePath": "__NETALERTX_LOG__/stderr.log",
|
||||
"textAreaCssClass": "logs logs-small"
|
||||
},
|
||||
{
|
||||
"buttons": [
|
||||
{
|
||||
"labelStringCode": "Maint_PurgeLog",
|
||||
"event": "logManage('IP_changes.log', 'cleanLog')"
|
||||
}
|
||||
],
|
||||
"fileName": "IP_changes.log",
|
||||
"filePath": "__NETALERTX_LOG__/IP_changes.log",
|
||||
"textAreaCssClass": "logs logs-small"
|
||||
},
|
||||
{
|
||||
"buttons": [
|
||||
{
|
||||
"labelStringCode": "Maint_PurgeLog",
|
||||
"event": "logManage('crond.log', 'cleanLog')"
|
||||
}
|
||||
],
|
||||
"fileName": "crond.log",
|
||||
"filePath": "__NETALERTX_LOG__/crond.log",
|
||||
"textAreaCssClass": "logs logs-small"
|
||||
}
|
||||
]
|
||||
@@ -13,8 +13,35 @@
|
||||
// $DBFILE = dirname(__FILE__).'/../../../db/app.db';
|
||||
// $DBFILE_LOCKED_FILE = dirname(__FILE__).'/../../../log/db_is_locked.log';
|
||||
$scriptDir = realpath(dirname(__FILE__)); // Resolves symlinks to the actual physical path
|
||||
$DBFILE = $scriptDir . '/../../../db/app.db';
|
||||
$DBFILE_LOCKED_FILE = $scriptDir . '/../../../log/db_is_locked.log';
|
||||
$legacyDbPath = $scriptDir . '/../../../db/app.db';
|
||||
$legacyLogDir = $scriptDir . '/../../../log';
|
||||
|
||||
$dbFolderPath = rtrim(getenv('NETALERTX_DB') ?: '/data/db', '/');
|
||||
$logFolderPath = rtrim(getenv('NETALERTX_LOG') ?: '/tmp/log', '/');
|
||||
|
||||
// Fallback to legacy layout if the new location is missing but the legacy file still exists
|
||||
if (!is_dir($dbFolderPath) && file_exists($legacyDbPath)) {
|
||||
$dbFolderPath = dirname($legacyDbPath);
|
||||
}
|
||||
|
||||
if (!is_dir($dbFolderPath)) {
|
||||
@mkdir($dbFolderPath, 0775, true);
|
||||
}
|
||||
|
||||
$DBFILE = rtrim($dbFolderPath, '/') . '/app.db';
|
||||
if (!file_exists($DBFILE) && file_exists($legacyDbPath)) {
|
||||
$DBFILE = $legacyDbPath;
|
||||
}
|
||||
|
||||
if (!is_dir($logFolderPath) && is_dir($legacyLogDir)) {
|
||||
$logFolderPath = $legacyLogDir;
|
||||
}
|
||||
|
||||
if (!is_dir($logFolderPath)) {
|
||||
@mkdir($logFolderPath, 0775, true);
|
||||
}
|
||||
|
||||
$DBFILE_LOCKED_FILE = rtrim($logFolderPath, '/') . '/db_is_locked.log';
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
@@ -39,8 +66,10 @@ function SQLite3_connect($trytoreconnect = true, $retryCount = 0) {
|
||||
if (!file_exists($DBFILE)) {
|
||||
die("Database file not found: $DBFILE");
|
||||
}
|
||||
if (!file_exists(dirname($DBFILE_LOCKED_FILE))) {
|
||||
die("Log directory not found: " . dirname($DBFILE_LOCKED_FILE));
|
||||
|
||||
$lockDir = dirname($DBFILE_LOCKED_FILE);
|
||||
if (!is_dir($lockDir) && !@mkdir($lockDir, 0775, true)) {
|
||||
die("Log directory not found and could not be created: $lockDir");
|
||||
}
|
||||
|
||||
|
||||
@@ -130,6 +159,7 @@ class CustomDatabaseWrapper {
|
||||
$message = 'Error executing query (attempts: ' . $attempts . '), query: ' . $query;
|
||||
// write_notification($message);
|
||||
error_log("Query failed after {$this->maxRetries} attempts: " . $this->sqlite->lastErrorMsg());
|
||||
return false;
|
||||
}
|
||||
|
||||
public function query_log_add($query)
|
||||
@@ -187,7 +217,7 @@ function OpenDB($DBPath = null) {
|
||||
|
||||
if (strlen($DBFILE) == 0) {
|
||||
$message = 'Database not available';
|
||||
echo '<script>alert('.$message.')</script>';
|
||||
echo '<script>alert("'.$message.'")</script>';
|
||||
write_notification($message);
|
||||
|
||||
die('<div style="padding-left:150px">'.$message.'</div>');
|
||||
@@ -197,7 +227,7 @@ function OpenDB($DBPath = null) {
|
||||
$db = new CustomDatabaseWrapper($DBFILE);
|
||||
} catch (Exception $e) {
|
||||
$message = "Error connecting to the database";
|
||||
echo '<script>alert('.$message.'": ' . $e->getMessage() . '")</script>';
|
||||
echo '<script>alert("'.$message.': '.$e->getMessage().'")</script>';
|
||||
write_notification($message);
|
||||
die('<div style="padding-left:150px">'.$message.'</div>');
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<?php
|
||||
ini_set('error_log', '../../log/app.php_errors.log'); // initializing the app.php_errors.log file for the maintenance section
|
||||
$logPath = rtrim(getenv('NETALERTX_LOG') ?: '/tmp/log', '/') . '/app.php_errors.log';
|
||||
ini_set('error_log', $logPath); // initializing the app.php_errors.log file for the maintenance section
|
||||
require dirname(__FILE__).'/../templates/globals.php';
|
||||
require dirname(__FILE__).'/db.php';
|
||||
require dirname(__FILE__).'/util.php';
|
||||
|
||||
@@ -18,7 +18,8 @@ if ($_SERVER['REQUEST_METHOD'] === 'GET') {
|
||||
// Check if file parameter is provided
|
||||
if ($file) {
|
||||
// Define the folder where files are located
|
||||
$filePath = "/app/config/" . basename($file);
|
||||
$configRoot = getenv('NETALERTX_CONFIG') ?: '/data/config';
|
||||
$filePath = rtrim($configRoot, '/') . "/" . basename($file);
|
||||
|
||||
// Check if the file exists and is readable
|
||||
if (file_exists($filePath) && is_readable($filePath)) {
|
||||
|
||||
@@ -11,6 +11,8 @@ require dirname(__FILE__).'/../server/init.php';
|
||||
//------------------------------------------------------------------------------
|
||||
// Handle incoming requests
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'GET') {
|
||||
$configRoot = getenv('NETALERTX_CONFIG') ?: '/data/config';
|
||||
$apiRoot = getenv('NETALERTX_API') ?: '/tmp/api';
|
||||
// Get query string parameter ?file=settings_table.json
|
||||
$file = isset($_GET['file']) ? $_GET['file'] : null;
|
||||
|
||||
@@ -19,10 +21,10 @@ if ($_SERVER['REQUEST_METHOD'] === 'GET') {
|
||||
// Define the folder where files are located
|
||||
if ($file == "workflows.json")
|
||||
{
|
||||
$filePath = "/app/config/" . basename($file);
|
||||
$filePath = rtrim($configRoot, '/') . "/" . basename($file);
|
||||
} else
|
||||
{
|
||||
$filePath = "/app/api/" . basename($file);
|
||||
$filePath = rtrim($apiRoot, '/') . "/" . basename($file);
|
||||
}
|
||||
|
||||
// Check if the file exists
|
||||
@@ -59,7 +61,8 @@ if ($_SERVER['REQUEST_METHOD'] === 'GET') {
|
||||
}
|
||||
|
||||
$file = $_GET['file'];
|
||||
$filePath = "/app/config/" . basename($file);
|
||||
$configRoot = getenv('NETALERTX_CONFIG') ?: '/data/config';
|
||||
$filePath = rtrim($configRoot, '/') . "/" . basename($file);
|
||||
|
||||
// Save new workflows.json (replace existing content)
|
||||
if (file_put_contents($filePath, json_encode($decodedData, JSON_PRETTY_PRINT))) {
|
||||
|
||||
@@ -16,8 +16,9 @@ if ($_SERVER['REQUEST_METHOD'] === 'GET') {
|
||||
|
||||
// Check if file parameter is provided
|
||||
if ($file) {
|
||||
// Define the folder where files are located
|
||||
$filePath = "/app/log/" . basename($file);
|
||||
// Define the folder where files are located
|
||||
$logBasePath = rtrim(getenv('NETALERTX_LOG') ?: '/tmp/log', '/');
|
||||
$filePath = $logBasePath . '/' . basename($file);
|
||||
|
||||
// Check if the file exists
|
||||
if (file_exists($filePath)) {
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
require dirname(__FILE__).'/../templates/globals.php';
|
||||
require dirname(__FILE__).'/../templates/skinUI.php';
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// check if authenticated
|
||||
require_once $_SERVER['DOCUMENT_ROOT'] . '/php/templates/security.php';
|
||||
@@ -176,7 +177,10 @@ function checkPermissions($files)
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------------------
|
||||
|
||||
// 🔺----- API ENDPOINTS SUPERSEDED -----🔺
|
||||
// check server/api_server/api_server_start.py for equivalents
|
||||
// equivalent: /messaging/in-app/write
|
||||
// 🔺----- API ENDPOINTS SUPERSEDED -----🔺
|
||||
function displayMessage($message, $logAlert = FALSE, $logConsole = TRUE, $logFile = TRUE, $logEcho = FALSE)
|
||||
{
|
||||
global $logFolderPath, $log_file, $timestamp;
|
||||
@@ -234,7 +238,10 @@ function displayMessage($message, $logAlert = FALSE, $logConsole = TRUE, $logFil
|
||||
|
||||
}
|
||||
|
||||
|
||||
// 🔺----- API ENDPOINTS SUPERSEDED -----🔺
|
||||
// check server/api_server/api_server_start.py for equivalents
|
||||
// equivalent: /logs/add-to-execution-queue
|
||||
// 🔺----- API ENDPOINTS SUPERSEDED -----🔺
|
||||
// ----------------------------------------------------------------------------------------
|
||||
// Adds an action to perform into the execution_queue.log file
|
||||
function addToExecutionQueue($action)
|
||||
@@ -257,13 +264,17 @@ function addToExecutionQueue($action)
|
||||
|
||||
|
||||
// ----------------------------------------------------------------------------------------
|
||||
// 🔺----- API ENDPOINTS SUPERSEDED -----🔺
|
||||
// check server/api_server/api_server_start.py for equivalents
|
||||
// equivalent: /logs DELETE
|
||||
// 🔺----- API ENDPOINTS SUPERSEDED -----🔺
|
||||
function cleanLog($logFile)
|
||||
{
|
||||
global $logFolderPath, $timestamp;
|
||||
|
||||
$path = "";
|
||||
|
||||
$allowedFiles = ['app.log', 'app_front.log', 'IP_changes.log', 'stdout.log', 'stderr.log', 'app.php_errors.log', 'execution_queue.log', 'db_is_locked.log'];
|
||||
$allowedFiles = ['app.log', 'app_front.log', 'IP_changes.log', 'stdout.log', 'stderr.log', 'app.php_errors.log', 'execution_queue.log', 'db_is_locked.log', 'nginx-error.log', 'crond.log'];
|
||||
|
||||
if(in_array($logFile, $allowedFiles))
|
||||
{
|
||||
@@ -312,7 +323,7 @@ function saveSettings()
|
||||
|
||||
$txt = $txt."#-----------------AUTOGENERATED FILE-----------------#\n";
|
||||
$txt = $txt."# #\n";
|
||||
$txt = $txt."# Generated: ".$timestamp." #\n";
|
||||
$txt = $txt."# Generated: ".$timestamp." #\n";
|
||||
$txt = $txt."# #\n";
|
||||
$txt = $txt."# Config file for the LAN intruder detection app: #\n";
|
||||
$txt = $txt."# https://github.com/jokob-sk/NetAlertX #\n";
|
||||
@@ -321,7 +332,12 @@ function saveSettings()
|
||||
|
||||
// collect all groups
|
||||
|
||||
$decodedSettings = json_decode($SETTINGS, true);
|
||||
$decodedSettings = json_decode((string)$SETTINGS, true);
|
||||
|
||||
if ($decodedSettings === null) {
|
||||
echo "Error: Invalid JSON in settings data.";
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ($decodedSettings as $setting) {
|
||||
if( in_array($setting[0] , $groups) == false) {
|
||||
@@ -418,6 +434,10 @@ function saveSettings()
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------------------------
|
||||
// 🔺----- API ENDPOINTS SUPERSEDED -----🔺
|
||||
// check server/api_server/api_server_start.py for equivalents
|
||||
// equivalent: /graphql LangStrings endpoint
|
||||
// 🔺----- API ENDPOINTS SUPERSEDED -----🔺
|
||||
function getString ($setKey, $default) {
|
||||
|
||||
$result = lang($setKey);
|
||||
@@ -430,9 +450,14 @@ function getString ($setKey, $default) {
|
||||
return $default;
|
||||
}
|
||||
// -------------------------------------------------------------------------------------------
|
||||
// 🔺----- API ENDPOINTS SUPERSEDED -----🔺
|
||||
// check server/api_server/api_server_start.py for equivalents
|
||||
// equivalent: /settings/<key>
|
||||
// 🔺----- API ENDPOINTS SUPERSEDED -----🔺
|
||||
function getSettingValue($setKey) {
|
||||
// Define the JSON endpoint URL
|
||||
$url = dirname(__FILE__).'/../../../api/table_settings.json';
|
||||
// Define the JSON endpoint URL
|
||||
$apiRoot = rtrim(getenv('NETALERTX_API') ?: '/tmp/api', '/');
|
||||
$url = $apiRoot . '/table_settings.json';
|
||||
|
||||
// Fetch the JSON data
|
||||
$json = file_get_contents($url);
|
||||
|
||||
@@ -7,6 +7,11 @@
|
||||
|
||||
require dirname(__FILE__).'/../templates/globals.php';
|
||||
|
||||
function get_notification_store_path(): string {
|
||||
$apiRoot = getenv('NETALERTX_API') ?: '/tmp/api';
|
||||
return rtrim($apiRoot, '/') . '/user_notifications.json';
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// check if authenticated
|
||||
require_once $_SERVER['DOCUMENT_ROOT'] . '/php/templates/security.php';
|
||||
@@ -69,7 +74,7 @@ function generate_guid() {
|
||||
// ----------------------------------------------------------------------------------------
|
||||
// Logs a notification in in-app notification system
|
||||
function write_notification($content, $level = "interrupt") {
|
||||
$NOTIFICATION_API_FILE = '/app/api/user_notifications.json';
|
||||
$NOTIFICATION_API_FILE = get_notification_store_path();
|
||||
|
||||
// Generate GUID
|
||||
$guid = generate_guid();
|
||||
@@ -102,7 +107,7 @@ function write_notification($content, $level = "interrupt") {
|
||||
// ----------------------------------------------------------------------------------------
|
||||
// Removes a notification based on GUID
|
||||
function remove_notification($guid) {
|
||||
$NOTIFICATION_API_FILE = '/app/api/user_notifications.json';
|
||||
$NOTIFICATION_API_FILE = get_notification_store_path();
|
||||
|
||||
// Read existing notifications
|
||||
$notifications = json_decode(file_get_contents($NOTIFICATION_API_FILE), true);
|
||||
@@ -119,7 +124,7 @@ function remove_notification($guid) {
|
||||
// ----------------------------------------------------------------------------------------
|
||||
// Deletes all notifications
|
||||
function notifications_clear() {
|
||||
$NOTIFICATION_API_FILE = '/app/api/user_notifications.json';
|
||||
$NOTIFICATION_API_FILE = get_notification_store_path();
|
||||
|
||||
// Clear notifications by writing an empty array to the file
|
||||
file_put_contents($NOTIFICATION_API_FILE, json_encode(array()));
|
||||
@@ -128,7 +133,7 @@ function notifications_clear() {
|
||||
// ----------------------------------------------------------------------------------------
|
||||
// Mark a notification read based on GUID
|
||||
function mark_notification_as_read($guid) {
|
||||
$NOTIFICATION_API_FILE = '/app/api/user_notifications.json';
|
||||
$NOTIFICATION_API_FILE = get_notification_store_path();
|
||||
$max_attempts = 3;
|
||||
$attempts = 0;
|
||||
|
||||
@@ -177,7 +182,7 @@ function notifications_mark_all_read() {
|
||||
|
||||
// ----------------------------------------------------------------------------------------
|
||||
function get_unread_notifications() {
|
||||
$NOTIFICATION_API_FILE = '/app/api/user_notifications.json';
|
||||
$NOTIFICATION_API_FILE = get_notification_store_path();
|
||||
|
||||
// Read existing notifications
|
||||
if (file_exists($NOTIFICATION_API_FILE) && is_readable($NOTIFICATION_API_FILE)) {
|
||||
|
||||
@@ -15,7 +15,19 @@ if (isset($_SESSION["login"]) && $_SESSION["login"] == 1) {
|
||||
|
||||
// Check if a valid cookie is present
|
||||
$CookieSaveLoginName = "NetAlertX_SaveLogin";
|
||||
$config_file = "../../../config/app.conf"; // depends on where this file is called from
|
||||
|
||||
// Use environment-aware config path
|
||||
$configFolderPath = rtrim(getenv('NETALERTX_CONFIG') ?: '/data/config', '/');
|
||||
$config_file = $configFolderPath . '/app.conf';
|
||||
|
||||
// Fallback to legacy path if new location doesn't exist
|
||||
if (!file_exists($config_file)) {
|
||||
$legacyPath = "../../../config/app.conf";
|
||||
if (file_exists($legacyPath)) {
|
||||
$config_file = $legacyPath;
|
||||
}
|
||||
}
|
||||
|
||||
$config_file_lines = file($config_file);
|
||||
$config_file_lines = array_values(preg_grep('/^SETPWD_password.*=/', $config_file_lines));
|
||||
$password_line = explode("'", $config_file_lines[0]);
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
// ## Global constants and TimeZone processing
|
||||
// ######################################################################
|
||||
|
||||
$configFolderPath = "/app/config/";
|
||||
$logFolderPath = "/app/log/";
|
||||
$configFolderPath = rtrim(getenv('NETALERTX_CONFIG') ?: '/data/config', '/') . '/';
|
||||
$logFolderPath = rtrim(getenv('NETALERTX_LOG') ?: '/tmp/log', '/') . '/';
|
||||
|
||||
$config_file = "app.conf";
|
||||
$workflows_file = "workflows.json";
|
||||
|
||||
@@ -674,7 +674,7 @@
|
||||
"Systeminfo_System_Uptime": "Uptime:",
|
||||
"Systeminfo_This_Client": "Aquest Client",
|
||||
"Systeminfo_USB_Devices": "Dispositius USB",
|
||||
"TICKER_MIGRATE_TO_NETALERTX": "⚠ S'han detectat punts muntatge antics. Ves a <a href=\"https://github.com/jokob-sk/NetAlertX/blob/main/docs/MIGRATION.md\" target=\"_blank\">aquesta guia</a> per migrar les noves <code>/app/config</code> i <code>/app/db</code> carpetes i al <code>netalertx</code> contenidor.",
|
||||
"TICKER_MIGRATE_TO_NETALERTX": "⚠ S'han detectat punts muntatge antics. Ves a <a href=\"https://github.com/jokob-sk/NetAlertX/blob/main/docs/MIGRATION.md\" target=\"_blank\">aquesta guia</a> per migrar les noves <code>/data/config</code> i <code>/data/db</code> carpetes i al <code>netalertx</code> contenidor.",
|
||||
"TIMEZONE_description": "Fus horari per mostrar les estadístiques correctament. <a target=\"_blank\" href=\"https://en.wikipedia.org/wiki/List_of_tz_database_time_zones\" rel=\"nofollow\">aquí</a>.",
|
||||
"TIMEZONE_name": "Fus horari",
|
||||
"UI_DEV_SECTIONS_description": "Seleccioneu quins elements de la interfície d'usuari per ocultar a les pàgines de dispositius.",
|
||||
|
||||
@@ -674,7 +674,7 @@
|
||||
"Systeminfo_System_Uptime": "Uptime:",
|
||||
"Systeminfo_This_Client": "This Client",
|
||||
"Systeminfo_USB_Devices": "USB devices",
|
||||
"TICKER_MIGRATE_TO_NETALERTX": "⚠ Old mount locations detected. Follow <a href=\"https://github.com/jokob-sk/NetAlertX/blob/main/docs/MIGRATION.md\" target=\"_blank\">this guide</a> to migrate to the new <code>/app/config</code> and <code>/app/db</code> folders and the <code>netalertx</code> container.",
|
||||
"TICKER_MIGRATE_TO_NETALERTX": "⚠ Old mount locations detected. Follow <a href=\"https://github.com/jokob-sk/NetAlertX/blob/main/docs/MIGRATION.md\" target=\"_blank\">this guide</a> to migrate to the new <code>/data/config</code> and <code>/data/db</code> folders and the <code>netalertx</code> container.",
|
||||
"TIMEZONE_description": "Time zone to display stats correctly. Find your time zone <a target=\"_blank\" href=\"https://en.wikipedia.org/wiki/List_of_tz_database_time_zones\" rel=\"nofollow\">here</a>.",
|
||||
"TIMEZONE_name": "Time zone",
|
||||
"UI_DEV_SECTIONS_description": "Select which UI elements to hide in the devices pages.",
|
||||
|
||||
@@ -734,7 +734,7 @@
|
||||
"Systeminfo_System_Uptime": "Tiempo de actividad:",
|
||||
"Systeminfo_This_Client": "Este cliente",
|
||||
"Systeminfo_USB_Devices": "Dispositivos USB",
|
||||
"TICKER_MIGRATE_TO_NETALERTX": "⚠ Ubicaciones de montaje antiguas detectadas. Siga <a href=\"https://github.com/jokob-sk/NetAlertX/blob/main/docs/MIGRATION.md\" target=\"_blank\">esta guía</a> para migrar a las nuevas carpetas <code>/app/config</code> y <code>/app/db</code> y el contenedor <code>netalertx</code>.",
|
||||
"TICKER_MIGRATE_TO_NETALERTX": "⚠ Ubicaciones de montaje antiguas detectadas. Siga <a href=\"https://github.com/jokob-sk/NetAlertX/blob/main/docs/MIGRATION.md\" target=\"_blank\">esta guía</a> para migrar a las nuevas carpetas <code>/data/config</code> y <code>/data/db</code> y el contenedor <code>netalertx</code>.",
|
||||
"TIMEZONE_description": "La zona horaria para mostrar las estadísticas correctamente. Encuentra tu zona horaria <a target=\"_blank\" href=\"https://en.wikipedia.org/wiki/List_of_tz_database_time_zones\" rel=\"nofollow\">aquí</a>.",
|
||||
"TIMEZONE_name": "Zona horaria",
|
||||
"UI_DEV_SECTIONS_description": "Seleccione los elementos de la interfaz de usuario que desea ocultar en las páginas de dispositivos.",
|
||||
|
||||
@@ -674,7 +674,7 @@
|
||||
"Systeminfo_System_Uptime": "Durée d'activité :",
|
||||
"Systeminfo_This_Client": "Ce client",
|
||||
"Systeminfo_USB_Devices": "Appareils USB",
|
||||
"TICKER_MIGRATE_TO_NETALERTX": "⚠ Emplacement de point de montage obsolète détecté. Suivez <a href=\"https://github.com/jokob-sk/NetAlertX/blob/main/docs/MIGRATION.md\" target=\"_blank\">ce guide</a> pour migrer vers les nouveaux dossiers <code>/app/config</code> and <code>/app/db</code> et le container <code>netalertx</code>.",
|
||||
"TICKER_MIGRATE_TO_NETALERTX": "⚠ Emplacement de point de montage obsolète détecté. Suivez <a href=\"https://github.com/jokob-sk/NetAlertX/blob/main/docs/MIGRATION.md\" target=\"_blank\">ce guide</a> pour migrer vers les nouveaux dossiers <code>/data/config</code> and <code>/data/db</code> et le container <code>netalertx</code>.",
|
||||
"TIMEZONE_description": "Fuseau horaire pour afficher correctement les statistiques. Trouvez votre fuseau horaire <a target=\"_blank\" href=\"https://en.wikipedia.org/wiki/List_of_tz_database_time_zones\" rel=\"nofollow\">ici</a>.",
|
||||
"TIMEZONE_name": "Fuseau horaire",
|
||||
"UI_DEV_SECTIONS_description": "Slecetionnez quels éléments de l'interface graphique masquer dans les pages des appareils.",
|
||||
|
||||
@@ -674,7 +674,7 @@
|
||||
"Systeminfo_System_Uptime": "Tempo di attività:",
|
||||
"Systeminfo_This_Client": "Questo client",
|
||||
"Systeminfo_USB_Devices": "Dispositivi USB",
|
||||
"TICKER_MIGRATE_TO_NETALERTX": "⚠ Rilevate vecchie posizioni di montaggio. Segui <a href=\"https://github.com/jokob-sk/NetAlertX/blob/main/docs/MIGRATION.md\" target=\"_blank\">questa guida</a> per migrare alle nuove cartelle <code> /app/config</code> e <code>/app/db</code> e al contenitore <code>netalertx</code>.",
|
||||
"TICKER_MIGRATE_TO_NETALERTX": "⚠ Rilevate vecchie posizioni di montaggio. Segui <a href=\"https://github.com/jokob-sk/NetAlertX/blob/main/docs/MIGRATION.md\" target=\"_blank\">questa guida</a> per migrare alle nuove cartelle <code> /data/config</code> e <code>/data/db</code> e al contenitore <code>netalertx</code>.",
|
||||
"TIMEZONE_description": "Fuso orario per visualizzare correttamente le statistiche. Trova il tuo fuso orario <a target=\"_blank\" href=\"https://en.wikipedia.org/wiki/List_of_tz_database_time_zones\" rel=\"nofollow\">qui</a>.",
|
||||
"TIMEZONE_name": "Fuso orario",
|
||||
"UI_DEV_SECTIONS_description": "Seleziona quali elementi della UI nascondere nella pagina dei dispositivi.",
|
||||
|
||||
@@ -674,7 +674,7 @@
|
||||
"Systeminfo_System_Uptime": "Oppetid:",
|
||||
"Systeminfo_This_Client": "Denne klienten",
|
||||
"Systeminfo_USB_Devices": "USB-enheter",
|
||||
"TICKER_MIGRATE_TO_NETALERTX": "⚠ Eldre Mount-lokasjoner oppdaget. Følg <a href=\"https://github.com/jokob-sk/NetAlertX/blob/main/docs/MIGRATION.md\" target=\"_blank\">denne guiden</a> for å migrere til den nye <code>/app/config</code> og <code>/app/db </code> mappene og <code>netalertx</code> containeren.",
|
||||
"TICKER_MIGRATE_TO_NETALERTX": "⚠ Eldre Mount-lokasjoner oppdaget. Følg <a href=\"https://github.com/jokob-sk/NetAlertX/blob/main/docs/MIGRATION.md\" target=\"_blank\">denne guiden</a> for å migrere til den nye <code>/data/config</code> og <code>/data/db </code> mappene og <code>netalertx</code> containeren.",
|
||||
"TIMEZONE_description": "Tidssone for å vise statistikk riktig. Finn din tidssone <a target=\"_blank\" href=\"https://en.wikipedia.org/wiki/List_of_tz_database_time_zones\" rel=\"nofollow\">her</a>.",
|
||||
"TIMEZONE_name": "Tidssone",
|
||||
"UI_DEV_SECTIONS_description": "Velg hvilke UI -elementer du vil skjule på enhetssiden.",
|
||||
|
||||
@@ -674,7 +674,7 @@
|
||||
"Systeminfo_System_Uptime": "Czas pracy:",
|
||||
"Systeminfo_This_Client": "Ten klient",
|
||||
"Systeminfo_USB_Devices": "Urządzenia USB",
|
||||
"TICKER_MIGRATE_TO_NETALERTX": "⚠ Wykryto stare lokalizacje montowania. Skorzystaj z <a href=\"https://github.com/jokob-sk/NetAlertX/blob/main/docs/MIGRATION.md\" target=\"_blank\">tego przewodnika</a>, aby przeprowadzić migrację do nowych folderów <code>/app/config</code> i <code>/app/db</code> oraz kontenera <code>netalertx</code>.",
|
||||
"TICKER_MIGRATE_TO_NETALERTX": "⚠ Wykryto stare lokalizacje montowania. Skorzystaj z <a href=\"https://github.com/jokob-sk/NetAlertX/blob/main/docs/MIGRATION.md\" target=\"_blank\">tego przewodnika</a>, aby przeprowadzić migrację do nowych folderów <code>/data/config</code> i <code>/data/db</code> oraz kontenera <code>netalertx</code>.",
|
||||
"TIMEZONE_description": "Ustaw strefę czasową, aby statystyki były wyświetlane poprawnie. Znajdź swoją strefę czasową <a target=\"_blank\" href=\"https://en.wikipedia.org/wiki/List_of_tz_database_time_zones\" rel=\"nofollow\">tutaj</a>.",
|
||||
"TIMEZONE_name": "Strefa czasowa",
|
||||
"UI_DEV_SECTIONS_description": "Wybierz elementy interfejsu użytkownika (UI), które chcesz ukryć na stronach Urządzeń.",
|
||||
|
||||
@@ -674,7 +674,7 @@
|
||||
"Systeminfo_System_Uptime": "Время работы:",
|
||||
"Systeminfo_This_Client": "Этот клиент",
|
||||
"Systeminfo_USB_Devices": "USB-устройства",
|
||||
"TICKER_MIGRATE_TO_NETALERTX": "⚠ Обнаружены устаревшие местоположения. Следуйте этому руководству <a href=\"https://github.com/jokob-sk/NetAlertX/blob/main/docs/MIGRATION.md\" target=\"_blank\"></a>, чтобы перейти на новые <code>/app/config</code> и <code>/app/db</code> папки и контейнер <code>netalertx</code>.",
|
||||
"TICKER_MIGRATE_TO_NETALERTX": "⚠ Обнаружены устаревшие местоположения. Следуйте этому руководству <a href=\"https://github.com/jokob-sk/NetAlertX/blob/main/docs/MIGRATION.md\" target=\"_blank\"></a>, чтобы перейти на новые <code>/data/config</code> и <code>/data/db</code> папки и контейнер <code>netalertx</code>.",
|
||||
"TIMEZONE_description": "Часовой пояс для корректного отображения статистики. Найдите свой часовой пояс <a target=\"_blank\" href=\"https://en.wikipedia.org/wiki/List_of_tz_database_time_zones\" rel=\"nofollow\">здесь</a>.",
|
||||
"TIMEZONE_name": "Часовой пояс",
|
||||
"UI_DEV_SECTIONS_description": "Выберите, какие элементы интерфейса нужно скрыть на страницах «Устройства».",
|
||||
|
||||
@@ -674,7 +674,7 @@
|
||||
"Systeminfo_System_Uptime": "Час роботи:",
|
||||
"Systeminfo_This_Client": "Цей клієнт",
|
||||
"Systeminfo_USB_Devices": "USB-пристрої",
|
||||
"TICKER_MIGRATE_TO_NETALERTX": "⚠ Виявлено старі місця монтування. Дотримуйтеся <a href=\"https://github.com/jokob-sk/NetAlertX/blob/main/docs/MIGRATION.md\" target=\"_blank\">цього посібника</a>, щоб перейти на новий <code> папки /app/config</code> і <code>/app/db</code> і контейнер <code>netalertx</code>.",
|
||||
"TICKER_MIGRATE_TO_NETALERTX": "⚠ Виявлено старі місця монтування. Дотримуйтеся <a href=\"https://github.com/jokob-sk/NetAlertX/blob/main/docs/MIGRATION.md\" target=\"_blank\">цього посібника</a>, щоб перейти на нові папки <code>/data/config</code> і <code>/data/db</code> та контейнер <code>netalertx</code>.",
|
||||
"TIMEZONE_description": "Часовий пояс для правильного відображення статистики. Знайдіть свій часовий пояс <a target=\"_blank\" href=\"https://en.wikipedia.org/wiki/List_of_tz_database_time_zones\" rel=\"nofollow\">тут</a>.",
|
||||
"TIMEZONE_name": "Часовий пояс",
|
||||
"UI_DEV_SECTIONS_description": "Виберіть, які елементи інтерфейсу користувача приховати на сторінках пристроїв.",
|
||||
|
||||
@@ -674,7 +674,7 @@
|
||||
"Systeminfo_System_Uptime": "正常运行时间:",
|
||||
"Systeminfo_This_Client": "此客户",
|
||||
"Systeminfo_USB_Devices": "USB 设备",
|
||||
"TICKER_MIGRATE_TO_NETALERTX": "⚠ 检测到旧的挂载位置。请按照<a href=\"https://github.com/jokob-sk/NetAlertX/blob/main/docs/MIGRATION.md\" target=\"_blank\">本指南</a>迁移到新的 <code>/app/config</code> 和 <code>/app/db</code> 文件夹以及 <code>netalertx</code> 容器。",
|
||||
"TICKER_MIGRATE_TO_NETALERTX": "⚠ 检测到旧的挂载位置。请按照<a href=\"https://github.com/jokob-sk/NetAlertX/blob/main/docs/MIGRATION.md\" target=\"_blank\">本指南</a>迁移到新的 <code>/data/config</code> 和 <code>/data/db</code> 文件夹以及 <code>netalertx</code> 容器。",
|
||||
"TIMEZONE_description": "时区可正确显示统计数据。在<a target=\"_blank\" href=\"https://en.wikipedia.org/wiki/List_of_tz_database_time_zones\" rel=\"nofollow\">此处</a>查找您的时区。",
|
||||
"TIMEZONE_name": "时区",
|
||||
"UI_DEV_SECTIONS_description": "选择在设备页面中隐藏哪些 UI 元素。",
|
||||
|
||||
@@ -1,7 +1,18 @@
|
||||
<?php
|
||||
|
||||
// Constants
|
||||
define('CONFIG_PATH', $_SERVER['DOCUMENT_ROOT'] . "/../config/app.conf");
|
||||
$configFolderPath = rtrim(getenv('NETALERTX_CONFIG') ?: '/data/config', '/');
|
||||
$legacyConfigPath = $_SERVER['DOCUMENT_ROOT'] . "/../config/app.conf";
|
||||
|
||||
// Use environment variable path, fallback to legacy
|
||||
if (file_exists($configFolderPath . '/app.conf')) {
|
||||
define('CONFIG_PATH', $configFolderPath . '/app.conf');
|
||||
} else if (file_exists($legacyConfigPath)) {
|
||||
define('CONFIG_PATH', $legacyConfigPath);
|
||||
} else {
|
||||
define('CONFIG_PATH', $configFolderPath . '/app.conf'); // default to new location
|
||||
}
|
||||
|
||||
define('COOKIE_SAVE_LOGIN_NAME', "NetAlertX_SaveLogin");
|
||||
|
||||
// Utility Functions
|
||||
@@ -48,7 +59,7 @@ if (!empty($_REQUEST['action']) && $_REQUEST['action'] == 'logout') {
|
||||
|
||||
// Load configuration
|
||||
if (!file_exists(CONFIG_PATH)) {
|
||||
die("Configuration file not found in " . $_SERVER['DOCUMENT_ROOT'] . "/../config/app.conf");
|
||||
die("Configuration file not found in " . CONFIG_PATH);
|
||||
}
|
||||
$configLines = file(CONFIG_PATH);
|
||||
|
||||
|
||||
@@ -1,23 +1,18 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
import os
|
||||
import pathlib
|
||||
import sys
|
||||
import json
|
||||
import sqlite3
|
||||
from pytz import timezone
|
||||
|
||||
# Define the installation path and extend the system path for plugin imports
|
||||
INSTALL_PATH = "/app"
|
||||
INSTALL_PATH = os.getenv('NETALERTX_APP', '/app')
|
||||
sys.path.extend([f"{INSTALL_PATH}/front/plugins", f"{INSTALL_PATH}/server"])
|
||||
|
||||
from plugin_helper import Plugin_Object, Plugin_Objects, decodeBase64
|
||||
from plugin_utils import get_plugins_configs
|
||||
from const import logPath
|
||||
from plugin_helper import Plugin_Objects
|
||||
from logger import mylog, Logger
|
||||
from const import pluginsPath, fullDbPath, logPath
|
||||
from helper import timeNowTZ, get_setting_value
|
||||
from helper import get_setting_value
|
||||
|
||||
from messaging.in_app import write_notification
|
||||
import conf
|
||||
|
||||
# Make sure the TIMEZONE for logging is correct
|
||||
@@ -54,26 +49,27 @@ def main():
|
||||
|
||||
# insert devices into the lats_result.log
|
||||
# make sure the below mapping is mapped in config.json, for example:
|
||||
#"database_column_definitions": [
|
||||
# "database_column_definitions": [
|
||||
# {
|
||||
# "column": "Object_PrimaryID", <--------- the value I save into primaryId
|
||||
# "mapped_to_column": "cur_MAC", <--------- gets inserted into the CurrentScan DB table column cur_MAC
|
||||
# "column": "Object_PrimaryID", <--------- the value I save into primaryId
|
||||
# "mapped_to_column": "cur_MAC", <--------- gets inserted into the CurrentScan DB
|
||||
# table column cur_MAC
|
||||
#
|
||||
for device in device_data:
|
||||
plugin_objects.add_object(
|
||||
primaryId = device['mac_address'],
|
||||
secondaryId = device['ip_address'],
|
||||
watched1 = device['hostname'],
|
||||
watched2 = device['vendor'],
|
||||
watched3 = device['device_type'],
|
||||
watched4 = device['last_seen'],
|
||||
extra = '',
|
||||
foreignKey = device['mac_address']
|
||||
# helpVal1 = "Something1", # Optional Helper values to be passed for mapping into the app
|
||||
# helpVal2 = "Something1", # If you need to use even only 1, add the remaining ones too
|
||||
# helpVal3 = "Something1", # and set them to 'null'. Check the the docs for details:
|
||||
# helpVal4 = "Something1", # https://github.com/jokob-sk/NetAlertX/blob/main/docs/PLUGINS_DEV.md
|
||||
)
|
||||
plugin_objects.add_object(
|
||||
primaryId = device['mac_address'],
|
||||
secondaryId = device['ip_address'],
|
||||
watched1 = device['hostname'],
|
||||
watched2 = device['vendor'],
|
||||
watched3 = device['device_type'],
|
||||
watched4 = device['last_seen'],
|
||||
extra = '',
|
||||
foreignKey = device['mac_address']
|
||||
# helpVal1 = "Something1", # Optional Helper values to be passed for mapping into the app
|
||||
# helpVal2 = "Something1", # If you need to use even only 1, add the remaining ones too
|
||||
# helpVal3 = "Something1", # and set them to 'null'. Check the the docs for details:
|
||||
# helpVal4 = "Something1", # https://github.com/jokob-sk/NetAlertX/blob/main/docs/PLUGINS_DEV.md
|
||||
)
|
||||
|
||||
mylog('verbose', [f'[{pluginName}] New entries: "{len(device_data)}"'])
|
||||
|
||||
|
||||
@@ -1,31 +1,20 @@
|
||||
#!/usr/bin/env python
|
||||
# Just a testing library plugin for development purposes
|
||||
import json
|
||||
import subprocess
|
||||
import argparse
|
||||
import os
|
||||
import pathlib
|
||||
import sys
|
||||
from datetime import datetime
|
||||
import time
|
||||
import re
|
||||
import hashlib
|
||||
import sqlite3
|
||||
|
||||
|
||||
# Register NetAlertX directories
|
||||
INSTALL_PATH="/app"
|
||||
INSTALL_PATH = os.getenv('NETALERTX_APP', '/app')
|
||||
sys.path.extend([f"{INSTALL_PATH}/front/plugins", f"{INSTALL_PATH}/server"])
|
||||
|
||||
# NetAlertX modules
|
||||
import conf
|
||||
from const import apiPath, confFileName, logPath
|
||||
from plugin_utils import getPluginObject
|
||||
from const import logPath
|
||||
from plugin_helper import Plugin_Objects
|
||||
from logger import mylog, Logger, append_line_to_file
|
||||
from helper import timeNowTZ, get_setting_value, bytes_to_string, sanitize_string, cleanDeviceName
|
||||
from models.notification_instance import NotificationInstance
|
||||
from database import DB, get_device_stats
|
||||
from logger import mylog
|
||||
from helper import get_setting_value
|
||||
|
||||
pluginName = 'TESTONLY'
|
||||
|
||||
|
||||
@@ -2,45 +2,46 @@
|
||||
|
||||
import json
|
||||
import subprocess
|
||||
import argparse
|
||||
import os
|
||||
import pathlib
|
||||
import sys
|
||||
from datetime import datetime
|
||||
|
||||
# Register NetAlertX directories
|
||||
INSTALL_PATH="/app"
|
||||
INSTALL_PATH = os.getenv("NETALERTX_APP", "/app")
|
||||
sys.path.extend([f"{INSTALL_PATH}/front/plugins", f"{INSTALL_PATH}/server"])
|
||||
|
||||
import conf
|
||||
from const import confFileName, logPath
|
||||
from utils.datetime_utils import timeNowDB
|
||||
from plugin_helper import Plugin_Objects
|
||||
from logger import mylog, Logger, append_line_to_file
|
||||
from helper import timeNowTZ, get_setting_value
|
||||
from logger import mylog, Logger
|
||||
from helper import get_setting_value
|
||||
from models.notification_instance import NotificationInstance
|
||||
from database import DB
|
||||
from pytz import timezone
|
||||
|
||||
# Make sure the TIMEZONE for logging is correct
|
||||
conf.tz = timezone(get_setting_value('TIMEZONE'))
|
||||
conf.tz = timezone(get_setting_value("TIMEZONE"))
|
||||
|
||||
# Make sure log level is initialized correctly
|
||||
Logger(get_setting_value('LOG_LEVEL'))
|
||||
Logger(get_setting_value("LOG_LEVEL"))
|
||||
|
||||
pluginName = 'APPRISE'
|
||||
|
||||
LOG_PATH = logPath + '/plugins'
|
||||
RESULT_FILE = os.path.join(LOG_PATH, f'last_result.{pluginName}.log')
|
||||
pluginName = "APPRISE"
|
||||
|
||||
LOG_PATH = logPath + "/plugins"
|
||||
RESULT_FILE = os.path.join(LOG_PATH, f"last_result.{pluginName}.log")
|
||||
|
||||
|
||||
def main():
|
||||
|
||||
mylog('verbose', [f'[{pluginName}](publisher) In script'])
|
||||
|
||||
mylog("verbose", [f"[{pluginName}](publisher) In script"])
|
||||
|
||||
# Check if basic config settings supplied
|
||||
if check_config() == False:
|
||||
mylog('none', [f'[{pluginName}] ⚠ ERROR: Publisher notification gateway not set up correctly. Check your {confFileName} {pluginName}_* variables.'])
|
||||
mylog(
|
||||
"none",
|
||||
[
|
||||
f"[{pluginName}] ⚠ ERROR: Publisher notification gateway not set up correctly. Check your {confFileName} {pluginName}_* variables."
|
||||
],
|
||||
)
|
||||
return
|
||||
|
||||
# Create a database connection
|
||||
@@ -58,14 +59,13 @@ def main():
|
||||
|
||||
# Process the new notifications (see the Notifications DB table for structure or check the /php/server/query_json.php?file=table_notifications.json endpoint)
|
||||
for notification in new_notifications:
|
||||
|
||||
# Send notification
|
||||
result = send(notification["HTML"], notification["Text"])
|
||||
result = send(notification["HTML"], notification["Text"])
|
||||
|
||||
# Log result
|
||||
plugin_objects.add_object(
|
||||
primaryId = pluginName,
|
||||
secondaryId = timeNowTZ(),
|
||||
secondaryId = timeNowDB(),
|
||||
watched1 = notification["GUID"],
|
||||
watched2 = result,
|
||||
watched3 = 'null',
|
||||
@@ -76,29 +76,33 @@ def main():
|
||||
|
||||
plugin_objects.write_result_file()
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
|
||||
# -------------------------------------------------------------------------------
|
||||
def check_config():
|
||||
if get_setting_value('APPRISE_HOST') == '' or (get_setting_value('APPRISE_URL') == '' and get_setting_value('APPRISE_TAG') == ''):
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
if get_setting_value("APPRISE_HOST") == "" or (
|
||||
get_setting_value("APPRISE_URL") == ""
|
||||
and get_setting_value("APPRISE_TAG") == ""
|
||||
):
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
|
||||
# -------------------------------------------------------------------------------
|
||||
def send(html, text):
|
||||
|
||||
payloadData = ''
|
||||
result = ''
|
||||
payloadData = ""
|
||||
result = ""
|
||||
|
||||
# limit = 1024 * 1024 # 1MB limit (1024 bytes * 1024 bytes = 1MB)
|
||||
limit = get_setting_value('APPRISE_SIZE')
|
||||
limit = get_setting_value("APPRISE_SIZE")
|
||||
|
||||
# truncate size
|
||||
if get_setting_value('APPRISE_PAYLOAD') == 'html':
|
||||
if get_setting_value("APPRISE_PAYLOAD") == "html":
|
||||
if len(html) > limit:
|
||||
payloadData = html[:limit] + "<h1>(text was truncated)</h1>"
|
||||
else:
|
||||
payloadData = html
|
||||
if get_setting_value('APPRISE_PAYLOAD') == 'text':
|
||||
if get_setting_value("APPRISE_PAYLOAD") == "text":
|
||||
if len(text) > limit:
|
||||
payloadData = text[:limit] + " (text was truncated)"
|
||||
else:
|
||||
@@ -106,36 +110,55 @@ def send(html, text):
|
||||
|
||||
# Define Apprise compatible payload (https://github.com/caronc/apprise-api#stateless-solution)
|
||||
|
||||
target_key = "tag" if get_setting_value('APPRISE_TARGETTYPE') == 'tag' else "urls"
|
||||
target_value = get_setting_value('APPRISE_TAG') if target_key == 'tag' else get_setting_value('APPRISE_URL')
|
||||
target_key = "tag" if get_setting_value("APPRISE_TARGETTYPE") == "tag" else "urls"
|
||||
target_value = (
|
||||
get_setting_value("APPRISE_TAG")
|
||||
if target_key == "tag"
|
||||
else get_setting_value("APPRISE_URL")
|
||||
)
|
||||
|
||||
_json_payload = {
|
||||
target_key: target_value,
|
||||
"title": "NetAlertX Notifications",
|
||||
"format": get_setting_value('APPRISE_PAYLOAD'),
|
||||
"body": payloadData
|
||||
"format": get_setting_value("APPRISE_PAYLOAD"),
|
||||
"body": payloadData,
|
||||
}
|
||||
|
||||
try:
|
||||
# try runnning a subprocess
|
||||
p = subprocess.Popen(["curl","-i","-X", "POST" ,"-H", "Content-Type:application/json" ,"-d", json.dumps(_json_payload), get_setting_value('APPRISE_HOST')], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||
p = subprocess.Popen(
|
||||
[
|
||||
"curl",
|
||||
"-i",
|
||||
"-X",
|
||||
"POST",
|
||||
"-H",
|
||||
"Content-Type:application/json",
|
||||
"-d",
|
||||
json.dumps(_json_payload),
|
||||
get_setting_value("APPRISE_HOST"),
|
||||
],
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.STDOUT,
|
||||
)
|
||||
stdout, stderr = p.communicate()
|
||||
|
||||
# write stdout and stderr into .log files for debugging if needed
|
||||
# Log the stdout and stderr
|
||||
mylog('debug', [stdout, stderr])
|
||||
mylog("debug", [stdout, stderr])
|
||||
|
||||
# log result
|
||||
result = stdout
|
||||
|
||||
except subprocess.CalledProcessError as e:
|
||||
# An error occurred, handle it
|
||||
mylog('none', [e.output])
|
||||
mylog("none", [e.output])
|
||||
|
||||
# log result
|
||||
result = e.output
|
||||
|
||||
return result
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
|
||||
@@ -1,12 +1,7 @@
|
||||
#!/usr/bin/env python
|
||||
import json
|
||||
import subprocess
|
||||
import argparse
|
||||
import os
|
||||
import pathlib
|
||||
import sys
|
||||
import re
|
||||
from datetime import datetime
|
||||
from email.mime.multipart import MIMEMultipart
|
||||
from email.mime.text import MIMEText
|
||||
from email.header import Header
|
||||
@@ -17,15 +12,16 @@ import socket
|
||||
import ssl
|
||||
|
||||
# Register NetAlertX directories
|
||||
INSTALL_PATH="/app"
|
||||
INSTALL_PATH = os.getenv('NETALERTX_APP', '/app')
|
||||
sys.path.extend([f"{INSTALL_PATH}/front/plugins", f"{INSTALL_PATH}/server"])
|
||||
|
||||
# NetAlertX modules
|
||||
import conf
|
||||
from const import confFileName, logPath
|
||||
from plugin_helper import Plugin_Objects
|
||||
from logger import mylog, Logger, append_line_to_file
|
||||
from helper import timeNowTZ, get_setting_value, hide_email
|
||||
from utils.datetime_utils import timeNowDB
|
||||
from logger import mylog, Logger
|
||||
from helper import get_setting_value, hide_email
|
||||
from models.notification_instance import NotificationInstance
|
||||
from database import DB
|
||||
from pytz import timezone
|
||||
@@ -66,15 +62,15 @@ def main():
|
||||
new_notifications = notifications.getNew()
|
||||
|
||||
# mylog('verbose', [f'[{pluginName}] new_notifications: ', new_notifications])
|
||||
mylog('verbose', [f'[{pluginName}] SMTP_SERVER: ', get_setting_value("SMTP_SERVER")])
|
||||
mylog('verbose', [f'[{pluginName}] SMTP_PORT: ', get_setting_value("SMTP_PORT")])
|
||||
mylog('verbose', [f'[{pluginName}] SMTP_SKIP_LOGIN: ', get_setting_value("SMTP_SKIP_LOGIN")])
|
||||
# mylog('verbose', [f'[{pluginName}] SMTP_USER: ', get_setting_value("SMTP_USER")])
|
||||
mylog('verbose', [f'[{pluginName}] SMTP_SERVER: ', get_setting_value("SMTP_SERVER")])
|
||||
mylog('verbose', [f'[{pluginName}] SMTP_PORT: ', get_setting_value("SMTP_PORT")])
|
||||
mylog('verbose', [f'[{pluginName}] SMTP_SKIP_LOGIN: ', get_setting_value("SMTP_SKIP_LOGIN")])
|
||||
# mylog('verbose', [f'[{pluginName}] SMTP_USER: ', get_setting_value("SMTP_USER")])
|
||||
# mylog('verbose', [f'[{pluginName}] SMTP_PASS: ', get_setting_value("SMTP_PASS")])
|
||||
mylog('verbose', [f'[{pluginName}] SMTP_SKIP_TLS: ', get_setting_value("SMTP_SKIP_TLS")])
|
||||
mylog('verbose', [f'[{pluginName}] SMTP_FORCE_SSL: ', get_setting_value("SMTP_FORCE_SSL")])
|
||||
# mylog('verbose', [f'[{pluginName}] SMTP_REPORT_TO: ', get_setting_value("SMTP_REPORT_TO")])
|
||||
# mylog('verbose', [f'[{pluginName}] SMTP_REPORT_FROM: ', get_setting_value("SMTP_REPORT_FROM")])
|
||||
mylog('verbose', [f'[{pluginName}] SMTP_SKIP_TLS: ', get_setting_value("SMTP_SKIP_TLS")])
|
||||
mylog('verbose', [f'[{pluginName}] SMTP_FORCE_SSL: ', get_setting_value("SMTP_FORCE_SSL")])
|
||||
# mylog('verbose', [f'[{pluginName}] SMTP_REPORT_TO: ', get_setting_value("SMTP_REPORT_TO")])
|
||||
# mylog('verbose', [f'[{pluginName}] SMTP_REPORT_FROM: ', get_setting_value("SMTP_REPORT_FROM")])
|
||||
|
||||
|
||||
# Process the new notifications (see the Notifications DB table for structure or check the /php/server/query_json.php?file=table_notifications.json endpoint)
|
||||
@@ -86,7 +82,7 @@ def main():
|
||||
# Log result
|
||||
plugin_objects.add_object(
|
||||
primaryId = pluginName,
|
||||
secondaryId = timeNowTZ(),
|
||||
secondaryId = timeNowDB(),
|
||||
watched1 = notification["GUID"],
|
||||
watched2 = result,
|
||||
watched3 = 'null',
|
||||
|
||||
@@ -14,17 +14,18 @@ from pytz import timezone
|
||||
|
||||
|
||||
# Register NetAlertX directories
|
||||
INSTALL_PATH = "/app"
|
||||
INSTALL_PATH = os.getenv('NETALERTX_APP', '/app')
|
||||
sys.path.extend([f"{INSTALL_PATH}/front/plugins", f"{INSTALL_PATH}/server"])
|
||||
|
||||
# NetAlertX modules
|
||||
import conf
|
||||
from const import confFileName, logPath
|
||||
from plugin_utils import getPluginObject
|
||||
from utils.plugin_utils import getPluginObject
|
||||
from plugin_helper import Plugin_Objects
|
||||
from logger import mylog, Logger
|
||||
from helper import timeNowTZ, get_setting_value, bytes_to_string, \
|
||||
from helper import get_setting_value, bytes_to_string, \
|
||||
sanitize_string, normalize_string
|
||||
from utils.datetime_utils import timeNowDB
|
||||
from database import DB, get_device_stats
|
||||
|
||||
|
||||
@@ -364,7 +365,6 @@ def mqtt_create_client():
|
||||
return
|
||||
except Exception as err:
|
||||
mylog('verbose', [f"[{pluginName}] {err} Reconnect failed. Retrying..."])
|
||||
pass
|
||||
|
||||
reconnect_delay *= RECONNECT_RATE
|
||||
reconnect_delay = min(reconnect_delay, MAX_RECONNECT_DELAY)
|
||||
@@ -567,7 +567,7 @@ def prepTimeStamp(datetime_str):
|
||||
except ValueError:
|
||||
mylog('verbose', [f"[{pluginName}] Timestamp conversion failed of string '{datetime_str}'"])
|
||||
# Use the current time if the input format is invalid
|
||||
parsed_datetime = timeNowTZ() # Assuming this function returns the current time with timezone
|
||||
parsed_datetime = datetime.now(conf.tz)
|
||||
|
||||
# Convert to the required format with 'T' between date and time and ensure the timezone is included
|
||||
return parsed_datetime.isoformat() # This will include the timezone offset
|
||||
|
||||
@@ -2,24 +2,21 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
import json
|
||||
import subprocess
|
||||
import argparse
|
||||
import os
|
||||
import pathlib
|
||||
import sys
|
||||
import requests
|
||||
from datetime import datetime
|
||||
from base64 import b64encode
|
||||
|
||||
# Register NetAlertX directories
|
||||
INSTALL_PATH="/app"
|
||||
INSTALL_PATH = os.getenv('NETALERTX_APP', '/app')
|
||||
sys.path.extend([f"{INSTALL_PATH}/front/plugins", f"{INSTALL_PATH}/server"])
|
||||
|
||||
import conf
|
||||
from const import confFileName, logPath
|
||||
from plugin_helper import Plugin_Objects, handleEmpty
|
||||
from logger import mylog, Logger, append_line_to_file
|
||||
from helper import timeNowTZ, get_setting_value
|
||||
from utils.datetime_utils import timeNowDB
|
||||
from logger import mylog, Logger
|
||||
from helper import get_setting_value
|
||||
from models.notification_instance import NotificationInstance
|
||||
from database import DB
|
||||
from pytz import timezone
|
||||
@@ -63,12 +60,12 @@ def main():
|
||||
for notification in new_notifications:
|
||||
|
||||
# Send notification
|
||||
response_text, response_status_code = send(notification["HTML"], notification["Text"])
|
||||
response_text, response_status_code = send(notification["HTML"], notification["Text"])
|
||||
|
||||
# Log result
|
||||
plugin_objects.add_object(
|
||||
primaryId = pluginName,
|
||||
secondaryId = timeNowTZ(),
|
||||
secondaryId = timeNowDB(),
|
||||
watched1 = notification["GUID"],
|
||||
watched2 = handleEmpty(response_text),
|
||||
watched3 = response_status_code,
|
||||
|
||||
@@ -1,33 +1,34 @@
|
||||
#!/usr/bin/env python3
|
||||
import conf
|
||||
from const import confFileName, logPath
|
||||
from pytz import timezone
|
||||
|
||||
import os
|
||||
import pathlib
|
||||
import sys
|
||||
import json
|
||||
import requests
|
||||
|
||||
# Register NetAlertX directories
|
||||
INSTALL_PATH="/app"
|
||||
INSTALL_PATH = os.getenv("NETALERTX_APP", "/app")
|
||||
sys.path.extend([f"{INSTALL_PATH}/front/plugins", f"{INSTALL_PATH}/server"])
|
||||
|
||||
from plugin_helper import Plugin_Objects, handleEmpty # noqa: E402
|
||||
from logger import mylog, Logger # noqa: E402
|
||||
from helper import timeNowTZ, get_setting_value, hide_string # noqa: E402
|
||||
from helper import get_setting_value, hide_string # noqa: E402
|
||||
from utils.datetime_utils import timeNowDB
|
||||
from models.notification_instance import NotificationInstance # noqa: E402
|
||||
from database import DB # noqa: E402
|
||||
import conf
|
||||
from const import confFileName, logPath
|
||||
from pytz import timezone
|
||||
|
||||
# Make sure the TIMEZONE for logging is correct
|
||||
conf.tz = timezone(get_setting_value('TIMEZONE'))
|
||||
conf.tz = timezone(get_setting_value("TIMEZONE"))
|
||||
|
||||
# Make sure log level is initialized correctly
|
||||
Logger(get_setting_value('LOG_LEVEL'))
|
||||
Logger(get_setting_value("LOG_LEVEL"))
|
||||
|
||||
pluginName = "PUSHOVER"
|
||||
|
||||
LOG_PATH = logPath + '/plugins'
|
||||
RESULT_FILE = os.path.join(LOG_PATH, f'last_result.{pluginName}.log')
|
||||
LOG_PATH = logPath + "/plugins"
|
||||
RESULT_FILE = os.path.join(LOG_PATH, f"last_result.{pluginName}.log")
|
||||
|
||||
|
||||
def main():
|
||||
@@ -63,7 +64,7 @@ def main():
|
||||
# Log result
|
||||
plugin_objects.add_object(
|
||||
primaryId=pluginName,
|
||||
secondaryId=timeNowTZ(),
|
||||
secondaryId=timeNowDB(),
|
||||
watched1=notification["GUID"],
|
||||
watched2=handleEmpty(response_text),
|
||||
watched3=response_status_code,
|
||||
|
||||
@@ -2,24 +2,20 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
import json
|
||||
import subprocess
|
||||
import argparse
|
||||
import os
|
||||
import pathlib
|
||||
import sys
|
||||
import requests
|
||||
from datetime import datetime
|
||||
from base64 import b64encode
|
||||
|
||||
# Register NetAlertX directories
|
||||
INSTALL_PATH="/app"
|
||||
INSTALL_PATH = os.getenv('NETALERTX_APP', '/app')
|
||||
sys.path.extend([f"{INSTALL_PATH}/front/plugins", f"{INSTALL_PATH}/server"])
|
||||
|
||||
import conf
|
||||
from const import confFileName, logPath
|
||||
from plugin_helper import Plugin_Objects, handleEmpty
|
||||
from logger import mylog, Logger, append_line_to_file
|
||||
from helper import timeNowTZ, get_setting_value, hide_string
|
||||
from logger import mylog, Logger
|
||||
from helper import get_setting_value, hide_string
|
||||
from utils.datetime_utils import timeNowDB
|
||||
from models.notification_instance import NotificationInstance
|
||||
from database import DB
|
||||
from pytz import timezone
|
||||
@@ -68,7 +64,7 @@ def main():
|
||||
# Log result
|
||||
plugin_objects.add_object(
|
||||
primaryId = pluginName,
|
||||
secondaryId = timeNowTZ(),
|
||||
secondaryId = timeNowDB(),
|
||||
watched1 = notification["GUID"],
|
||||
watched2 = handleEmpty(response_text),
|
||||
watched3 = response_status_code,
|
||||
|
||||
@@ -1,22 +1,19 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
import json
|
||||
import subprocess
|
||||
import argparse
|
||||
import os
|
||||
import pathlib
|
||||
import sys
|
||||
from datetime import datetime
|
||||
|
||||
# Register NetAlertX directories
|
||||
INSTALL_PATH = "/app"
|
||||
INSTALL_PATH = os.getenv('NETALERTX_APP', '/app')
|
||||
sys.path.extend([f"{INSTALL_PATH}/front/plugins", f"{INSTALL_PATH}/server"])
|
||||
|
||||
import conf
|
||||
from const import confFileName, logPath
|
||||
from plugin_helper import Plugin_Objects
|
||||
from logger import mylog, Logger, append_line_to_file
|
||||
from helper import timeNowTZ, get_setting_value
|
||||
from utils.datetime_utils import timeNowDB
|
||||
from logger import mylog, Logger
|
||||
from helper import get_setting_value
|
||||
from models.notification_instance import NotificationInstance
|
||||
from database import DB
|
||||
from pytz import timezone
|
||||
@@ -65,7 +62,7 @@ def main():
|
||||
# Log result
|
||||
plugin_objects.add_object(
|
||||
primaryId=pluginName,
|
||||
secondaryId=timeNowTZ(),
|
||||
secondaryId=timeNowDB(),
|
||||
watched1=notification["GUID"],
|
||||
watched2=result,
|
||||
watched3='null',
|
||||
|
||||
@@ -3,26 +3,22 @@
|
||||
|
||||
import json
|
||||
import subprocess
|
||||
import argparse
|
||||
import os
|
||||
import pathlib
|
||||
import sys
|
||||
import requests
|
||||
from datetime import datetime
|
||||
from base64 import b64encode
|
||||
import hashlib
|
||||
import hmac
|
||||
|
||||
# Register NetAlertX directories
|
||||
INSTALL_PATH="/app"
|
||||
INSTALL_PATH = os.getenv('NETALERTX_APP', '/app')
|
||||
sys.path.extend([f"{INSTALL_PATH}/front/plugins", f"{INSTALL_PATH}/server"])
|
||||
|
||||
|
||||
import conf
|
||||
from const import logPath, confFileName
|
||||
from plugin_helper import Plugin_Objects, handleEmpty
|
||||
from logger import mylog, Logger, append_line_to_file
|
||||
from helper import timeNowTZ, get_setting_value, hide_string, write_file
|
||||
from utils.datetime_utils import timeNowDB
|
||||
from logger import mylog, Logger
|
||||
from helper import get_setting_value, write_file
|
||||
from models.notification_instance import NotificationInstance
|
||||
from database import DB
|
||||
from pytz import timezone
|
||||
@@ -71,7 +67,7 @@ def main():
|
||||
# Log result
|
||||
plugin_objects.add_object(
|
||||
primaryId = pluginName,
|
||||
secondaryId = timeNowTZ(),
|
||||
secondaryId = timeNowDB(),
|
||||
watched1 = notification["GUID"],
|
||||
watched2 = handleEmpty(response_stdout),
|
||||
watched3 = handleEmpty(response_stderr),
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
import os
|
||||
import time
|
||||
import pathlib
|
||||
@@ -8,93 +7,89 @@ import sys
|
||||
import re
|
||||
import base64
|
||||
import subprocess
|
||||
from time import strftime
|
||||
|
||||
# Register NetAlertX directories
|
||||
INSTALL_PATH="/app"
|
||||
sys.path.extend([f"{INSTALL_PATH}/front/plugins", f"{INSTALL_PATH}/server"])
|
||||
|
||||
from database import DB
|
||||
from plugin_helper import Plugin_Object, Plugin_Objects, handleEmpty
|
||||
from plugin_helper import Plugin_Objects, handleEmpty
|
||||
from logger import mylog, Logger, append_line_to_file
|
||||
from helper import timeNowTZ, get_setting_value
|
||||
from helper import get_setting_value
|
||||
from const import logPath, applicationPath
|
||||
import conf
|
||||
from pytz import timezone
|
||||
|
||||
# Make sure the TIMEZONE for logging is correct
|
||||
conf.tz = timezone(get_setting_value('TIMEZONE'))
|
||||
conf.tz = timezone(get_setting_value("TIMEZONE"))
|
||||
|
||||
# Make sure log level is initialized correctly
|
||||
Logger(get_setting_value('LOG_LEVEL'))
|
||||
Logger(get_setting_value("LOG_LEVEL"))
|
||||
|
||||
pluginName = 'ARPSCAN'
|
||||
|
||||
LOG_PATH = logPath + '/plugins'
|
||||
LOG_FILE = os.path.join(LOG_PATH, f'script.{pluginName}.log')
|
||||
RESULT_FILE = os.path.join(LOG_PATH, f'last_result.{pluginName}.log')
|
||||
pluginName = "ARPSCAN"
|
||||
|
||||
LOG_PATH = logPath + "/plugins"
|
||||
LOG_FILE = os.path.join(LOG_PATH, f"script.{pluginName}.log")
|
||||
RESULT_FILE = os.path.join(LOG_PATH, f"last_result.{pluginName}.log")
|
||||
|
||||
|
||||
def main():
|
||||
|
||||
parser = argparse.ArgumentParser(description='Import devices from settings')
|
||||
parser.add_argument('userSubnets', nargs='+', help="list of subnets with options")
|
||||
values = parser.parse_args()
|
||||
parser = argparse.ArgumentParser(description="Import devices from settings")
|
||||
parser.add_argument("userSubnets", nargs="+", help="list of subnets with options")
|
||||
values = parser.parse_args()
|
||||
|
||||
# Assuming Plugin_Objects is a class or function that reads data from the RESULT_FILE
|
||||
# and returns a list of objects called 'devices'.
|
||||
plugin_objects = Plugin_Objects(RESULT_FILE)
|
||||
|
||||
# Print a message to indicate that the script is starting.
|
||||
mylog('verbose', [f'[{pluginName}] In script '])
|
||||
mylog("verbose", [f"[{pluginName}] In script "])
|
||||
|
||||
# holds a list of user-submitted subnets.
|
||||
# mylog('verbose', ['[ARP Scan] values.userSubnets: ', values.userSubnets])
|
||||
|
||||
# holds a list of user-submitted subnets.
|
||||
# mylog('verbose', ['[ARP Scan] values.userSubnets: ', values.userSubnets])
|
||||
|
||||
# Extract the base64-encoded subnet information from the first element of the userSubnets list.
|
||||
# The format of the element is assumed to be like 'userSubnets=b<base64-encoded-data>'.
|
||||
userSubnetsParamBase64 = values.userSubnets[0].split('userSubnets=b')[1]
|
||||
# The format of the element is assumed to be like 'userSubnets=<base64-encoded-data>'.
|
||||
userSubnetsParamBase64 = values.userSubnets[0].split("userSubnets=")[1]
|
||||
|
||||
# Printing the extracted base64-encoded subnet information.
|
||||
# mylog('verbose', ['[ARP Scan] userSubnetsParamBase64: ', userSubnetsParamBase64])
|
||||
|
||||
# mylog('verbose', ['[ARP Scan] userSubnetsParamBase64: ', userSubnetsParamBase64])
|
||||
|
||||
# Decode the base64-encoded subnet information to get the actual subnet information in ASCII format.
|
||||
userSubnetsParam = base64.b64decode(userSubnetsParamBase64).decode('ascii')
|
||||
userSubnetsParam = base64.b64decode(userSubnetsParamBase64).decode("ascii")
|
||||
|
||||
# Print the decoded subnet information.
|
||||
mylog('verbose', [f'[{pluginName}] userSubnetsParam: ', userSubnetsParam])
|
||||
mylog("verbose", [f"[{pluginName}] userSubnetsParam: ", userSubnetsParam])
|
||||
|
||||
# Check if the decoded subnet information contains multiple subnets separated by commas.
|
||||
# If it does, split the string into a list of individual subnets.
|
||||
# Otherwise, create a list with a single element containing the subnet information.
|
||||
if ',' in userSubnetsParam:
|
||||
subnets_list = userSubnetsParam.split(',')
|
||||
if "," in userSubnetsParam:
|
||||
subnets_list = userSubnetsParam.split(",")
|
||||
else:
|
||||
subnets_list = [userSubnetsParam]
|
||||
|
||||
|
||||
# Create a database connection
|
||||
db = DB() # instance of class DB
|
||||
db.open()
|
||||
|
||||
|
||||
# Execute the ARP scanning process on the list of subnets (whether it's one or multiple subnets).
|
||||
# The function 'execute_arpscan' is assumed to be defined elsewhere in the code.
|
||||
unique_devices = execute_arpscan(subnets_list)
|
||||
|
||||
|
||||
for device in unique_devices:
|
||||
plugin_objects.add_object(
|
||||
primaryId = handleEmpty(device['mac']), # MAC (Device Name)
|
||||
secondaryId = handleEmpty(device['ip']), # IP Address
|
||||
watched1 = handleEmpty(device['ip']), # Device Name
|
||||
watched2 = handleEmpty(device.get('hw', '')), # Vendor (assuming it's in the 'hw' field)
|
||||
watched3 = handleEmpty(device.get('interface', '')), # Add the interface
|
||||
watched4 = '',
|
||||
extra = pluginName,
|
||||
foreignKey = "")
|
||||
primaryId=handleEmpty(device["mac"]), # MAC (Device Name)
|
||||
secondaryId=handleEmpty(device["ip"]), # IP Address
|
||||
watched1=handleEmpty(device["ip"]), # Device Name
|
||||
watched2=handleEmpty(
|
||||
device.get("hw", "")
|
||||
), # Vendor (assuming it's in the 'hw' field)
|
||||
watched3=handleEmpty(device.get("interface", "")), # Add the interface
|
||||
watched4="",
|
||||
extra=pluginName,
|
||||
foreignKey="",
|
||||
)
|
||||
|
||||
plugin_objects.write_result_file()
|
||||
|
||||
@@ -107,17 +102,19 @@ def execute_arpscan(userSubnets):
|
||||
devices_list = []
|
||||
|
||||
# scan each interface
|
||||
|
||||
for interface in userSubnets :
|
||||
|
||||
arpscan_output = execute_arpscan_on_interface (interface)
|
||||
for interface in userSubnets:
|
||||
|
||||
arpscan_output = execute_arpscan_on_interface(interface)
|
||||
|
||||
mylog("verbose", [f"[{pluginName}] arpscan_output: ", arpscan_output])
|
||||
|
||||
mylog('verbose', [f'[{pluginName}] arpscan_output: ', arpscan_output])
|
||||
|
||||
# Search IP + MAC + Vendor as regular expresion
|
||||
re_ip = r'(?P<ip>((2[0-5]|1[0-9]|[0-9])?[0-9]\.){3}((2[0-5]|1[0-9]|[0-9])?[0-9]))'
|
||||
re_mac = r'(?P<mac>([0-9a-fA-F]{2}[:-]){5}([0-9a-fA-F]{2}))'
|
||||
re_hw = r'(?P<hw>.*)'
|
||||
re_ip = (
|
||||
r"(?P<ip>((2[0-5]|1[0-9]|[0-9])?[0-9]\.){3}((2[0-5]|1[0-9]|[0-9])?[0-9]))"
|
||||
)
|
||||
re_mac = r"(?P<mac>([0-9a-fA-F]{2}[:-]){5}([0-9a-fA-F]{2}))"
|
||||
re_hw = r"(?P<hw>.*)"
|
||||
re_pattern = re.compile(rf"{re_ip}\s+{re_mac}\s{re_hw}")
|
||||
|
||||
devices_list_tmp = [
|
||||
@@ -127,44 +124,61 @@ def execute_arpscan(userSubnets):
|
||||
|
||||
devices_list += devices_list_tmp
|
||||
|
||||
# mylog('debug', ['[ARP Scan] Found: Devices including duplicates ', len(devices_list) ])
|
||||
|
||||
# Delete duplicate MAC
|
||||
unique_mac = []
|
||||
unique_devices = []
|
||||
# mylog('debug', ['[ARP Scan] Found: Devices including duplicates ', len(devices_list) ])
|
||||
|
||||
for device in devices_list :
|
||||
if device['mac'] not in unique_mac:
|
||||
unique_mac.append(device['mac'])
|
||||
unique_devices.append(device)
|
||||
# Delete duplicate MAC
|
||||
unique_mac = []
|
||||
unique_devices = []
|
||||
|
||||
for device in devices_list:
|
||||
if device["mac"] not in unique_mac:
|
||||
unique_mac.append(device["mac"])
|
||||
unique_devices.append(device)
|
||||
|
||||
# return list
|
||||
mylog('verbose', [f'[{pluginName}] All devices List len:', len(devices_list)])
|
||||
mylog('verbose', [f'[{pluginName}] Devices List:', devices_list])
|
||||
mylog("verbose", [f"[{pluginName}] All devices List len:", len(devices_list)])
|
||||
mylog("verbose", [f"[{pluginName}] Devices List:", devices_list])
|
||||
|
||||
mylog('verbose', [f'[{pluginName}] Found: Devices without duplicates ', len(unique_devices) ])
|
||||
mylog(
|
||||
"verbose",
|
||||
[f"[{pluginName}] Found: Devices without duplicates ", len(unique_devices)],
|
||||
)
|
||||
|
||||
return unique_devices
|
||||
|
||||
|
||||
def execute_arpscan_on_interface(interface):
|
||||
# Prepare command arguments
|
||||
arpscan_args = get_setting_value('ARPSCAN_ARGS').split() + interface.split()
|
||||
arpscan_args = get_setting_value("ARPSCAN_ARGS").split() + interface.split()
|
||||
|
||||
# Optional duration in seconds (0 = run once)
|
||||
try:
|
||||
scan_duration = int(get_setting_value('ARPSCAN_DURATION'))
|
||||
scan_duration = int(get_setting_value("ARPSCAN_DURATION"))
|
||||
except Exception:
|
||||
scan_duration = 0 # default: single run
|
||||
|
||||
# Get timeout from plugin settings (default 30 seconds if not set)
|
||||
try:
|
||||
timeout_seconds = int(get_setting_value("ARPSCAN_RUN_TIMEOUT"))
|
||||
except Exception:
|
||||
timeout_seconds = 30
|
||||
|
||||
results = []
|
||||
start_time = time.time()
|
||||
|
||||
while True:
|
||||
try:
|
||||
result = subprocess.check_output(arpscan_args, universal_newlines=True)
|
||||
result = subprocess.check_output(
|
||||
arpscan_args, universal_newlines=True, timeout=timeout_seconds
|
||||
)
|
||||
results.append(result)
|
||||
except subprocess.CalledProcessError as e:
|
||||
except subprocess.CalledProcessError:
|
||||
result = ""
|
||||
except subprocess.TimeoutExpired:
|
||||
mylog(
|
||||
"warning",
|
||||
[f"[{pluginName}] arp-scan timed out after {timeout_seconds}s"],
|
||||
)
|
||||
result = ""
|
||||
# stop looping if duration not set or expired
|
||||
if scan_duration == 0 or (time.time() - start_time) > scan_duration:
|
||||
@@ -175,10 +189,10 @@ def execute_arpscan_on_interface(interface):
|
||||
return "\n".join(results)
|
||||
|
||||
|
||||
# ===============================================================================
|
||||
# BEGIN
|
||||
# ===============================================================================
|
||||
|
||||
|
||||
#===============================================================================
|
||||
# BEGIN
|
||||
#===============================================================================
|
||||
if __name__ == '__main__':
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
import os
|
||||
import sys
|
||||
|
||||
INSTALL_PATH = "/app"
|
||||
INSTALL_PATH = os.getenv('NETALERTX_APP', '/app')
|
||||
sys.path.extend([f"{INSTALL_PATH}/front/plugins", f"{INSTALL_PATH}/server"])
|
||||
pluginName = "ASUSWRT"
|
||||
|
||||
@@ -15,8 +15,7 @@ from asusrouter.modules.connection import ConnectionState
|
||||
from const import logPath
|
||||
from helper import get_setting_value
|
||||
from logger import Logger, mylog
|
||||
from plugin_helper import (Plugin_Object, Plugin_Objects, decodeBase64,
|
||||
handleEmpty)
|
||||
from plugin_helper import (Plugin_Objects, handleEmpty)
|
||||
from pytz import timezone
|
||||
|
||||
conf.tz = timezone(get_setting_value("TIMEZONE"))
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
#!/usr/bin/env python3
|
||||
import os
|
||||
import sys
|
||||
import json
|
||||
import socket
|
||||
import ipaddress
|
||||
from zeroconf import Zeroconf, ServiceBrowser, ServiceInfo, InterfaceChoice, IPVersion
|
||||
from zeroconf.asyncio import AsyncZeroconf
|
||||
from zeroconf import Zeroconf
|
||||
|
||||
INSTALL_PATH = "/app"
|
||||
INSTALL_PATH = os.getenv('NETALERTX_APP', '/app')
|
||||
sys.path.extend([f"{INSTALL_PATH}/front/plugins", f"{INSTALL_PATH}/server"])
|
||||
|
||||
from plugin_helper import Plugin_Objects
|
||||
|
||||
@@ -344,7 +344,7 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"default_value": "/app/config",
|
||||
"default_value": "/data/config",
|
||||
"options": [],
|
||||
"localized": [
|
||||
"name",
|
||||
@@ -367,15 +367,15 @@
|
||||
"description": [
|
||||
{
|
||||
"language_code": "en_us",
|
||||
"string": "Where the <code>devices.csv</code> file should be saved. For example <code>/app/config</code>."
|
||||
"string": "Where the <code>devices.csv</code> file should be saved. For example <code>/data/config</code>."
|
||||
},
|
||||
{
|
||||
"language_code": "es_es",
|
||||
"string": "Donde se debe guardar el archivo <code>devices.csv</code>. Por ejemplo <code>/app/config</code>."
|
||||
"string": "Donde se debe guardar el archivo <code>devices.csv</code>. Por ejemplo <code>/data/config</code>."
|
||||
},
|
||||
{
|
||||
"language_code": "de_de",
|
||||
"string": "Wo die Datei <code>devices.csv</code> gespeichert werden soll. Zum Beispiel <code>/app/config</code>."
|
||||
"string": "Wo die Datei <code>devices.csv</code> gespeichert werden soll. Zum Beispiel <code>/data/config</code>."
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,23 +1,19 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
import os
|
||||
import pathlib
|
||||
import argparse
|
||||
import sys
|
||||
import hashlib
|
||||
import csv
|
||||
import sqlite3
|
||||
from io import StringIO
|
||||
from datetime import datetime
|
||||
|
||||
# Register NetAlertX directories
|
||||
INSTALL_PATH="/app"
|
||||
INSTALL_PATH = os.getenv('NETALERTX_APP', '/app')
|
||||
sys.path.extend([f"{INSTALL_PATH}/front/plugins", f"{INSTALL_PATH}/server"])
|
||||
|
||||
from plugin_helper import Plugin_Object, Plugin_Objects, decodeBase64
|
||||
from logger import mylog, Logger, append_line_to_file
|
||||
from helper import timeNowTZ, get_setting_value
|
||||
from const import logPath, applicationPath, fullDbPath
|
||||
from logger import mylog, Logger
|
||||
from helper import get_setting_value
|
||||
from const import logPath, fullDbPath
|
||||
import conf
|
||||
from pytz import timezone
|
||||
|
||||
|
||||
@@ -1,88 +1,110 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
import os
|
||||
import pathlib
|
||||
import argparse
|
||||
import sys
|
||||
import hashlib
|
||||
import csv
|
||||
import sqlite3
|
||||
from io import StringIO
|
||||
from datetime import datetime
|
||||
|
||||
# Register NetAlertX directories
|
||||
INSTALL_PATH="/app"
|
||||
INSTALL_PATH = os.getenv("NETALERTX_APP", "/app")
|
||||
sys.path.extend([f"{INSTALL_PATH}/front/plugins", f"{INSTALL_PATH}/server"])
|
||||
|
||||
from plugin_helper import Plugin_Object, Plugin_Objects, decodeBase64
|
||||
from logger import mylog, Logger, append_line_to_file
|
||||
from helper import timeNowTZ, get_setting_value
|
||||
from const import logPath, applicationPath, fullDbPath
|
||||
from logger import mylog, Logger
|
||||
from helper import get_setting_value
|
||||
from const import logPath, fullDbPath
|
||||
import conf
|
||||
from pytz import timezone
|
||||
|
||||
# Make sure the TIMEZONE for logging is correct
|
||||
conf.tz = timezone(get_setting_value('TIMEZONE'))
|
||||
conf.tz = timezone(get_setting_value("TIMEZONE"))
|
||||
|
||||
# Make sure log level is initialized correctly
|
||||
Logger(get_setting_value('LOG_LEVEL'))
|
||||
Logger(get_setting_value("LOG_LEVEL"))
|
||||
|
||||
pluginName = 'DBCLNP'
|
||||
|
||||
LOG_PATH = logPath + '/plugins'
|
||||
LOG_FILE = os.path.join(LOG_PATH, f'script.{pluginName}.log')
|
||||
RESULT_FILE = os.path.join(LOG_PATH, f'last_result.{pluginName}.log')
|
||||
pluginName = "DBCLNP"
|
||||
|
||||
LOG_PATH = logPath + "/plugins"
|
||||
LOG_FILE = os.path.join(LOG_PATH, f"script.{pluginName}.log")
|
||||
RESULT_FILE = os.path.join(LOG_PATH, f"last_result.{pluginName}.log")
|
||||
|
||||
|
||||
def main():
|
||||
|
||||
PLUGINS_KEEP_HIST = int(get_setting_value("PLUGINS_KEEP_HIST"))
|
||||
HRS_TO_KEEP_NEWDEV = int(get_setting_value("HRS_TO_KEEP_NEWDEV"))
|
||||
HRS_TO_KEEP_OFFDEV = int(get_setting_value("HRS_TO_KEEP_OFFDEV"))
|
||||
DAYS_TO_KEEP_EVENTS = int(get_setting_value("DAYS_TO_KEEP_EVENTS"))
|
||||
CLEAR_NEW_FLAG = get_setting_value("CLEAR_NEW_FLAG")
|
||||
|
||||
mylog('verbose', [f'[{pluginName}] In script'])
|
||||
PLUGINS_KEEP_HIST = int(get_setting_value("PLUGINS_KEEP_HIST"))
|
||||
HRS_TO_KEEP_NEWDEV = int(get_setting_value("HRS_TO_KEEP_NEWDEV"))
|
||||
HRS_TO_KEEP_OFFDEV = int(get_setting_value("HRS_TO_KEEP_OFFDEV"))
|
||||
DAYS_TO_KEEP_EVENTS = int(get_setting_value("DAYS_TO_KEEP_EVENTS"))
|
||||
CLEAR_NEW_FLAG = get_setting_value("CLEAR_NEW_FLAG")
|
||||
|
||||
mylog("verbose", [f"[{pluginName}] In script"])
|
||||
|
||||
# Execute cleanup/upkeep
|
||||
cleanup_database(
|
||||
fullDbPath,
|
||||
DAYS_TO_KEEP_EVENTS,
|
||||
HRS_TO_KEEP_NEWDEV,
|
||||
HRS_TO_KEEP_OFFDEV,
|
||||
PLUGINS_KEEP_HIST,
|
||||
CLEAR_NEW_FLAG,
|
||||
)
|
||||
|
||||
mylog("verbose", [f"[{pluginName}] Cleanup complete"])
|
||||
|
||||
# Execute cleanup/upkeep
|
||||
cleanup_database(fullDbPath, DAYS_TO_KEEP_EVENTS, HRS_TO_KEEP_NEWDEV, HRS_TO_KEEP_OFFDEV, PLUGINS_KEEP_HIST, CLEAR_NEW_FLAG)
|
||||
|
||||
mylog('verbose', [f'[{pluginName}] Cleanup complete'])
|
||||
|
||||
return 0
|
||||
|
||||
#===============================================================================
|
||||
|
||||
# ===============================================================================
|
||||
# Cleanup / upkeep database
|
||||
#===============================================================================
|
||||
def cleanup_database (dbPath, DAYS_TO_KEEP_EVENTS, HRS_TO_KEEP_NEWDEV, HRS_TO_KEEP_OFFDEV, PLUGINS_KEEP_HIST, CLEAR_NEW_FLAG):
|
||||
# ===============================================================================
|
||||
def cleanup_database(
|
||||
dbPath,
|
||||
DAYS_TO_KEEP_EVENTS,
|
||||
HRS_TO_KEEP_NEWDEV,
|
||||
HRS_TO_KEEP_OFFDEV,
|
||||
PLUGINS_KEEP_HIST,
|
||||
CLEAR_NEW_FLAG,
|
||||
):
|
||||
"""
|
||||
Cleaning out old records from the tables that don't need to keep all data.
|
||||
"""
|
||||
|
||||
mylog('verbose', [f'[{pluginName}] Upkeep Database:' ])
|
||||
mylog("verbose", [f"[{pluginName}] Upkeep Database:"])
|
||||
|
||||
# Connect to the App database
|
||||
conn = sqlite3.connect(dbPath, timeout=30)
|
||||
cursor = conn.cursor()
|
||||
conn = sqlite3.connect(dbPath, timeout=30)
|
||||
cursor = conn.cursor()
|
||||
|
||||
# -----------------------------------------------------
|
||||
# Cleanup Online History
|
||||
mylog('verbose', [f'[{pluginName}] Online_History: Delete all but keep latest 150 entries'])
|
||||
cursor.execute ("""DELETE from Online_History where "Index" not in (
|
||||
mylog(
|
||||
"verbose",
|
||||
[f"[{pluginName}] Online_History: Delete all but keep latest 150 entries"],
|
||||
)
|
||||
cursor.execute(
|
||||
"""DELETE from Online_History where "Index" not in (
|
||||
SELECT "Index" from Online_History
|
||||
order by Scan_Date desc limit 150)""")
|
||||
|
||||
order by Scan_Date desc limit 150)"""
|
||||
)
|
||||
|
||||
# -----------------------------------------------------
|
||||
# Cleanup Events
|
||||
mylog('verbose', [f'[{pluginName}] Events: Delete all older than {str(DAYS_TO_KEEP_EVENTS)} days (DAYS_TO_KEEP_EVENTS setting)'])
|
||||
cursor.execute (f"""DELETE FROM Events
|
||||
WHERE eve_DateTime <= date('now', '-{str(DAYS_TO_KEEP_EVENTS)} day')""")
|
||||
# -----------------------------------------------------
|
||||
mylog(
|
||||
"verbose",
|
||||
[
|
||||
f"[{pluginName}] Events: Delete all older than {str(DAYS_TO_KEEP_EVENTS)} days (DAYS_TO_KEEP_EVENTS setting)"
|
||||
],
|
||||
)
|
||||
cursor.execute(
|
||||
f"""DELETE FROM Events
|
||||
WHERE eve_DateTime <= date('now', '-{str(DAYS_TO_KEEP_EVENTS)} day')"""
|
||||
)
|
||||
# -----------------------------------------------------
|
||||
# Trim Plugins_History entries to less than PLUGINS_KEEP_HIST setting per unique "Plugin" column entry
|
||||
mylog('verbose', [f'[{pluginName}] Plugins_History: Trim Plugins_History entries to less than {str(PLUGINS_KEEP_HIST)} per Plugin (PLUGINS_KEEP_HIST setting)'])
|
||||
mylog(
|
||||
"verbose",
|
||||
[
|
||||
f"[{pluginName}] Plugins_History: Trim Plugins_History entries to less than {str(PLUGINS_KEEP_HIST)} per Plugin (PLUGINS_KEEP_HIST setting)"
|
||||
],
|
||||
)
|
||||
|
||||
# Build the SQL query to delete entries that exceed the limit per unique "Plugin" column entry
|
||||
delete_query = f"""DELETE FROM Plugins_History
|
||||
@@ -101,11 +123,16 @@ def cleanup_database (dbPath, DAYS_TO_KEEP_EVENTS, HRS_TO_KEEP_NEWDEV, HRS_TO_KE
|
||||
# -----------------------------------------------------
|
||||
# Trim Notifications entries to less than DBCLNP_NOTIFI_HIST setting
|
||||
|
||||
histCount = get_setting_value('DBCLNP_NOTIFI_HIST')
|
||||
histCount = get_setting_value("DBCLNP_NOTIFI_HIST")
|
||||
|
||||
mylog('verbose', [f'[{pluginName}] Plugins_History: Trim Notifications entries to less than {histCount}'])
|
||||
mylog(
|
||||
"verbose",
|
||||
[
|
||||
f"[{pluginName}] Plugins_History: Trim Notifications entries to less than {histCount}"
|
||||
],
|
||||
)
|
||||
|
||||
# Build the SQL query to delete entries
|
||||
# Build the SQL query to delete entries
|
||||
delete_query = f"""DELETE FROM Notifications
|
||||
WHERE "Index" NOT IN (
|
||||
SELECT "Index"
|
||||
@@ -119,14 +146,13 @@ def cleanup_database (dbPath, DAYS_TO_KEEP_EVENTS, HRS_TO_KEEP_NEWDEV, HRS_TO_KE
|
||||
|
||||
cursor.execute(delete_query)
|
||||
|
||||
|
||||
# -----------------------------------------------------
|
||||
# Trim Workflow entries to less than WORKFLOWS_AppEvents_hist setting
|
||||
histCount = get_setting_value('WORKFLOWS_AppEvents_hist')
|
||||
histCount = get_setting_value("WORKFLOWS_AppEvents_hist")
|
||||
|
||||
mylog('verbose', [f'[{pluginName}] Trim AppEvents to less than {histCount}'])
|
||||
mylog("verbose", [f"[{pluginName}] Trim AppEvents to less than {histCount}"])
|
||||
|
||||
# Build the SQL query to delete entries
|
||||
# Build the SQL query to delete entries
|
||||
delete_query = f"""DELETE FROM AppEvents
|
||||
WHERE "Index" NOT IN (
|
||||
SELECT "Index"
|
||||
@@ -141,38 +167,52 @@ def cleanup_database (dbPath, DAYS_TO_KEEP_EVENTS, HRS_TO_KEEP_NEWDEV, HRS_TO_KE
|
||||
cursor.execute(delete_query)
|
||||
conn.commit()
|
||||
|
||||
|
||||
# -----------------------------------------------------
|
||||
# Cleanup New Devices
|
||||
if HRS_TO_KEEP_NEWDEV != 0:
|
||||
mylog('verbose', [f'[{pluginName}] Devices: Delete all New Devices older than {str(HRS_TO_KEEP_NEWDEV)} hours (HRS_TO_KEEP_NEWDEV setting)'])
|
||||
mylog(
|
||||
"verbose",
|
||||
[
|
||||
f"[{pluginName}] Devices: Delete all New Devices older than {str(HRS_TO_KEEP_NEWDEV)} hours (HRS_TO_KEEP_NEWDEV setting)"
|
||||
],
|
||||
)
|
||||
query = f"""DELETE FROM Devices WHERE devIsNew = 1 AND devFirstConnection < date('now', '-{str(HRS_TO_KEEP_NEWDEV)} hour')"""
|
||||
mylog('verbose', [f'[{pluginName}] Query: {query} '])
|
||||
cursor.execute (query)
|
||||
|
||||
mylog("verbose", [f"[{pluginName}] Query: {query} "])
|
||||
cursor.execute(query)
|
||||
|
||||
# -----------------------------------------------------
|
||||
# Cleanup Offline Devices
|
||||
if HRS_TO_KEEP_OFFDEV != 0:
|
||||
mylog('verbose', [f'[{pluginName}] Devices: Delete all New Devices older than {str(HRS_TO_KEEP_OFFDEV)} hours (HRS_TO_KEEP_OFFDEV setting)'])
|
||||
mylog(
|
||||
"verbose",
|
||||
[
|
||||
f"[{pluginName}] Devices: Delete all New Devices older than {str(HRS_TO_KEEP_OFFDEV)} hours (HRS_TO_KEEP_OFFDEV setting)"
|
||||
],
|
||||
)
|
||||
query = f"""DELETE FROM Devices WHERE devPresentLastScan = 0 AND devLastConnection < date('now', '-{str(HRS_TO_KEEP_OFFDEV)} hour')"""
|
||||
mylog('verbose', [f'[{pluginName}] Query: {query} '])
|
||||
cursor.execute (query)
|
||||
mylog("verbose", [f"[{pluginName}] Query: {query} "])
|
||||
cursor.execute(query)
|
||||
|
||||
# -----------------------------------------------------
|
||||
# Clear New Flag
|
||||
if CLEAR_NEW_FLAG != 0:
|
||||
mylog('verbose', [f'[{pluginName}] Devices: Clear "New Device" flag for all devices older than {str(CLEAR_NEW_FLAG)} hours (CLEAR_NEW_FLAG setting)'])
|
||||
mylog(
|
||||
"verbose",
|
||||
[
|
||||
f'[{pluginName}] Devices: Clear "New Device" flag for all devices older than {str(CLEAR_NEW_FLAG)} hours (CLEAR_NEW_FLAG setting)'
|
||||
],
|
||||
)
|
||||
query = f"""UPDATE Devices SET devIsNew = 0 WHERE devIsNew = 1 AND date(devFirstConnection, '+{str(CLEAR_NEW_FLAG)} hour') < date('now')"""
|
||||
# select * from Devices where devIsNew = 1 AND date(devFirstConnection, '+3 hour' ) < date('now')
|
||||
mylog('verbose', [f'[{pluginName}] Query: {query} '])
|
||||
# select * from Devices where devIsNew = 1 AND date(devFirstConnection, '+3 hour' ) < date('now')
|
||||
mylog("verbose", [f"[{pluginName}] Query: {query} "])
|
||||
cursor.execute(query)
|
||||
|
||||
|
||||
# -----------------------------------------------------
|
||||
# De-dupe (de-duplicate) from the Plugins_Objects table
|
||||
# TODO This shouldn't be necessary - probably a concurrency bug somewhere in the code :(
|
||||
mylog('verbose', [f'[{pluginName}] Plugins_Objects: Delete all duplicates'])
|
||||
cursor.execute("""
|
||||
# De-dupe (de-duplicate) from the Plugins_Objects table
|
||||
# TODO This shouldn't be necessary - probably a concurrency bug somewhere in the code :(
|
||||
mylog("verbose", [f"[{pluginName}] Plugins_Objects: Delete all duplicates"])
|
||||
cursor.execute(
|
||||
"""
|
||||
DELETE FROM Plugins_Objects
|
||||
WHERE rowid > (
|
||||
SELECT MIN(rowid) FROM Plugins_Objects p2
|
||||
@@ -181,8 +221,8 @@ def cleanup_database (dbPath, DAYS_TO_KEEP_EVENTS, HRS_TO_KEEP_NEWDEV, HRS_TO_KE
|
||||
AND Plugins_Objects.Object_SecondaryID = p2.Object_SecondaryID
|
||||
AND Plugins_Objects.UserData = p2.UserData
|
||||
)
|
||||
""")
|
||||
|
||||
"""
|
||||
)
|
||||
|
||||
conn.commit()
|
||||
|
||||
@@ -190,18 +230,18 @@ def cleanup_database (dbPath, DAYS_TO_KEEP_EVENTS, HRS_TO_KEEP_NEWDEV, HRS_TO_KE
|
||||
cursor.execute("PRAGMA wal_checkpoint(TRUNCATE);")
|
||||
cursor.execute("PRAGMA wal_checkpoint(FULL);")
|
||||
|
||||
mylog('verbose', [f'[{pluginName}] WAL checkpoint executed to truncate file.'])
|
||||
mylog("verbose", [f"[{pluginName}] WAL checkpoint executed to truncate file."])
|
||||
|
||||
# Shrink DB
|
||||
mylog('verbose', [f'[{pluginName}] Shrink Database'])
|
||||
cursor.execute ("VACUUM;")
|
||||
mylog("verbose", [f"[{pluginName}] Shrink Database"])
|
||||
cursor.execute("VACUUM;")
|
||||
|
||||
# Close the database connection
|
||||
conn.close()
|
||||
|
||||
conn.close()
|
||||
|
||||
#===============================================================================
|
||||
|
||||
# ===============================================================================
|
||||
# BEGIN
|
||||
#===============================================================================
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
# ===============================================================================
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
@@ -1,26 +1,17 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
import os
|
||||
import pathlib
|
||||
import argparse
|
||||
import sys
|
||||
import hashlib
|
||||
import csv
|
||||
import subprocess
|
||||
import re
|
||||
import base64
|
||||
import sqlite3
|
||||
from io import StringIO
|
||||
from datetime import datetime
|
||||
|
||||
# Register NetAlertX directories
|
||||
INSTALL_PATH="/app"
|
||||
INSTALL_PATH = os.getenv('NETALERTX_APP', '/app')
|
||||
sys.path.extend([f"{INSTALL_PATH}/front/plugins", f"{INSTALL_PATH}/server"])
|
||||
|
||||
from plugin_helper import Plugin_Object, Plugin_Objects, decodeBase64
|
||||
from logger import mylog, Logger, append_line_to_file
|
||||
from helper import timeNowTZ, get_setting_value, check_IP_format
|
||||
from const import logPath, applicationPath, fullDbPath
|
||||
from logger import mylog, Logger
|
||||
from helper import get_setting_value, check_IP_format
|
||||
from const import logPath
|
||||
import conf
|
||||
from pytz import timezone
|
||||
|
||||
|
||||
@@ -1,21 +1,19 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import pathlib
|
||||
import subprocess
|
||||
import argparse
|
||||
import os
|
||||
import sys
|
||||
import chardet
|
||||
|
||||
# Register NetAlertX directories
|
||||
INSTALL_PATH="/app"
|
||||
INSTALL_PATH = os.getenv('NETALERTX_APP', '/app')
|
||||
sys.path.extend([f"{INSTALL_PATH}/front/plugins", f"{INSTALL_PATH}/server"])
|
||||
|
||||
from plugin_helper import Plugin_Object, Plugin_Objects, handleEmpty, is_mac
|
||||
from plugin_helper import Plugin_Objects, handleEmpty, is_mac
|
||||
from logger import mylog, Logger
|
||||
from dhcp_leases import DhcpLeases
|
||||
from helper import timeNowTZ, get_setting_value
|
||||
from helper import get_setting_value
|
||||
import conf
|
||||
from const import logPath
|
||||
from pytz import timezone
|
||||
|
||||
@@ -8,12 +8,12 @@ from datetime import datetime
|
||||
import sys
|
||||
|
||||
# Register NetAlertX directories
|
||||
INSTALL_PATH="/app"
|
||||
INSTALL_PATH = os.getenv('NETALERTX_APP', '/app')
|
||||
sys.path.extend([f"{INSTALL_PATH}/front/plugins", f"{INSTALL_PATH}/server"])
|
||||
|
||||
from plugin_helper import Plugin_Objects, Plugin_Object
|
||||
from logger import mylog, Logger
|
||||
from helper import timeNowTZ, get_setting_value
|
||||
from helper import get_setting_value
|
||||
import conf
|
||||
from pytz import timezone
|
||||
from const import logPath
|
||||
@@ -44,8 +44,11 @@ def main():
|
||||
nmapArgs = ['sudo', 'nmap', '--privileged', '--script', 'broadcast-dhcp-discover']
|
||||
|
||||
try:
|
||||
# Number of DHCP discovery probes to send
|
||||
dhcp_probes = 1
|
||||
newLines = [datetime.now().strftime("%Y-%m-%d %H:%M:%S")]
|
||||
|
||||
# Initialize a list to store output lines from the scan
|
||||
newLines = []
|
||||
|
||||
for _ in range(dhcp_probes):
|
||||
output = subprocess.check_output(nmapArgs, universal_newlines=True, stderr=subprocess.STDOUT, timeout=timeoutSec)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user