Commit Graph

31 Commits

Author SHA1 Message Date
Pouzor cf7777e0af feat: improve IoT device detection via two-phase scan and mDNS discovery
- Two-phase nmap: ARP sweep first to find all alive hosts (incl. IoT with
  no open TCP ports), then port scan only alive hosts
- mDNS/Bonjour discovery via zeroconf for Shelly, ESPHome, HomeKit devices
- Add CoAP ports (5683, 5684, 4915) to port scan and IoT type hints
- Expand MAC OUI table with Shelly, Espressif, Sonoff, Tapo, Hue, IKEA, Tuya
- IoT vendor MAC takes precedence over generic HTTP port type hints
- Reorder suggest_node_type priority: iot now beats server
2026-04-02 17:41:51 +02:00
Pouzor 057891f7d5 feat: add stop scan button in UI with backend cancellation support
- POST /scan/{run_id}/stop endpoint signals running scan to cancel
- Scanner checks cancellation flag between CIDR ranges and hosts, exits early
- Cancelled scans get status 'cancelled' instead of 'done'
- Stop button (red StopCircle) shown in Scan History panel for running scans
- 6 new backend tests, 5 new frontend tests
2026-04-01 14:18:44 +02:00
Pouzor e9152df17a fix: remove stale reschedule call from scan config after settings endpoint split 2026-03-31 00:01:31 +02:00
Pouzor e7fc091701 fix: reschedule APScheduler job immediately when status check interval is updated
Interval was read once at startup — changing it via API had no effect
until server restart. Now calls reschedule_status_checks() after saving.
2026-03-30 23:59:47 +02:00
Pouzor 68c7672cea feat: split scan config and app settings into separate endpoints
- New GET/POST /api/v1/settings for status check interval
- Scan /api/v1/scan/config now handles ranges only
- Frontend: settingsApi client, SettingsPanel uses settingsApi
- ScanConfigModal no longer reads/writes interval
- 4 new backend tests for settings endpoint
2026-03-30 23:06:51 +02:00
Pouzor ce4af14ee6 feat: deduplicate pending devices and skip canvas/hidden nodes on scan
- At scan start, purge any pending entries whose IPs already exist in canvas
- Skip canvas nodes (approved) during scan — don't re-add to pending
- Skip hidden devices during scan — respect user's hide decision
- Add 4 tests covering all new behaviors
2026-03-30 22:01:47 +02:00
Pouzor 9c92d39629 fix: return 401 (not 500) when bcrypt hash is malformed (#21)
- verify_password catches ValueError from passlib so a mangled hash
  ($ signs stripped by shell/Docker) returns False instead of crashing
- Settings.check_password_hash logs a clear startup error with fix
  instructions when AUTH_PASSWORD_HASH doesn't start with '$2'
2026-03-29 16:03:40 +02:00
Pouzor 4ccdbed711 feat: add label position (inside/outside) and text size to Zone modal
- Label position toggle: inside (default) or outside the border
- Outside mode renders the label above/below the zone based on text_position
- Text size selector: 10/12/14/16/18/20px (default 12)
- Both fields persisted in custom_colors (no backend schema change needed)
- 8 new frontend tests, 1 new backend test
2026-03-29 03:04:29 +02:00
Pouzor 210304394e feat: read-only live view at /view?key=<LIVEVIEW_KEY>
Implements issue #5. Off by default; set LIVEVIEW_KEY in .env to enable.
No JWT required — key-based auth via ?key= query param.
Returns 403 when disabled or key is wrong.
Read-only ReactFlow canvas (pan/zoom, no editing).
Standalone mode loads from localStorage without a key.
Includes 8 backend tests and 9 frontend tests.
2026-03-28 15:27:54 +01:00
Pouzor ae29d2c8f5 fix(tests): correct integration test API shapes and test isolation 2026-03-28 12:36:28 +01:00
Pouzor cc68fcf1c1 test: add deployment test tiers (shellcheck, hadolint, Docker smoke, integration)
Tier 1 — quality.yml: ShellCheck on lxc-install.sh, hadolint on both Dockerfiles
Tier 2 — docker-ci.yml: build images, smoke-test backend health + frontend 200
Tier 3 — test_integration.py: full stack pytest (auth, canvas save/reload, dimensions)
Also adds Docker healthcheck to backend service in docker-compose.yml
2026-03-28 12:33:35 +01:00
Pouzor 2a9cbc5932 feat: resizable nodes with width/height persistence
Add NodeResizer to BaseNode so users can drag corners to resize any node.
Persist width/height through the full stack: DB model, schemas, canvas
save/load route, and migration for existing databases.

Add tests covering save, update, clear, and load of node dimensions.
2026-03-28 11:57:09 +01:00
Pouzor 06ec18a137 feat: add hardware specs to nodes (CPU, RAM, Disk) with canvas display
- Add cpu_count, cpu_model, ram_gb, disk_gb, show_hardware fields to NodeData
- NodeModal: collapsible Hardware section with toggle to show specs on canvas
- BaseNode: 2-line hardware section with Cpu/MemoryStick/HardDrive icons, only renders set fields
- DetailPanel: Hardware section with GB/TB formatting
- Backend: schema, model, DB migration, canvas save/load support
- Tests: frontend (NodeModal, DetailPanel) + backend (canvas persist/load)
2026-03-21 00:01:15 +01:00
Pouzor e14a9e87aa fix: stop exposing JWT in WebSocket URL query param
Token was visible in server logs, browser history, and proxy access logs.
Backend now accepts the connection first, then validates a JSON auth
message {"token": "<jwt>"} sent by the client on open before adding
the socket to the active connections pool.
2026-03-18 00:49:03 +01:00
Pouzor 300567c88d feat(mcp): expose parent_id in update_node tool
- Add parent_id to NodeUpdate schema in backend so PATCH /nodes/{id}
  accepts it (was silently ignored before)
- Expose parent_id in update_node MCP tool schema so the MCP SDK
  forwards it to the backend instead of stripping it
- Add regression tests in both backend and MCP layers
2026-03-13 21:29:27 +01:00
Pouzor e3f8c27a04 test: add backend tests for MCP service key auth in deps.py 2026-03-13 16:42:48 +01:00
Pouzor a56204a5f2 refactor: replace config.yml with .env for all configuration
All settings (auth credentials, scanner ranges, status_checker interval)
now live in a single .env file via pydantic-settings. config.yml and
config.yml.example are deleted.

- Settings: add auth_username, auth_password_hash, scanner_ranges,
  status_checker_interval; add load_overrides()/save_overrides() for
  persisting runtime changes to data/scan_config.json
- auth.py: read credentials directly from settings
- scan.py: read ranges/interval from settings; write-back via save_overrides()
- scheduler.py: read interval directly from settings
- main.py: call settings.load_overrides() at startup
- docker-compose.yml: remove config.yml volume mount, add new env vars
- conftest.py: set settings fields directly instead of writing a temp config.yml
2026-03-09 11:26:49 +01:00
Pouzor 45189e2748 fix: generate test config.yml in conftest for CI
Login route reads config.yml for credentials but it is gitignored.
Session-scoped autouse fixture now writes a minimal config.yml to a
temp path and patches settings.config_path so test_scan.py auth
fixtures work in CI without a real config file.
2026-03-09 09:30:53 +01:00
Pouzor 8da39327ab test: add tests for status WebSocket and scheduler
- test_status.py: 8 tests covering WS auth rejection/acceptance, broadcast_status (dead connection removal, no response_time, no connections), broadcast_scan_update
- test_scheduler.py: 9 tests covering _load_interval variants, _run_status_checks DB update/last_seen/error handling, start/stop lifecycle
- scheduler.py: reinitialize AsyncIOScheduler on each start() to avoid stale event loop across test restarts
2026-03-09 01:32:31 +01:00
Pouzor 20fe4ed9e4 fix: set SECRET_KEY in test conftest before app imports for CI 2026-03-09 00:17:59 +01:00
Pouzor 0cd263537f test: cover custom_icon persistence and cctv icon presence 2026-03-08 12:01:46 +01:00
Pouzor 88634aeb1d feat: add Camera node type + flag RTSP/camera ports during scan
- New 'camera' node type with Camera icon (frontend + types)
- Scanner fingerprint: camera ports (554, 8554, 37777, 34567, 2020) now suggest 'camera' instead of 'iot'
- service_signatures.json: all camera/NVR entries updated to suggested_node_type=camera
- 'camera' added to priority list in suggest_node_type (above iot)
2026-03-08 11:55:26 +01:00
Pouzor e306cd7b49 test: add missing tests for cluster edges, handles, colors, and none check method
- backend: cluster edge creation with source/target handles, handle persistence through PATCH
- frontend: cluster color in edgeColors, onConnect preserves sourceHandle/targetHandle
2026-03-08 11:41:09 +01:00
Pouzor f93afd0646 test: add coverage for none check method, re-scan update, icon registry 2026-03-08 01:12:32 +01:00
Pouzor 2441d72b41 fix: banner_regex sigs require actual banner match — prevents wrong type inference
Banner-specific signatures (e.g. AdGuard on port 3000) were incorrectly
matching when nmap returned no banner, causing wrong suggested_node_type
(e.g. 'router' for a media server). Now a signature with banner_regex is
only matched when a banner is present AND matches the regex.

Also make _PORT_TYPE_HINTS always contribute to suggest_node_type found set
(not just as elif fallback) so well-known ports like 8006 still resolve to
proxmox even when a generic sig also matched.
2026-03-07 23:31:33 +01:00
Pouzor 1a72f9fa20 feat: improve service detection — expanded signatures, port hints, better nmap args
- Expand service_signatures.json from 35 → ~120 entries covering *arr apps,
  smart-home (HA, MQTT, ESPHome), cameras (RTSP, Dahua, Tapo, Reolink),
  network gear (MikroTik, UniFi, Pi-hole), auth (Authelia, Authentik, Vault),
  monitoring (Grafana, InfluxDB, Loki, Uptime Kuma), containers (Portainer),
  and many more home lab services
- Expand nmap port range to cover home lab ports (8989, 7878, 8123, 554, 1883, etc.)
  and increase --host-timeout from 30s to 120s for reliable -sV detection
- Add _PORT_TYPE_HINTS fallback in suggest_node_type for ports without signatures
  (cameras→iot, MQTT→iot, Proxmox→proxmox, MikroTik→router, etc.)
- Show actual port/protocol (e.g. TCP/9999) instead of "unknown_service" so
  all open ports are visible even when not matched
- Update tests to reflect new unknown-port label format
2026-03-07 16:33:03 +01:00
Pouzor 1811afa749 fix: upgrade fastapi to 0.135.1, fix starlette CVEs, update auth test assertions
- fastapi 0.115.0 → 0.135.1 pulls starlette 0.52.1 (fixes CVE-2024-47874, CVE-2025-54121)
- pip-audit: ignore CVE-2024-23342 (ecdsa Minerva attack, no fix exists; app uses HS256 only)
- Update auth guard tests: FastAPI 0.135 returns 401 (not 403) when Bearer token is missing
2026-03-07 15:18:28 +01:00
Pouzor ba91d0f545 test: improve coverage across frontend and backend
Frontend (22 → 37 tests):
- canvasStore: add tests for onNodesChange, onEdgesChange, onConnect,
  addNode with parentId, updateEdge, deleteEdge, setProxmoxContainerMode
  ON/OFF, loadCanvas parent-before-child ordering
- edgeColors: 6 tests for EDGE_DEFAULT_COLORS (all types, hex format, values)

Backend (40 → 80 tests):
- test_canvas (new): load empty canvas, default viewport, save creates/updates/
  deletes nodes+edges, viewport upsert, custom_colors, edge custom_color+path_style,
  auth guard
- test_fingerprint (new): match_port (known, unknown, wrong protocol, banner match,
  banner no-match, no banner), fingerprint_ports (matched, unknown, mixed, empty,
  default protocol), suggest_node_type (proxmox, server, generic, priority)
- test_nodes: update/delete 404, custom_colors CRUD, container_mode CRUD, auth guard
- test_edges: update edge, update/delete 404, custom_color, path_style, auth guard
2026-03-07 15:01:00 +01:00
Pouzor 8a18ded2bc fix: remove trailing slash from GET/POST routes to prevent 307→403 redirect
FastAPI's redirect_slashes=True causes GET /api/v1/canvas to 307 redirect
to /api/v1/canvas/ — axios follows the redirect but drops the Authorization
header, resulting in 403. Fixed by declaring routes as empty string instead
of '/' so no redirect is issued. Same fix applied to nodes and edges routes.
Updated all tests to use paths without trailing slashes.
2026-03-07 01:01:24 +01:00
Pouzor 0bd714a68b feat: Phase 3 & 4 — monitoring, discovery, polish, deployment
Phase 3 — Discovery & Monitoring:
- Network scanner: nmap wrapper + mock fallback, fingerprint service (35 signatures)
- Status checker: ping/http/https/tcp/ssh/prometheus/health per-node checks
- APScheduler: status checks every 60s, WebSocket broadcast
- WebSocket /ws/status: live node status updates to frontend
- Sidebar panels: Pending Devices, Hidden Devices, Scan History
- Auth token persisted to localStorage (survive page refresh)
- 24 new backend tests (scan flow + status_checker)

Phase 4 — Polish & Deployment:
- Auto-layout: Dagre hierarchical TB via Toolbar button
- Export PNG: html-to-image download via Toolbar button
- Scan config modal: CIDR ranges + check interval, GET/POST /api/v1/scan/config
- Dockerfile.backend (Python 3.13 slim + nmap), Dockerfile.frontend (nginx)
- docker-compose.yml with data volume and NET_RAW cap for ping
- scripts/lxc-install.sh: Proxmox VE systemd bootstrap
- README.md: quick-start, config reference, stack overview
2026-03-07 00:45:50 +01:00
Pouzor 44a448e26d fix: all lint errors, test + lint pre-commit passing
Frontend:
- Split nodeTypes/edgeTypes into separate .ts files (react-refresh)
- Remove setState-in-effect in NodeModal (key prop reset)
- Fix handleSave accessed before declaration (useRef pattern)
- Exclude src/components/ui/** from eslint (shadcn generated)
- Use defineConfig from vitest/config for test type support

Backend:
- ruff --fix: sort imports, datetime.UTC alias
- Raise line-length to 120, ignore E501 in tests
- Break long update_node/update_edge signatures
- pyproject.toml: per-file-ignores for tests
2026-03-06 23:58:10 +01:00