Commit Graph

33 Commits

Author SHA1 Message Date
f67ee24155 Merge branch 'feature/metrics-history'
All checks were successful
Backend Tests / pytest (Python 3.12) (push) Successful in 50s
Deploy to Production / Trigger production update (push) Successful in 7s
2026-06-22 02:47:45 +05:00
38d485be4b fix(disk-resize): honest task status, auto-grow FS, CRLF-safe guest exec
Backend fixes for VM/LXC disk resize:
- resize_vm_disk/resize_container_disk now honor wait_for_task and raise
  the real Proxmox exitstatus (e.g. "disk 'scsi0' does not exist") instead
  of always returning success.
- add grow_vm_filesystem: after a VM resize, rescan the disk (retry loop,
  needed for SATA/SCSI), growpart the partition and resize the FS
  (ext*/xfs/btrfs) via the guest agent; surface its output to the API.
- execute_script normalizes CRLF/CR to LF so pasted scripts and growpart
  don't break on trailing \r ("partition-number must be a number").

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-22 02:43:03 +05:00
074983a1f6 fix(metrics): correct cpu/disk% chart scaling, evict stale collector state, lighten nic query, drop polling & dead RRD code
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-21 21:35:02 +05:00
6769e5b218 feat(metrics): add /metrics read endpoints for vm and container 2026-06-21 21:35:02 +05:00
1cc4d86b18 feat(clone): real disk-copy progress, owner assignment and DB registration
- _do_clone_sync now tails the PVE clone task log and maps the disk
  transfer percentage to deploy progress 50→95% instead of a static 70%.
- Add owner_id to CloneRequest; admins may assign the clone to another
  user (mirrors LXC deploy). Register the cloned instance via
  save_vm_instance so it has an owner and is visible to limited users.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 00:38:26 +05:00
de967c35e0 refactor(rbac): migrate API permission checks to resource:action format
Replace all legacy dotted permission codes (vms.view, proxmox.manage, ...)
in PermissionChecker/has_permission across app/api with the new
resource:action format (vm:view, server:manage, ...), per LEGACY_PERMISSION_MAP.
All 33 codes used by the API are now registered in the permission registry.

Also switch the /api/auth/me admin branch to get_all_permissions_v2 so the
endpoint emits permissions in the new format only.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-20 02:07:48 +05:00
e581f19e60 refactor(api): remove duplicate/superseded endpoints
Drop dead route handlers that have an active replacement and zero callers
(frontend, backend, tests):
- create-lxc, create-lxc-smart  -> superseded by POST /lxc/deploy
- clone-lxc                      -> superseded by container/{vmid}/clone
- {server_id}/lxc-templates      -> duplicate of /lxc-templates/{server_id}
- dashboard/stats               -> dashboard uses resources/all + alerts
- permissions (v1)              -> superseded by permissions/v2

The identically-named ProxmoxClient/SecurityService methods are kept (they
back the surviving endpoints).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15 00:14:35 +05:00
9dd992c6b9 fix(security): enforce VM access control on status endpoints; fix CI artifact upload
Some checks failed
Backend Tests / pytest (Python 3.12) (push) Has been cancelled
- get_vm_status and get_container_status were missing require_vm_access(),
  allowing any user with vms.view to see any VM status regardless of ownership.
  Now users with vms.view.own can only access their own VMs (403 otherwise).
- Downgrade upload-artifact@v4 → @v3: v4 is not supported on Gitea (GHES)
  and caused CI failures on every run.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-06-13 18:58:15 +05:00
50f2ee1966 fix(security): authenticate VNC/terminal WebSockets and harden SSH/config
Some checks failed
Backend Tests / pytest (Python 3.12) (push) Failing after 46s
Critical security and robustness fixes from a code audit:

- WS terminal/VNC now require a JWT (?token=), validate the session,
  vms.console permission and VM ownership before bridging to Proxmox
  (previously the LXC terminal gave unauthenticated root shell access).
  Frontend ConsolePage passes the token in both WS URLs.
- Fix SSH fallback for `pvecm delnode`: SSHClient now supports password
  auth (it previously crashed with TypeError); ProxmoxClient stores the
  password for SSH fallbacks and the host port suffix is stripped.
- Add production startup warnings for default ADMIN_PASSWORD and wildcard
  CORS_ORIGINS.
- dashboard: use tz-aware utcnow() instead of naive datetime.utcnow().
- crypto: narrow decrypt_value except to InvalidToken and log failures.
- ssh_client: guard the shared connection cache with a lock; make
  unknown-host-key rejection opt-in via SSH_REJECT_UNKNOWN_HOSTS.
- logging: send loguru console output to stderr instead of stdout.
- Sync version fallback to 1.5.1; remove one-off refactor scripts.
- Add CODE_REVIEW.md audit report.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-13 06:31:26 +05:00
bfd92102cf Refactor backend: Split models and proxmox_client into modules, add alembic
Some checks failed
Backend Tests / pytest (Python 3.12) (push) Failing after 2m27s
2026-06-13 03:53:27 +05:00
markmorado
c317c4d81a fix: deduplicate SSH keys on LXC/VM create; filter qemu templates in wizard
Some checks failed
Backend Tests / pytest (Python 3.12) (push) Has been cancelled
2026-05-08 13:06:23 +02:00
markmorado
323d1e57fa feat: SSH keys management, backups fixes, instances/nodes/templates improvements, dev mode support
Some checks failed
Backend Tests / pytest (Python 3.12) (push) Has been cancelled
2026-05-07 00:30:21 +02:00
markmorado
a92cb0df33 fix(vms): auto-stop running VM/CT before destroy
The destroy endpoints returned 400 'Cannot delete a running container.
Stop it first.' when the user clicked Destroy on a running instance.
Since the destroy dialog already requires typing the instance name to
confirm, the action is unambiguously intentional — now both endpoints
issue stop_container/stop_vm with force=True, wait up to 15s for the
status to leave 'running', and only then proceed with deletion.
2026-05-06 21:11:15 +02:00
markmorado
aba2eb3583 feat(lxc): support reinstall for LXC created from CT template file
Previously the reinstall endpoint required cached.template_id (clone-from-VM-
template flow), so LXC instances created via the wizard from an OS template
file (e.g. local:vztmpl/debian-13-...tar.zst) returned 'VM has no associated
template; reinstall is not available'.

Now reinstall works in two modes:
  1) clone-from-template (existing): VM/LXC with cached.template_id pointing
     to OSTemplate.vmid
  2) ostemplate-file (new, LXC only): cached.template_name like
     'storage:vztmpl/...tar.zst'. Reinstall stops, deletes, then re-creates
     the container with the same VMID using create_lxc_container, preserving
     network config, storage, rootfs size, swap, features, ssh_keys, etc.
     pulled from the previous container config + VMInstance cache.
2026-05-06 20:11:05 +02:00
markmorado
79d6864d43 feat(vm-ops): make reinstall/clone/change-password async via DeployTask
Backend:
- Add DeployTask.kind column ('deploy'|'reinstall'|'clone'|'change_password')
  + Migration 23: ALTER TABLE deploy_tasks ADD kind, drop NOT NULL on template_id
- Update DeployTask.to_dict() with kind-aware Russian descriptions
- New backend/app/api/proxmox/async_ops.py with three workers:
    _do_reinstall_sync, _do_clone_sync, _do_change_password_sync
  All run on shared _deploy_executor thread pool from templates.py and
  report progress through _update_deploy_task (DB + WebSocket broadcast)
- Rewrite five vms.py endpoints to return 202 + DeployTaskStartResponse:
    POST /vm/{vmid}/clone, /container/{vmid}/clone,
    POST /vm/{vmid}/reinstall,
    POST /vm/{vmid}/change-password, /container/{vmid}/change-password

Frontend:
- DeployTask: add 'kind' and 'server_id' fields
- useCloneInstance/useReinstallInstance/useChangePassword now return
  AsyncTaskResponse {task_id, status, name}; remove invalidateQueries
- InstanceActionDialogs: each onSuccess pushes task to deploy-tasks-store
  with appropriate kind, shows 'launched in background' toast
- InstancesPage:
  * ghostRows only for kind in {deploy, clone}, deduped by (server_id, vmid)
  * reinstall / change_password tasks shown as inline overlay (badge + ring)
    on the existing VM row's status column
2026-04-30 00:08:41 +02:00
markmorado
89d70654fc fix(reinstall): use OSTemplate.vmid (not template_vmid) and convert memory bytes->MB
- AttributeError: OSTemplate has no 'template_vmid' field; the actual column is 'vmid'.
- Use OSTemplate.get_vmid_for_node() to support cross-node replicated templates,
  falling back to source_node/vmid otherwise.
- VMInstance.memory is stored in bytes, but Proxmox config expects MB.
2026-04-29 23:48:48 +02:00
markmorado
5169bf80a3 fix: resolve FastAPI route ordering conflict for clone/reinstall/change-password
Specific endpoints (clone, reinstall, change-password) were being intercepted
by the generic /{action} handler because they were registered AFTER it.

Fix: move CloneRequest, ChangePasswordRequest models, _resolve_server,
_get_client_or_503 helpers, and all 5 specific endpoint functions BEFORE
the generic @router.post('.../{action}') handlers.

Remove duplicate definitions from the old 'Additional Instance Operations'
section at the end of the file.
2026-04-29 23:00:33 +02:00
markmorado
1d08c815a7 fix: frontend-backend integration — 14 issues resolved
- nginx: add proxy routes for /settings/, /templates/, /ipam/, /logs/, /admin/, /health
- dashboard: fix data format mismatch (servers[] not nodes[]/vms[])
- dashboard: add WebSocket subscription to dashboard_metrics for real-time updates
- dashboard: connect activity feed to /logs/api/logs endpoint
- dashboard: reduce polling to 15s with WS as primary update source
- graphs: fix rrddata response unwrap (backend returns {data:[],timeframe:''})
- graphs: add i18n translations for chart titles
- console: add xterm.js CSS import for proper terminal rendering
- console: add i18n translations (connecting, reconnect)
- workspace: add switcher dropdown in TopBar
- workspace: add edit dialog in WorkspacesPage
- workspace: create Zustand store with localStorage persistence
- workspace: add X-Active-Workspace header to API client
- sidebar: replace generic icon with actual favicon.svg logo
- instances: translate RowActionMenu and InstanceDetailPage strings
- locales: add graphs, console, workspace, instance tab keys to en/ru
2026-04-07 00:46:04 +02:00
markmorado
32acd74b38 fix: LXC vncproxy - generate-password unsupported, use ticket as VNCAuth password 2026-03-27 21:10:49 +01:00
markmorado
c189dab5e9 fix: VNC auth - add generate-password:1 to vncproxy, fallback to ticket
Root cause of 'Connection closed' / VNCAuth failure:
- vncproxy was called without generate-password=1
- vnc_data.password was None → noVNC sent empty DES-encrypted challenge → Proxmox rejected

Fix:
- Add generate-password=1 to both qemu and lxc vncproxy requests
- If Proxmox returns password field (Proxmox 8.x VNCAuth): use it
- If not (Proxmox 9.x NoAuth): fall back to ticket as password
- Add diagnostic logging (password_len, has_generate_password)
2026-03-27 21:08:40 +01:00
markmorado
cd48c839f8 fix: VNC - pass URL directly to noVNC instead of existing WebSocket
Remove the 'onFirstMessage' hack where backend sent VNC password as
first text WebSocket message and frontend passed the already-open WS
to noVNC's RFBClass. This caused RFB handshake failures because noVNC
couldn't properly manage a WebSocket it didn't create.

Changes:
- Backend: remove send_text(password) before proxy start
- Backend: improve proxy error logging from debug to info level
- Frontend (virtual_machines.html, instance_detail.html): pass WebSocket
  URL string to RFBClass constructor - noVNC creates and manages its own
  WebSocket connection, allowing proper RFB protocol handshake
2026-03-27 20:57:04 +01:00
markmorado
4ef79112be fix: wait for Proxmox resize task before VM start (v1.2.0)
- configure_vm: capture resize UPID and call wait_for_task(120s)
  before returning; prevents lock conflict when VM is started right
  after disk resize
- resize_vm_disk / resize_container_disk (x2): same fix applied
- remove unnecessary restart_vm() after manual QEMU disk resize —
  resize is a storage-layer operation and does not require a reboot;
  the auto-restart was causing a second lock conflict

Fixes: can't lock file '/var/lock/qemu-server/lock-xxx.conf' - got timeout
Fixes: command '/usr/bin/qemu-img resize' failed: got timeout
2026-03-16 20:23:06 +01:00
markmorado
bd1777f67b fix: always send first VNC password message (empty for NoAuth in Proxmox 9.x) 2026-03-16 18:42:07 +01:00
markmorado
7940efc958 fix: remove generate-password from vncproxy (not supported in Proxmox 9.x) 2026-03-16 18:31:58 +01:00
markmorado
57575d58ce feat: v1.1.8 — user-server assignment, VM/LXC ownership, smart login redirect, RBAC migrations
- Migration 20: user_servers association table (User ↔ ProxmoxServer)
- API: GET/PUT /api/users/{id}/servers — assign servers to users with workspace-conflict validation
- API: GET /api/users/{id}/server-assignments — enriched server list (assigned + compatible flags)
- Non-admin server filter: intersection of assigned_servers and workspace servers
- Migration 21: dashboard:view = false for user role (new key format)
- Migration 22: proxmox.cluster.manage → cluster:manage for admin role
- API: GET/PUT /api/{server_id}/vm/{vmid}/owner — VM/LXC owner assignment (admin-only)
- Login: smart redirect based on user permissions after authentication
- Login: returnUrl param support for post-login redirect
- Roles API: user_count field per role in GET /api/roles
- node_uptime added to server detail API response
- Remove redundant PermissionChecker from GET /users HTML route
2026-03-16 08:27:36 +01:00
markmorado
4940cbe77f v1.1.6: fix LXC xterm.js terminal (termproxy auth handshake)
- Add mandatory Proxmox termproxy auth: send 'USERNAME:VNCTICKET\n' as
  first WebSocket message, wait for 'OK' before starting PTY proxy
- Fix server.username → server.api_user (token suffix stripped)
- Fix CSRFPreventionToken sent in body → header only
- Fix additional_headers → extra_headers (websockets v12)
- Add URL-encoding for vncticket, PVEAuthCookie WS header, subprotocols=['binary']
- Forward initial terminal data arriving with OK frame
- Bump VERSION 1.1.5 → 1.1.6
2026-03-16 03:07:14 +01:00
markmorado
5a67981f9a feat: v1.1.5 — UPID task tracking, Task Drawer clear button, auto-expire, pve CLI fix 2026-03-15 20:34:17 +01:00
markmorado
bef0bcc542 feat: add Workspace feature — scoped server groups with user access and sidebar selector 2026-03-15 01:00:24 +01:00
markmorado
3947dae7b9 feat: v1.1.3 — dashboard alerts panel, sparkline charts, log sanitisation, CSS vars 2026-03-10 14:10:00 +01:00
markmorado
b3db346cfb feat: v1.1.0 — backup module, IPAM node binding, HA, task queue, theme CSS
- Backup module: full CRUD for storages, backup files, scheduled jobs (APScheduler)
- Proxmox client: storage/backup/restore/HA methods
- IPAM: proxmox_node field, server+node binding for networks and allocations
- Task queue service: improvements and new task types
- Monitoring worker: extended metrics and alerts
- HA support: add/remove VMs from Proxmox HA groups
- tasks.html: new task tracking page
- proxmox_vms.html: major UI update (bulk ops, HA, snapshots)
- backups.html: full backup management UI (3 tabs)
- base.html: sidebar backup link, websocket manager
- theme.css: full CSS variables theme, 395 lines added
- migrations: migration #15 (BackupJob), #16 (proxmox_node)
- RBAC: backup resource permissions
- websocket_manager.py: new module for WS connections
2026-03-08 18:29:29 +01:00
markmorado
c07d9e8711 fix: deduplicate dashboard node charts by cluster, not just by node name
Previously seenNodes used only the node name as the dedup key, causing
independent servers that happen to share a default node name (e.g. "pve")
to hide each other's charts.

Fix: /api/{server_id}/nodes now also returns cluster_id — the actual
Proxmox cluster name for clustered servers, or "standalone-{server_id}"
for single-node servers. The dashboard deduplicates by
"cluster_id::node_name", so:
- nodes in the same cluster → deduped correctly (shown once)
- nodes on independent servers with the same name → both shown

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-08 02:37:18 +01:00
markmorado
8c5a896683 fix: correct relative imports in proxmox subpackage and CSP headers
- Fix all `from ..` → `from ...` in app/api/proxmox/*.py after the
  proxmox.py split (PR #6) placed modules one level deeper, causing
  ModuleNotFoundError on startup
- Broaden Content-Security-Policy to allow CDN sources used in templates:
  cdnjs.cloudflare.com (Font Awesome), cdn.jsdelivr.net (Bootstrap,
  Chart.js, xterm), fonts.googleapis.com/gstatic.com (Google Fonts),
  esm.sh (noVNC) — the overly-strict CSP was blocking all icons and
  charts from loading in the browser

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-08 01:56:08 +01:00
Claude
6fb7406d27 refactor: split proxmox.py (4676 lines) into package with 7 modules
- _helpers.py: shared helpers (check_vm_access, _get_proxmox_client, etc.)
- servers.py: HTML pages, server CRUD, resources, node status (16 routes)
- sdn.py: SDN zones/vnets/subnets (10 routes)
- snapshots.py: VM/container snapshots + archives (10 routes)
- vms.py: VM/LXC control, HA, monitoring, VNC, terminal, bulk ops (42 routes)
- tasks.py: Proxmox task management (6 routes)
- __init__.py: re-exports unified router (84 routes total, no public API change)

https://claude.ai/code/session_019rK2wvaWg5zXFwmAPPEB7R
2026-03-07 17:35:17 +00:00