docs: release notes and version bump for v1.15.0
Some checks failed
Deploy to Production / Trigger production update (push) Failing after 10s

This commit is contained in:
2026-08-13 15:41:27 +05:00
parent 8337539e9e
commit a7fda196b5
7 changed files with 187 additions and 13 deletions

View File

@@ -4,6 +4,45 @@ All notable changes to PVEmanager will be documented in this file.
--- ---
## [v1.15.0] - 2026-08-13
### 🚀 One-Command Bootstrap
- **`bootstrap.sh`** — installs the panel with a single command on either a plain Debian/Ubuntu host or directly on a Proxmox VE host. On a PVE host it provisions a Debian 12 LXC, installs the panel inside it via the existing `deploy.sh`, creates a local `root@pam` API token and registers that host as the panel's first node — no Proxmox password is ever transmitted
- Tolerates `apt-get update` failures from an unsubscribed PVE host's enterprise repo instead of aborting the whole bootstrap before `git`/`curl` are installed
- Registers the host at the correct `/proxmox/api/servers` path (the previous `/api/servers` 404'd, since the Proxmox router is mounted under `/proxmox`)
- Removed the insecure `pvemanager_secure_password` fallback for `POSTGRES_PASSWORD` in `compose.yml` — every deploy path already generates a real `.env` before the stack starts
### 💿 Images & Instances
- **ISO eject/mount rework** — the Images page now shows real ISO files already present on node storage (including ones that predate the node joining the panel); "Detach" is renamed **Eject**, the current image is shown inside the select, and a new **"boot from disk"** / **"boot from this ISO"** checkbox controls boot order on eject/mount
- Applying a new boot order reboots the guest via a **hybrid restart** — graceful ACPI shutdown with a forceStop fallback, then start — avoiding the guest-ping timeout seen on a freshly installed OS with no QEMU Guest Agent yet
### 🔒 RBAC & Access
- **`/profile` split from `/settings`** — account settings (profile, password, 2FA, SSH keys) moved to their own permission-free `/profile` page reached from the user menu and Ctrl+K, so revoking `setting:view` to keep a user out of panel settings no longer locks them out of their own account. `/settings` keeps the panel-wide tabs behind `setting:view`, with saving behind `setting:update`, security behind `setting:manage`, and SMTP/Telegram behind admin-only
- **Routes are now guarded by permission**, not just hidden from the sidebar — `/users`, `/logs` and `/settings` (and their tabs) previously rendered for anyone who typed the URL and let the backend answer with 403s; denied users are now redirected to the first page they can open
- Migration 42 drops `setting:view` from roles that don't also hold `setting:update`/`setting:manage`, since those grants only existed to reach the old combined settings page
### 👤 Ownership & Quotas
- **Owner assignment fixed at creation time** — newly created VMs/LXCs were silently ending up with `owner_id = NULL` because the sync job that registers a still-cloning VM beats the deploy flow to the database row; owner is now set on that update path too. A VM created by an admin on behalf of a user now also injects the **user's** SSH key instead of the admin's
- **Backfill migration** for instances created before the fix above, recovered from `deploy_tasks` and `audit_logs` and applied only when every piece of evidence agrees on the same existing user; ambiguous instances are left ownerless for `assign_instances_owner.py` to resolve by hand
- **Create Instance is disabled once a quota has no headroom left**, instead of only refusing inside the wizard — the button shows a tooltip naming which limits are full (skipped for admins, whose own quota doesn't apply to instances they create for someone else)
- The pre-wizard quota block is now distinct from the in-wizard one: no headroom at all keeps the wizard from opening; headroom that a chosen configuration would exceed opens the wizard but disables Next/Deploy with the offending metric named
### 🐛 Bug Fixes
- **Production deployments were bypassing TLS** — `compose.prod.yml`'s `ports: []` didn't actually drop the published `8000`/`3001` ports (Compose concatenates list-valued fields across `-f` files instead of overriding), so an SSL deployment kept answering unencrypted on both ports alongside HTTPS. Fixed with the `!reset` tag
- **SSL deployments reported "Failed to obtain SSL certificate" after successfully issuing one** — the success check couldn't read certbot's root-owned, mode-0700 output directory as a non-root user; the check now verifies the certificate from inside the container and falls back to HTTP only if nginx actually fails to come up with the new config
- **The frontend bundle's cold load could render a blank page** — the general request-rate limit (`10r/s`) applied to static assets too and started rejecting the SPA's ~86 preloaded chunks with 503s; and once that was fixed, `limit_conn addr 10` capped the whole page to 10 concurrent HTTP/2 streams (a browser multiplexes a page's entire load over one connection), producing the same blank page from a different limiter. Static assets now have their own rate-limit zone, and both `limit_conn` caps are raised
- **nginx workers were unable to open enough file descriptors** to match `worker_connections 2048` (Docker's default `RLIMIT_NOFILE` of 1024 quietly halved the real ceiling); raised to 8192
- Dropped `listen 443 ssl http2` (deprecated since nginx 1.25.1) and `ssl_stapling` (no OCSP responder in current Let's Encrypt certs) — both warned on nginx's periodic reload
- **Node uptime showed the wrong value on multi-node clusters** — the server card took `Math.max` over every cluster node's uptime instead of the current node's own, so rebooting one node could still show another node's higher uptime
- **The terminated-sessions toast never translated** — the message was assembled server-side in Python and returned verbatim, so the Russian UI showed "Terminated 0 sessions"; the count is now returned separately and the phrase is composed client-side (Russian needs four plural forms against English's two)
---
## [v1.14.0] - 2026-07-24 ## [v1.14.0] - 2026-07-24
### 🐛 Bug Fixes ### 🐛 Bug Fixes

View File

@@ -4,6 +4,45 @@
--- ---
## [v1.15.0] - 2026-08-13
### 🚀 Установка одной командой
- **`bootstrap.sh`** — устанавливает панель одной командой как на обычном хосте Debian/Ubuntu, так и напрямую на хосте Proxmox VE. На хосте PVE скрипт создаёт LXC на Debian 12, устанавливает панель внутри через существующий `deploy.sh`, создаёт локальный API-токен `root@pam` и регистрирует этот хост как первую ноду панели — пароль от Proxmox никуда не передаётся
- Скрипт переживает сбой `apt-get update` из-за недоступного enterprise-репозитория на неподписанном хосте PVE вместо того, чтобы прерывать весь bootstrap ещё до установки `git`/`curl`
- Регистрация хоста теперь идёт по правильному пути `/proxmox/api/servers` (раньше использовался `/api/servers`, отвечавший 404, — роутер Proxmox подключён под префиксом `/proxmox`)
- Убран небезопасный дефолт `pvemanager_secure_password` для `POSTGRES_PASSWORD` в `compose.yml` — на любом пути установки `.env` с реальным паролем уже создаётся до запуска стека
### 💿 Образы и инстансы
- **Переработка извлечения/монтирования ISO** — страница «Образы» теперь показывает реальные ISO-файлы, уже лежащие в хранилище ноды (включая те, что появились там до подключения ноды к панели); «Отсоединить» переименовано в **«Извлечь»**, текущий смонтированный образ отображается прямо в списке выбора, а новый чекбокс **«загрузка с диска»** / **«загрузка с этого ISO»** управляет порядком загрузки при извлечении/монтировании
- Применение нового порядка загрузки перезагружает гостевую систему через **гибридный рестарт** — штатное ACPI-выключение с откатом на принудительную остановку, затем запуск — что убирает таймаут ping гостя на свежеустановленной ОС, где ещё нет QEMU Guest Agent
### 🔒 RBAC и доступ
- **`/profile` отделён от `/settings`** — настройки аккаунта (профиль, пароль, 2FA, SSH-ключи) переехали на собственную страницу `/profile`, не требующую никакого разрешения, и открываются из меню пользователя и Ctrl+K. Теперь отзыв `setting:view`, чтобы не пускать пользователя в настройки панели, больше не блокирует доступ к его собственному аккаунту. `/settings` по-прежнему скрывает общепанельные вкладки за `setting:view`, сохранение — за `setting:update`, безопасность — за `setting:manage`, а SMTP/Telegram — только для админов
- **Маршруты теперь защищены по правам**, а не просто скрыты из сайдбара — раньше `/users`, `/logs` и `/settings` (и их вкладки) открывались по прямому переходу на URL кому угодно, а бэкенд лишь отвечал 403 на конкретные запросы; теперь пользователь без доступа перенаправляется на первую доступную ему страницу
- Миграция 42 снимает `setting:view` с ролей, у которых нет также `setting:update`/`setting:manage`, — этот доступ раньше выдавался только ради общей страницы настроек, включавшей профиль
### 👤 Владение инстансами и квоты
- **Исправлено назначение владельца при создании** — новые VM/LXC оставались с `owner_id = NULL`, потому что задача синхронизации, регистрирующая ещё клонирующуюся VM, опережала флоу деплоя и первой создавала запись в БД; теперь владелец проставляется и на этом пути обновления записи. VM, созданная админом от имени пользователя, теперь получает SSH-ключ именно **этого пользователя**, а не админа
- **Миграция для бэкфилла** владельца у инстансов, созданных до исправления выше: владелец восстанавливается из `deploy_tasks` и `audit_logs` и проставляется только тогда, когда все доступные данные указывают на одного и того же существующего пользователя; неоднозначные случаи остаются без владельца — для них нужен ручной запуск `assign_instances_owner.py`
- **Кнопка «Создать инстанс» отключается**, когда по какой-то квоте совсем не осталось свободного места, а не только отказывает внутри мастера — подсказка называет, какие именно лимиты исчерпаны (проверка пропускается для админов, чья личная квота не имеет отношения к инстансам, создаваемым для других)
- Блокировка перед открытием мастера теперь отделена от блокировки внутри него: полностью исчерпанная квота не даёт открыть мастер вовсе; квота, которую превысит именно выбранная конфигурация, открывает мастер, но блокирует «Далее»/«Развернуть» с указанием превышенного показателя
### 🐛 Исправления
- **Продакшн-деплой не отключал открытые порты** — `ports: []` в `compose.prod.yml` на деле ничего не убирал (Compose при слиянии нескольких `-f` файлов не заменяет списковые поля, а объединяет их), поэтому SSL-деплой продолжал отвечать без шифрования на портах `8000`/`3001` параллельно с HTTPS. Исправлено через тег `!reset`
- **SSL-деплой сообщал о неудаче даже после успешного выпуска сертификата** — проверка успеха не могла прочитать директорию certbot, принадлежащую root и имеющую права 0700, если деплой запускался не от root; теперь сертификат проверяется изнутри контейнера, а откат на HTTP выполняется только если nginx реально не поднялся с новым конфигом
- **Первая загрузка фронтенда могла показать пустую страницу** — общий лимит частоты запросов (`10r/s`) применялся и к статике, отклоняя часть из ~86 подгружаемых чанков SPA с 503; после исправления этого проявился второй лимитер — `limit_conn addr 10` ограничивал всю страницу 10 одновременными HTTP/2-потоками (браузер мультиплексирует всю загрузку страницы через одно соединение), давая тот же эффект пустой страницы. Теперь у статики отдельная зона лимита частоты, а оба лимита `limit_conn` подняты
- **Воркеры nginx не могли открыть достаточно файловых дескрипторов** под `worker_connections 2048` — дефолтный `RLIMIT_NOFILE` Docker в 1024 незаметно урезал реальный потолок вдвое; поднят до 8192
- Убраны `listen 443 ssl http2` (устарела с nginx 1.25.1) и `ssl_stapling` (у текущих сертификатов Let's Encrypt нет OCSP-респондера) — обе директивы выдавали предупреждения при периодическом reload nginx
- **Аптайм ноды показывался неверно в мультинодовых кластерах** — карточка сервера брала `Math.max` по аптайму всех нод кластера вместо аптайма именно текущей, поэтому перезагрузка одной ноды могла по-прежнему показывать более высокий аптайм другой
- **Тост о завершении сессий никогда не переводился** — сообщение собиралось на бэкенде на Python и возвращалось как есть, поэтому в русском интерфейсе показывалось "Terminated 0 sessions"; теперь количество возвращается отдельным полем, а фраза собирается на клиенте (для русского нужно четыре формы множественного числа против двух в английском)
---
## [v1.14.0] - 2026-07-24 ## [v1.14.0] - 2026-07-24
### 🐛 Исправления ### 🐛 Исправления

View File

@@ -4,7 +4,7 @@
**Modern web panel for managing Proxmox servers, virtual machines and LXC containers** **Modern web panel for managing Proxmox servers, virtual machines and LXC containers**
[![Version](https://img.shields.io/badge/version-1.13.0-blue?style=flat-square)](CHANGELOG.md) [![Version](https://img.shields.io/badge/version-1.15.0-blue?style=flat-square)](CHANGELOG.md)
[![License](https://img.shields.io/badge/license-MIT-green?style=flat-square)](LICENSE) [![License](https://img.shields.io/badge/license-MIT-green?style=flat-square)](LICENSE)
[![Docker](https://img.shields.io/badge/docker-ready-2496ED?style=flat-square&logo=docker&logoColor=white)](compose.yml) [![Docker](https://img.shields.io/badge/docker-ready-2496ED?style=flat-square&logo=docker&logoColor=white)](compose.yml)
[![Python](https://img.shields.io/badge/python-3.12-3776AB?style=flat-square&logo=python&logoColor=white)](backend/) [![Python](https://img.shields.io/badge/python-3.12-3776AB?style=flat-square&logo=python&logoColor=white)](backend/)
@@ -28,6 +28,7 @@ PVEmanager is a self-hosted web panel that provides a unified interface for mana
- **Snapshot management** — create, rollback, delete snapshots with an async queue system - **Snapshot management** — create, rollback, delete snapshots with an async queue system
- **OS templates** — deploy VMs from templates; cross-node replication handled automatically - **OS templates** — deploy VMs from templates; cross-node replication handled automatically
- **Cloud image catalog** — browse, download, and convert OS images from cloud repositories (default Proxmox mirrors and custom sources); auto-convert to VM templates with architecture-aware filtering - **Cloud image catalog** — browse, download, and convert OS images from cloud repositories (default Proxmox mirrors and custom sources); auto-convert to VM templates with architecture-aware filtering
- **ISO eject/mount** — attach or eject an ISO on a running VM, with a boot-order switch (boot from disk or from the ISO) applied via a graceful ACPI-then-forceStop hybrid reboot
- **CT template deployment** — create LXC containers directly from Proxmox CT template files (`.tar.zst`, `.tar.gz`) with full IPAM and SSH key integration - **CT template deployment** — create LXC containers directly from Proxmox CT template files (`.tar.zst`, `.tar.gz`) with full IPAM and SSH key integration
- **Unified Create Instance Wizard** — multi-step wizard supporting both QEMU VM templates and LXC CT templates in a single flow - **Unified Create Instance Wizard** — multi-step wizard supporting both QEMU VM templates and LXC CT templates in a single flow
- **SDN support** — manage Software-Defined Networking zones, VNets, and subnets; edit zones and VNets; auto-create IPAM networks for subnets - **SDN support** — manage Software-Defined Networking zones, VNets, and subnets; edit zones and VNets; auto-create IPAM networks for subnets
@@ -47,10 +48,11 @@ PVEmanager is a self-hosted web panel that provides a unified interface for mana
- **xterm.js terminal** — full interactive shell directly in the browser for LXC containers - **xterm.js terminal** — full interactive shell directly in the browser for LXC containers
- **Remote commands** — execute bash commands via QEMU Guest Agent - **Remote commands** — execute bash commands via QEMU Guest Agent
- **SSH Keys Management** — personal SSH key library per user; keys selectable when deploying VMs/LXC; admin can manage keys for any user - **SSH Keys Management** — personal SSH key library per user; keys selectable when deploying VMs/LXC; admin can manage keys for any user
- **RBAC v2** — granular role-based access control with per-resource permissions - **RBAC v2** — granular role-based access control with per-resource permissions; routes are guarded server- and client-side, not just hidden from the sidebar
- **Permission-free profile page** — account settings (profile, password, 2FA, SSH keys) live at `/profile` and never require a panel-settings permission, so restricting `setting:view` can't lock a user out of their own account
- **Server assignment** — directly assign specific Proxmox servers to users with workspace-conflict validation - **Server assignment** — directly assign specific Proxmox servers to users with workspace-conflict validation
- **VM/LXC ownership** — assign an owner user to any VM or LXC container; non-privileged users see only their own instances everywhere, including the dashboard and per-node resource views - **VM/LXC ownership** — assign an owner user to any VM or LXC container; non-privileged users see only their own instances everywhere, including the dashboard and per-node resource views; owner is set automatically on create, with a migration to backfill pre-existing instances
- **Per-user quotas** — limit each user's number of instances and total vCPU, RAM and disk; enforced at deploy time (HTTP 429), with admin management and self-service usage view - **Per-user quotas** — limit each user's number of instances and total vCPU, RAM and disk; enforced at deploy time (HTTP 429); the Create Instance entry point itself is disabled once a quota has no headroom left, naming which limit is full
- **Session management** — active session list, remote session revocation, login protection - **Session management** — active session list, remote session revocation, login protection
- **Smart login redirect** — users are automatically routed to the first page they have access to - **Smart login redirect** — users are automatically routed to the first page they have access to
@@ -91,6 +93,16 @@ docker compose up -d
> **Important:** Change the default password immediately after first login. > **Important:** Change the default password immediately after first login.
### One-command bootstrap
`bootstrap.sh` installs the panel with a single command, on either a plain Debian/Ubuntu host or directly on a Proxmox VE host — on a PVE host it provisions a Debian 12 LXC, installs the panel inside it, and registers that host as the panel's first node automatically (no Proxmox password is ever transmitted):
```bash
git clone https://git.tzim.uz/markmorado/pvemanager.git
cd pvemanager
bash bootstrap.sh
```
--- ---
## Requirements ## Requirements

View File

@@ -4,7 +4,7 @@
**Современная веб-панель для управления серверами Proxmox, виртуальными машинами и контейнерами LXC** **Современная веб-панель для управления серверами Proxmox, виртуальными машинами и контейнерами LXC**
[![Версия](https://img.shields.io/badge/версия-1.13.0-blue?style=flat-square)](CHANGELOG.md) [![Версия](https://img.shields.io/badge/версия-1.15.0-blue?style=flat-square)](CHANGELOG.md)
[![Лицензия](https://img.shields.io/badge/лицензия-MIT-green?style=flat-square)](LICENSE) [![Лицензия](https://img.shields.io/badge/лицензия-MIT-green?style=flat-square)](LICENSE)
[![Docker](https://img.shields.io/badge/docker-готов-2496ED?style=flat-square&logo=docker&logoColor=white)](compose.yml) [![Docker](https://img.shields.io/badge/docker-готов-2496ED?style=flat-square&logo=docker&logoColor=white)](compose.yml)
[![Python](https://img.shields.io/badge/python-3.12-3776AB?style=flat-square&logo=python&logoColor=white)](backend/) [![Python](https://img.shields.io/badge/python-3.12-3776AB?style=flat-square&logo=python&logoColor=white)](backend/)
@@ -28,6 +28,7 @@ PVEmanager — это самостоятельно размещаемая веб
- **Управление снимками** — создавайте, откатывайте, удаляйте снимки с помощью асинхронной системы очередей - **Управление снимками** — создавайте, откатывайте, удаляйте снимки с помощью асинхронной системы очередей
- **Шаблоны ОС** — развертывайте ВМ из шаблонов; автоматическая репликация между узлами - **Шаблоны ОС** — развертывайте ВМ из шаблонов; автоматическая репликация между узлами
- **Каталог облачных образов** — просмотр и скачивание образов ОС из облачных репозиториев (стандартные зеркала Proxmox и кастомные источники); автоматическая конвертация в шаблоны VM с фильтрацией по архитектуре - **Каталог облачных образов** — просмотр и скачивание образов ОС из облачных репозиториев (стандартные зеркала Proxmox и кастомные источники); автоматическая конвертация в шаблоны VM с фильтрацией по архитектуре
- **Извлечение/монтирование ISO** — подключайте или извлекайте ISO на работающей ВМ, с переключателем порядка загрузки (с диска или с ISO), применяемым через гибридную перезагрузку — штатное ACPI-выключение с откатом на принудительную остановку
- **Развёртывание CT-шаблонов** — создавайте LXC-контейнеры напрямую из CT-файлов Proxmox (`.tar.zst`, `.tar.gz`) с интеграцией IPAM и SSH-ключей - **Развёртывание CT-шаблонов** — создавайте LXC-контейнеры напрямую из CT-файлов Proxmox (`.tar.zst`, `.tar.gz`) с интеграцией IPAM и SSH-ключей
- **Единый мастер создания инстансов** — многошаговый мастер для создания как VM-шаблонов QEMU, так и LXC CT-шаблонов - **Единый мастер создания инстансов** — многошаговый мастер для создания как VM-шаблонов QEMU, так и LXC CT-шаблонов
- **Поддержка SDN** — управляйте зонами Software-Defined Networking, VNet и подсетями; редактируйте зоны и VNet; автоматическое создание сетей IPAM для подсетей - **Поддержка SDN** — управляйте зонами Software-Defined Networking, VNet и подсетями; редактируйте зоны и VNet; автоматическое создание сетей IPAM для подсетей
@@ -45,10 +46,13 @@ PVEmanager — это самостоятельно размещаемая веб
### Доступ и консоль ### Доступ и консоль
- **Консоль VNC** — консоль в браузере через noVNC, не требуется клиентское ПО - **Консоль VNC** — консоль в браузере через noVNC, не требуется клиентское ПО
- **Терминал xterm.js** — полностью интерактивный shell непосредственно в браузере для контейнеров LXC - **Терминал xterm.js** — полностью интерактивный shell непосредственно в браузере для контейнеров LXC
- **Удаленные команды** — выполняйте команды bash через QEMU Guest Agent- **Управление SSH-ключами** — личная библиотека SSH-ключей для каждого пользователя; ключи можно выбирать при развёртывании VM/LXC; админ может управлять ключами любого пользователя- **RBAC v2** — детальный контроль доступа на основе ролей с разрешениями для каждого ресурса - **Удаленные команды** — выполняйте команды bash через QEMU Guest Agent
- **Управление SSH-ключами** — личная библиотека SSH-ключей для каждого пользователя; ключи можно выбирать при развёртывании VM/LXC; админ может управлять ключами любого пользователя
- **RBAC v2** — детальный контроль доступа на основе ролей с разрешениями для каждого ресурса; маршруты защищены и на бэкенде, и на фронтенде, а не просто скрыты из сайдбара
- **Страница профиля без разрешений** — настройки аккаунта (профиль, пароль, 2FA, SSH-ключи) находятся на странице `/profile` и не требуют никакого разрешения на настройки панели, так что ограничение `setting:view` не может заблокировать пользователю доступ к собственному аккаунту
- **Назначение серверов** — напрямую назначайте конкретные серверы Proxmox пользователям с проверкой конфликтов рабочих областей - **Назначение серверов** — напрямую назначайте конкретные серверы Proxmox пользователям с проверкой конфликтов рабочих областей
- **Владелец ВМ/LXC** — назначайте пользователя-владельца любой ВМ или контейнеру LXC; непривилегированные пользователи везде видят только свои инстансы, включая дашборд и ресурсы по ноде - **Владелец ВМ/LXC** — назначайте пользователя-владельца любой ВМ или контейнеру LXC; непривилегированные пользователи везде видят только свои инстансы, включая дашборд и ресурсы по ноде; владелец теперь проставляется автоматически при создании, а для уже существующих инстансов есть миграция для бэкфилла
- **Квоты на пользователя** — ограничение количества инстансов и суммарных vCPU, RAM и диска для каждого пользователя; проверка при развёртывании (HTTP 429), управление администратором и просмотр собственного использования - **Квоты на пользователя** — ограничение количества инстансов и суммарных vCPU, RAM и диска для каждого пользователя; проверка при развёртывании (HTTP 429); сама кнопка «Создать инстанс» отключается, когда квота исчерпана, с указанием какого именно лимита не хватает
- **Управление сессиями** — список активных сессий, отзыв удаленных сессий, защита входа - **Управление сессиями** — список активных сессий, отзыв удаленных сессий, защита входа
- **Умное перенаправление входа** — пользователи автоматически маршрутизируются на первую страницу, к которой у них есть доступ - **Умное перенаправление входа** — пользователи автоматически маршрутизируются на первую страницу, к которой у них есть доступ
@@ -89,6 +93,16 @@ docker compose up -d
> **Важно:** Измените пароль по умолчанию сразу после первого входа. > **Важно:** Измените пароль по умолчанию сразу после первого входа.
### Установка одной командой
`bootstrap.sh` устанавливает панель одной командой — как на обычном хосте Debian/Ubuntu, так и напрямую на хосте Proxmox VE: на хосте PVE скрипт сам создаёт LXC на Debian 12, устанавливает панель внутри и регистрирует этот хост как первую ноду панели (пароль от Proxmox никуда не передаётся):
```bash
git clone https://git.tzim.uz/markmorado/pvemanager.git
cd pvemanager
bash bootstrap.sh
```
--- ---
## Требования ## Требования

View File

@@ -1 +1 @@
1.14.0 1.15.0

39
WIKI.md
View File

@@ -102,6 +102,21 @@ docker compose up -d
# Specify domain and email for Let's Encrypt # Specify domain and email for Let's Encrypt
``` ```
#### 4. One-command bootstrap (`bootstrap.sh`)
A single script that installs the panel on either a plain Debian/Ubuntu host or directly on a Proxmox VE host — it detects which by checking for `/etc/pve`:
```bash
git clone https://git.tzim.uz/markmorado/pvemanager.git
cd pvemanager
bash bootstrap.sh
```
- **Plain Debian/Ubuntu host** — installs Docker and `git`/`curl`/`openssl`/`jq` if missing, clones the repo into `PVEMANAGER_DIR` (default `/opt/pvemanager`) and hands off to `deploy.sh --standalone`.
- **Proxmox VE host** — creates a Debian 12 LXC (DHCP on `vmbr0`, `nesting=1,keyctl=1` for Docker inside), installs the panel inside it via the same flow, then — once the panel answers `GET /health` — creates a `root@pam!pvemanager` API token locally on the PVE host (`pvesh create ... --privsep 0`) and registers that host as the panel's first node. **No Proxmox password is ever transmitted** to or through the panel.
- Configuration is via environment variables (`PVEMANAGER_REPO`, `PVEMANAGER_VERSION`, `PVEMANAGER_DIR`, and `PVEMANAGER_LXC_*` for the container's vmid/storage/bridge/resources) — see the header of `bootstrap.sh` for the full list and defaults.
- Tolerates `apt-get update` failures from an unsubscribed PVE host's enterprise repo instead of aborting before `git`/`curl` are installed.
### Environment Variables ### Environment Variables
#### Main (`.env`) #### Main (`.env`)
@@ -702,6 +717,15 @@ The **Images** module allows you to browse, download, and manage OS images from
The create-instance wizard offers a **blank VM** kind for ISO-based installs: it provisions an empty disk with the chosen ISO mounted on `ide2` and set first in the boot order, so the VM boots straight into the installer over the built-in noVNC console. Selecting a Windows guest automatically presets OVMF firmware, a SATA disk and an e1000 NIC, since the installer has no virtio drivers loaded yet. The create-instance wizard offers a **blank VM** kind for ISO-based installs: it provisions an empty disk with the chosen ISO mounted on `ide2` and set first in the boot order, so the VM boots straight into the installer over the built-in noVNC console. Selecting a Windows guest automatically presets OVMF firmware, a SATA disk and an e1000 NIC, since the installer has no virtio drivers loaded yet.
### Ejecting and Mounting an ISO
From a VM's detail page, the ISO dialog shows every ISO already on the node's storage — including ones that predate the node joining the panel — and the image currently mounted, if any:
- **Mount** — pick an ISO from storage; optionally check **"boot from this ISO"** to put the CD-ROM drive first in the boot order.
- **Eject** (renamed from "Detach") — remove the mounted ISO; optionally check **"boot from disk"** to put the primary disk first in the boot order.
Either action that changes boot order applies it via a **hybrid reboot**: a graceful ACPI shutdown with a `forceStop` fallback, then start. This avoids the guest-ping timeout that a plain restart hits on a freshly installed OS with no QEMU Guest Agent running yet.
### Custom Mirrors (Admin Only) ### Custom Mirrors (Admin Only)
Administrators can configure custom image repositories: Administrators can configure custom image repositories:
@@ -2226,7 +2250,9 @@ Any VM or LXC container can have an **owner** — a regular user responsible for
Ownership is enforced everywhere a non-privileged user sees instances, not just the instance list. The live-resource endpoints — `/api/resources/all` (dashboard) and `/api/{server_id}/resources` (node/server page) — filter VMs/LXC by owner (matching each `vmid` against the `vm_instances` cache), so a user with only `proxmox.view` cannot see other owners' instances through the node page. **Admins** and roles with **`vm:manage`** are exempt and see everything. Ownership is enforced everywhere a non-privileged user sees instances, not just the instance list. The live-resource endpoints — `/api/resources/all` (dashboard) and `/api/{server_id}/resources` (node/server page) — filter VMs/LXC by owner (matching each `vmid` against the `vm_instances` cache), so a user with only `proxmox.view` cannot see other owners' instances through the node page. **Admins** and roles with **`vm:manage`** are exempt and see everything.
Instances synced from Proxmox before ownership existed have `owner_id = NULL`. To bulk-assign an owner to them, run the helper script shipped in the backend image: **Owner is set automatically on create** (`vm_create`/`ct_create`), including when an admin creates an instance on behalf of another user — the deploy flow injects that owner's SSH key (merged with any pasted/library keys) rather than the creator's.
Instances synced from Proxmox before ownership existed, or created before the fix above, have `owner_id = NULL`. A one-time migration backfills these automatically from `deploy_tasks` and `audit_logs` (`vm_create`/`ct_create`/`lxc_create` entries), but only when every applicable piece of evidence names the same existing user — ambiguous cases (e.g. a reused VMID) are deliberately left ownerless. To assign an owner to whatever is left, run the helper script shipped in the backend image:
```bash ```bash
docker compose exec app python assign_instances_owner.py docker compose exec app python assign_instances_owner.py
@@ -2260,13 +2286,22 @@ Per-user resource quotas cap how much a user can provision. Four metrics are lim
- **Enforcement** — deploying a VM/LXC that would push any metric over its limit is rejected with **HTTP 429** and a bilingual message (`Превышена квота … / Quota exceeded …`) showing the projected value vs. the limit. - **Enforcement** — deploying a VM/LXC that would push any metric over its limit is rejected with **HTTP 429** and a bilingual message (`Превышена квота … / Quota exceeded …`) showing the projected value vs. the limit.
- **Usage source** — current usage is computed from the `vm_instances` cache (`owner_id`), where RAM is stored in MB and disk in GB (no conversion needed). - **Usage source** — current usage is computed from the `vm_instances` cache (`owner_id`), where RAM is stored in MB and disk in GB (no conversion needed).
### Two levels of client-side blocking
The backend's 429 is the only real enforcement, but the UI now warns before a user reaches it, using the same `exhaustedMetrics()` logic (`features/instances/quota.ts`) in both places so the two can't disagree:
- **No headroom at all** (used ≥ limit on any metric) — the **Create Instance** entry point itself is disabled, with a tooltip naming exactly which limits are full. No configuration would fit, so the wizard does not open.
- **Headroom left, but the chosen configuration would exceed it** — the wizard opens normally; from the configuration step onward, the metric that would be exceeded is named and **Next**/**Deploy** are disabled until the request is reduced.
Neither block applies to **admins** — their own quota says nothing about an instance they're creating on behalf of someone else, and the figures shown to them are informational only.
### Managing quotas (Admin) ### Managing quotas (Admin)
**Users** page → quota dialog. Live usage bars show current consumption against each limit. Leave a field empty for *unlimited*. Requires the `quota:manage` permission (`quota:view` to read). **Users** page → quota dialog. Live usage bars show current consumption against each limit. Leave a field empty for *unlimited*. Requires the `quota:manage` permission (`quota:view` to read).
### Self-service ### Self-service
Each user sees their own limits and current usage on the **Settings** page. The **Create Instance** wizard shows the remaining quota as a hint on the configuration step. Each user sees their own limits and current usage on the **Profile** page. The **Create Instance** wizard shows the remaining quota as a hint on the configuration step.
### REST API ### REST API

View File

@@ -101,6 +101,21 @@ docker compose up -d
# Указать домен и email для Let's Encrypt # Указать домен и email для Let's Encrypt
``` ```
#### 4. Установка одной командой (`bootstrap.sh`)
Один скрипт, который устанавливает панель как на обычном хосте Debian/Ubuntu, так и напрямую на хосте Proxmox VE — среда определяется по наличию `/etc/pve`:
```bash
git clone https://git.tzim.uz/markmorado/pvemanager.git
cd pvemanager
bash bootstrap.sh
```
- **Обычный хост Debian/Ubuntu** — при необходимости ставит Docker и `git`/`curl`/`openssl`/`jq`, клонирует репозиторий в `PVEMANAGER_DIR` (по умолчанию `/opt/pvemanager`) и передаёт управление `deploy.sh --standalone`.
- **Хост Proxmox VE** — создаёт LXC на Debian 12 (DHCP на `vmbr0`, `nesting=1,keyctl=1` для Docker внутри), устанавливает панель внутри по тому же сценарию, а после того как панель ответит на `GET /health`, создаёт на самом хосте PVE локальный API-токен `root@pam!pvemanager` (`pvesh create ... --privsep 0`) и регистрирует этот хост как первую ноду панели. **Пароль от Proxmox никуда не передаётся.**
- Настраивается через переменные окружения (`PVEMANAGER_REPO`, `PVEMANAGER_VERSION`, `PVEMANAGER_DIR`, а также `PVEMANAGER_LXC_*` — vmid, хранилище, мост и ресурсы контейнера) — полный список и значения по умолчанию см. в заголовке `bootstrap.sh`.
- Скрипт переживает сбой `apt-get update` из-за недоступного enterprise-репозитория на неподписанном хосте PVE, не прерывая bootstrap до установки `git`/`curl`.
### Переменные окружения ### Переменные окружения
#### Основные (`.env`) #### Основные (`.env`)
@@ -701,6 +716,15 @@ RAM: 512 МБ – 128 ГБ
Мастер создания инстанса предлагает тип **«пустая ВМ»** для установки с ISO: создаётся ВМ с пустым диском и смонтированным на `ide2` образом, который ставится первым в порядке загрузки, — гость сразу попадает в установщик через встроенную консоль noVNC. Выбор гостя Windows автоматически задаёт прошивку OVMF, SATA-диск и сетевую карту e1000, так как у установщика ещё нет драйверов virtio. Мастер создания инстанса предлагает тип **«пустая ВМ»** для установки с ISO: создаётся ВМ с пустым диском и смонтированным на `ide2` образом, который ставится первым в порядке загрузки, — гость сразу попадает в установщик через встроенную консоль noVNC. Выбор гостя Windows автоматически задаёт прошивку OVMF, SATA-диск и сетевую карту e1000, так как у установщика ещё нет драйверов virtio.
### Извлечение и монтирование ISO
На странице ВМ диалог ISO показывает все образы, уже лежащие в хранилище ноды — включая те, что появились там до подключения ноды к панели, — и текущий смонтированный образ, если он есть:
- **Монтировать** — выберите ISO из хранилища; опционально отметьте **«загрузка с этого ISO»**, чтобы поставить CD-ROM первым в порядке загрузки.
- **Извлечь** (переименовано из «Отсоединить») — снимает смонтированный ISO; опционально отметьте **«загрузка с диска»**, чтобы поставить основной диск первым в порядке загрузки.
Любое действие, меняющее порядок загрузки, применяется через **гибридную перезагрузку**: штатное ACPI-выключение с откатом на принудительную остановку, затем запуск. Это убирает таймаут ping гостя, с которым сталкивается обычный рестарт на свежеустановленной ОС, где ещё не запущен QEMU Guest Agent.
### Кастомные зеркала (только админ) ### Кастомные зеркала (только админ)
Администраторы могут настроить кастомные репозитории образов: Администраторы могут настроить кастомные репозитории образов:
@@ -2139,7 +2163,9 @@ curl -H "Authorization: Bearer eyJ..." \
Владение применяется везде, где непривилегированный пользователь видит инстансы, а не только в списке инстансов. Эндпоинты живых ресурсов — `/api/resources/all` (дашборд) и `/api/{server_id}/resources` (страница узла/сервера) — фильтруют ВМ/LXC по владельцу (сопоставляя каждый `vmid` с кэшем `vm_instances`), поэтому пользователь только с `proxmox.view` не может видеть инстансы других владельцев через страницу узла. **Админы** и роли с **`vm:manage`** освобождены от этого и видят всё. Владение применяется везде, где непривилегированный пользователь видит инстансы, а не только в списке инстансов. Эндпоинты живых ресурсов — `/api/resources/all` (дашборд) и `/api/{server_id}/resources` (страница узла/сервера) — фильтруют ВМ/LXC по владельцу (сопоставляя каждый `vmid` с кэшем `vm_instances`), поэтому пользователь только с `proxmox.view` не может видеть инстансы других владельцев через страницу узла. **Админы** и роли с **`vm:manage`** освобождены от этого и видят всё.
Инстансы, синхронизированные из Proxmox до появления владения, имеют `owner_id = NULL`. Чтобы массово назначить им владельца, запустите вспомогательный скрипт из образа бэкенда: **Владелец теперь проставляется автоматически при создании** (`vm_create`/`ct_create`), в том числе когда ВМ создаёт админ от имени другого пользователя — деплой инжектирует SSH-ключ именно этого владельца (объединённый с любыми вставленными вручную или взятыми из библиотеки ключами), а не ключ создавшего.
Инстансы, синхронизированные из Proxmox до появления владения, или созданные до исправления выше, имеют `owner_id = NULL`. Разовая миграция бэкфиллит их автоматически из `deploy_tasks` и `audit_logs` (записи `vm_create`/`ct_create`/`lxc_create`), но только когда все доступные данные указывают на одного и того же существующего пользователя — неоднозначные случаи (например, переиспользованный VMID) намеренно остаются без владельца. Чтобы назначить владельца тому, что осталось, запустите вспомогательный скрипт из образа бэкенда:
```bash ```bash
docker compose exec app python assign_instances_owner.py docker compose exec app python assign_instances_owner.py
@@ -2173,13 +2199,22 @@ docker compose exec app python assign_instances_owner.py
- **Применение** — развёртывание VM/LXC, которое превысит любую метрику, отклоняется с **HTTP 429** и двуязычным сообщением (`Превышена квота … / Quota exceeded …`) с прогнозируемым значением и лимитом. - **Применение** — развёртывание VM/LXC, которое превысит любую метрику, отклоняется с **HTTP 429** и двуязычным сообщением (`Превышена квота … / Quota exceeded …`) с прогнозируемым значением и лимитом.
- **Источник использования** — текущее использование вычисляется из кэша `vm_instances` (`owner_id`), где RAM хранится в МБ, а диск — в ГБ (без конвертации). - **Источник использования** — текущее использование вычисляется из кэша `vm_instances` (`owner_id`), где RAM хранится в МБ, а диск — в ГБ (без конвертации).
### Два уровня блокировки на клиенте
Реально применяет ограничение только backend'ный HTTP 429, но интерфейс теперь предупреждает заранее, используя в обоих местах одну и ту же функцию `exhaustedMetrics()` (`features/instances/quota.ts`), чтобы они не могли разойтись:
- **Свободного места совсем не осталось** (использовано ≥ лимита хотя бы по одной метрике) — сама точка входа **«Создать инстанс»** отключается, а подсказка называет, какие именно лимиты исчерпаны. Ни одна конфигурация не влезет, поэтому мастер даже не открывается.
- **Место есть, но выбранная конфигурация его превысит** — мастер открывается как обычно; начиная с шага конфигурации называется метрика, которая была бы превышена, а **«Далее»**/**«Развернуть»** заблокированы, пока запрос не уменьшат.
Ни одна из блокировок не применяется к **админам** — их личная квота не имеет отношения к инстансу, который они создают для кого-то другого, а показанные им цифры носят чисто информационный характер.
### Управление квотами (админ) ### Управление квотами (админ)
Страница **Пользователи** → диалог квот. Живые полосы использования показывают текущее потребление против каждого лимита. Оставьте поле пустым для *без ограничений*. Требует право `quota:manage` (`quota:view` для чтения). Страница **Пользователи** → диалог квот. Живые полосы использования показывают текущее потребление против каждого лимита. Оставьте поле пустым для *без ограничений*. Требует право `quota:manage` (`quota:view` для чтения).
### Self-service ### Self-service
Каждый пользователь видит свои лимиты и текущее использование на странице **Настройки**. Мастер **создания инстанса** показывает остаток квоты подсказкой на шаге конфигурации. Каждый пользователь видит свои лимиты и текущее использование на странице **Профиль**. Мастер **создания инстанса** показывает остаток квоты подсказкой на шаге конфигурации.
### REST API ### REST API