mirror of
https://github.com/jokob-sk/NetAlertX.git
synced 2025-12-07 09:36:05 -08:00
Compare commits
22 Commits
pr-1279
...
b6567ab5fc
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b6567ab5fc | ||
|
|
f71c2fbe94 | ||
|
|
aeb03f50ba | ||
|
|
734db423ee | ||
|
|
4f47dbfe14 | ||
|
|
d23bf45310 | ||
|
|
9c366881f1 | ||
|
|
9dd482618b | ||
|
|
84cc01566d | ||
|
|
ac7b912b45 | ||
|
|
62852f1b2f | ||
|
|
b659a0f06d | ||
|
|
fb3620a378 | ||
|
|
9d56e13818 | ||
|
|
43c5a11271 | ||
|
|
ac957ce599 | ||
|
|
3567906fcd | ||
|
|
be6801d98f | ||
|
|
bb9b242d0a | ||
|
|
5f27d3b9aa | ||
|
|
93af0e9d19 | ||
|
|
398e2a896f |
@@ -80,8 +80,9 @@ ENV SYSTEM_SERVICES=/services
|
|||||||
ENV SYSTEM_SERVICES_SCRIPTS=${SYSTEM_SERVICES}/scripts
|
ENV SYSTEM_SERVICES_SCRIPTS=${SYSTEM_SERVICES}/scripts
|
||||||
ENV SYSTEM_SERVICES_CONFIG=${SYSTEM_SERVICES}/config
|
ENV SYSTEM_SERVICES_CONFIG=${SYSTEM_SERVICES}/config
|
||||||
ENV SYSTEM_NGINX_CONFIG=${SYSTEM_SERVICES_CONFIG}/nginx
|
ENV SYSTEM_NGINX_CONFIG=${SYSTEM_SERVICES_CONFIG}/nginx
|
||||||
ENV SYSTEM_NGINX_CONFIG_FILE=${SYSTEM_NGINX_CONFIG}/nginx.conf
|
ENV SYSTEM_NGINX_CONFIG_TEMPLATE=${SYSTEM_NGINX_CONFIG}/netalertx.conf.template
|
||||||
ENV SYSTEM_SERVICES_ACTIVE_CONFIG=/tmp/nginx/active-config
|
ENV SYSTEM_SERVICES_ACTIVE_CONFIG=/tmp/nginx/active-config
|
||||||
|
ENV SYSTEM_SERVICES_ACTIVE_CONFIG_FILE=${SYSTEM_SERVICES_ACTIVE_CONFIG}/nginx.conf
|
||||||
ENV SYSTEM_SERVICES_PHP_FOLDER=${SYSTEM_SERVICES_CONFIG}/php
|
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_PHP_FPM_D=${SYSTEM_SERVICES_PHP_FOLDER}/php-fpm.d
|
||||||
ENV SYSTEM_SERVICES_CROND=${SYSTEM_SERVICES_CONFIG}/crond
|
ENV SYSTEM_SERVICES_CROND=${SYSTEM_SERVICES_CONFIG}/crond
|
||||||
@@ -138,6 +139,9 @@ RUN install -d -o ${NETALERTX_USER} -g ${NETALERTX_GROUP} -m 700 ${READ_WRITE_FO
|
|||||||
sh -c "find ${NETALERTX_APP} -type f \( -name '*.sh' -o -name 'speedtest-cli' \) \
|
sh -c "find ${NETALERTX_APP} -type f \( -name '*.sh' -o -name 'speedtest-cli' \) \
|
||||||
-exec chmod 750 {} \;"
|
-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 the virtualenv from the builder stage
|
||||||
COPY --from=builder --chown=20212:20212 ${VIRTUAL_ENV} ${VIRTUAL_ENV}
|
COPY --from=builder --chown=20212:20212 ${VIRTUAL_ENV} ${VIRTUAL_ENV}
|
||||||
|
|
||||||
|
|||||||
@@ -1,118 +0,0 @@
|
|||||||
# DO NOT MODIFY THIS FILE DIRECTLY. IT IS AUTO-GENERATED BY .devcontainer/scripts/generate-configs.sh
|
|
||||||
# Generated from: install/production-filesystem/services/config/nginx/netalertx.conf.template
|
|
||||||
|
|
||||||
# Set number of worker processes automatically based on number of CPU cores.
|
|
||||||
worker_processes auto;
|
|
||||||
|
|
||||||
# Enables the use of JIT for regular expressions to speed-up their processing.
|
|
||||||
pcre_jit on;
|
|
||||||
|
|
||||||
# Configures default error logger.
|
|
||||||
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
|
|
||||||
# a worker process.
|
|
||||||
worker_connections 1024;
|
|
||||||
}
|
|
||||||
|
|
||||||
http {
|
|
||||||
# Mapping of temp paths for various nginx modules.
|
|
||||||
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.
|
|
||||||
include /services/config/nginx/mime.types;
|
|
||||||
default_type application/octet-stream;
|
|
||||||
|
|
||||||
# Name servers used to resolve names of upstream servers into addresses.
|
|
||||||
# It's also needed when using tcpsocket and udpsocket in Lua modules.
|
|
||||||
#resolver 1.1.1.1 1.0.0.1 [2606:4700:4700::1111] [2606:4700:4700::1001];
|
|
||||||
|
|
||||||
# Don't tell nginx version to the clients. Default is 'on'.
|
|
||||||
server_tokens off;
|
|
||||||
|
|
||||||
# Specifies the maximum accepted body size of a client request, as
|
|
||||||
# indicated by the request header Content-Length. If the stated content
|
|
||||||
# length is greater than this size, then the client receives the HTTP
|
|
||||||
# error code 413. Set to 0 to disable. Default is '1m'.
|
|
||||||
client_max_body_size 1m;
|
|
||||||
|
|
||||||
# Sendfile copies data between one FD and other from within the kernel,
|
|
||||||
# which is more efficient than read() + write(). Default is off.
|
|
||||||
sendfile on;
|
|
||||||
|
|
||||||
# Causes nginx to attempt to send its HTTP response head in one packet,
|
|
||||||
# instead of using partial frames. Default is 'off'.
|
|
||||||
tcp_nopush on;
|
|
||||||
|
|
||||||
|
|
||||||
# Enables the specified protocols. Default is TLSv1 TLSv1.1 TLSv1.2.
|
|
||||||
# TIP: If you're not obligated to support ancient clients, remove TLSv1.1.
|
|
||||||
ssl_protocols TLSv1.2 TLSv1.3;
|
|
||||||
|
|
||||||
# Path of the file with Diffie-Hellman parameters for EDH ciphers.
|
|
||||||
# TIP: Generate with: `openssl dhparam -out /etc/ssl/nginx/dh2048.pem 2048`
|
|
||||||
#ssl_dhparam /etc/ssl/nginx/dh2048.pem;
|
|
||||||
|
|
||||||
# Specifies that our cipher suits should be preferred over client ciphers.
|
|
||||||
# Default is 'off'.
|
|
||||||
ssl_prefer_server_ciphers on;
|
|
||||||
|
|
||||||
# Enables a shared SSL cache with size that can hold around 8000 sessions.
|
|
||||||
# Default is 'none'.
|
|
||||||
ssl_session_cache shared:SSL:2m;
|
|
||||||
|
|
||||||
# Specifies a time during which a client may reuse the session parameters.
|
|
||||||
# Default is '5m'.
|
|
||||||
ssl_session_timeout 1h;
|
|
||||||
|
|
||||||
# Disable TLS session tickets (they are insecure). Default is 'on'.
|
|
||||||
ssl_session_tickets off;
|
|
||||||
|
|
||||||
|
|
||||||
# Enable gzipping of responses.
|
|
||||||
gzip on;
|
|
||||||
|
|
||||||
# Set the Vary HTTP header as defined in the RFC 2616. Default is 'off'.
|
|
||||||
gzip_vary on;
|
|
||||||
|
|
||||||
|
|
||||||
# Specifies the main log format.
|
|
||||||
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
|
|
||||||
'$status $body_bytes_sent "$http_referer" '
|
|
||||||
'"$http_user_agent" "$http_x_forwarded_for"';
|
|
||||||
|
|
||||||
# Sets the path, format, and configuration for a buffered log write.
|
|
||||||
access_log /tmp/log/nginx-access.log main;
|
|
||||||
|
|
||||||
|
|
||||||
# Virtual host config
|
|
||||||
server {
|
|
||||||
listen 0.0.0.0:20211 default_server;
|
|
||||||
large_client_header_buffers 4 16k;
|
|
||||||
root /app/front;
|
|
||||||
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:/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;
|
|
||||||
|
|
||||||
fastcgi_param PHP_VALUE "xdebug.remote_enable=1";
|
|
||||||
fastcgi_connect_timeout 75;
|
|
||||||
fastcgi_send_timeout 600;
|
|
||||||
fastcgi_read_timeout 600;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -30,33 +30,4 @@ cat "${DEVCONTAINER_DIR}/resources/devcontainer-Dockerfile" >> "$OUT_FILE"
|
|||||||
|
|
||||||
echo "Generated $OUT_FILE using root dir $ROOT_DIR" >&2
|
echo "Generated $OUT_FILE using root dir $ROOT_DIR" >&2
|
||||||
|
|
||||||
# Generate devcontainer nginx config from production template
|
|
||||||
echo "Generating devcontainer nginx config"
|
|
||||||
NGINX_TEMPLATE="${ROOT_DIR}/install/production-filesystem/services/config/nginx/netalertx.conf.template"
|
|
||||||
NGINX_OUT="${DEVCONTAINER_DIR}/resources/devcontainer-overlay/services/config/nginx/netalertx.conf.template"
|
|
||||||
|
|
||||||
# Create output directory if it doesn't exist
|
|
||||||
mkdir -p "$(dirname "$NGINX_OUT")"
|
|
||||||
|
|
||||||
# Start with header comment
|
|
||||||
cat > "$NGINX_OUT" << 'EOF'
|
|
||||||
# DO NOT MODIFY THIS FILE DIRECTLY. IT IS AUTO-GENERATED BY .devcontainer/scripts/generate-configs.sh
|
|
||||||
# Generated from: install/production-filesystem/services/config/nginx/netalertx.conf.template
|
|
||||||
|
|
||||||
EOF
|
|
||||||
|
|
||||||
# Process the template: replace listen directive and inject Xdebug params
|
|
||||||
sed 's/${LISTEN_ADDR}:${PORT}/0.0.0.0:20211/g' "$NGINX_TEMPLATE" | \
|
|
||||||
awk '
|
|
||||||
/fastcgi_param SCRIPT_NAME \$fastcgi_script_name;/ {
|
|
||||||
print $0
|
|
||||||
print ""
|
|
||||||
print " fastcgi_param PHP_VALUE \"xdebug.remote_enable=1\";"
|
|
||||||
next
|
|
||||||
}
|
|
||||||
{ print }
|
|
||||||
' >> "$NGINX_OUT"
|
|
||||||
|
|
||||||
echo "Generated $NGINX_OUT from $NGINX_TEMPLATE" >&2
|
|
||||||
|
|
||||||
echo "Done."
|
echo "Done."
|
||||||
@@ -50,9 +50,6 @@ 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 rm -rf /entrypoint.d
|
||||||
sudo ln -s "${SOURCE_DIR}/install/production-filesystem/entrypoint.d" /entrypoint.d
|
sudo ln -s "${SOURCE_DIR}/install/production-filesystem/entrypoint.d" /entrypoint.d
|
||||||
|
|
||||||
@@ -67,6 +64,7 @@ for dir in \
|
|||||||
"${SYSTEM_SERVICES_RUN_LOG}" \
|
"${SYSTEM_SERVICES_RUN_LOG}" \
|
||||||
"${SYSTEM_SERVICES_ACTIVE_CONFIG}" \
|
"${SYSTEM_SERVICES_ACTIVE_CONFIG}" \
|
||||||
"${NETALERTX_PLUGINS_LOG}" \
|
"${NETALERTX_PLUGINS_LOG}" \
|
||||||
|
"${SYSTEM_SERVICES_RUN_TMP}" \
|
||||||
"/tmp/nginx/client_body" \
|
"/tmp/nginx/client_body" \
|
||||||
"/tmp/nginx/proxy" \
|
"/tmp/nginx/proxy" \
|
||||||
"/tmp/nginx/fastcgi" \
|
"/tmp/nginx/fastcgi" \
|
||||||
@@ -75,9 +73,6 @@ for dir in \
|
|||||||
sudo install -d -m 777 "${dir}"
|
sudo install -d -m 777 "${dir}"
|
||||||
done
|
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
|
for var in "${LOG_FILES[@]}"; do
|
||||||
path=${!var}
|
path=${!var}
|
||||||
|
|||||||
4
.github/workflows/docker_dev.yml
vendored
4
.github/workflows/docker_dev.yml
vendored
@@ -3,12 +3,12 @@ name: docker
|
|||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- next_release
|
- main
|
||||||
tags:
|
tags:
|
||||||
- '*.*.*'
|
- '*.*.*'
|
||||||
pull_request:
|
pull_request:
|
||||||
branches:
|
branches:
|
||||||
- next_release
|
- main
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
docker_dev:
|
docker_dev:
|
||||||
|
|||||||
@@ -77,8 +77,9 @@ ENV SYSTEM_SERVICES=/services
|
|||||||
ENV SYSTEM_SERVICES_SCRIPTS=${SYSTEM_SERVICES}/scripts
|
ENV SYSTEM_SERVICES_SCRIPTS=${SYSTEM_SERVICES}/scripts
|
||||||
ENV SYSTEM_SERVICES_CONFIG=${SYSTEM_SERVICES}/config
|
ENV SYSTEM_SERVICES_CONFIG=${SYSTEM_SERVICES}/config
|
||||||
ENV SYSTEM_NGINX_CONFIG=${SYSTEM_SERVICES_CONFIG}/nginx
|
ENV SYSTEM_NGINX_CONFIG=${SYSTEM_SERVICES_CONFIG}/nginx
|
||||||
ENV SYSTEM_NGINX_CONFIG_FILE=${SYSTEM_NGINX_CONFIG}/nginx.conf
|
ENV SYSTEM_NGINX_CONFIG_TEMPLATE=${SYSTEM_NGINX_CONFIG}/netalertx.conf.template
|
||||||
ENV SYSTEM_SERVICES_ACTIVE_CONFIG=/tmp/nginx/active-config
|
ENV SYSTEM_SERVICES_ACTIVE_CONFIG=/tmp/nginx/active-config
|
||||||
|
ENV SYSTEM_SERVICES_ACTIVE_CONFIG_FILE=${SYSTEM_SERVICES_ACTIVE_CONFIG}/nginx.conf
|
||||||
ENV SYSTEM_SERVICES_PHP_FOLDER=${SYSTEM_SERVICES_CONFIG}/php
|
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_PHP_FPM_D=${SYSTEM_SERVICES_PHP_FOLDER}/php-fpm.d
|
||||||
ENV SYSTEM_SERVICES_CROND=${SYSTEM_SERVICES_CONFIG}/crond
|
ENV SYSTEM_SERVICES_CROND=${SYSTEM_SERVICES_CONFIG}/crond
|
||||||
|
|||||||
@@ -33,6 +33,11 @@ Get visibility of what's going on on your WIFI/LAN network and enable presence d
|
|||||||
|
|
||||||
## 🚀 Quick Start
|
## 🚀 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:
|
Start NetAlertX in seconds with Docker:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
|||||||
@@ -85,10 +85,10 @@ services:
|
|||||||
network_mode: "host"
|
network_mode: "host"
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
volumes:
|
volumes:
|
||||||
- local/path/config:/home/pi/pialert/config
|
- /local/path/config:/home/pi/pialert/config
|
||||||
- local/path/db:/home/pi/pialert/db
|
- /local/path/db:/home/pi/pialert/db
|
||||||
# (optional) useful for debugging if you have issues setting up the container
|
# (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:
|
environment:
|
||||||
- TZ=Europe/Berlin
|
- TZ=Europe/Berlin
|
||||||
- PORT=20211
|
- PORT=20211
|
||||||
@@ -104,10 +104,10 @@ services:
|
|||||||
network_mode: "host"
|
network_mode: "host"
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
volumes:
|
volumes:
|
||||||
- local/path/config:/data/config # 🆕 This has changed
|
- /local/path/config:/data/config # 🆕 This has changed
|
||||||
- local/path/db:/data/db # 🆕 This has changed
|
- /local/path/db:/data/db # 🆕 This has changed
|
||||||
# (optional) useful for debugging if you have issues setting up the container
|
# (optional) useful for debugging if you have issues setting up the container
|
||||||
- local/path/logs:/tmp/log # 🆕 This has changed
|
- /local/path/logs:/tmp/log # 🆕 This has changed
|
||||||
environment:
|
environment:
|
||||||
- TZ=Europe/Berlin
|
- TZ=Europe/Berlin
|
||||||
- PORT=20211
|
- PORT=20211
|
||||||
@@ -131,10 +131,10 @@ services:
|
|||||||
network_mode: "host"
|
network_mode: "host"
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
volumes:
|
volumes:
|
||||||
- local/path/config/pialert.conf:/home/pi/pialert/config/pialert.conf
|
- /local/path/config/pialert.conf:/home/pi/pialert/config/pialert.conf
|
||||||
- local/path/db/pialert.db:/home/pi/pialert/db/pialert.db
|
- /local/path/db/pialert.db:/home/pi/pialert/db/pialert.db
|
||||||
# (optional) useful for debugging if you have issues setting up the container
|
# (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:
|
environment:
|
||||||
- TZ=Europe/Berlin
|
- TZ=Europe/Berlin
|
||||||
- PORT=20211
|
- PORT=20211
|
||||||
@@ -150,10 +150,10 @@ services:
|
|||||||
network_mode: "host"
|
network_mode: "host"
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
volumes:
|
volumes:
|
||||||
- local/path/config/app.conf:/data/config/app.conf # 🆕 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
|
- /local/path/db/app.db:/data/db/app.db # 🆕 This has changed
|
||||||
# (optional) useful for debugging if you have issues setting up the container
|
# (optional) useful for debugging if you have issues setting up the container
|
||||||
- local/path/logs:/tmp/log # 🆕 This has changed
|
- /local/path/logs:/tmp/log # 🆕 This has changed
|
||||||
environment:
|
environment:
|
||||||
- TZ=Europe/Berlin
|
- TZ=Europe/Berlin
|
||||||
- PORT=20211
|
- PORT=20211
|
||||||
@@ -190,10 +190,10 @@ services:
|
|||||||
network_mode: "host"
|
network_mode: "host"
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
volumes:
|
volumes:
|
||||||
- local/path/config:/data/config
|
- /local/path/config:/data/config
|
||||||
- local/path/db:/data/db
|
- /local/path/db:/data/db
|
||||||
# (optional) useful for debugging if you have issues setting up the container
|
# (optional) useful for debugging if you have issues setting up the container
|
||||||
- local/path/logs:/tmp/log
|
- /local/path/logs:/tmp/log
|
||||||
environment:
|
environment:
|
||||||
- TZ=Europe/Berlin
|
- TZ=Europe/Berlin
|
||||||
- PORT=20211
|
- PORT=20211
|
||||||
@@ -207,10 +207,10 @@ services:
|
|||||||
network_mode: "host"
|
network_mode: "host"
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
volumes:
|
volumes:
|
||||||
- local/path/config:/data/config
|
- /local/path/config:/data/config
|
||||||
- local/path/db:/data/db
|
- /local/path/db:/data/db
|
||||||
# (optional) useful for debugging if you have issues setting up the container
|
# (optional) useful for debugging if you have issues setting up the container
|
||||||
- local/path/logs:/tmp/log
|
- /local/path/logs:/tmp/log
|
||||||
environment:
|
environment:
|
||||||
- TZ=Europe/Berlin
|
- TZ=Europe/Berlin
|
||||||
- PORT=20211
|
- PORT=20211
|
||||||
@@ -234,10 +234,10 @@ services:
|
|||||||
network_mode: "host"
|
network_mode: "host"
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
volumes:
|
volumes:
|
||||||
- local/path/config:/data/config
|
- /local/path/config:/data/config
|
||||||
- local/path/db:/data/db
|
- /local/path/db:/data/db
|
||||||
# (optional) useful for debugging if you have issues setting up the container
|
# (optional) useful for debugging if you have issues setting up the container
|
||||||
- local/path/logs:/tmp/log
|
- /local/path/logs:/tmp/log
|
||||||
environment:
|
environment:
|
||||||
- TZ=Europe/Berlin
|
- TZ=Europe/Berlin
|
||||||
- PORT=20211
|
- PORT=20211
|
||||||
@@ -253,11 +253,19 @@ services:
|
|||||||
|
|
||||||
```sh
|
```sh
|
||||||
docker run -it --rm --name netalertx --user "0" \
|
docker run -it --rm --name netalertx --user "0" \
|
||||||
-v local/path/config:/data/config \
|
-v /local/path/config:/data/config \
|
||||||
-v local/path/db:/data/db \
|
-v /local/path/db:/data/db \
|
||||||
ghcr.io/jokob-sk/netalertx:latest
|
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
|
7. Stop the container
|
||||||
8. Update the `docker-compose.yml` as per example below.
|
8. Update the `docker-compose.yml` as per example below.
|
||||||
|
|
||||||
@@ -273,10 +281,10 @@ services:
|
|||||||
- NET_BIND_SERVICE # 🆕 New line
|
- NET_BIND_SERVICE # 🆕 New line
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
volumes:
|
volumes:
|
||||||
- local/path/config:/data/config
|
- /local/path/config:/data/config
|
||||||
- local/path/db:/data/db
|
- /local/path/db:/data/db
|
||||||
# (optional) useful for debugging if you have issues setting up the container
|
# (optional) useful for debugging if you have issues setting up the container
|
||||||
#- local/path/logs:/tmp/log
|
#- /local/path/logs:/tmp/log
|
||||||
environment:
|
environment:
|
||||||
- TZ=Europe/Berlin
|
- TZ=Europe/Berlin
|
||||||
- PORT=20211
|
- PORT=20211
|
||||||
|
|||||||
@@ -64,6 +64,7 @@ Device-detecting plugins insert values into the `CurrentScan` database table. T
|
|||||||
| `LUCIRPC` | [luci_import](https://github.com/jokob-sk/NetAlertX/tree/main/front/plugins/luci_import/) | 🔍 | Import connected devices from OpenWRT | | |
|
| `LUCIRPC` | [luci_import](https://github.com/jokob-sk/NetAlertX/tree/main/front/plugins/luci_import/) | 🔍 | Import connected devices from OpenWRT | | |
|
||||||
| `MAINT` | [maintenance](https://github.com/jokob-sk/NetAlertX/tree/main/front/plugins/maintenance/) | ⚙ | Maintenance of logs, etc. | | |
|
| `MAINT` | [maintenance](https://github.com/jokob-sk/NetAlertX/tree/main/front/plugins/maintenance/) | ⚙ | Maintenance of logs, etc. | | |
|
||||||
| `MQTT` | [_publisher_mqtt](https://github.com/jokob-sk/NetAlertX/tree/main/front/plugins/_publisher_mqtt/) | ▶️ | MQTT for synching to Home Assistant | | |
|
| `MQTT` | [_publisher_mqtt](https://github.com/jokob-sk/NetAlertX/tree/main/front/plugins/_publisher_mqtt/) | ▶️ | MQTT for synching to Home Assistant | | |
|
||||||
|
| `MTSCAN` | [mikrotik_scan](https://github.com/jokob-sk/NetAlertX/tree/main/front/plugins/mikrotik_scan/) | 🔍 | Mikrotik device import & sync | | |
|
||||||
| `NBTSCAN` | [nbtscan_scan](https://github.com/jokob-sk/NetAlertX/tree/main/front/plugins/nbtscan_scan/) | 🆎 | Nbtscan (NetBIOS-based) name resolution | | |
|
| `NBTSCAN` | [nbtscan_scan](https://github.com/jokob-sk/NetAlertX/tree/main/front/plugins/nbtscan_scan/) | 🆎 | Nbtscan (NetBIOS-based) name resolution | | |
|
||||||
| `NEWDEV` | [newdev_template](https://github.com/jokob-sk/NetAlertX/tree/main/front/plugins/newdev_template/) | ⚙ | New device template | | Yes |
|
| `NEWDEV` | [newdev_template](https://github.com/jokob-sk/NetAlertX/tree/main/front/plugins/newdev_template/) | ⚙ | New device template | | Yes |
|
||||||
| `NMAP` | [nmap_scan](https://github.com/jokob-sk/NetAlertX/tree/main/front/plugins/nmap_scan/) | ♻ | Nmap port scanning & discovery | | |
|
| `NMAP` | [nmap_scan](https://github.com/jokob-sk/NetAlertX/tree/main/front/plugins/nmap_scan/) | ♻ | Nmap port scanning & discovery | | |
|
||||||
|
|||||||
@@ -462,10 +462,17 @@
|
|||||||
|
|
||||||
switch (orderTopologyBy[0]) {
|
switch (orderTopologyBy[0]) {
|
||||||
case "Name":
|
case "Name":
|
||||||
const nameCompare = a.devName.localeCompare(b.devName);
|
// ensuring string
|
||||||
return nameCompare !== 0 ? nameCompare : parsePort(a.devParentPort) - parsePort(b.devParentPort);
|
const nameA = (a.devName ?? "").toString();
|
||||||
|
const nameB = (b.devName ?? "").toString();
|
||||||
|
const nameCompare = nameA.localeCompare(nameB);
|
||||||
|
return nameCompare !== 0
|
||||||
|
? nameCompare
|
||||||
|
: parsePort(a.devParentPort) - parsePort(b.devParentPort);
|
||||||
|
|
||||||
case "Port":
|
case "Port":
|
||||||
return parsePort(a.devParentPort) - parsePort(b.devParentPort);
|
return parsePort(a.devParentPort) - parsePort(b.devParentPort);
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return a.rowid - b.rowid;
|
return a.rowid - b.rowid;
|
||||||
}
|
}
|
||||||
|
|||||||
2
front/php/templates/language/ar_ar.json
Executable file → Normal file
2
front/php/templates/language/ar_ar.json
Executable file → Normal file
@@ -761,4 +761,4 @@
|
|||||||
"settings_system_label": "تسمية النظام",
|
"settings_system_label": "تسمية النظام",
|
||||||
"settings_update_item_warning": "تحذير تحديث العنصر",
|
"settings_update_item_warning": "تحذير تحديث العنصر",
|
||||||
"test_event_tooltip": "تلميح اختبار الحدث"
|
"test_event_tooltip": "تلميح اختبار الحدث"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ from const import confFileName, logPath
|
|||||||
from utils.datetime_utils import timeNowDB
|
from utils.datetime_utils import timeNowDB
|
||||||
from plugin_helper import Plugin_Objects
|
from plugin_helper import Plugin_Objects
|
||||||
from logger import mylog, Logger
|
from logger import mylog, Logger
|
||||||
from helper import timeNowTZ, get_setting_value
|
from helper import get_setting_value
|
||||||
from models.notification_instance import NotificationInstance
|
from models.notification_instance import NotificationInstance
|
||||||
from database import DB
|
from database import DB
|
||||||
from pytz import timezone
|
from pytz import timezone
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ from const import confFileName, logPath
|
|||||||
from plugin_helper import Plugin_Objects
|
from plugin_helper import Plugin_Objects
|
||||||
from utils.datetime_utils import timeNowDB
|
from utils.datetime_utils import timeNowDB
|
||||||
from logger import mylog, Logger
|
from logger import mylog, Logger
|
||||||
from helper import timeNowTZ, get_setting_value, hide_email
|
from helper import get_setting_value, hide_email
|
||||||
from models.notification_instance import NotificationInstance
|
from models.notification_instance import NotificationInstance
|
||||||
from database import DB
|
from database import DB
|
||||||
from pytz import timezone
|
from pytz import timezone
|
||||||
|
|||||||
@@ -419,6 +419,41 @@
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"function": "IP_MATCH_NAME",
|
||||||
|
"type": {
|
||||||
|
"dataType": "boolean",
|
||||||
|
"elements": [
|
||||||
|
{
|
||||||
|
"elementType": "input",
|
||||||
|
"elementOptions": [
|
||||||
|
{
|
||||||
|
"type": "checkbox"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"transformers": []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"default_value": true,
|
||||||
|
"options": [],
|
||||||
|
"localized": [
|
||||||
|
"name",
|
||||||
|
"description"
|
||||||
|
],
|
||||||
|
"name": [
|
||||||
|
{
|
||||||
|
"language_code": "en_us",
|
||||||
|
"string": "Name IP match"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": [
|
||||||
|
{
|
||||||
|
"language_code": "en_us",
|
||||||
|
"string": "If checked, the application will guess the name also by IPs, not only MACs. This approach works if your IPs are mostly static."
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"function": "replace_preset_icon",
|
"function": "replace_preset_icon",
|
||||||
"type": {
|
"type": {
|
||||||
|
|||||||
@@ -44,3 +44,4 @@ More Info:
|
|||||||
|
|
||||||
Report Date: 2021-12-08 12:30
|
Report Date: 2021-12-08 12:30
|
||||||
Server: Synology-NAS
|
Server: Synology-NAS
|
||||||
|
Link: netalertx.com
|
||||||
|
|||||||
@@ -1,12 +1,3 @@
|
|||||||
<!--
|
|
||||||
#---------------------------------------------------------------------------------#
|
|
||||||
# NetAlertX #
|
|
||||||
# Open Source Network Guard / WIFI & LAN intrusion detector #
|
|
||||||
# #
|
|
||||||
# report_template.html - Back module. Template to email reporting in HTML format #
|
|
||||||
#---------------------------------------------------------------------------------#
|
|
||||||
-->
|
|
||||||
|
|
||||||
<html>
|
<html>
|
||||||
<head></head>
|
<head></head>
|
||||||
<body>
|
<body>
|
||||||
@@ -20,11 +11,11 @@
|
|||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td height=200 valign=top style="padding: 10px">
|
<td height=200 valign=top style="padding: 10px">
|
||||||
<NEW_DEVICES_TABLE>
|
NEW_DEVICES_TABLE
|
||||||
<DOWN_DEVICES_TABLE>
|
DOWN_DEVICES_TABLE
|
||||||
<DOWN_RECONNECTED_TABLE>
|
DOWN_RECONNECTED_TABLE
|
||||||
<EVENTS_TABLE>
|
EVENTS_TABLE
|
||||||
<PLUGINS_TABLE>
|
PLUGINS_TABLE
|
||||||
|
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
@@ -34,11 +25,11 @@
|
|||||||
<table width=100% bgcolor=#3c8dbc cellpadding=5px cellspacing=0 style="font-size: 10px; border-bottom-left-radius: 5px; border-bottom-right-radius: 5px;">
|
<table width=100% bgcolor=#3c8dbc cellpadding=5px cellspacing=0 style="font-size: 10px; border-bottom-left-radius: 5px; border-bottom-right-radius: 5px;">
|
||||||
<tr>
|
<tr>
|
||||||
<td width=50% style="text-align:center;color: white;" bgcolor="#3c8dbc">
|
<td width=50% style="text-align:center;color: white;" bgcolor="#3c8dbc">
|
||||||
<NEW_VERSION>
|
NEW_VERSION
|
||||||
| Sent: <REPORT_DATE>
|
| Sent: REPORT_DATE
|
||||||
| Server: <SERVER_NAME>
|
| Server: <a href="REPORT_DASHBOARD_URL" target="_blank" style="color:#ffffff;">SERVER_NAME</a>
|
||||||
| Built: <BUILD_DATE>
|
| Built: BUILD_DATE
|
||||||
| Version: <BUILD_VERSION>
|
| Version: BUILD_VERSION
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
<NEW_DEVICES_TABLE>
|
NEW_DEVICES_TABLE
|
||||||
<DOWN_DEVICES_TABLE>
|
DOWN_DEVICES_TABLE
|
||||||
<DOWN_RECONNECTED_TABLE>
|
DOWN_RECONNECTED_TABLE
|
||||||
<EVENTS_TABLE>
|
EVENTS_TABLE
|
||||||
<PLUGINS_TABLE>
|
PLUGINS_TABLE
|
||||||
|
|
||||||
Report Date: <REPORT_DATE>
|
Report Date: REPORT_DATE
|
||||||
Server: <SERVER_NAME>
|
Server: SERVER_NAME
|
||||||
<NEW_VERSION>
|
Link: REPORT_DASHBOARD_URL
|
||||||
|
NEW_VERSION
|
||||||
@@ -1,7 +1,4 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
echo "Initializing php-fpm..."
|
echo "Initializing php-fpm..."
|
||||||
# Set up PHP-FPM directories and socket configuration
|
# Set up PHP-FPM directories and socket configuration
|
||||||
install -d -o netalertx -g netalertx /services/config/run
|
|
||||||
|
|
||||||
|
|
||||||
echo "php-fpm initialized."
|
echo "php-fpm initialized."
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
/tmp/nginx/active-config
|
|
||||||
@@ -5,8 +5,6 @@ set -euo pipefail
|
|||||||
LOG_DIR=${NETALERTX_LOG}
|
LOG_DIR=${NETALERTX_LOG}
|
||||||
RUN_DIR=${SYSTEM_SERVICES_RUN}
|
RUN_DIR=${SYSTEM_SERVICES_RUN}
|
||||||
TMP_DIR=/tmp/nginx
|
TMP_DIR=/tmp/nginx
|
||||||
SYSTEM_NGINX_CONFIG_TEMPLATE="/services/config/nginx/netalertx.conf.template"
|
|
||||||
SYSTEM_NGINX_CONFIG_FILE="/services/config/nginx/conf.active/netalertx.conf"
|
|
||||||
|
|
||||||
# Create directories if they don't exist
|
# Create directories if they don't exist
|
||||||
mkdir -p "${LOG_DIR}" "${RUN_DIR}" "${TMP_DIR}"
|
mkdir -p "${LOG_DIR}" "${RUN_DIR}" "${TMP_DIR}"
|
||||||
@@ -33,9 +31,9 @@ done
|
|||||||
|
|
||||||
TEMP_CONFIG_FILE=$(mktemp "${TMP_DIR}/netalertx.conf.XXXXXX")
|
TEMP_CONFIG_FILE=$(mktemp "${TMP_DIR}/netalertx.conf.XXXXXX")
|
||||||
if envsubst '${LISTEN_ADDR} ${PORT}' < "${SYSTEM_NGINX_CONFIG_TEMPLATE}" > "${TEMP_CONFIG_FILE}" 2>/dev/null; then
|
if envsubst '${LISTEN_ADDR} ${PORT}' < "${SYSTEM_NGINX_CONFIG_TEMPLATE}" > "${TEMP_CONFIG_FILE}" 2>/dev/null; then
|
||||||
mv "${TEMP_CONFIG_FILE}" "${SYSTEM_NGINX_CONFIG_FILE}"
|
mv "${TEMP_CONFIG_FILE}" "${SYSTEM_SERVICES_ACTIVE_CONFIG_FILE}"
|
||||||
else
|
else
|
||||||
echo "Note: Unable to write to ${SYSTEM_NGINX_CONFIG_FILE}. Using default configuration."
|
echo "Note: Unable to write to ${SYSTEM_SERVICES_ACTIVE_CONFIG_FILE}. Using default configuration."
|
||||||
rm -f "${TEMP_CONFIG_FILE}"
|
rm -f "${TEMP_CONFIG_FILE}"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -49,10 +47,10 @@ chmod -R 777 "/tmp/nginx" 2>/dev/null || true
|
|||||||
|
|
||||||
# Execute nginx with overrides
|
# Execute nginx with overrides
|
||||||
# echo the full nginx command then run it
|
# echo the full nginx command then run it
|
||||||
echo "Starting /usr/sbin/nginx -p \"${RUN_DIR}/\" -c \"${SYSTEM_NGINX_CONFIG_FILE}\" -g \"error_log /dev/stderr; error_log ${NETALERTX_LOG}/nginx-error.log; daemon off;\" &"
|
echo "Starting /usr/sbin/nginx -p \"${RUN_DIR}/\" -c \"${SYSTEM_SERVICES_ACTIVE_CONFIG_FILE}\" -g \"error_log /dev/stderr; error_log ${NETALERTX_LOG}/nginx-error.log; daemon off;\" &"
|
||||||
/usr/sbin/nginx \
|
/usr/sbin/nginx \
|
||||||
-p "${RUN_DIR}/" \
|
-p "${RUN_DIR}/" \
|
||||||
-c "${SYSTEM_NGINX_CONFIG_FILE}" \
|
-c "${SYSTEM_SERVICES_ACTIVE_CONFIG_FILE}" \
|
||||||
-g "error_log /dev/stderr; error_log ${NETALERTX_LOG}/nginx-error.log; daemon off;" &
|
-g "error_log /dev/stderr; error_log ${NETALERTX_LOG}/nginx-error.log; daemon off;" &
|
||||||
nginx_pid=$!
|
nginx_pid=$!
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,11 @@ INSTALL_PATH = os.getenv("NETALERTX_APP", "/app")
|
|||||||
|
|
||||||
def handle_sync_get():
|
def handle_sync_get():
|
||||||
"""Handle GET requests for SYNC (NODE → HUB)."""
|
"""Handle GET requests for SYNC (NODE → HUB)."""
|
||||||
file_path = INSTALL_PATH + "/api/table_devices.json"
|
|
||||||
|
# get all dwevices from the api endpoint
|
||||||
|
api_path = os.environ.get('NETALERTX_API', '/tmp/api')
|
||||||
|
|
||||||
|
file_path = f"/{api_path}/table_devices.json"
|
||||||
|
|
||||||
try:
|
try:
|
||||||
with open(file_path, "rb") as f:
|
with open(file_path, "rb") as f:
|
||||||
|
|||||||
@@ -673,7 +673,7 @@ def importConfigs(pm, db, all_plugins):
|
|||||||
# Check if app was upgraded
|
# Check if app was upgraded
|
||||||
|
|
||||||
buildTimestamp, new_version = getBuildTimeStampAndVersion()
|
buildTimestamp, new_version = getBuildTimeStampAndVersion()
|
||||||
prev_version = conf.VERSION
|
prev_version = conf.VERSION if conf.VERSION != '' else "unknown"
|
||||||
|
|
||||||
mylog('debug', [f"[Config] buildTimestamp | prev_version | .VERSION file: '{buildTimestamp}|{prev_version}|{new_version}'"])
|
mylog('debug', [f"[Config] buildTimestamp | prev_version | .VERSION file: '{buildTimestamp}|{prev_version}|{new_version}'"])
|
||||||
|
|
||||||
@@ -684,7 +684,7 @@ def importConfigs(pm, db, all_plugins):
|
|||||||
# ccd(key, default, config_dir, name, inputtype, options, group, events=None, desc="", setJsonMetadata=None, overrideTemplate=None, forceDefault=False)
|
# ccd(key, default, config_dir, name, inputtype, options, group, events=None, desc="", setJsonMetadata=None, overrideTemplate=None, forceDefault=False)
|
||||||
ccd('VERSION', new_version , c_d, '_KEEP_', '_KEEP_', '_KEEP_', '_KEEP_', None, "_KEEP_", None, None, True)
|
ccd('VERSION', new_version , c_d, '_KEEP_', '_KEEP_', '_KEEP_', '_KEEP_', None, "_KEEP_", None, None, True)
|
||||||
|
|
||||||
write_notification(f'[Upgrade] : App upgraded from {prev_version} to {new_version} 🚀 Please clear the cache: <ol> <li>Click OK below</li> <li>Clear the browser cache (shift + browser refresh button)</li> <li> Clear app cache with the <i class="fa-solid fa-rotate"></i> (reload) button in the header</li><li>Go to Settings and click Save</li> </ol> Check out new features and what has changed in the <a href="https://github.com/jokob-sk/NetAlertX/releases" target="_blank">📓 release notes</a>.', 'interrupt', timeNowDB())
|
write_notification(f'[Upgrade] : App upgraded from <code>{prev_version}</code> to <code>{new_version}</code> 🚀 Please clear the cache: <ol> <li>Click OK below</li> <li>Clear the browser cache (shift + browser refresh button)</li> <li> Clear app cache with the <i class="fa-solid fa-rotate"></i> (reload) button in the header</li><li>Go to Settings and click Save</li> </ol> Check out new features and what has changed in the <a href="https://github.com/jokob-sk/NetAlertX/releases" target="_blank">📓 release notes</a>.', 'interrupt', timeNowDB())
|
||||||
|
|
||||||
|
|
||||||
# -----------------
|
# -----------------
|
||||||
|
|||||||
@@ -13,9 +13,6 @@ sys.path.extend([f"{INSTALL_PATH}/server"])
|
|||||||
|
|
||||||
from const import apiPath
|
from const import apiPath
|
||||||
from logger import mylog
|
from logger import mylog
|
||||||
from helper import (
|
|
||||||
timeNowTZ,
|
|
||||||
)
|
|
||||||
|
|
||||||
import conf
|
import conf
|
||||||
from const import applicationPath, logPath, apiPath, confFileName, reportTemplatesPath
|
from const import applicationPath, logPath, apiPath, confFileName, reportTemplatesPath
|
||||||
@@ -23,6 +20,9 @@ from logger import mylog
|
|||||||
from utils.datetime_utils import timeNowDB
|
from utils.datetime_utils import timeNowDB
|
||||||
|
|
||||||
|
|
||||||
|
NOTIFICATION_API_FILE = apiPath + 'user_notifications.json'
|
||||||
|
|
||||||
|
|
||||||
# Show Frontend User Notification
|
# Show Frontend User Notification
|
||||||
def write_notification(content, level="alert", timestamp=None):
|
def write_notification(content, level="alert", timestamp=None):
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -19,7 +19,6 @@ INSTALL_PATH = os.getenv("NETALERTX_APP", "/app")
|
|||||||
sys.path.extend([f"{INSTALL_PATH}/server"])
|
sys.path.extend([f"{INSTALL_PATH}/server"])
|
||||||
|
|
||||||
from helper import (
|
from helper import (
|
||||||
get_timezone_offset,
|
|
||||||
get_setting_value,
|
get_setting_value,
|
||||||
)
|
)
|
||||||
from logger import mylog
|
from logger import mylog
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ from helper import (
|
|||||||
removeDuplicateNewLines,
|
removeDuplicateNewLines,
|
||||||
write_file,
|
write_file,
|
||||||
get_setting_value,
|
get_setting_value,
|
||||||
get_timezone_offset,
|
getBuildTimeStampAndVersion,
|
||||||
)
|
)
|
||||||
from messaging.in_app import write_notification
|
from messaging.in_app import write_notification
|
||||||
from utils.datetime_utils import timeNowDB, get_timezone_offset
|
from utils.datetime_utils import timeNowDB, get_timezone_offset
|
||||||
@@ -26,6 +26,7 @@ from utils.datetime_utils import timeNowDB, get_timezone_offset
|
|||||||
class NotificationInstance:
|
class NotificationInstance:
|
||||||
def __init__(self, db):
|
def __init__(self, db):
|
||||||
self.db = db
|
self.db = db
|
||||||
|
self.serverUrl = get_setting_value("REPORT_DASHBOARD_URL")
|
||||||
|
|
||||||
# Create Notifications table if missing
|
# Create Notifications table if missing
|
||||||
self.db.sql.execute("""CREATE TABLE IF NOT EXISTS "Notifications" (
|
self.db.sql.execute("""CREATE TABLE IF NOT EXISTS "Notifications" (
|
||||||
@@ -109,83 +110,71 @@ class NotificationInstance:
|
|||||||
if conf.newVersionAvailable:
|
if conf.newVersionAvailable:
|
||||||
newVersionText = "🚀A new version is available."
|
newVersionText = "🚀A new version is available."
|
||||||
|
|
||||||
mail_text = mail_text.replace("<NEW_VERSION>", newVersionText)
|
mail_text = mail_text.replace("NEW_VERSION", newVersionText)
|
||||||
mail_html = mail_html.replace("<NEW_VERSION>", newVersionText)
|
mail_html = mail_html.replace("NEW_VERSION", newVersionText)
|
||||||
|
|
||||||
# Report "REPORT_DATE" in Header & footer
|
# Report "REPORT_DATE" in Header & footer
|
||||||
timeFormated = timeNowDB()
|
timeFormated = timeNowDB()
|
||||||
mail_text = mail_text.replace('<REPORT_DATE>', timeFormated)
|
mail_text = mail_text.replace("REPORT_DATE", timeFormated)
|
||||||
mail_html = mail_html.replace('<REPORT_DATE>', timeFormated)
|
mail_html = mail_html.replace("REPORT_DATE", timeFormated)
|
||||||
|
|
||||||
# Report "SERVER_NAME" in Header & footer
|
# Report "SERVER_NAME" in Header & footer
|
||||||
mail_text = mail_text.replace("<SERVER_NAME>", socket.gethostname())
|
mail_text = mail_text.replace("SERVER_NAME", socket.gethostname())
|
||||||
mail_html = mail_html.replace("<SERVER_NAME>", socket.gethostname())
|
mail_html = mail_html.replace("SERVER_NAME", socket.gethostname())
|
||||||
|
|
||||||
# Report "VERSION" in Header & footer
|
# Report "VERSION" in Header & footer
|
||||||
try:
|
buildTimestamp, newBuildVersion = getBuildTimeStampAndVersion()
|
||||||
VERSIONFILE = subprocess.check_output(
|
|
||||||
["php", applicationPath + "/front/php/templates/version.php"],
|
|
||||||
timeout=5,
|
|
||||||
).decode("utf-8")
|
|
||||||
except Exception as e:
|
|
||||||
mylog("debug", [f"[Notification] Unable to read version.php: {e}"])
|
|
||||||
VERSIONFILE = "unknown"
|
|
||||||
|
|
||||||
mail_text = mail_text.replace("<BUILD_VERSION>", VERSIONFILE)
|
mail_text = mail_text.replace("BUILD_VERSION", newBuildVersion)
|
||||||
mail_html = mail_html.replace("<BUILD_VERSION>", VERSIONFILE)
|
mail_html = mail_html.replace("BUILD_VERSION", newBuildVersion)
|
||||||
|
|
||||||
# Report "BUILD" in Header & footer
|
# Report "BUILD" in Header & footer
|
||||||
try:
|
mail_text = mail_text.replace("BUILD_DATE", str(buildTimestamp))
|
||||||
BUILDFILE = subprocess.check_output(
|
mail_html = mail_html.replace("BUILD_DATE", str(buildTimestamp))
|
||||||
["php", applicationPath + "/front/php/templates/build.php"],
|
|
||||||
timeout=5,
|
|
||||||
).decode("utf-8")
|
|
||||||
except Exception as e:
|
|
||||||
mylog("debug", [f"[Notification] Unable to read build.php: {e}"])
|
|
||||||
BUILDFILE = "unknown"
|
|
||||||
|
|
||||||
mail_text = mail_text.replace("<BUILD_DATE>", BUILDFILE)
|
# Report "REPORT_DASHBOARD_URL" in footer
|
||||||
mail_html = mail_html.replace("<BUILD_DATE>", BUILDFILE)
|
mail_text = mail_text.replace("REPORT_DASHBOARD_URL", self.serverUrl)
|
||||||
|
mail_html = mail_html.replace("REPORT_DASHBOARD_URL", self.serverUrl)
|
||||||
|
|
||||||
# Start generating the TEXT & HTML notification messages
|
# Start generating the TEXT & HTML notification messages
|
||||||
# new_devices
|
# new_devices
|
||||||
# ---
|
# ---
|
||||||
html, text = construct_notifications(self.JSON, "new_devices")
|
html, text = construct_notifications(self.JSON, "new_devices")
|
||||||
|
|
||||||
mail_text = mail_text.replace("<NEW_DEVICES_TABLE>", text + "\n")
|
mail_text = mail_text.replace("NEW_DEVICES_TABLE", text + "\n")
|
||||||
mail_html = mail_html.replace("<NEW_DEVICES_TABLE>", html)
|
mail_html = mail_html.replace("NEW_DEVICES_TABLE", html)
|
||||||
mylog("verbose", ["[Notification] New Devices sections done."])
|
mylog("verbose", ["[Notification] New Devices sections done."])
|
||||||
|
|
||||||
# down_devices
|
# down_devices
|
||||||
# ---
|
# ---
|
||||||
html, text = construct_notifications(self.JSON, "down_devices")
|
html, text = construct_notifications(self.JSON, "down_devices")
|
||||||
|
|
||||||
mail_text = mail_text.replace("<DOWN_DEVICES_TABLE>", text + "\n")
|
mail_text = mail_text.replace("DOWN_DEVICES_TABLE", text + "\n")
|
||||||
mail_html = mail_html.replace("<DOWN_DEVICES_TABLE>", html)
|
mail_html = mail_html.replace("DOWN_DEVICES_TABLE", html)
|
||||||
mylog("verbose", ["[Notification] Down Devices sections done."])
|
mylog("verbose", ["[Notification] Down Devices sections done."])
|
||||||
|
|
||||||
# down_reconnected
|
# down_reconnected
|
||||||
# ---
|
# ---
|
||||||
html, text = construct_notifications(self.JSON, "down_reconnected")
|
html, text = construct_notifications(self.JSON, "down_reconnected")
|
||||||
|
|
||||||
mail_text = mail_text.replace("<DOWN_RECONNECTED_TABLE>", text + "\n")
|
mail_text = mail_text.replace("DOWN_RECONNECTED_TABLE", text + "\n")
|
||||||
mail_html = mail_html.replace("<DOWN_RECONNECTED_TABLE>", html)
|
mail_html = mail_html.replace("DOWN_RECONNECTED_TABLE", html)
|
||||||
mylog("verbose", ["[Notification] Reconnected Down Devices sections done."])
|
mylog("verbose", ["[Notification] Reconnected Down Devices sections done."])
|
||||||
|
|
||||||
# events
|
# events
|
||||||
# ---
|
# ---
|
||||||
html, text = construct_notifications(self.JSON, "events")
|
html, text = construct_notifications(self.JSON, "events")
|
||||||
|
|
||||||
mail_text = mail_text.replace("<EVENTS_TABLE>", text + "\n")
|
mail_text = mail_text.replace("EVENTS_TABLE", text + "\n")
|
||||||
mail_html = mail_html.replace("<EVENTS_TABLE>", html)
|
mail_html = mail_html.replace("EVENTS_TABLE", html)
|
||||||
mylog("verbose", ["[Notification] Events sections done."])
|
mylog("verbose", ["[Notification] Events sections done."])
|
||||||
|
|
||||||
# plugins
|
# plugins
|
||||||
# ---
|
# ---
|
||||||
html, text = construct_notifications(self.JSON, "plugins")
|
html, text = construct_notifications(self.JSON, "plugins")
|
||||||
|
|
||||||
mail_text = mail_text.replace("<PLUGINS_TABLE>", text + "\n")
|
mail_text = mail_text.replace("PLUGINS_TABLE", text + "\n")
|
||||||
mail_html = mail_html.replace("<PLUGINS_TABLE>", html)
|
mail_html = mail_html.replace("PLUGINS_TABLE", html)
|
||||||
|
|
||||||
mylog("verbose", ["[Notification] Plugins sections done."])
|
mylog("verbose", ["[Notification] Plugins sections done."])
|
||||||
|
|
||||||
|
|||||||
@@ -40,16 +40,18 @@ class NameResolver:
|
|||||||
raw = result[0][0]
|
raw = result[0][0]
|
||||||
return ResolvedName(raw, self.clean_device_name(raw, False))
|
return ResolvedName(raw, self.clean_device_name(raw, False))
|
||||||
|
|
||||||
# Check by IP
|
# Check name by IP if enabled
|
||||||
sql.execute(f"""
|
if get_setting_value('NEWDEV_IP_MATCH_NAME'):
|
||||||
SELECT Watched_Value2 FROM Plugins_Objects
|
|
||||||
WHERE Plugin = '{plugin}' AND Object_SecondaryID = '{pIP}'
|
sql.execute(f"""
|
||||||
""")
|
SELECT Watched_Value2 FROM Plugins_Objects
|
||||||
result = sql.fetchall()
|
WHERE Plugin = '{plugin}' AND Object_SecondaryID = '{pIP}'
|
||||||
# self.db.commitDB() # Issue #1251: Optimize name resolution lookup
|
""")
|
||||||
if result:
|
result = sql.fetchall()
|
||||||
raw = result[0][0]
|
# self.db.commitDB() # Issue #1251: Optimize name resolution lookup
|
||||||
return ResolvedName(raw, self.clean_device_name(raw, True))
|
if result:
|
||||||
|
raw = result[0][0]
|
||||||
|
return ResolvedName(raw, self.clean_device_name(raw, True))
|
||||||
|
|
||||||
return nameNotFound
|
return nameNotFound
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,13 @@ import copy
|
|||||||
import os
|
import os
|
||||||
import pathlib
|
import pathlib
|
||||||
import re
|
import re
|
||||||
|
import shutil
|
||||||
|
import socket
|
||||||
import subprocess
|
import subprocess
|
||||||
|
import time
|
||||||
|
from collections.abc import Callable, Iterable
|
||||||
|
|
||||||
|
from _pytest.outcomes import Skipped
|
||||||
import pytest
|
import pytest
|
||||||
import yaml
|
import yaml
|
||||||
|
|
||||||
@@ -29,6 +35,55 @@ CONTAINER_PATHS = {
|
|||||||
|
|
||||||
TMPFS_ROOT = "/tmp:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
|
TMPFS_ROOT = "/tmp:uid=20211,gid=20211,mode=1700,rw,noexec,nosuid,nodev,async,noatime,nodiratime"
|
||||||
|
|
||||||
|
DEFAULT_HTTP_PORT = int(os.environ.get("NETALERTX_DEFAULT_HTTP_PORT", "20211"))
|
||||||
|
COMPOSE_PORT_WAIT_TIMEOUT = int(os.environ.get("NETALERTX_COMPOSE_PORT_WAIT_TIMEOUT", "180"))
|
||||||
|
COMPOSE_SETTLE_WAIT_SECONDS = int(os.environ.get("NETALERTX_COMPOSE_SETTLE_WAIT", "15"))
|
||||||
|
PREFERRED_CUSTOM_PORTS = (22111, 22112)
|
||||||
|
HOST_ADDR_ENV = os.environ.get("NETALERTX_HOST_ADDRS", "")
|
||||||
|
|
||||||
|
|
||||||
|
def _discover_host_addresses() -> tuple[str, ...]:
|
||||||
|
"""Return candidate loopback addresses for reaching host-mode containers."""
|
||||||
|
|
||||||
|
candidates: list[str] = ["127.0.0.1"]
|
||||||
|
|
||||||
|
if HOST_ADDR_ENV:
|
||||||
|
env_addrs = [addr.strip() for addr in HOST_ADDR_ENV.split(",") if addr.strip()]
|
||||||
|
candidates.extend(env_addrs)
|
||||||
|
|
||||||
|
ip_cmd = shutil.which("ip")
|
||||||
|
if ip_cmd:
|
||||||
|
try:
|
||||||
|
route_proc = subprocess.run(
|
||||||
|
[ip_cmd, "-4", "route", "show", "default"],
|
||||||
|
stdout=subprocess.PIPE,
|
||||||
|
stderr=subprocess.PIPE,
|
||||||
|
text=True,
|
||||||
|
check=False,
|
||||||
|
timeout=5,
|
||||||
|
)
|
||||||
|
except (OSError, subprocess.TimeoutExpired):
|
||||||
|
route_proc = None
|
||||||
|
if route_proc and route_proc.returncode == 0 and route_proc.stdout:
|
||||||
|
match = re.search(r"default\s+via\s+(?P<gateway>\S+)", route_proc.stdout)
|
||||||
|
if match:
|
||||||
|
gateway = match.group("gateway")
|
||||||
|
candidates.append(gateway)
|
||||||
|
|
||||||
|
# Deduplicate while preserving order
|
||||||
|
seen: set[str] = set()
|
||||||
|
deduped: list[str] = []
|
||||||
|
for addr in candidates:
|
||||||
|
if addr not in seen:
|
||||||
|
deduped.append(addr)
|
||||||
|
seen.add(addr)
|
||||||
|
|
||||||
|
return tuple(deduped)
|
||||||
|
|
||||||
|
|
||||||
|
HOST_ADDRESS_CANDIDATES = _discover_host_addresses()
|
||||||
|
LAST_PORT_SUCCESSES: dict[int, str] = {}
|
||||||
|
|
||||||
pytestmark = [pytest.mark.docker, pytest.mark.compose]
|
pytestmark = [pytest.mark.docker, pytest.mark.compose]
|
||||||
|
|
||||||
IMAGE = os.environ.get("NETALERTX_TEST_IMAGE", "netalertx-test")
|
IMAGE = os.environ.get("NETALERTX_TEST_IMAGE", "netalertx-test")
|
||||||
@@ -151,12 +206,142 @@ def _extract_conflict_container_name(output: str) -> str | None:
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def _port_is_free(port: int) -> bool:
|
||||||
|
"""Return True if a TCP port is available on localhost."""
|
||||||
|
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
|
||||||
|
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||||
|
try:
|
||||||
|
sock.bind(("127.0.0.1", port))
|
||||||
|
except OSError:
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def _wait_for_ports(ports: Iterable[int], timeout: int = COMPOSE_PORT_WAIT_TIMEOUT) -> None:
|
||||||
|
"""Block until every port in the iterable accepts TCP connections or timeout expires."""
|
||||||
|
|
||||||
|
remaining = set(ports)
|
||||||
|
deadline = time.time() + timeout
|
||||||
|
last_errors: dict[int, dict[str, BaseException]] = {port: {} for port in remaining}
|
||||||
|
|
||||||
|
while remaining and time.time() < deadline:
|
||||||
|
ready: list[int] = []
|
||||||
|
for port in list(remaining):
|
||||||
|
for addr in HOST_ADDRESS_CANDIDATES:
|
||||||
|
try:
|
||||||
|
with socket.create_connection((addr, port), timeout=2):
|
||||||
|
ready.append(port)
|
||||||
|
LAST_PORT_SUCCESSES[port] = addr
|
||||||
|
break
|
||||||
|
except OSError as exc:
|
||||||
|
last_errors.setdefault(port, {})[addr] = exc
|
||||||
|
else:
|
||||||
|
continue
|
||||||
|
for port in ready:
|
||||||
|
remaining.discard(port)
|
||||||
|
if remaining:
|
||||||
|
time.sleep(1)
|
||||||
|
|
||||||
|
if remaining:
|
||||||
|
details: list[str] = []
|
||||||
|
for port in sorted(remaining):
|
||||||
|
addr_errors = last_errors.get(port, {})
|
||||||
|
if addr_errors:
|
||||||
|
error_summary = ", ".join(f"{addr}: {err}" for addr, err in addr_errors.items())
|
||||||
|
else:
|
||||||
|
error_summary = "no connection attempts recorded"
|
||||||
|
details.append(f"{port} -> {error_summary}")
|
||||||
|
raise TimeoutError(
|
||||||
|
"Ports did not become ready before timeout: " + "; ".join(details)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _select_custom_ports() -> tuple[int, int]:
|
||||||
|
"""Choose a pair of non-default ports, preferring the standard high test pair when free."""
|
||||||
|
preferred_http, preferred_graphql = PREFERRED_CUSTOM_PORTS
|
||||||
|
if _port_is_free(preferred_http) and _port_is_free(preferred_graphql):
|
||||||
|
return preferred_http, preferred_graphql
|
||||||
|
|
||||||
|
# Fall back to scanning ephemeral range for the first free consecutive pair.
|
||||||
|
for port in range(30000, 60000, 2):
|
||||||
|
if _port_is_free(port) and _port_is_free(port + 1):
|
||||||
|
return port, port + 1
|
||||||
|
|
||||||
|
raise RuntimeError("Unable to locate two free high ports for compose testing")
|
||||||
|
|
||||||
|
|
||||||
|
def _make_port_check_hook(ports: tuple[int, ...]) -> Callable[[], None]:
|
||||||
|
"""Return a callback that waits for the provided ports to accept TCP connections."""
|
||||||
|
|
||||||
|
def _hook() -> None:
|
||||||
|
for port in ports:
|
||||||
|
LAST_PORT_SUCCESSES.pop(port, None)
|
||||||
|
time.sleep(COMPOSE_SETTLE_WAIT_SECONDS)
|
||||||
|
_wait_for_ports(ports, timeout=COMPOSE_PORT_WAIT_TIMEOUT)
|
||||||
|
|
||||||
|
return _hook
|
||||||
|
|
||||||
|
|
||||||
|
def _write_normal_startup_compose(
|
||||||
|
base_dir: pathlib.Path,
|
||||||
|
project_name: str,
|
||||||
|
env_overrides: dict[str, str] | None,
|
||||||
|
) -> pathlib.Path:
|
||||||
|
"""Generate a compose file for the normal startup scenario with optional environment overrides."""
|
||||||
|
|
||||||
|
compose_config = copy.deepcopy(COMPOSE_CONFIGS["normal_startup"])
|
||||||
|
service = compose_config["services"]["netalertx"]
|
||||||
|
|
||||||
|
data_volume_name = f"{project_name}_data"
|
||||||
|
service["volumes"][0]["source"] = data_volume_name
|
||||||
|
|
||||||
|
if env_overrides:
|
||||||
|
service_env = service.setdefault("environment", {})
|
||||||
|
service_env.update(env_overrides)
|
||||||
|
|
||||||
|
compose_config["volumes"] = {data_volume_name: {}}
|
||||||
|
|
||||||
|
compose_file = base_dir / "docker-compose.yml"
|
||||||
|
with open(compose_file, "w") as f:
|
||||||
|
yaml.dump(compose_config, f)
|
||||||
|
|
||||||
|
return compose_file
|
||||||
|
|
||||||
|
|
||||||
|
def _assert_ports_ready(
|
||||||
|
result: subprocess.CompletedProcess,
|
||||||
|
project_name: str,
|
||||||
|
ports: tuple[int, ...],
|
||||||
|
) -> str:
|
||||||
|
"""Validate the post-up hook succeeded and return sanitized compose logs for further assertions."""
|
||||||
|
|
||||||
|
post_error = getattr(result, "post_up_error", None)
|
||||||
|
clean_output = ANSI_ESCAPE.sub("", result.output)
|
||||||
|
port_hosts = {port: LAST_PORT_SUCCESSES.get(port) for port in ports}
|
||||||
|
result.port_hosts = port_hosts # type: ignore[attr-defined]
|
||||||
|
|
||||||
|
if post_error:
|
||||||
|
pytest.fail(
|
||||||
|
"Port readiness check failed for project"
|
||||||
|
f" {project_name} on ports {ports}: {post_error}\n"
|
||||||
|
f"Compose logs:\n{clean_output}"
|
||||||
|
)
|
||||||
|
|
||||||
|
port_summary = ", ".join(
|
||||||
|
f"{port}@{addr if addr else 'unresolved'}" for port, addr in port_hosts.items()
|
||||||
|
)
|
||||||
|
print(f"[compose port hosts] {project_name}: {port_summary}")
|
||||||
|
|
||||||
|
return clean_output
|
||||||
|
|
||||||
|
|
||||||
def _run_docker_compose(
|
def _run_docker_compose(
|
||||||
compose_file: pathlib.Path,
|
compose_file: pathlib.Path,
|
||||||
project_name: str,
|
project_name: str,
|
||||||
timeout: int = 5,
|
timeout: int = 5,
|
||||||
env_vars: dict | None = None,
|
env_vars: dict | None = None,
|
||||||
detached: bool = False,
|
detached: bool = False,
|
||||||
|
post_up: Callable[[], None] | None = None,
|
||||||
) -> subprocess.CompletedProcess:
|
) -> subprocess.CompletedProcess:
|
||||||
"""Run docker compose up and capture output."""
|
"""Run docker compose up and capture output."""
|
||||||
cmd = [
|
cmd = [
|
||||||
@@ -219,10 +404,21 @@ def _run_docker_compose(
|
|||||||
continue
|
continue
|
||||||
return proc
|
return proc
|
||||||
|
|
||||||
|
post_up_exc: BaseException | None = None
|
||||||
|
skip_exc: Skipped | None = None
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if detached:
|
if detached:
|
||||||
up_result = _run_with_conflict_retry(up_cmd, timeout)
|
up_result = _run_with_conflict_retry(up_cmd, timeout)
|
||||||
|
|
||||||
|
if post_up:
|
||||||
|
try:
|
||||||
|
post_up()
|
||||||
|
except Skipped as exc:
|
||||||
|
skip_exc = exc
|
||||||
|
except BaseException as exc: # noqa: BLE001 - bubble the root cause through the result payload
|
||||||
|
post_up_exc = exc
|
||||||
|
|
||||||
logs_cmd = cmd + ["logs"]
|
logs_cmd = cmd + ["logs"]
|
||||||
logs_result = subprocess.run(
|
logs_result = subprocess.run(
|
||||||
logs_cmd,
|
logs_cmd,
|
||||||
@@ -255,6 +451,9 @@ def _run_docker_compose(
|
|||||||
|
|
||||||
# Combine stdout and stderr
|
# Combine stdout and stderr
|
||||||
result.output = result.stdout + result.stderr
|
result.output = result.stdout + result.stderr
|
||||||
|
result.post_up_error = post_up_exc # type: ignore[attr-defined]
|
||||||
|
if skip_exc is not None:
|
||||||
|
raise skip_exc
|
||||||
|
|
||||||
# Surface command context and IO for any caller to aid debugging
|
# Surface command context and IO for any caller to aid debugging
|
||||||
print("\n[compose command]", " ".join(up_cmd))
|
print("\n[compose command]", " ".join(up_cmd))
|
||||||
@@ -339,43 +538,34 @@ def test_normal_startup_no_warnings_compose(tmp_path: pathlib.Path) -> None:
|
|||||||
"""
|
"""
|
||||||
base_dir = tmp_path / "normal_startup"
|
base_dir = tmp_path / "normal_startup"
|
||||||
base_dir.mkdir()
|
base_dir.mkdir()
|
||||||
|
default_http_port = DEFAULT_HTTP_PORT
|
||||||
|
default_ports = (default_http_port,)
|
||||||
|
if not _port_is_free(default_http_port):
|
||||||
|
pytest.skip(
|
||||||
|
"Default NetAlertX ports are already bound on this host; "
|
||||||
|
"skipping compose normal-startup validation."
|
||||||
|
)
|
||||||
|
|
||||||
project_name = "netalertx-normal"
|
default_dir = base_dir / "default"
|
||||||
|
default_dir.mkdir()
|
||||||
|
default_project = "netalertx-normal-default"
|
||||||
|
|
||||||
# Create compose file mirroring production docker-compose.yml
|
default_compose_file = _write_normal_startup_compose(default_dir, default_project, None)
|
||||||
compose_config = copy.deepcopy(COMPOSE_CONFIGS["normal_startup"])
|
default_result = _run_docker_compose(
|
||||||
service = compose_config["services"]["netalertx"]
|
default_compose_file,
|
||||||
|
default_project,
|
||||||
|
timeout=60,
|
||||||
|
detached=True,
|
||||||
|
post_up=_make_port_check_hook(default_ports),
|
||||||
|
)
|
||||||
|
default_output = _assert_ports_ready(default_result, default_project, default_ports)
|
||||||
|
|
||||||
data_volume_name = f"{project_name}_data"
|
assert "Startup pre-checks" in default_output
|
||||||
|
assert "❌" not in default_output
|
||||||
service["volumes"][0]["source"] = data_volume_name
|
|
||||||
|
|
||||||
service.setdefault("environment", {})
|
|
||||||
service["environment"].update({
|
|
||||||
"PORT": "22111",
|
|
||||||
"GRAPHQL_PORT": "22112",
|
|
||||||
})
|
|
||||||
|
|
||||||
compose_config["volumes"] = {
|
|
||||||
data_volume_name: {},
|
|
||||||
}
|
|
||||||
|
|
||||||
compose_file = base_dir / "docker-compose.yml"
|
|
||||||
with open(compose_file, 'w') as f:
|
|
||||||
yaml.dump(compose_config, f)
|
|
||||||
|
|
||||||
# Run docker compose
|
|
||||||
result = _run_docker_compose(compose_file, project_name, detached=True)
|
|
||||||
|
|
||||||
clean_output = ANSI_ESCAPE.sub("", result.output)
|
|
||||||
|
|
||||||
# Check that startup completed without critical issues and mounts table shows success
|
|
||||||
assert "Startup pre-checks" in clean_output
|
|
||||||
assert "❌" not in clean_output
|
|
||||||
|
|
||||||
data_line = ""
|
data_line = ""
|
||||||
data_parts: list[str] = []
|
data_parts: list[str] = []
|
||||||
for line in clean_output.splitlines():
|
for line in default_output.splitlines():
|
||||||
if CONTAINER_PATHS['data'] not in line or '|' not in line:
|
if CONTAINER_PATHS['data'] not in line or '|' not in line:
|
||||||
continue
|
continue
|
||||||
parts = [segment.strip() for segment in line.split('|')]
|
parts = [segment.strip() for segment in line.split('|')]
|
||||||
@@ -387,15 +577,46 @@ def test_normal_startup_no_warnings_compose(tmp_path: pathlib.Path) -> None:
|
|||||||
break
|
break
|
||||||
|
|
||||||
assert data_line, "Expected /data row in mounts table"
|
assert data_line, "Expected /data row in mounts table"
|
||||||
|
assert data_parts[1] == CONTAINER_PATHS['data'], f"Unexpected path column in /data row: {data_parts}"
|
||||||
|
assert data_parts[2] == "✅" and data_parts[3] == "✅", (
|
||||||
|
f"Unexpected mount row values for /data: {data_parts[2:4]}"
|
||||||
|
)
|
||||||
|
|
||||||
parts = data_parts
|
assert "Write permission denied" not in default_output
|
||||||
assert parts[1] == CONTAINER_PATHS['data'], f"Unexpected path column in /data row: {parts}"
|
assert "CRITICAL" not in default_output
|
||||||
assert parts[2] == "✅" and parts[3] == "✅", f"Unexpected mount row values for /data: {parts[2:4]}"
|
assert "⚠️" not in default_output
|
||||||
|
|
||||||
# Ensure no critical errors or permission problems surfaced
|
custom_http, custom_graphql = _select_custom_ports()
|
||||||
assert "Write permission denied" not in clean_output
|
assert custom_http != default_http_port
|
||||||
assert "CRITICAL" not in clean_output
|
custom_ports = (custom_http,)
|
||||||
assert "⚠️" not in clean_output
|
|
||||||
|
custom_dir = base_dir / "custom"
|
||||||
|
custom_dir.mkdir()
|
||||||
|
custom_project = "netalertx-normal-custom"
|
||||||
|
|
||||||
|
custom_compose_file = _write_normal_startup_compose(
|
||||||
|
custom_dir,
|
||||||
|
custom_project,
|
||||||
|
{
|
||||||
|
"PORT": str(custom_http),
|
||||||
|
"GRAPHQL_PORT": str(custom_graphql),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
custom_result = _run_docker_compose(
|
||||||
|
custom_compose_file,
|
||||||
|
custom_project,
|
||||||
|
timeout=60,
|
||||||
|
detached=True,
|
||||||
|
post_up=_make_port_check_hook(custom_ports),
|
||||||
|
)
|
||||||
|
custom_output = _assert_ports_ready(custom_result, custom_project, custom_ports)
|
||||||
|
|
||||||
|
assert "Startup pre-checks" in custom_output
|
||||||
|
assert "❌" not in custom_output
|
||||||
|
assert "Write permission denied" not in custom_output
|
||||||
|
assert "CRITICAL" not in custom_output
|
||||||
|
assert "⚠️" not in custom_output
|
||||||
|
|
||||||
|
|
||||||
def test_ram_disk_mount_analysis_compose(tmp_path: pathlib.Path) -> None:
|
def test_ram_disk_mount_analysis_compose(tmp_path: pathlib.Path) -> None:
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ from datetime import datetime, timedelta
|
|||||||
INSTALL_PATH = os.getenv('NETALERTX_APP', '/app')
|
INSTALL_PATH = os.getenv('NETALERTX_APP', '/app')
|
||||||
sys.path.extend([f"{INSTALL_PATH}/front/plugins", f"{INSTALL_PATH}/server"])
|
sys.path.extend([f"{INSTALL_PATH}/front/plugins", f"{INSTALL_PATH}/server"])
|
||||||
|
|
||||||
from helper import timeNowTZ, get_setting_value
|
from helper import get_setting_value
|
||||||
from api_server.api_server_start import app
|
from api_server.api_server_start import app
|
||||||
|
|
||||||
@pytest.fixture(scope="session")
|
@pytest.fixture(scope="session")
|
||||||
|
|||||||
Reference in New Issue
Block a user