diff --git a/.env.example b/.env.example index fef3ea3..3ba2020 100644 --- a/.env.example +++ b/.env.example @@ -3,7 +3,6 @@ PROXMOX_API_URL=http://proxmox.lan:8006/api2/json PROXMOX_WEB_URL=http://proxmox.lan:8006 -PROXMOX_CONSOLE_TOKEN=root@pam!monitorapp PROXMOX_TOKEN_ID=root@pam!monitorapp PROXMOX_TOKEN_SECRET= API_POLL_INTERVAL=30000 diff --git a/CLAUDE.md b/CLAUDE.md index a603c98..d693fc8 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -27,22 +27,23 @@ src/ │ ├── layout.js # Root layout (Inter font, metadata) │ └── page.js # Entry — renders ├── components/ -│ ├── Dashboard.js # Main UI: tabs, summary cards, power action handler -│ ├── NodeList.js / NodeCard.js # Node overview -│ ├── VMList.js / LXCList.js # VM/LXC list views (each row has Console/Start/Stop buttons) +│ ├── Dashboard.js # Main UI: tabs, summary cards, polling, power action handler +│ ├── NodeList.js / NodeCard.js # Node overview (collapsible cards with subtotals) +│ ├── VMList.js / LXCList.js # VM/LXC lists grouped by node with search, power actions, console links, IPs │ └── StatusBadge.js # Status indicator └── lib/ - └── proxmox.js # Proxmox API client — getNodes, getVMs, getLXC, getSummary, powerControl + └── proxmox.js # Proxmox API client — getNodes, getVMs, getLXC, getSummary, powerControl, getConfigIPs ``` ## Key Patterns - **Dual API endpoints**: Queries use `/api2/json`, power operations use `/api2/extjs`. Power actions send empty body `{}`. - **API routes** aggregate data across all Proxmox nodes before returning to the client. They are thin wrappers around `lib/proxmox.js`. -- **Dashboard** uses direct React state + manual `axios.get()` calls for power actions (instant UI sync), and SWR with `refreshInterval` for periodic polling of the same endpoints. +- **Dashboard** uses direct React state + manual `setInterval` polling (not SWR) for periodic data fetching. Power actions use direct `axios.get()` calls for instant UI sync. - **Power action flow**: `handlePowerAction` in Dashboard.js fires `POST /api/power`, then immediately re-fetches all four caches (`/api/nodes`, `/api/vms`, `/api/lxc`, `/api/status`) and sets state directly. Use `requestAnimationFrame` flush before the API call so status messages paint. - **Auth**: PVEAPIToken header: `Authorization: PVEAPIToken=${TOKEN_ID}=${TOKEN_SECRET}`. -- **Console**: VMList/LXCList build `consoleUrl` from `PROXMOX_WEB_URL` with `?console={qemu|lxc}&vmid={id}&node={node}` params. Dashboard opens it via `window.open()` in a new tab so browser session cookies handle Proxmox auth. +- **Console**: VMList/LXCList build `consoleUrl` from `PROXMOX_WEB_URL` with `?console={qemu|lxc}&vmid={id}&node={node}&resize=scale` params. Dashboard opens it via `window.open()` in a new tab so browser session cookies handle Proxmox auth. LXC console uses `xtermjs=1`, QEMU uses `novnc=1`. +- **Per-VM/LXC IPs**: `getConfigIPs()` in proxmox.js fetches QEMU IPs from `/agent/network-get-interfaces`, LXC IPs from `/interfaces` endpoint (fallback to `/config` `net0`-`net7` parsing). IPv4 only. - **TLS**: `NODE_TLS_REJECT_UNAUTHORIZED=0` in `.env.local` for self-signed certs. Shows a console warning — acceptable for internal lab use. - **Tailwind CSS 4**: Uses `@tailwindcss/postcss` config. CSS vars in `globals.css` for theming. - **All API routes return 502 on Proxmox failures** with `{ error, data: null }`. @@ -55,7 +56,7 @@ src/ | `PROXMOX_TOKEN_ID` | Yes | API token ID | | `PROXMOX_TOKEN_SECRET` | Yes | API token secret | | `PROXMOX_WEB_URL` | No | Web UI URL (defaults from PROXMOX_API_URL) | -| `NEXT_PUBLIC_API_POLL_INTERVAL` | No | Poll interval ms (default 30000) | +| `API_POLL_INTERVAL` | No | Poll interval ms (default 30000) | | `NODE_TLS_REJECT_UNAUTHORIZED` | No | `0` for self-signed certs | ## Git Workflow diff --git a/README.md b/README.md index 177d312..40ba908 100644 --- a/README.md +++ b/README.md @@ -4,17 +4,24 @@ A real-time dashboard for monitoring Proxmox VE nodes, VMs, and LXC containers. ## Features -- **Node overview** — CPU, memory, uptime, and load for each Proxmox node +- **Node overview** — CPU, memory, uptime, and load for each Proxmox node, grouped into collapsible cards - **VM & LXC tracking** — status, resource usage across all nodes - **Summary cards** — total nodes, running/stopped VMs, aggregate memory -- **Auto-refresh** — polling every 30s (configurable) -- **Tabbed navigation** — switch between Nodes, VMs, and LXCs +- **Per-VM/LXC IP addresses** — IPv4 from guest agent (QEMU) or network config (LXC), displayed below VM/LXC name +- **Power control** — Start/stop/restart buttons per row +- **Console links** — Opens Proxmox web UI console in a new tab +- **Search / filter** — Client-side filtering by name, node, or status with auto-expand +- **Group by node** — Collapsible node cards with running/stopped subtotals +- **Manual refresh** — Button in header to refresh all data +- **Custom poll interval** — Adjustable polling interval (default 30s) via header input +- **Uptime display** — Formatted as "Xd Yh Zm" +- **Auto-refresh** — Manual `setInterval` polling every 30s (configurable) ## Tech Stack - **Framework** — Next.js 15 (App Router) - **UI** — React 19, Tailwind CSS 4 -- **Data fetching** — SWR (auto-refresh), Axios +- **Data fetching** — Axios with manual `setInterval` polling - **Proxmox** — API2 Token-based auth ## Getting Started @@ -32,7 +39,7 @@ cp .env.example .env.local ``` PROXMOX_API_URL=https://your-proxmox-host:8006/api2/json PROXMOX_TOKEN_ID=root@pam!token-name -PROXMOX_TOKEN_SECRET=your-secret-uuid +PROXMOX_TOKEN_SECRET= API_POLL_INTERVAL=30000 NODE_TLS_REJECT_UNAUTHORIZED=0 ``` @@ -69,27 +76,30 @@ src/ │ │ ├── nodes/route.js # Proxmox node stats │ │ ├── vms/route.js # QEMU VMs across all nodes │ │ ├── lxc/route.js # LXC containers across all nodes -│ │ └── status/route.js # Aggregated summary +│ │ ├── status/route.js # Aggregated summary +│ │ ├── power/route.js # Power control endpoint +│ │ └── proxy/[...]/ # Reverse proxy to Proxmox web UI │ ├── layout.js │ └── page.js ├── components/ -│ ├── Dashboard.js # Main dashboard with tabs & summary -│ ├── NodeList.js # Node cards -│ ├── NodeCard.js # Single node display -│ ├── VMList.js # VM list view -│ ├── LXCList.js # LXC list view -│ └── StatusBadge.js # Online/good/warn/error/dead +│ ├── Dashboard.js # Main dashboard with tabs, summary, polling +│ ├── NodeList.js # Node overview +│ ├── NodeCard.js # Single node card +│ ├── VMList.js # VM list with group by node, filter, power actions +│ ├── LXCList.js # LXC list with group by node, filter, power actions +│ └── StatusBadge.js # Status indicator └── lib/ - └── proxmox.js # Proxmox API client & data mappers + └── proxmox.js # Proxmox API client — nodes, VMs, LXC, IPs, power control ``` ## Environment Variables | Variable | Required | Description | |---|---|---| -| `PROXMOX_API_URL` | Yes | Base URL of the Proxmox API | +| `PROXMOX_API_URL` | Yes | Base URL of the Proxmox API (e.g. `https://host:8006/api2/json`) | | `PROXMOX_TOKEN_ID` | Yes | API token ID (e.g. `root@pam!monitorapp`) | | `PROXMOX_TOKEN_SECRET` | Yes | API token secret | +| `PROXMOX_WEB_URL` | No | Web UI URL (defaults from `PROXMOX_API_URL`). Used for console links. | | `API_POLL_INTERVAL` | No | Refresh interval in ms (default: `30000`) | | `NODE_TLS_REJECT_UNAUTHORIZED` | No | Set to `0` for self-signed certs | @@ -98,6 +108,14 @@ src/ | Route | Method | Description | |---|---|---| | `/api/nodes` | GET | All Proxmox nodes | -| `/api/vms` | GET | All QEMU VMs | -| `/api/lxc` | GET | All LXC containers | +| `/api/vms` | GET | All QEMU VMs (with IPv4 from guest agent) | +| `/api/lxc` | GET | All LXC containers (with IPv4 from interfaces/config) | | `/api/status` | GET | Aggregated summary stats | +| `/api/power` | POST | Power control (start/stop/restart) | +| `/api/proxy/*` | GET | Reverse proxy to Proxmox web UI | + +## Per-VM/LXC IP Addresses + +- **QEMU VMs**: fetched from `/nodes//qemu//agent/network-get-interfaces` (requires QEMU guest agent installed) +- **LXC**: fetched from `/nodes//lxc//interfaces` (preferred) or parsed from `/config` `net0`-`net7` fields (fallback) +- IPv4 only — IPv6 addresses are excluded diff --git a/backlog.md b/backlog.md index c831bf4..81794d6 100644 --- a/backlog.md +++ b/backlog.md @@ -28,15 +28,15 @@ ## Quick Wins (low effort, high value) ### Per-VM/LXC IP Addresses -- LXCs: parses `ip=` and `ip6=` from `net0`, `net1`, etc. fields in `/nodes//lxc//config`. -- VMs: fetches from `/nodes//qemu//agent/network-get-interfaces` and reads `ipaddresses`. -- Displays IPs below the name in the table. +- LXCs: fetches from `/interfaces` endpoint; falls back to `ip=` parsing from `net0`-`net7` in `/config`. +- VMs: fetches from `/agent/network-get-interfaces` (requires QEMU guest agent). +- IPv4 only. Displays IPs below the name in the table. - **Status: done** ### Search / Filter - Client-side text input filtering table rows by name/node/status - Uses `useMemo` for performant filtering, auto-expands single node and filter-matched nodes -- **Status: done (merged to main)** +- **Status: done** ## Medium Effort @@ -51,9 +51,6 @@ - Show vCPU count, total disk size alongside usage stats - Note: disk.total is not populated by Proxmox API — only disk.used is available -### Per-VM IP Addresses -- Done — uses `/nodes//qemu//agent/network-get-interfaces` for VMs and config `net0`/`net1` parsing for LXCs. - ### Per-VM CPU/Memory Trend - Periodic polling to build a small trend chart