Commit Graph

154 Commits

Author SHA1 Message Date
Pouzor 3fb3bf016b fix: snapshot history on drag start so undo restores pre-move position 2026-03-12 12:06:04 +01:00
Pouzor ba032a45af feat: canvas history (undo/redo), copy/paste nodes, node search, shortcuts modal
- Undo/Redo (Ctrl+Z / Ctrl+Y): 50-entry snapshot stack in canvasStore; snapshot before all mutations and on node drag stop
- Copy/Paste (Ctrl+C / Ctrl+V): copy selected nodes to clipboard, paste with +50px offset and new IDs
- Node search (Ctrl+K): spotlight overlay — fuzzy search by label/IP/hostname, jumps + focuses matched node
- Shortcuts modal (?): lists all keyboard shortcuts, accessible via ? key or toolbar ? button
- Toolbar: undo/redo buttons (disabled when stack empty), ? help button
2026-03-12 11:56:38 +01:00
Pouzor 68b35a0c30 feat: improve edge flow animation speed, size, and direction
- Slow down animation (6s → 10s, proxmox cluster 20s)
- Larger dot: length 20, width 2× edge stroke
- Reverse direction to travel parent→child
- Proxmox-to-proxmox edges ping-pong (bidirectional cluster animation)
2026-03-12 10:47:12 +01:00
Pouzor 41cfccbd37 feat: add edge flow animation (dot traveling source→target)
- Add animated toggle per edge in EdgeModal (cyan switch, "Flow Animation")
- SVG-native <animate> element for reliable cross-browser dot animation
- Persist animated field: backend model, schemas (EdgeBase/EdgeUpdate/EdgeSave), DB migration
- Include animated in App.tsx edgesToSave serialization so it survives save/reload
- Add animated: bool to EdgeData TypeScript type
2026-03-12 10:23:18 +01:00
Pouzor 55a842cdad Update Readme 2026-03-11 16:41:40 +01:00
Pouzor 7074c5387b docs: add screenshots to README 2026-03-11 16:38:51 +01:00
Pouzor 15f210470a fix: remove duplicate hideIp property in canvasStore v1.1.1 2026-03-11 16:21:15 +01:00
Pouzor 271bbf2d01 feat: add hide IP toggle in sidebar
- Add hideIp / toggleHideIp to canvasStore (UI-only, not persisted)
- Add maskIp util: 192.168.1.115 → 192.168.XX.XX (non-IPv4 passthrough)
- BaseNode reads hideIp from store and masks last two octets when active
- Sidebar shows Eye/EyeOff toggle below Scan Network, highlights when active
2026-03-11 16:15:22 +01:00
Pouzor 8affcda09d fix: improve auto-layout peer alignment and edge direction
- Exclude peer-to-peer edges from Dagre to keep same-type nodes at same rank
- Snap peer group Y to average, re-space X preserving user's original order
- Reverse upward edges (sourceHandle=top) in Dagre so child nodes always render below parent
- Add layout.test.ts covering peer alignment, X ordering, and upward edge cases
2026-03-11 14:29:29 +01:00
Pouzor 92d505f78c feat: add logo, favicon, and theme system
- Add custom SVG favicon and Logo component (house + network nodes motif)
- Update page title to Homelable with meta description
- Show Logo in sidebar header and toolbar
- Add theme store and ThemeModal for canvas style switching
- Refactor node colors and edge styles for theme support
2026-03-11 14:29:15 +01:00
Pouzor 16de7cd390 fix: accept string | null in Select onValueChange (Base UI type) 2026-03-10 17:40:21 +01:00
Pouzor 0fb091b12c fix: resolve TS build errors for CI
- Add groupRect entry to NODE_DEFAULT_COLORS (required by Record<NodeType, NodeColors>)
- Explicitly type Select onValueChange callbacks as (v: string) to satisfy
  stricter type inference in tsc -b build mode
2026-03-10 17:37:00 +01:00
Pouzor 1f1dfd807b feat: add/remove services in detail panel + fix service URL detection
- Any TCP service (except port 22 and known non-HTTP protocols) is now
  clickable — fixes Sonarr, Radarr, Jellyfin, Proxmox web UI, etc.
- HTTPS auto-detected for port 443/8443 or names containing ssl/tls/https
- Non-HTTP blocklist: FTP, SMTP, DNS, LDAP, SMB, MySQL, Postgres,
  Redis, MongoDB, Kafka, Memcached, etc.
- Inline "Add service" form in detail panel (name, port, tcp/udp)
- Remove button (×) on hover for each service row
- Group rect nodes excluded from detail panel
- getServiceUrl extracted to utils/serviceUrl.ts
- 13 new tests (79 total, all pass)
v1.1.0
2026-03-10 17:29:41 +01:00
Pouzor 1e72366d03 feat: add Group Rectangle — resizable decorative zones on canvas
- New GroupRectNode: rounded rect with NodeResizer (8 handles), no
  connection handles, always behind network nodes via negative zIndex
- GroupRectModal: label, font preset (Inter/Mono/Serif), 3×3 text
  position grid, text/border/background color pickers, z-order 1–9
- Sidebar: "Add Rectangle" button (below Add Node), group rects
  excluded from node count stats
- Save/load: size persisted in custom_colors.width/height, no backend
  schema changes required
- elevateNodesOnSelect=false on ReactFlow so selected rects never
  pop above network nodes
- Tests: 3 new store tests + 9 GroupRectModal tests (66 total, all pass)
2026-03-10 16:59:40 +01:00
Pouzor c0f1d1ff1a chore: bump version to 1.0.0 v1.0.0 2026-03-10 00:30:05 +01:00
Pouzor 6f7f6be5b6 fix: remove asChild from TooltipTrigger — base-ui does not support Radix asChild prop 2026-03-10 00:11:56 +01:00
Pouzor 564f339927 feat: interactive CTID and root password prompts in Proxmox install script
- Prompt for container ID with auto-detected default (pvesh nextid)
- Prompt for root password with confirmation loop instead of auto-generating
- Both still overridable via CTID/ROOT_PASSWORD env vars for automation
2026-03-10 00:04:16 +01:00
Pouzor 99ab490886 fix: replace ping-based wait with apt-get/ip route checks, bootstrap curl before installer
- ping not available in base Debian 12 LXC — was silently hitting 20s timeout
- curl may also be missing — install it explicitly before running lxc-install.sh
- use ip route check for network readiness instead of ping
2026-03-09 23:59:55 +01:00
Pouzor cae745e7d8 fix: replace tr/head password gen with openssl rand to avoid SIGPIPE killing script under set -o pipefail 2026-03-09 23:50:03 +01:00
Pouzor 5ae8fb242c feat: generate random root password for LXC container and display it at end of install 2026-03-09 23:45:06 +01:00
Pouzor 22d8439981 fix: correct pvesm awk column ($3 not $2 for active status) and rename HOSTNAME to CT_HOSTNAME to avoid bash reserved variable clash 2026-03-09 23:40:31 +01:00
Pouzor 9db0931665 fix: auto-detect available LXC storage instead of hardcoding local-lvm 2026-03-09 23:37:21 +01:00
Pouzor e669d631a4 fix: split LXC install into host creator + container installer
- install-proxmox.sh: runs on PVE host, creates Debian 12 LXC via pct,
  then calls lxc-install.sh inside the container (tteck community-scripts pattern)
- lxc-install.sh: runs inside the container, fixed to use .env instead of
  config.yml, correct bcrypt hash, proper CORS_ORIGINS with container IP
- README: clarify that install-proxmox.sh runs on the PVE host
2026-03-09 23:33:25 +01:00
Pouzor 8ace4b1da8 fix: add sourceHandle/targetHandle to all demo edges so connections use bottom→top 2026-03-09 22:51:13 +01:00
Pouzor 5246770a3a fix: ensure data directory exists before SQLite engine init
On fresh installs the data/ dir may not exist, causing SQLite to create a
stub file without write permissions. mkdir(parents=True, exist_ok=True) runs
at import time before any DB operation.

Also add data/.gitignore to prevent homelab.db from ever being committed.
2026-03-09 22:38:26 +01:00
Pouzor 20d9473f38 feat: detect virtual machines from MAC OUI and show badge on pending devices
Backend:
- Add MAC OUI lookup table (QEMU 52:54:00, Proxmox bc:24:11, VMware, VBox, Hyper-V)
- suggest_node_type() now accepts mac param and uses OUI as a low-priority hint

Frontend:
- Detect VM type from MAC prefix on pending device cards
- Show colored badge (QEMU / PVE / VMware / VBox / Hyper-V) in orange with tooltip
2026-03-09 22:22:38 +01:00
Pouzor 6ae7f7768f fix: replace datetime.UTC with timezone.utc for Python 3.10 compatibility
datetime.UTC was introduced in Python 3.11. Users on 3.10 get an ImportError.
Also lower ruff/mypy target-version from py313 to py310 to match minimum supported version.
2026-03-09 22:01:32 +01:00
Pouzor 99b40fa9e9 fix: prioritize named service over SSH/HTTP/HTTPS for pending device card title 2026-03-09 21:54:15 +01:00
Pouzor 1d6d8f0178 feat: enhance pending device cards with service badges and recognized service title
- Show recognized service name (Home Assistant, Synology, Proxmox, etc.) as
  card title instead of IP when detected via service signatures
- Keep IP displayed smaller below when title differs from IP
- Add SSH / HTTP / HTTPS badges as colored pills
- Show +N badge for additional services beyond the three common ones
2026-03-09 21:46:40 +01:00
Pouzor 58ce2ad199 Update readme for --standalone command 2026-03-09 14:27:10 +01:00
Pouzor 7dbd625ff2 fix: use standalone nginx config without backend upstream in standalone mode
Nginx fails to start when the 'backend' upstream doesn't exist.
Standalone build now copies a minimal nginx config that only serves the SPA.
2026-03-09 14:09:40 +01:00
Pouzor 7ba5e888b4 feat: add standalone (frontend-only) mode via VITE_STANDALONE build flag
- App.tsx: bypass auth gate, swap canvas save/load to localStorage
- Sidebar.tsx: hide scan views and Scan Network button
- useStatusPolling.ts: skip WebSocket connection
- Dockerfile.frontend: accept VITE_STANDALONE build arg
- docker-publish.yml: build ghcr.io/pouzor/homelable-frontend-standalone image
- docker-compose.standalone.yml: frontend-only compose file
- install.sh: add --standalone flag

Usage:
  curl -fsSL .../install.sh | bash -s -- --standalone
  cd homelable && docker compose up -d
2026-03-09 14:00:25 +01:00
Pouzor 1cd93ed250 fix: add linux/arm64 to Docker build platforms for Apple Silicon support 2026-03-09 13:33:49 +01:00
Pouzor 452f3b0860 feat: add one-liner install script
curl -fsSL https://raw.githubusercontent.com/Pouzor/homelable/main/install.sh | bash

Handles fresh install and updates. Pulls prebuilt images, creates .env from
.env.example on first run. Update README Quick Start accordingly.
2026-03-09 12:03:27 +01:00
Pouzor 6a7113193f feat: add GitHub Actions Docker publish workflow and prebuilt install
- Build and push backend/frontend images to ghcr.io on every push to main and on version tags
- Add docker-compose.prebuilt.yml for pull-based install (no clone needed)
- Update README Quick Start with inline install one-liner using prebuilt images
2026-03-09 12:01:04 +01:00
Pouzor f3a9d95b18 change readme 2026-03-09 11:59:02 +01:00
Pouzor 2de5a5b3aa docs: update README configuration section to reflect .env-based setup
- Replace config.yml references with .env variables
- Document single-quote requirement for AUTH_PASSWORD_HASH
- Fix dev mode setup to copy from root .env.example
2026-03-09 11:57:11 +01:00
Pouzor 5078f4af92 fix: quote AUTH_PASSWORD_HASH in .env.example to prevent Docker dollar-sign interpolation
Bcrypt hashes contain $ chars that Docker Compose interpolates as variables.
Single-quoting the value prevents interpolation in both Docker Compose and python-dotenv.
2026-03-09 11:53:37 +01:00
Pouzor 6f46913b80 fix: use env_file in docker-compose to avoid bcrypt hash dollar-sign misparse
Docker Compose interpolates \$ in environment values as variable refs, breaking
the bcrypt hash (\$2b\$12\$...). Switching to env_file passes values verbatim.
2026-03-09 11:45:35 +01:00
Pouzor 84860b0d99 fix: consolidate .env.example to root, add missing auth defaults
- Add AUTH_USERNAME, AUTH_PASSWORD_HASH (admin/admin) to root .env.example
- Remove duplicate backend/.env.example — root file is the single source of truth
- Fix fresh-install login failure caused by missing password hash
2026-03-09 11:42:00 +01:00
Pouzor dafcd12e40 fix: default admin/admin hash in .env.example, remove required constraint on AUTH_PASSWORD_HASH 2026-03-09 11:32:32 +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 acc5bc6a64 fix: cast Service[] to ServiceInfo[] in pending device approve 2026-03-09 11:09:03 +01:00
Pouzor c6733e4dcd fix: resolve TypeScript build errors blocking Docker image
- nodeColors.ts: add missing camera/printer/computer/cpl entries to Record<NodeType, NodeColors>
- CanvasContainer.tsx: use ConnectionMode enum instead of bare string literal
- export.ts: cast style object to Partial<CSSStyleDeclaration> for CSS custom property
- Sidebar.tsx: cast suggested_type to NodeType to satisfy NodeData type
2026-03-09 11:07:22 +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 41e29ac199 fix: suppress mypy false positive on pydantic-settings required field 2026-03-09 00:15:42 +01:00
Pouzor 6ace796c8b security: fix C2, H5 and M1
C2 - JWT token was stored in localStorage (XSS-accessible):
  - Switch Zustand persist storage from localStorage to sessionStorage
  - Token is now scoped to the current tab and cleared on browser close

H5 - docker-compose had unsafe SECRET_KEY fallback:
  - Replace ${SECRET_KEY:-change_me_in_production} with :? syntax
  - Docker Compose now aborts with a clear error if SECRET_KEY is unset

M1 - Login endpoint had timing leak allowing username enumeration:
  - Always call verify_password() regardless of username match
  - Use hmac.compare_digest() for constant-time username comparison
  - Both checks run every time; attacker cannot distinguish wrong
    username from wrong password via response timing
2026-03-09 00:13:01 +01:00
Pouzor dbfc8a2a32 security: fix C3, C1 and H1
C3 - config.yml contains credentials, remove from git tracking:
  - Add backend/config.yml to .gitignore
  - git rm --cached to untrack it
  - Add backend/config.yml.example with instructions

C1 - SECRET_KEY must come from .env, no unsafe default:
  - Remove hardcoded "change_me_in_production" default from config.py
  - App now fails to start if SECRET_KEY is not set (pydantic required field)
  - Generate real random key in backend/.env (gitignored)
  - Add backend/.env.example for new contributors

H1 - WebSocket /ws/status was unauthenticated:
  - Backend: require ?token= query param, validate via decode_token(),
    close with code 1008 (Policy Violation) if missing or invalid
  - Frontend: append ?token=<jwt> to WebSocket URL
2026-03-09 00:05:10 +01:00