From 806fe1caec4e5fdd69d1e396a86d740bf157cb1e Mon Sep 17 00:00:00 2001 From: Olivier Date: Mon, 18 May 2026 12:22:05 +0200 Subject: [PATCH] =?UTF-8?q?feat:=20DNS=5FSERVER=20env=20var=20=E2=80=94=20?= =?UTF-8?q?pre-fills=20discovery=20UI,=20optional=20for=20scan?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - DNS_SERVER env var configures the default DNS server for PTR lookups - GET /api/discovery/config exposes it to the frontend - DiscoveryModal fetches it on mount and pre-fills the field (editable) - dns_server is now optional in ScanRequest (default empty string) - PTR lookup is skipped when dns_server is empty — scan still proceeds - Validator only runs when dns_server is non-empty - .env.example, docker-compose.yml, READMEs (fr/en/es) updated Co-Authored-By: Claude Sonnet 4.6 --- .env.example | 10 ++++++++++ README.es.md | 1 + README.fr.md | 1 + README.md | 1 + backend/routers/discovery.py | 21 +++++++++++++++------ docker-compose.yml | 1 + frontend/src/api.js | 5 +++-- frontend/src/components/DiscoveryModal.vue | 13 ++++++++----- frontend/src/i18n.js | 6 +++--- 9 files changed, 43 insertions(+), 16 deletions(-) diff --git a/.env.example b/.env.example index 6ae9fec..bbb3155 100644 --- a/.env.example +++ b/.env.example @@ -57,3 +57,13 @@ BIND_ADDRESS=0.0.0.0 # DOCKER_UID=1000 DOCKER_GID=1000 + +# ── DNS server for auto-discovery ──────────────────────────────────────────── +# IP address of the DNS server used for PTR (reverse DNS) lookups during +# host discovery. Pre-fills the field in the discovery UI (user can override). +# Leave empty to disable reverse DNS lookups by default. +# +# Examples: +# DNS_SERVER=192.168.1.1 (your router / local DNS) +# DNS_SERVER= (disabled — no PTR lookups) +DNS_SERVER= diff --git a/README.es.md b/README.es.md index dad093f..b4daa32 100644 --- a/README.es.md +++ b/README.es.md @@ -100,6 +100,7 @@ Toda la configuración se realiza mediante variables de entorno. Ver `.env.examp | `INITIAL_ADMIN_PASSWORD` | _(vacío)_ | Contraseña de bootstrap del admin. Si no se define, se usa `admin/admin` con cambio forzado. | | `ALLOWED_ORIGINS` | `*` | Orígenes CORS permitidos (separados por comas). Definir en tu dominio en producción. | | `BIND_ADDRESS` | `0.0.0.0` | Dirección IP de escucha. Definir en la interfaz frente al reverse proxy. | +| `DNS_SERVER` | _(vacío)_ | Servidor DNS para lookups PTR durante el descubrimiento automático. Pre-rellena el campo en la UI (modificable por escaneo). Dejar vacío para desactivar el DNS inverso. | | `DOCKER_UID` / `DOCKER_GID` | `1000` | UID/GID para el proceso backend. Debe coincidir con el usuario propietario de `./db_data/`. | ```bash diff --git a/README.fr.md b/README.fr.md index 0de4ed4..6ef743f 100644 --- a/README.fr.md +++ b/README.fr.md @@ -100,6 +100,7 @@ Toute la configuration se fait via des variables d'environnement. Voir `.env.exa | `INITIAL_ADMIN_PASSWORD` | _(vide)_ | Mot de passe admin de bootstrap. Si non défini, `admin/admin` est utilisé avec changement forcé. | | `ALLOWED_ORIGINS` | `*` | Origines CORS autorisées (séparées par des virgules). À définir sur votre domaine en production. | | `BIND_ADDRESS` | `0.0.0.0` | Adresse IP d'écoute. À définir sur l'interface face au reverse proxy. | +| `DNS_SERVER` | _(vide)_ | Serveur DNS pour les lookups PTR lors de la découverte automatique. Pré-remplit le champ dans l'UI (modifiable par scan). Laisser vide pour désactiver le reverse DNS. | | `DOCKER_UID` / `DOCKER_GID` | `1000` | UID/GID pour le processus backend. Doit correspondre à l'utilisateur propriétaire de `./db_data/`. | ```bash diff --git a/README.md b/README.md index 2a45c5e..af3b5a7 100644 --- a/README.md +++ b/README.md @@ -100,6 +100,7 @@ All configuration is via environment variables. See `.env.example` for the full | `INITIAL_ADMIN_PASSWORD` | _(empty)_ | Bootstrap admin password. If unset, `admin/admin` is used with forced change. | | `ALLOWED_ORIGINS` | `*` | CORS allowed origins (comma-separated). Set to your domain in production. | | `BIND_ADDRESS` | `0.0.0.0` | IP address to listen on. Set to the interface facing the reverse proxy. | +| `DNS_SERVER` | _(empty)_ | DNS server for PTR lookups during auto-discovery. Pre-fills the field in the UI (overridable per scan). Leave empty to disable reverse DNS. | | `DOCKER_UID` / `DOCKER_GID` | `1000` | UID/GID for the backend process. Must match the host user owning `./db_data/`. | ```bash diff --git a/backend/routers/discovery.py b/backend/routers/discovery.py index 83a73f7..ecc562a 100644 --- a/backend/routers/discovery.py +++ b/backend/routers/discovery.py @@ -1,5 +1,6 @@ import errno import ipaddress +import os import socket import subprocess import time @@ -16,6 +17,8 @@ router = APIRouter() MAX_HOSTS_PER_TARGET = 1024 # refuse les /21 et plus larges MAX_HOSTS_TOTAL = 4096 # cap global sur l'ensemble des targets +_ENV_DNS = os.environ.get("DNS_SERVER", "").strip() + class ScanTarget(BaseModel): vlan_id: int @@ -23,20 +26,26 @@ class ScanTarget(BaseModel): class ScanRequest(BaseModel): - dns_server: str + dns_server: str = "" targets: list[ScanTarget] tcp_check: bool = False @field_validator("dns_server") @classmethod def _dns_server(cls, v: str) -> str: - try: - ipaddress.ip_address(v) - except ValueError: - raise ValueError(f"dns_server must be a valid IP address, got: {v!r}") + if v: + try: + ipaddress.ip_address(v) + except ValueError: + raise ValueError(f"dns_server must be a valid IP address, got: {v!r}") return v +@router.get("/config") +def get_config(): + return {"dns_server": _ENV_DNS} + + class DiscoveredHost(BaseModel): ip: str hostname: Optional[str] = None @@ -109,7 +118,7 @@ def _scan_one(ip: str, dns_server: str, vlan_id: int, cidr: str, tcp_check: bool return None if tcp_check and not _tcp_check(ip): return None - hostname = _ptr_lookup(ip, dns_server) + hostname = _ptr_lookup(ip, dns_server) if dns_server else None return DiscoveredHost(ip=ip, hostname=hostname, vlan_id=vlan_id, cidr=cidr) diff --git a/docker-compose.yml b/docker-compose.yml index babb03e..6f9606a 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -16,6 +16,7 @@ services: - SECRET_KEY=${SECRET_KEY} - INITIAL_ADMIN_PASSWORD=${INITIAL_ADMIN_PASSWORD} - ALLOWED_ORIGINS=${ALLOWED_ORIGINS:-*} + - DNS_SERVER=${DNS_SERVER:-} # ── Docker secret alternative for SECRET_KEY ───────────────────────────── # Instead of passing SECRET_KEY via environment, you can mount a secret file. # The backend reads data/secret_key.txt when SECRET_KEY env var is unset. diff --git a/frontend/src/api.js b/frontend/src/api.js index d65c10f..944c8d7 100644 --- a/frontend/src/api.js +++ b/frontend/src/api.js @@ -46,6 +46,7 @@ export const devicesApi = { } export const discoveryApi = { - scan: (data) => http.post('/discovery/scan', data), - ping: (ips) => http.post('/discovery/ping', { ips }), + config: () => http.get('/discovery/config'), + scan: (data) => http.post('/discovery/scan', data), + ping: (ips) => http.post('/discovery/ping', { ips }), } diff --git a/frontend/src/components/DiscoveryModal.vue b/frontend/src/components/DiscoveryModal.vue index 3e55686..34bec67 100644 --- a/frontend/src/components/DiscoveryModal.vue +++ b/frontend/src/components/DiscoveryModal.vue @@ -173,7 +173,7 @@